From: compudj Date: Fri, 24 Oct 2008 01:22:30 +0000 (+0000) Subject: tag lttv 0.11.3-23102008 X-Git-Tag: v0.12.20~368 X-Git-Url: https://git.lttng.org./?a=commitdiff_plain;h=13ab9fcb70625b727b3a959d8cd4fc03938f5dfa;p=lttv.git tag lttv 0.11.3-23102008 git-svn-id: http://ltt.polymtl.ca/svn@3121 04897980-b3bd-0310-b5e0-8ef037075253 --- diff --git a/tags/lttv-0.11.3-23102008/AUTHORS b/tags/lttv-0.11.3-23102008/AUTHORS new file mode 100644 index 00000000..032edf04 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/AUTHORS @@ -0,0 +1,25 @@ +Linux Trace Toolkit Viewer + +Contributors : + +Michel Dagenais (New trace format, lttv main) +Mathieu Desnoyers (Kernel Tracer, Directory structure, build with automake/conf, + lttv gui, control flow view, gui cooperative trace reading + scheduler with interruptible foreground and background + computation, detailed event list (rewrite), trace reading + library (rewrite)) +Benoit Des Ligneris, Éric Clement (Cluster adaptation, work in progress) +Xang-Xiu Yang (trace reading library and converter, lttv gui, + detailed event list and statistics view) +Tom Zanussi (RelayFS) + +Strongly inspired from the original Linux Trace Toolkit Visualizer made by +Karim Yaghmour. + +Linux Trace Toolkit Viewer, Copyright (C) 2004 + Michel Dagenais + Mathieu Desnoyers + Xang-Xiu Yang +Linux Trace Toolkit comes with ABSOLUTELY NO WARRANTY. +This is free software, and you are welcome to redistribute it +under certain conditions. See COPYING for details. diff --git a/tags/lttv-0.11.3-23102008/ChangeLog b/tags/lttv-0.11.3-23102008/ChangeLog new file mode 100644 index 00000000..388ec47e --- /dev/null +++ b/tags/lttv-0.11.3-23102008/ChangeLog @@ -0,0 +1,45 @@ +LinuxTraceToolkit ChangeLog + +11/03/2006 LTTV 0.8.30 + Change statistics tree structure. + Add user space functions support to statistics. +09/03/2006 LTTV 0.8.28 + Add usertrace support. + Bugfix : handle badly number syscall xml correctly. + Add network_ip_interface XML. +05/03/2006 LTTV 0.8.26 + Add mouse scroll support. + Add user_generic.xml facility. +01/03/2006 LTTV 0.8.25 + Fix process end of life in state.c +28/02/2006 LTTV 0.8.24 + Fix heartbeat support +27/02/2006 LTTV 0.8.23 + Add kernel_thread information in state. +15/02/2006 LTTV 0.8.21 + Add support for data with network byte order. +15/02/2006 LTTV 0.8.20 + Fix end of trace NULL pointer problem in debug output of + lttvwindow. +11/02/2006 LTTV 0.8.19 + Copy process names upon fork. + Put statedump to "unknown" mode until ltt-statedump is fixed. +09/02/2006 LTTV 0.8.15 + Fix error handling of filter.c. +08/02/2006 LTTV 0.8.14 + Fix sequence pop in parser.c. +07/02/2006 LTTV 0.8.13 + Add state dump. +07/02/2006 LTTV 0.8.12 + Fix tracontrol timing problem with su. +06/02/2006 LTTV 0.8.11 + Fix GtkTreeView using "vertical-separator" style property. +06/02/2006 LTTV 0.8.10 + Now handle correctly events with a 0xFFFF size (dynamically). + Make lttd multithreaded. +10/01/2006 LTTV 0.8.4 + Add SoftIRQ mode. + Fix state request from GUI detailed event list. + +29/05/2003 Subversion repository preliminary files addition + diff --git a/tags/lttv-0.11.3-23102008/Makefile.am b/tags/lttv-0.11.3-23102008/Makefile.am new file mode 100644 index 00000000..4f542dcf --- /dev/null +++ b/tags/lttv-0.11.3-23102008/Makefile.am @@ -0,0 +1,6 @@ +# WARNING : ltt must come before lttv, so that the traceread library is +# up to date + +SUBDIRS = ltt lttv doc + +EXTRA_DIST = QUICKSTART diff --git a/tags/lttv-0.11.3-23102008/NEWS b/tags/lttv-0.11.3-23102008/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/tags/lttv-0.11.3-23102008/QUICKSTART b/tags/lttv-0.11.3-23102008/QUICKSTART new file mode 100644 index 00000000..eecf9bfe --- /dev/null +++ b/tags/lttv-0.11.3-23102008/QUICKSTART @@ -0,0 +1,412 @@ +Linux Trace Toolkit Quickstart +------------------------------ +Author : Mathieu Desnoyers, September 2005 +Last update : July 31, 2008 + + +This document is made of four parts : the first one explains how to install +LTTng and LTTV from Debian and RPM binary packages, the second one explains how +to install LTTng and LTTV from sources and the third one describes the steps +to follow to trace a system and view it. The fourth and last part explains +briefly how to add a new trace point to the kernel and to user space +applications. + +What you will typically want is to read sections 2 and 3 : install LTTng from +sources and use it. + +These operations are made for installing the LTTng 0.12 tracer on a linux 2.6.X +kernel. You will also find instructions for installation of LTTV 0.8.x : the +Linux Trace Toolkit Viewer. + +To see the list of compatibilities between LTTng, ltt-control, LTTV and +markers-userspace, please refer to : +http://ltt.polymtl.ca > LTTng+LTTV versions compatibility + + + +The following lttng patch is necessary to have the tracing hooks in the kernel. +The following ltt-control module controls the tracing. + +Required programs and libraries are assumed to be automatically installed in an +installation with Debian or RPM packages. In the case of an installation from +sources, the dependencies are listed. + + +** Current development status ** + +LTTng : +supported architectures : +Intel Pentium (UP/SMP) with TSC +PowerPC 32 and 64 bits +ARM +x86_64 +C2 Microsystems (variant of MIPS) + +LTTV : +supported architectures : +Intel i386 and better +Intel 64 bits +PowerPC 32 and 64 bits + + +*********************************************************** +** Section 1 * Installation from Debian or RPM packages ** +*********************************************************** + +* Create custom LTTV Debian packages + +Use : dpkg-buildpackage -rfakeroot + +You should then have your LTTV .deb files created for your architecture. + +* Create custom LTTng packages + +For building LTTng Debian packages : + +Get the build tree with patches applies as explained in section 2. + +make menuconfig (or xconfig or config) (customize your configuration) +make-kpkg kernel_image + +You will then see your freshly created .deb in /usr/src. Install it with +dpkg -i /usr/src/(image-name).deb + +Then, follow the section "Editing the system wide configuration" in section 2. + + +*********************************************************** +** Section 2 * Installation from sources ** +*********************************************************** + +* Prerequisites + +Tools needed to follow the package download steps : + +o wget +o bzip2 +o gzip +o tar + +You have to install the standard development libraries and programs necessary +to compile a kernel : + +(from Documentation/Changes in the Linux kernel tree) +o Gnu C 2.95.3 # gcc --version +o Gnu make 3.79.1 # make --version +o binutils 2.12 # ld -v +o util-linux 2.10o # fdformat --version +o module-init-tools 0.9.10 # depmod -V + +You might also want to have libncurses5 to have the text mode kernel +configuration menu, but there are alternatives. + +Prerequisites for LTTV 0.x.x installation are : + +gcc 3.2 or better +gtk 2.4 or better development libraries + (Debian : libgtk2.0, libgtk2.0-dev) + (Fedora : gtk2, gtk2-devel) + note : For Fedora users : this might require at least core 3 from Fedora, + or you might have to compile your own GTK2 library. +glib 2.4 or better development libraries + (Debian : libglib2.0-0, libglib2.0-dev) + (Fedora : glib2, glib2-devel) +libpopt development libraries + (Debian : libpopt0, libpopt-dev) + (Fedora : popt) +libpango development libraries + (Debian : libpango1.0, libpango1.0-dev) + (Fedora : pango, pango-devel) +libc6 development librairies + (Debian : libc6, libc6-dev) + (Fedora : glibc, glibc) + + +* Getting the LTTng packages + +su - +mkdir /usr/src/lttng +cd /usr/src/lttng +(see http://ltt.polymtl.ca/lttng for package listing) +wget http://ltt.polymtl.ca/lttng/patch-2.6.X-lttng-0.x.xx.tar.bz2 +bzip2 -cd patch-2.6.X-lttng-0.x.xx.tar.bz2 | tar xvof - + + +* Getting LTTng kernel sources + +su - +cd /usr/src +wget http://kernel.org/pub/linux/kernel/v2.6/linux-2.6.X.tar.bz2 +bzip2 -cd linux-2.6.X.tar.bz2 | tar xvof - +cd linux-2.6.X +- For LTTng 0.9.4- cat /usr/src/lttng/patch*-2.6.X-lttng-0.x.xx* | patch -p1 +- For LTTng 0.9.5+ apply the patches in the order specified in the series file, + or use quilt +cd .. +mv linux-2.6.X linux-2.6.X-lttng-0.x.xx + + +* Installing a LTTng kernel + +su - +cd /usr/src/linux-2.6.X-lttng-0.x.xx +make menuconfig (or make xconfig or make config) + Select the < Help > button if you are not familiar with kernel + configuration. + Items preceded by [*] means they has to be built into the kernel. + Items preceded by [M] means they has to be built as modules. + Items preceded by [ ] means they should be removed. + go to the "General setup" section + Select the following options : + [*] Activate tracepoints + [*] Activate markers + [*] Activate userspace markers ABI + <*> Compile generic tracing probes + Linux Trace Toolkit ---> + [LTTng fine-grained-timestamping] + [*] Linux Trace Toolkit Instrumentation Support + or <*> Linux Trace Toolkit Relay+DebugFS Support + or <*> Linux Trace Toolkit Serializer + or <*> Linux Trace Toolkit Marker Control + or <*> Linux Trace Toolkit Tracer + It makes no difference for the rest of the procedure whether the Tracer + is compiled built-in or as a module. + activate : + [*] Align Linux Trace Toolkit Traces + Linux Trace Toolkit Netlink Controller + Linux Trace Toolkit State Dump + your choice (see < Help >) : + [ ] Write heartbeat event to shrink traces + [ ] Support trace extraction from crash dump + Select + Select + Select +make +make modules_install +(if necessary, create a initrd with mkinitrd or your preferate alternative) +(mkinitrd -o /boot/initrd.img-2.6.X-lttng-0.x.xx 2.6.X-lttng-0.x.xx) + +-- on X86, X86_64 +make install +reboot +Select the Linux 2.6.17-lttng-0.x.xx kernel in your boot loader. + +-- on PowerPC +cp vmlinux.strip /boot/vmlinux-2.6.X-lttng-0.x.xx +cp System.map /boot/System.map-2.6.X-lttng-0.x.xx +cp .config /boot/config-2.6.X-lttng-0.x.xx +depmod -ae -F /boot/System.map-2.6.X-lttng-0.x.xx 2.6.X-lttng-0.x.xx +mkinitrd /boot/initrd.img-2.6.X-lttng-0.x.xx 2.6.X-lttng-0.x.xx +(edit /etc/yaboot.conf to add a new entry pointing to your kernel : the entry +that comes first is the default kernel) +ybin +select the right entry at the yaboot prompt (see choices : tab, select : type +the kernel name followed by enter) +Select the Linux 2.6.17-lttng-0.x.xx kernel in your boot loader. +-- + + + +* Editing the system wide configuration + +You must activate debugfs and specify a mount point. This is typically done in +fstab such that it happens at boot time. + +If you have never used DebugFS before, these operation would do this for you : + +mkdir /mnt/debugfs +cp /etc/fstab /etc/fstab.lttng.bkp +echo "debugfs /mnt/debugfs debugfs rw 0 0" >> /etc/fstab + +then, rebooting or issuing the following command will activate debugfs : + +mount /mnt/debugfs + +You need to load the LTT modules to be able to control tracing from user +space. This is done by issuing the following commands. Note however +these commands load all LTT modules. Depending on what options you chose to +compile statically, you may not need to issue all these commands. + +modprobe ltt-control +modprobe ltt-marker-control +modprobe ltt-tracer +modprobe ltt-serialize +modprobe ltt-relay +modprobe ipc-trace +modprobe kernel-trace +modprobe mm-trace +modprobe net-trace +modprobe fs-trace +modprobe syscall-trace +#if locking tracing is wanted, uncomment the following +#modprobe lockdep-trace + +If you want to have complete information about the kernel state (including all +the process names), you need to load the ltt-statedump module. This is done by +issuing the command : + +modprobe ltt-statedump + +You can automate at boot time loading the ltt-control module by : + +cp /etc/modules /etc/modules.bkp +echo ltt-control >> /etc/modules +echo ltt-marker-control >> /etc/modules +echo ltt-tracer >> /etc/modules +echo ltt-serialize >> /etc/modules +echo ltt-relay >> /etc/modules +echo ipc-trace >> /etc/modules +echo kernel-trace >> /etc/modules +echo mm-trace >> /etc/modules +echo net-trace >> /etc/modules +echo fs-trace >> /etc/modules +#if locking tracing is wanted, uncomment the following +#echo lockdep-trace >> /etc/modules + + +* Getting and installing the ltt-control package (on the traced machine) +(note : the ltt-control package contains lttd and lttctl. Although it has the +same name as the ltt-control kernel module, they are *not* the same thing.) +su - +cd /usr/src +wget http://ltt.polymtl.ca/lttng/ltt-control-0.x-xxxx2006.tar.gz +gzip -cd ltt-control-0.x-xxxx2006.tar.gz | tar xvof - +cd ltt-control-0.x-xxxx2006 +(refer to README to see the development libraries that must be installed on you +system) +./configure +make +make install + +* Getting and installing the markers-userspace package for user space tracing +See http://ltt.polymtl.ca/packages/markers-userspace-0.5.tar.bz2 or more recent. + + +* Getting and installing the LTTV package (on the visualisation machine, same or + different from the visualisation machine) + +su - +cd /usr/src +wget http://ltt.polymtl.ca/packages/lttv-0.x.xx-xxxx2008.tar.gz +gzip -cd lttv-0.x.xx-xxxx2008.tar.gz | tar xvof - +cd lttv-0.x.xx-xxxx2008 +(refer to README to see the development libraries that must be installed on your +system) +./configure +make +make install + + + + +*********************************************************** +** Section 3 * Using LTTng and LTTV ** +*********************************************************** + +* IMPORTANT : Arm Linux Kernel Markers after each boot + +ltt-armall + +* Use graphical LTTV to control tracing and analyse traces + +lttv-gui (or /usr/local/bin/lttv-gui) + - Spot the "Tracing Control" icon : click on it + (it's a traffic light icon) + - enter the root password + - click "start" + - click "stop" + - Yes + * You should now see a trace + +* Use text mode LTTng to control tracing + +The tracing can be controlled from a terminal by using the lttctl command (as +root). + +Start tracing : + +lttctl -n trace -d -l /mnt/debugfs/ltt -t /tmp/trace + +Stop tracing and destroy trace channels : + +lttctl -n trace -R + +see lttctl --help for details. + +(note : to see if the buffers has been filled, look at the dmesg output after +lttctl -R or after stopping tracing from the GUI, it will show an event lost +count. If it is the case, try using larger buffers. See lttctl --help to learn +how.) + +* Use text mode LTTV + +Feel free to look in /usr/local/lib/lttv/plugins to see all the text and +graphical plugins available. + +For example, a simple trace dump in text format is available with : + +lttv -m textDump -t /tmp/trace + +see lttv -m textDump --help for detailed command line options of textDump. + +It is, in the current state of the project, very useful to use "grep" on the +text output to filter by specific event fields. You can later copy the timestamp +of the events to the clipboard and paste them in the GUI by clicking on the +bottom right label "Current time". Support for this type of filtering should +be added to the filter module soon. + +* Hybrid mode + +Starting from LTTng 0.5.105 and ltt-control 0.20, a new mode can be used : +hybrid. It can be especially useful when studying big workloads on a long period +of time. + +When using this mode, the most important, low rate control information will be +recorded during all the trace by lttd (i.e. process creation/exit). The high +rate information (i.e. interrupt/traps/syscall entry/exit) will be kept in a +flight recorder buffer (now named flight-channelname_X). + +The following lttctl commands take an hybrid trace : + +Create trace channel, start lttd on normal channels, start tracing: +lttctl -n tracename -d -l /mnt/debugfs/ltt -t /tmp/trace1 -m hybrid + +Stop tracing, start lttd on flight recorder channels, destroy trace channels : +lttctl -n tracename -f -l /mnt/debugfs/ltt -t /tmp/trace1 -m hybrid + + +We will need to tweak what we consider "important" medium rate events. For +instance, thread branding events are actually considered a "high rate" event +when it should be considered "medium rate". The same should apply for the +state dump process enumeration. + +* Flight recorder mode + +The flight recorder mode writes data into overwritten buffers for all channels, +including control channels, except for the facilities tracefiles. + +The following lttctl commands take a flight recorder trace : + +lttctl -n trace -c -m flight +lttd -n -d -t /tmp/trace -c /mnt/debugfs/ltt/trace +lttctl -n trace -s +.. do stuff +lttctl -n trace -q +lttd -f -d -t /tmp/trace -c /mnt/debugfs/ltt/trace +lttctl -m trace -r + + +************************************************************** +** Section 4 * Adding new instrumentations with the markers ** +************************************************************** + +See Documentation/markers.txt and Documentation/tracepoints.txt in your kernel +tree. + +* Add new events to userspace programs with userspace markers +http://ltt.polymtl.ca/packages/ + +Get the latest markers-userspace-*.tar.bz2 and see the Makefile and examples. It +allows inserting markers in executables and libraries, currently only on x86_32 +and x86_64. + diff --git a/tags/lttv-0.11.3-23102008/README b/tags/lttv-0.11.3-23102008/README new file mode 100644 index 00000000..6cf451f4 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/README @@ -0,0 +1,75 @@ +LTT Viewing Tools package README +-------------------------------- +Mathieu Desnoyers +Last update: 2007/05/14 + + +This package contains the trace reading library and trace viewing tools for +the new Linux Trace Toolkit trace format. + +* Compiling + +gcc 3.2 or better +gtk 2.4 or better development libraries + (Debian : libgtk2.0, libgtk2.0-dev) + (Fedora : gtk2, gtk2-devel) + note : For Fedora users : this might require at least core 3 from Fedora, + or you might have to compile your own GTK2 library. +glib 2.4 or better development libraries + (Debian : libglib2.0-0, libglib2.0-dev) + (Fedora : glib2, glib2-devel) +libpopt development libraries + (Debian : libpopt0, libpopt-dev) + (Fedora : popt) +libpango development libraries + (Debian : libpango1.0, libpango1.0-dev) + (Fedora : pango, pango-devel) +libc6 development librairies + (Debian : libc6, libc6-dev) + (Fedora : glibc, glibc) + + +To compile the source tree from a tarball, simply follow these steps : + +- ./configure +- make +- make install + +After running ./configure, you can also go in specific subdirectories and +use make, make install. + + +* Quick Start + +See QUICKSTART. + + +* Source Tree Structure + +Here is the tree structure of the Linux Trace Toolkit Viewer package. + +ltt/ New trace format reading library. +README This file. +debian/ Debian config files (currently empty). +doc/ Documentation. +doc/user/ User related documentation. +doc/developer/ Developer related documentation. +lttv/ Linux Trace Toolkit trace analysis tool and viewer. +lttv/modules/ Linux Trace Toolkit analysis tool and viewer plugin modules. +specs/ RPM config files (currently empty). + + +* For Developers + +This source tree is based on the autotools suite from GNU to simplify +portability. Here are some things you should have on your system in order to +compile the subversion repository tree : + + +- GNU autotools (automake >=1.7, autoconf >=2.50, autoheader >=2.50) + (make sure your system wide "automake" points to a recent version!) +- GNU Libtool + (for more information, go to http://www.gnu.org/software/autoconf/) + +If you get the tree from the repository, you will need to use the autogen.sh +script. It calls all the GNU tools needed to prepare the tree configuration. diff --git a/tags/lttv-0.11.3-23102008/autogen.sh b/tags/lttv-0.11.3-23102008/autogen.sh new file mode 100755 index 00000000..87b034b3 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/autogen.sh @@ -0,0 +1,177 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +DIE=0 + +if [ -n "$GNOME2_DIR" ]; then + ACLOCAL_FLAGS="-I $GNOME2_DIR/share/aclocal $ACLOCAL_FLAGS" + LD_LIBRARY_PATH="$GNOME2_DIR/lib:$LD_LIBRARY_PATH" + PATH="$GNOME2_DIR/bin:$PATH" + export PATH + export LD_LIBRARY_PATH +fi + +(test -f $srcdir/configure.in) || { + echo -n "**Error**: Directory "\`$srcdir\'" does not look like the" + echo " top-level package directory" + exit 1 +} + +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`autoconf' installed." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +(grep "^AC_PROG_INTLTOOL" $srcdir/configure.in >/dev/null) && { + (intltoolize --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`intltool' installed." + echo "You can get it from:" + echo " ftp://ftp.gnome.org/pub/GNOME/" + DIE=1 + } +} + +(grep "^AM_PROG_XML_I18N_TOOLS" $srcdir/configure.in >/dev/null) && { + (xml-i18n-toolize --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`xml-i18n-toolize' installed." + echo "You can get it from:" + echo " ftp://ftp.gnome.org/pub/GNOME/" + DIE=1 + } +} + +(grep "^AM_PROG_LIBTOOL" $srcdir/configure.in >/dev/null) && { + (libtool --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`libtool' installed." + echo "You can get it from: ftp://ftp.gnu.org/pub/gnu/" + DIE=1 + } +} + +(grep "^AM_GLIB_GNU_GETTEXT" $srcdir/configure.in >/dev/null) && { + (grep "sed.*POTFILES" $srcdir/configure.in) > /dev/null || \ + (glib-gettextize --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`glib' installed." + echo "You can get it from: ftp://ftp.gtk.org/pub/gtk" + DIE=1 + } +} + +(automake --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`automake' installed." + echo "You can get it from: ftp://ftp.gnu.org/pub/gnu/" + DIE=1 + NO_AUTOMAKE=yes +} + + +# if no automake, don't bother testing for aclocal +test -n "$NO_AUTOMAKE" || (aclocal --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: Missing \`aclocal'. The version of \`automake'" + echo "installed doesn't appear recent enough." + echo "You can get automake from ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +if test "$DIE" -eq 1; then + exit 1 +fi + +if test -z "$*"; then + echo "**Warning**: I am going to run \`configure' with no arguments." + echo "If you wish to pass any to it, please specify them on the" + echo \`$0\'" command line." + echo +fi + +case $CC in +xlc ) + am_opt=--include-deps;; +esac + +for coin in `find $srcdir -path $srcdir/CVS -prune -o -name configure.in -print` +do + dr=`dirname $coin` + if test -f $dr/NO-AUTO-GEN; then + echo skipping $dr -- flagged as no auto-gen + else + echo processing $dr + ( cd $dr + + aclocalinclude="$ACLOCAL_FLAGS" + + if grep "^AM_GLIB_GNU_GETTEXT" configure.in >/dev/null; then + echo "Creating $dr/aclocal.m4 ..." + test -r $dr/aclocal.m4 || touch $dr/aclocal.m4 + echo "Running glib-gettextize... Ignore non-fatal messages." + echo "no" | glib-gettextize --force --copy + echo "Making $dr/aclocal.m4 writable ..." + test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4 + fi + if grep "^AC_PROG_INTLTOOL" configure.in >/dev/null; then + echo "Running intltoolize..." + intltoolize --copy --force --automake + fi + if grep "^AM_PROG_XML_I18N_TOOLS" configure.in >/dev/null; then + echo "Running xml-i18n-toolize..." + xml-i18n-toolize --copy --force --automake + fi + if grep "^AM_PROG_LIBTOOL" configure.in >/dev/null; then + if test -z "$NO_LIBTOOLIZE" ; then + echo "Running libtoolize..." + libtoolize --force --copy + fi + fi + echo "Running aclocal $aclocalinclude ..." + aclocal $aclocalinclude + if grep "^AM_CONFIG_HEADER" configure.in >/dev/null; then + echo "Running autoheader..." + autoheader + fi + echo "Running automake --gnu $am_opt ..." + automake --add-missing --gnu $am_opt + echo "Running autoconf ..." + autoconf + ) + fi +done + +conf_flags="--enable-maintainer-mode" + + +#if [ -a "$srcdir/include" ]; then +# echo -n Removing old system include behavior emulation... +# rm -rf $srcdir/include +# echo done. +#fi +#echo -n Creating the system include behavior emulation... +#mkdir $srcdir/include +#mkdir $srcdir/include/ltt +#ln -sf ../../LibLTT/ltt.h $srcdir/include/ltt/ltt.h +#mkdir $srcdir/include/lttv +#ln -sf ../../lttv/module.h $srcdir/include/lttv/module.h +#ln -sf ../../lttv/hook.h $srcdir/include/lttv/hook.h +#ln -sf ../../lttv/traceWindow.h $srcdir/include/lttv/traceWindow.h +#echo done. + + + +if test x$NOCONFIGURE = x; then + echo Running $srcdir/configure $conf_flags "$@" ... + $srcdir/configure $conf_flags "$@" \ + && echo Now type \`make\' to compile. || exit 1 +else + echo Skipping configure process. +fi diff --git a/tags/lttv-0.11.3-23102008/configure.in b/tags/lttv-0.11.3-23102008/configure.in new file mode 100644 index 00000000..fe4667c2 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/configure.in @@ -0,0 +1,143 @@ +# This file is part of the Linux Trace Toolkit viewer +# Copyright (C) 2003-2004 Mathieu Desnoyers +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License Version 2 as +# published by the Free Software Foundation; +# +# 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. + + + +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.57) +AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS) +#AC_WITH_LTDL # not needed ? +AM_INIT_AUTOMAKE(lttv,0.11.3-23102008) +AM_CONFIG_HEADER(config.h) +AM_PROG_LIBTOOL + +AM_PATH_GLIB_2_0(2.4.0, ,AC_MSG_ERROR([glib is required in order to compile LinuxTraceToolkit - download it from ftp://ftp.gtk.org/pub/gtk]) , gmodule) + +AM_PATH_GTK_2_0(2.4.0, ,AC_MSG_ERROR([gtk is required in order to compile GUI - download it from ftp://ftp.gtk.org/pub/gtk]) , gmodule) + +AC_PATH_PROGS(BASH, bash) + +AC_SYS_LARGEFILE + +# Checks for programs. +AC_PROG_CC + +# Checks for libraries. +AC_CHECK_LIB([popt], [poptGetNextOpt], POPT_LIBS="-lpopt",AC_MSG_ERROR([libpopt is required in order to compile LinuxTraceToolkit]) ) +#AC_CHECK_LIB([m], [round], M_LIBS="-lm",AC_MSG_ERROR([Mathematical libraries are missing.]) ) + +AC_CHECK_LIB([util], [forkpty], UTIL_LIBS="-lutil", AC_MSG_ERROR([libutil is +required in order to compile LinuxTraceToolkit])) + + +# pthread for lttd +#AC_CHECK_LIB(pthread, pthread_join,[THREAD_LIBS="-lpthread"], AC_MSG_ERROR([LinuxThreads is required in order to compile lttd])) + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([fcntl.h stdlib.h string.h sys/time.h unistd.h pthread.h]) + +AC_ISC_POSIX +AC_PROG_CC +AM_PROG_CC_STDC +AC_HEADER_STDC + +pkg_modules="gtk+-2.0 >= 2.0.0" +PKG_CHECK_MODULES(PACKAGE, [$pkg_modules]) +PACKAGE_CFLAGS="-Wall -Wformat" +AC_SUBST(PACKAGE_CFLAGS) +AC_SUBST(PACKAGE_LIBS) + +# Checks for typedefs, structures, and compiler characteristics. +AC_HEADER_STDBOOL +AC_C_CONST +AC_C_INLINE +AC_TYPE_OFF_T +AC_TYPE_SIZE_T +AC_HEADER_TIME + +# Checks for library functions. +AC_FUNC_ERROR_AT_LINE +#AC_FUNC_MALLOC +AC_FUNC_SELECT_ARGTYPES +AC_CHECK_FUNCS([select]) + +#CPPFLAGS="$CPPFLAGS -I" + +AM_CONDITIONAL(LTTVSTATIC, test "$enable_lttvstatic" = yes) +lttvlibdir="${libdir}/lttv" +lttvplugindir="${lttvlibdir}/plugins" +#lttlibdir="${libdir}/ltt" +top_lttvdir="\$(top_srcdir)/lttv" +top_lttvwindowdir="\$(top_srcdir)/lttv/modules/gui/lttvwindow" + +DEFAULT_INCLUDES="-I\$(top_srcdir) -I\$(top_builddir) -I\$(top_lttvdir) -I\$(top_lttvwindowdir)" + +#CPPFLAGS="${GLIB_CFLAGS}" +#AC_SUBST(CPPFLAGS) + +lttincludedir="${includedir}/ltt" +lttvincludedir="${includedir}/lttv" +lttvwindowincludedir="${includedir}/lttvwindow" +lttctlincludedir="${includedir}/liblttctl" + +AC_SUBST(POPT_LIBS) +AC_SUBST(UTIL_LIBS) +AC_SUBST(THREAD_LIBS) +AC_SUBST(lttvlibdir) +AC_SUBST(lttvplugindir) +#AC_SUBST(lttlibdir) +AC_SUBST(top_lttvdir) +AC_SUBST(top_lttvwindowdir) +AC_SUBST(DEFAULT_INCLUDES) +AC_SUBST(lttincludedir) +AC_SUBST(lttvincludedir) +AC_SUBST(lttvwindowincludedir) +AC_SUBST(lttctlincludedir) + + #lttv/modules/gui/tutorial/Makefile + #lttv/modules/gui/diskperformance/Makefile +AC_CONFIG_FILES([Makefile + lttv/Makefile + lttv/lttv/Makefile + lttv/modules/Makefile + lttv/modules/text/Makefile + lttv/modules/gui/Makefile + lttv/modules/gui/lttvwindow/Makefile + lttv/modules/gui/interrupts/Makefile + lttv/modules/gui/lttvwindow/lttvwindow/Makefile + lttv/modules/gui/lttvwindow/pixmaps/Makefile + lttv/modules/gui/controlflow/Makefile + lttv/modules/gui/detailedevents/Makefile + lttv/modules/gui/statistics/Makefile + lttv/modules/gui/histogram/Makefile + lttv/modules/gui/filter/Makefile + lttv/modules/gui/tracecontrol/Makefile + lttv/modules/gui/resourceview/Makefile + ltt/Makefile + doc/Makefile + doc/developer/Makefile + doc/developer/developer_guide/Makefile + doc/developer/developer_guide/docbook/Makefile + doc/developer/developer_guide/html/Makefile + doc/user/Makefile + doc/user/user_guide/Makefile + doc/user/user_guide/docbook/Makefile + doc/user/user_guide/html/Makefile]) +AC_OUTPUT diff --git a/tags/lttv-0.11.3-23102008/doc/Makefile.am b/tags/lttv-0.11.3-23102008/doc/Makefile.am new file mode 100644 index 00000000..528d881c --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = developer user diff --git a/tags/lttv-0.11.3-23102008/doc/developer/Makefile.am b/tags/lttv-0.11.3-23102008/doc/developer/Makefile.am new file mode 100644 index 00000000..9e2f266a --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = developer_guide + +EXTRA_DIST = coding.html format.html library-header.txt lttng-atomic-up.txt lttng-format-string.txt lttng-lttv-compatibility.html lttng-lttv-new-features.html lttng-lttv-roadmap.html lttng-new-marker-event-desc.txt lttng-synthetic-tsc-msb.txt lttng-userspace-tracing-lite.txt lttng-userspace-tracing.txt lttng-xen.txt lttng_java_instrumentation.txt lttv.html lttv_filter_specification.docbook lttv_gui_layout.txt lttv_guidetailed-event-list-redesign.txt lttv_hook_prio.txt lttv_process_traceset_strict_boundaries.txt lttv_requests_servicing_schedulers.txt lttvwindow_events_delivery.txt marker-types.txt program-header.txt time-monotonic-accurate.txt tracing_tools_overview.html tsc-smallv1.txt tsc-smallv2.txt tsc.txt xen-port.txt diff --git a/tags/lttv-0.11.3-23102008/doc/developer/coding.html b/tags/lttv-0.11.3-23102008/doc/developer/coding.html new file mode 100644 index 00000000..4822f45e --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/coding.html @@ -0,0 +1,32 @@ + + + + Coding practices + + + +

Coding practices

+ +

+The Linux Trace Toolkit viewer and libltt libraries use the coding standards +of the underlying glib and gtk libraries. +This includes: + +

    +
  • lower case file names without underscore but with an occasional dash, +
  • lower case function names with underscores separating words, +
  • type names starting with a capital letter and with capital letters +separating words. +
+ +

+Each file in the libltt library should contain a +LGPL header while each file in the LTT viewer +should contain a +GPL header. + + + + + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/Makefile.am b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/Makefile.am new file mode 100644 index 00000000..7df24ebb --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = docbook html + +EXTRA_DIST = developer_guide.dvi diff --git a/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/developer_guide.dvi b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/developer_guide.dvi new file mode 100644 index 00000000..b4379ec8 Binary files /dev/null and b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/developer_guide.dvi differ diff --git a/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/docbook/Makefile.am b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/docbook/Makefile.am new file mode 100644 index 00000000..e0c05ea8 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/docbook/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = developer_guide.docbook lttv-context.eps lttv-context.png diff --git a/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/docbook/developer_guide.docbook b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/docbook/developer_guide.docbook new file mode 100644 index 00000000..f4105324 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/docbook/developer_guide.docbook @@ -0,0 +1,668 @@ + + + + + + + +Linux Trace Toolkit Viewer Developer Guide + + +Mathieu +Desnoyers + + + +01/12/2004 +1.00.00 + + + +This document describes the basic steps necessary to develop within the +Linux Trace Toolkit Viewer project. + + + + + +Linux Trace Toolkit Viewer +text +module +context + + + + +Linux Trace Toolkit Viewer Text Module Tutorial + + +Introduction + +This chapter explains all the steps that are necessary to create a text module +in LTTV. + + + + +A typical module + +A typical module must have a init() and destroy() function. Please refer to +lttv/modules/text/textDump.c for the detail of these functions. + + +The init() function is called when the library is loaded and destroy() +inversely. It adds options to the command line by calling "lttv_option_add" from +option.h + + +The module communicates with the main lttv program through the use of global +attributes. Use lttv/attribute.h, lttv/iattribute.h and lttv/lttv.h, and then +LTTV_IATTRIBUTE(lttv_global_attributes()) to get the pointer to these +global attributes. + + +You can then add your hooks (functions that follows the prototype of a hook, as +defined in lttv/hook.h) in the different hook lists defined in lttv/lttv.h. Note +that hooks have an assigned priority. This is necessary to inform the trace +reader that a specific hook needs to be called, for example, before or after the +state update done for an event by the state module. For that specific example, a +hook could use the LTTV_PRIO_STATE-5 to get called before the state update and a +second hook could use the LTTV_PRIO_STATE+5 to get called after the state +update. This is especially important for graphical module, which is the subject +of a the chapter named "Linux Trace Toolkit Viewer Graphical Module Tutorial". + + +You should also take a look at lttv/state.c, where by_id hooks are used. When +you only need some specific events, you should use this interface. It makes the +event filtering sooner in the dispatch chain : you hook doesn't have to be +called for each event, only the ones selected. That improves the performances a +lot! + + +Note that you should use the lttv_trace_find_hook method from +lttv/tracecontext.h to connect the hook to the right facility/event type. See +state.c for an example. A problem that may arise is that the LttvTraceHook +structure must be passed as hook_data when registering the hook. In fact, it is +not necessary for it to be directly passed as the hook_data parameter. As long +as the hook function can access the LttvTraceHook fields necessary to parse the +LttEvent, there is no problem. In a complex viewer where you need a pointer to +your own data structure, just keep a pointer to the LttvTraceHook structure +inside your own data structure, and give to pointer to your data structure in +parameter as the hook_data. + + +Then, you should use the macro LTTV_MODULE, defined in lttv/module.h. It allows +you to specify the module name, a short and a long description, the init and +destroy functions and the module dependencies. That permits to the module +backend to load the right dependencies when needed. + + +A typical text module will depend on batchAnalysis for the batch computation of a +trace, and simply register before and after trace hooks, as weel as the most +important one : a event hook. + + + + +The hooks + +The before and after trace hooks only exists to be able to generate a report at +the end of a trace computation. The effective computation is done by the event +hooks. + + +These hooks does particular computation on data arriving as argument, a +call_data. The type of the call_data, when a hook is called during the trace +read, is a traceset context. It contains all the necessary information about the +read in progress. This is the base class from which inherits trace set +state, and trace set/trace/tracefile state is the base classe of trace +set/trace/tracefile statistics. All these types can be casted to another without +problem (a TracesetState, for example, can be casted to a TracesetContext, but +it's not true for the casting between a TraceContext and a TracesetContext, see +the chapter "How to use the trace reading context" for details). They offer the +input data and they give a container (the attributes of the trace set/trace/tracefile +statistics) to write the output of this hook. + + +The idea behind writing in the attributes container is to provide an extensible +way of storing any type of information. For example, a specific module that adds +statistics to a trace can store them there, and the statistic printout will +automatically include the results produced by the specific module. + + +Output data does not necessarily need to be stored in such a global container +though. If we think of data of which we need to keed track during the execution, +an event counter for example, we should create our own data structure that +contains this counter, and pass the address of the allocated structure as the +hook_data parameter of the hook list creation function. That way, the hook will +be called with its hook_data as first parameter, which it can read and write. We +can think of this structure as the data related to the function that persists +between each call to the hook. You must make sure that you cast the hook_data to +the type of the structure before you use it in the hook function. + + +The detail about how to access the different fields of the reading context (the +hook's call_data) will be discussed in the chapter "How to use the trace +reading context". + + + + + + + + +How to use the Linux Trace Toolkit Viewer's Reading Context + + +Introduction + +This chapter describes how to use the Linux Trace Toolkit reading context, a +data structure that is given as call data parameter of the modules'callbacks. + + +Linux Trace Toolkit Viewer provides a backend that reads the traces. In combines +them in tracesets. A trace is an abstaction over many tracefiles, one per CPU. +LTTV reads the whole trace together, providing the events to modules by calling +their pre-registered hook lists in a chronological order. + + + + +Why an event driven trace reader ? + +The complexity of synchronizing the tracesets is then hidden to the viewer. Some +future plans involve to be able to put many traces together in a trace set. +Before this becomes possible, the time of each trace must be synchronized in +some way. Some work is actually done to create a module that uses the network +traffic shared by different computers to synchronize the time of different +traces. + + +In order to make each module integrate well with each other, we made the trace +reader a simple hook caller. For each event it reads, it just calls the hook +lists for this event. For each event, it calls the by_id specific hooks +registered for this event and also the "main" hooks, registered for all events. +Note that the two hook lists are merged when called so the priority of the +hooks of each list is respected. For example, a hook of higher priority (20) in +the main list will be called before a hook of lower priority (40) from the +by_id specific list. + + + + +Using the reading context + +If you have read the tutorials about writing a text and a graphic module, you +should be fairly ready to use the information provided to your hook by the +reading API. + + +The data structures of the reading context are based on the gobject, a +object-oriented library from the glib. Some evolved types that are found in the +context also comes from the "glib" (GArray, GHashTable and so on). For detailed +information about "gobjects" and the "glib", see the www.gtk.org website. They provide a complete +API reference about the data types they provide. + + +The reading context is object oriented. It is described by the lttv/tracecontext.h +header. Is can be illustrated with this UML class diagram : + + + + + + + + + + + +Linux Trace Toolkit Viewer Reading Context Class Diagram + + + + +Though, for performance's sake, navigating through it is not as encapsulated as +it could. Consider the class attributes to be all public (no get/set functions). +Sometimes, iteration upon a specific element can be uneasy. For example, you may +have to get the number of tracefiles in a trace from the "vt" field of the trace +context to be able to iterate over all the tracefiles contained by the trace. + + +To facilitate the common operations on the reading context, LTTV now provides a +header that consists of simple macros : lttv/contextmacros.h. It gives an object +look-and-feel to the context classes. Simple "GET" macros can be used to easily +access the different fields are iterate over the elements (and get the total +number of elements too). + + + + + +Linux Trace Toolkit Viewer Graphical Module Tutorial + +Introduction + +As a matter of fact, most of the things said for the text modules still hold for +the graphical modules. However, the fact that every module must instanciate +objects (called viewers) more than once changes a little bit the scenario. It is +then impossible to use static structures : everything must be instanciated at +run-time, except the structures related to the module itself. + + + +The static part of a module + +A module must have a static part to be able to get loaded just like a text +module. Now, let's see the differences. The graphical module depends on the +"lttvwindow" module. See module.c from the control flow viewer for an example. + + +The init() and destroy() functions must register functions that can be called by +user interaction to instanciate the viewers. That's the goal of +lttvwindow_register_constructor() and lttvwindow_unregister_constructor() : +they register a function with a menu entry and an icon. The main window will +shown them in its interface and call the function when the button or menu item +is selected. This hook function must receive a pointer to a "Tab" object in +parameter. + + +Also note the presence of the destroy_walk() method. It is called when the +module is unloaded : it must destroy all the instances of the viewers from the +module. + + + +The dynamic part of a module : the viewer + +The dynamic part starts with the constructor of the viewer. It is called by the +main window when the corresponding button or menu item is selected. See +h_guicontrolflow() from control flow viewer eventhooks.c for an example. It does +basic connexion to the tab's events available : time window change notification, +current time notification, redraw notification, continue notification. All these +function should be implemented in your viewer if you want the data you shown to +be synchronised with the main window and the other viewers. It also calls the +background computation, which will be discussed in the next section. + + +This is also at this point that the viewer does create it's own memory footprint +: its inner structure. This structure will have to be passed as hook_data to +each function registered by the viewer : this is what makes the functions +"belong" to this instance of the viewer. + + + +How to request background computation + +You will also notice the presence of a request_background_data() called in the +constructor. This function, in eventhooks.c, does verify for the presence of the +state information that could be precomputed by the main window background +computation. If it has not been precomputed, we ask for a computation and show +partial data. We also register a hook that will be called (notified) by the main +window when the requested data will become ready, so the viewer can update +itself with the new data. If no partial information would have made sense in a +particular viewer, one could choose to shown a "waiting for computation" message +while waiting for the notification. See lttvwindow/lttvwindowtraces.h for the API +of the background requests. + + + + +How to handle events and use the graphical trace reading service + +The events that are delivered by the main window are defined in +lttvwindow/lttvwindow.h. Let's describe them and their use in details. Remember +that you can refer to the control flow viewer module as an example. + + + +Module Related API + +A viewer plugin is, before anything, a plugin. As a dynamically loadable +module, it thus has an init and a destroy function called whenever it is +loaded/initialized and unloaded/destroyed. A graphical module depends on +lttvwindow for construction of its viewer instances. In order to achieve +this, it must register its constructor function to the main window along +with button description or text menu entry description. A module keeps +a list of every viewer that currently sits in memory so it can destroy +them before the module gets unloaded/destroyed. + + +The contructor registration to the main windows adds button and menu +entry to each main window, thus allowing instanciation of viewers. + + + +Main Window + +The main window is a container that offers menus, buttons and a +notebook. Some of those menus and buttons are part of the core of the +main window, others are dynamically added and removed when modules are +loaded/unloaded. + + +The notebook contains as much tabs as wanted. Each tab is linked with +a set of traces (traceset). Each trace contains many tracefiles (one +per cpu). A trace corresponds to a kernel being traced. A traceset +corresponds to many traces read together. The time span of a traceset +goes from the earliest start of all the traces to the latest end of all +the traces. + + +Inside each tab are added the viewers. When they interact with the main +window through the lttvwindow API, they affect the other viewers located +in the same tab as they are. + + +The insertion of many viewers in a tab permits a quick look at all the +information wanted in a glance. The main window does merge the read +requests from all the viewers in the same tab in a way that every viewer +will get exactly the events it asked for, while the event reading loop +and state update are shared. It improves performance of events delivery +to the viewers. + + + + +Viewer Instance Related API + +The lifetime of a viewer is as follows. The viewer constructor function +is called each time an instance view is created (one subwindow of this +viewer type is created by the user either by clicking on the menu item +or the button corresponding to the viewer). Thereafter, the viewer gets +hooks called for different purposes by the window containing it. These +hooks are detailed below. It also has to deal with GTK Events. Finally, +it can be destructed by having its top level widget unreferenced by the +main window or by any GTK Event causing a "destroy-event" signal on the +its top widget. Another possible way for it do be destroyed is if the +module gets unloaded. The module unload function will have to emit a +"destroy" signal on each top level widget of all instances of its viewers. + + + +Notices from Main Window + + +time_window + +This is the time interval visible on the viewer's tab. Every + viewer that cares about being synchronised by respect to the + time with other viewers should register to this notification. + They should redraw all or part of their display when this + occurs. + + + +traceset + +This notification is called whenever a trace is added/removed + from the traceset. As it affects all the data displayed by the + viewer, it sould redraw itself totally. + + + + +filter + +This feature has not been implemented yet. + + + + +current_time + +Being able to zoom nearer a specific time or highlight a specific + time on every viewer in synchronicity implies that the viewer + has to shown a visual sign over the drawing or select an event + when it receives this notice. It should also inform the main + window with the appropriate report API function when a user + selects a specific time as being the current time. + + + + +dividor + +This notice links the positions of the horizontal dividors + between the graphic display zone of every viewer and their Y axis, + typically showing processes, cpus, ... + + + + + +Reporting Changes to the Main Window + +In most cases, the enclosing window knows about updates such as described +in the Notification section higher. There are a few cases, however, where +updates are caused by actions known by a view instance. For example, +clicking in a view may update the current time; all viewers within +the same window must be told about the new current time to change the +currently highlighted time point. A viewer reports such events by calling +lttvwindow_report_current_time on its lttvwindow. The lttvwindow will +consequently call current_time_notify for each of its contained viewers. + + +Available report methods are : + + + +lttvwindow_report_time_window : reports the new time window. + + + + +lttvwindow_report_current_time : reports the new current time. + + + + +lttvwindow_report_dividor : reports the new horizontal dividor's position. + + + + + + + +Requesting Events to Main Window + +Events can be requested by passing a EventsRequest structure to the main +window. They will be delivered later when the next g_idle functions +will be called. Event delivery is done by calling the event hook for +this event ID, or the main event hooks. A pointer to the EventsRequest +structure is passed as hook_data to the event hooks of the viewers. + + +EventsRequest consists in + + + +a pointer to the viewer specific data structure + + + + +a start timestamp or position + + + + +a stop_flag, ending the read process when set to TRUE + + + + +a end timestamp and/or position and/or number of events to read + + + + +hook lists to call for traceset/trace/tracefile begin and end, and for each + event (event hooks and event_by_id hooks). + + + + + +The main window will deliver events for every EventRequests it has +pending through an algorithm that guarantee that all events requested, +and only them, will be delivered to the viewer between the call of the +tracefile_begin hooks and the call of the tracefile_end hooks. + + +If a viewer wants to stop the event request at a certain point inside the +event hooks, it has to set the stop_flag to TRUE and return TRUE from the +hook function. Then return value will stop the process traceset. Then, +the main window will look for the stop_flag and remove the EventRequests +from its lists, calling the process_traceset_end for this request (it +removes hooks from the context and calls the after hooks). + + +It no stop_flag is risen, the end timestamp, end position or number +of events to read has to be reached to determine the end of the +request. Otherwise, the end of traceset does determine it. + + + +GTK Events + +Events and Signals + +GTK is quite different from the other graphical toolkits around +there. The main difference resides in that there are many X Windows +inside one GtkWindow, instead of just one. That means that X events are +delivered by the glib main loop directly to the widget corresponding to +the GdkWindow affected by the X event. + + +Event delivery to a widget emits a signal on that widget. Then, if a +handler is connected to this widget's signal, it will be executed. There +are default handlers for signals, connected at class instantiation +time. There is also the possibility to connect other handlers to these +signals, which is what should be done in most cases when a viewer needs +to interact with X in any way. + + + +Signal emission and propagation is described there : + + + + +http://www.gtk.org/tutorial/sec-signalemissionandpropagation.html + + + + + + +For further information on the GTK main loop (now a wrapper over glib main loop) +see : + + + + +http://developer.gnome.org/doc/API/2.0/gtk/gtk-General.html + + + + +http://developer.gnome.org/doc/API/2.0/glib/glib-The-Main-Event-Loop.html + + + + + + + +For documentation on event handling in GTK/GDK, see : + + + + +http://developer.gnome.org/doc/API/2.0/gdk/gdk-Events.html + + + + +http://developer.gnome.org/doc/API/2.0/gdk/gdk-Event-Structures.html + + + + + + + +Signals can be connected to handlers, emitted, propagated, blocked, +stopped. See : + + + + +http://developer.gnome.org/doc/API/2.0/gobject/gobject-Signals.html + + + + + + + + + +The "expose_event" + +Provides the exposed region in the GdkEventExpose structure. + + +There are two ways of dealing with exposures. The first one is to directly +draw on the screen and the second one is to draw in a pixmap buffer, +and then to update the screen when necessary. + + +In the first case, the expose event will be responsible for registering +hooks to process_traceset and require time intervals to the main +window. So, in this scenario, if a part of the screen is damaged, the +trace has to be read to redraw the screen. + + +In the second case, with a pixmap buffer, the expose handler is only +responsible of showing the pixmap buffer on the screen. If the pixmap +buffer has never been filled with a drawing, the expose handler may ask +for it to be filled. + + +The interest of using events request to the main window instead of reading +the events directly from the trace comes from the fact that the main +window does merge requests from the different viewers in the same tab so +that the read loop and the state update is shared. As viewers will, in +the common scenario, request the same events, only one pass through the +trace that will call the right hooks for the right intervals will be done. + + +When the traceset read is over for a events request, the traceset_end +hook is called. It has the responsibility of finishing the drawing if +some parts still need to be drawn and to show it on the screen (if the +viewer uses a pixmap buffer). + + +It can add dotted lines and such visual effects to enhance the user's +experience. + + + + + + + + + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/docbook/lttv-context.eps b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/docbook/lttv-context.eps new file mode 100644 index 00000000..fb522a42 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/docbook/lttv-context.eps @@ -0,0 +1,4227 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: GIMP PostScript file plugin V 1.17 by Peter Kirchgessner +%%Title: lttv-context.eps +%%CreationDate: Thu Dec 2 00:27:27 2004 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%Pages: 1 +%%BoundingBox: 14 14 447 327 +%%EndComments +%%BeginProlog +% Use own dictionary to avoid conflicts +10 dict begin +%%EndProlog +%%Page: 1 1 +% Translate for offset +14.173228346456693 14.173228346456693 translate +% Translate to begin of first scanline +0 312.65546218487395 translate +432 -312.65546218487395 scale +% Image geometry +952 689 8 +% Transformation matrix +[ 952 0 0 689 0 0 ] +% Strings to hold RGB-samples per scanline +/rstr 952 string def +/gstr 952 string def +/bstr 952 string def +{currentfile /ASCII85Decode filter /RunLengthDecode filter rstr readstring pop} +{currentfile /ASCII85Decode filter /RunLengthDecode filter gstr readstring pop} +{currentfile /ASCII85Decode filter /RunLengthDecode filter bstr readstring pop} +true 3 +%%BeginData: 253599 ASCII Bytes +colorimage +JcC<$JcC<$JcC<$JcEjlJ,~> +JcC<$JcC<$JcC<$JcEjlJ,~> +JcC<$JcC<$JcC<$JcEjlJ,~> +JcC<$huBn\JcC<$JcC<$]`3K~> +JcC<$huBn\JcC<$JcC<$]`3K~> +JcC<$huBn\JcC<$JcC<$]`3K~> +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +pA][DpAfdEo`"mkZN'n(JcC<$JcC<$^Ai]~> +pA][DpAfdEo`"mkZN'n(JcC<$JcC<$^Ai]~> +pA][DpAfdEo`"mkZMst+J:N4NJcC<$JcEIaJ,~> +pAY*mJcGQG!!%TMo`)&oJcC<$JcC<$^Ai]~> +pAY*mJcGQG!!%TMo`)&oJcC<$JcC<$^Ai]~> +pAY*mJcGQG!!%TMo`),qs+#\#JcC<$JcEIaJ,~> +pAY*mJcGQG!!%TMo`"mkZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!!%TMo`"mkZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!!%TMo`"mkZMst+J:N4NJcC<$JcEIaJ,~> +pAY*mirB#Ym/Qk]\,ZI.JcGEC!!'>)rW%NLJcC<$JcEIaJ,~> +pAY*mirB#Ym/Qk]\,ZI.JcGEC!!'>)rW%NLJcC<$JcEIaJ,~> +pAY*mirB#Ym/Qk]\,QI/J:[anrr<&)rrN1NJ:[`Os+13$s1\M`~> +pAY*mirB#Yr;Zcsrr;uup&G$lf)PaMiW&oXJcGEC!!'>)rW%NLJcC<$JcEIaJ,~> +pAY*mirB#Yr;Zcsrr;uup&G$lf)PaMiW&oXJcGEC!!'>)rW%NLJcC<$JcEIaJ,~> +pAY*mirB#Yr;Zcsrr;uup&G$lf)PaMiVroYJ:[anrr<&)rrN1NJ:[`Os+13$s1\M`~> +pAY*mirB#YrVufr!<;ut!ri6#rr2rurVultrVult!ri9#r;cfrr;cltrW)osr;cisrW)uur;bjW +rr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mirB#YrVufr!<;ut!ri6#rr2rurVultrVult!ri9#r;cfrr;cltrW)osr;cisrW)uur;bjW +rr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mirB#YrVufr!<;ut!ri6#rr2rurVultrVult!ri9#r;cfrr;cltrW)osr;cisrW)uur;bjW +!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mirB#Yr;Zcsrr;uus8W*!rr2rurVultrVuisr;Zcss8W*!rVult#6+Z's8N'!r;Zcs#6+Z' +s8N'!iW&oXJcGEC!!'>)rW%NLJcC<$JcEIaJ,~> +pAY*mirB#Yr;Zcsrr;uus8W*!rr2rurVultrVuisr;Zcss8W*!rVult#6+Z's8N'!r;Zcs#6+Z' +s8N'!iW&oXJcGEC!!'>)rW%NLJcC<$JcEIaJ,~> +pAY*mirB#Yr;Zcsrr;uus8W*!rr2rurVultrVuisr;Zcss8W*!rVult#6+Z's8N'!r;Zcs#6+Z' +s8N'!iVroYJ:[anrr<&)rrN1NJ:[`Os+13$s1\M`~> +pAY*mirB#Yr;Zcsrr;uurr;uu!WN/ts8N)ts8N)rs8N*!s8N)ts8N'%rr<'!s8E#ss8N''rr<'! +rr<&Xs8N(Ms7QBl!4)Y(!.k0$s+13$s1\M`~> +pAY*mirB#Yr;Zcsrr;uurr;uu!WN/ts8N)ts8N)rs8N*!s8N)ts8N'%rr<'!s8E#ss8N''rr<'! +rr<&Xs8N(Ms7QBl!4)Y(!.k0$s+13$s1\M`~> +pAY*mirB#Yr;Zcsrr;uurr;uu!WN/ts8N)ts8N)rs8N*!s8N)ts8N'%rr<'!s8E#ss8N''rr<'! +rr<&XrrE+MJcGEC!!'>)!W[b$JcC<$JcC<$^Ai]~> +pAY*mirB#Yr;Zcsrr;uurr;uu!WN/ts8N)ts8N)ts8;rts8N)ts82lrs8E#ts82lss8N)Xs8N(M +s7QBl!4)Y(!.k0$s+13$s1\M`~> +pAY*mirB#Yr;Zcsrr;uurr;uu!WN/ts8N)ts8N)ts8;rts8N)ts82lrs8E#ts82lss8N)Xs8N(M +s7QBl!4)Y(!.k0$s+13$s1\M`~> +pAY*mirB#Yr;Zcsrr;uurr;uu!WN/ts8N)ts8N)ts8;rts8N)ts82lrs8E#ts82lss8N)XrrE+M +JcGEC!!'>)!W[b$JcC<$JcC<$^Ai]~> +pAY*mirB#Yr;Zcsrr;uurr;uu!WN/ts8N)ts8N)us8N''rr<'!rr<&ts8N)qs8E#us8N)ss8N)X +s8N(Ms7QBl!4)Y(!.k0$s+13$s1\M`~> +pAY*mirB#Yr;Zcsrr;uurr;uu!WN/ts8N)ts8N)us8N''rr<'!rr<&ts8N)qs8E#us8N)ss8N)X +s8N(Ms7QBl!4)Y(!.k0$s+13$s1\M`~> +pAY*mirB#Yr;Zcsrr;uurr;uu!WN/ts8N)ts8N)us8N''rr<'!rr<&ts8N)qs8E#us8N)ss8N)X +rrE+MJcGEC!!'>)!W[b$JcC<$JcC<$^Ai]~> +pAY*mirB#Yr;Zcsrr;uurVultqu?ZrrVultrr;uu#6+Z's8N'!rVultq>^Hps8W*!r;ZcsiW&oX +JcGEC!!'>)rW%NLJcC<$JcEIaJ,~> +pAY*mirB#Yr;Zcsrr;uurVultqu?ZrrVultrr;uu#6+Z's8N'!rVultq>^Hps8W*!r;ZcsiW&oX +JcGEC!!'>)rW%NLJcC<$JcEIaJ,~> +pAY*mirB#Yr;Zcsrr;uurVultqu?ZrrVultrr;uu#6+Z's8N'!rVultq>^Hps8W*!r;ZcsiVroY +J:[anrr<&)rrN1NJ:[`Os+13$s1\M`~> +pAY*mirAlUs8W&us8W&us8W*!qu?ZrrVultrVucqs8W#ts8W#ts8W#trVufrrr;rtj8],ZJcGEC +!!'>)rW%NLJcC<$JcEIaJ,~> +pAY*mirAlUs8W&us8W&us8W*!qu?ZrrVultrVucqs8W#ts8W#ts8W#trVufrrr;rtj8],ZJcGEC +!!'>)rW%NLJcC<$JcEIaJ,~> +pAY*mirAlUs8W&us8W&us8W*!qu?ZrrVultrVucqs8W#ts8W#ts8W#trVufrrr;rtj8T,[J:[an +rr<&)rrN1NJ:[`Os+13$s1\M`~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +p]#dEo`0RCp&>!lZN'n(JcC<$JcC<$^Ai]~> +p]#dEo`0RCp&>!lZN'n(JcC<$JcC<$^Ai]~> +p]#dEp&G)CJcGEC!!'>)!W[b$JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&D/pJcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&D/pJcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QDr!<7S#s+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mkPkM^kl1V_li-qbnG`Rjs8N)^rr<&ls8N(Ms7QBl!4)Y(!.k0$s+13$s1\M`~> +pAY*mkPkM^kl1V_li-qbnG`Rjs8N)^rr<&ls8N(Ms7QBl!4)Y(!.k0$s+13$s1\M`~> +pAY*mkPkM^kl1V_li-qbnG`Rjs8N)^rr<&lrrE+MJcGEC!!'>)!W[b$JcC<$JcC<$^Ai]~> +pAY*mc2RbDr;Q`srr2rup&>!lrVlitrr2rupAY*moD\djq#C6lp](6nJcGEC!!'>)rW%NLJcC<$ +JcEIaJ,~> +pAY*mc2RbDr;Q`srr2rup&>!lrVlitrr2rupAY*moD\djq#C6lp](6nJcGEC!!'>)rW%NLJcC<$ +JcEIaJ,~> +pAY*mc2RbDr;Q`srr2rup&>!lrVlitrr2rupAY*moD\djq#C6lp\t6oJ:[anrr<&)rrN1NJ:[`O +s+13$s1\M`~> +pAY*mo`"pls8E#trs&Q(!!*$!rrDus!!)lqrW)rt!!*#u!!)utrVururW!!!!;uj%!<<'!rrE*! +rVururW)uu#lsu*!<3'!rrE)u!<2uu!<3!!!<<#urr;rtrr;uup&G$lJcGEC!!'>)rW%NLJcC<$ +JcEIaJ,~> +pAY*mo`"pls8E#trs&Q(!!*$!rrDus!!)lqrW)rt!!*#u!!)utrVururW!!!!;uj%!<<'!rrE*! +rVururW)uu#lsu*!<3'!rrE)u!<2uu!<3!!!<<#urr;rtrr;uup&G$lJcGEC!!'>)rW%NLJcC<$ +JcEIaJ,~> +pAY*mo`"pls8E#trs&Q(!!*$!rrDus!!)lqrW)rt!!*#u!!)utrVururW!!!!;uj%!<<'!rrE*! +rVururW)uu#lsu*!<3'!rrE)u!<2uu!<3!!!<<#urr;rtrr;uup&>$mJ:[anrr<&)rrN1NJ:[`O +s+13$s1\M`~> +pAY*mo`+pkrr3*$s8N'!rr3'#s8N)urr<&qrr<&urr<&rrr<&srr<&urr<&urr<&urr<&urrN3# +!<2uu!<2uu!<3#u!<3!$!<<'!!<3!#!<<'!rr3'#s8N)urr<&urriE&rrE'!pAb-mJcGEC!!'>) +rW%NLJcC<$JcEIaJ,~> +pAY*mo`+pkrr3*$s8N'!rr3'#s8N)urr<&qrr<&urr<&rrr<&srr<&urr<&urr<&urr<&urrN3# +!<2uu!<2uu!<3#u!<3!$!<<'!!<3!#!<<'!rr3'#s8N)urr<&urriE&rrE'!pAb-mJcGEC!!'>) +rW%NLJcC<$JcEIaJ,~> +pAY*mo`+pkrr3*$s8N'!rr3'#s8N)urr<&qrr<&urr<&rrr<&srr<&urr<&urr<&urr<&urrN3# +!<2uu!<2uu!<3#u!<3!$!<<'!!<3!#!<<'!rr3'#s8N)urr<&urriE&rrE'!pAY-nJ:[anrr<&) +rrN1NJ:[`Os+13$s1\M`~> +pAY*mo`"mkrVls"s8N)trrW9$rrE&u!!)`m!!)or!!)rs!!*#u!!*#u!!*#u!s&B$!<3!#!<<'! +rr2rurr2rurVls"s8N)trrW9$rrE&u!s&B$!<2uu!<2uu!:g*h!.k1Crr<&)s8E"Ls+13$s+13a +s*t~> +pAY*mo`"mkrVls"s8N)trrW9$rrE&u!!)`m!!)or!!)rs!!*#u!!*#u!!*#u!s&B$!<3!#!<<'! +rr2rurr2rurVls"s8N)trrW9$rrE&u!s&B$!<2uu!<2uu!:g*h!.k1Crr<&)s8E"Ls+13$s+13a +s*t~> +pAY*mo`"mkrVls"s8N)trrW9$rrE&u!!)`m!!)or!!)rs!!*#u!!*#u!!*#u!s&B$!<3!#!<<'! +rr2rurr2rurVls"s8N)trrW9$rrE&u!s&B$!<2uu!<2uu!:g'i!.]Y#p&>!lZMst+J:N4NJcC<$ +JcEIaJ,~> +pAY*mqu?Qos8N'!rVls"s8N)trr<&urrN3#!;QTm!;lcr!;uis!<2uu!<)p!!<3&urr<&urrW9$ +rrE&u!!*#u!!)ut!s&B$!<)p"!<<'!rr3'#s8N)us82les8N(Ms7QBl!4)Y(!.k0$s+13$s1\M`~> +pAY*mqu?Qos8N'!rVls"s8N)trr<&urrN3#!;QTm!;lcr!;uis!<2uu!<)p!!<3&urr<&urrW9$ +rrE&u!!*#u!!)ut!s&B$!<)p"!<<'!rr3'#s8N)us82les8N(Ms7QBl!4)Y(!.k0$s+13$s1\M`~> +pAY*mqu?Qos8N'!rVls"s8N)trr<&urrN3#!;QTm!;lcr!;uis!<2uu!<)p!!<3&urr<&urrW9$ +rrE&u!!*#u!!)ut!s&B$!<)p"!<<'!rr3'#s8N)us82lerrE+MJcGEC!!'>)!W[b$JcC<$JcC<$ +^Ai]~> +pAY*mo`"mkrVls"s8N)trr<&urrN3#!;ZWp!<2uu!;lcr!;uis!<2uu!<)p#!<3'!s7u]rrrE&u +!!*#u!!)ut!s&B$!<)p"!<<'!rr3'#s8N)urr<&ds8N(Ms7QBl!4)Y(!.k0$s+13$s1\M`~> +pAY*mo`"mkrVls"s8N)trr<&urrN3#!;ZWp!<2uu!;lcr!;uis!<2uu!<)p#!<3'!s7u]rrrE&u +!!*#u!!)ut!s&B$!<)p"!<<'!rr3'#s8N)urr<&ds8N(Ms7QBl!4)Y(!.k0$s+13$s1\M`~> +pAY*mo`"mkrVls"s8N)trr<&urrN3#!;ZWp!<2uu!;lcr!;uis!<2uu!<)p#!<3'!s7u]rrrE&u +!!*#u!!)ut!s&B$!<)p"!<<'!rr3'#s8N)urr<&drrE+MJcGEC!!'>)!W[b$JcC<$JcC<$^Ai]~> +pAY*mo`+pkrr3'#s8N)trr<&urrN3#!;ZWp!<2uu!;lcr!;uis!<2uu!<)p$!<3'!rrDus!W`6# +rr2rurr2rurVm!#s8N'!rr3<*s8N*!rr<'!rrE&u!!)Edrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mo`+pkrr3'#s8N)trr<&urrN3#!;ZWp!<2uu!;lcr!;uis!<2uu!<)p$!<3'!rrDus!W`6# +rr2rurr2rurVm!#s8N'!rr3<*s8N*!rr<'!rrE&u!!)Edrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mo`+pkrr3'#s8N)trr<&urrN3#!;ZWp!<2uu!;lcr!;uis!<2uu!<)p$!<3'!rrDus!W`6# +rr2rurr2rurVm!#s8N'!rr3<*s8N*!rr<'!rrE&u!!)Ed!<@Y$s7QBl!4)V+!.]TNs+13$s+13a +s*t~> +pAY*mo`"pls8E#trr<&trr<&trr<&ns82itrrE&uquHcs!!*#u!!)ut!s&B$!;c]s!<3&urrW9$ +rrE#t"9AK%!<<#urVult!WN0!rr<&us8;rfs8N(Ms7QBl!4)Y(!.k0$s+13$s1\M`~> +pAY*mo`"pls8E#trr<&trr<&trr<&ns82itrrE&uquHcs!!*#u!!)ut!s&B$!;c]s!<3&urrW9$ +rrE#t"9AK%!<<#urVult!WN0!rr<&us8;rfs8N(Ms7QBl!4)Y(!.k0$s+13$s1\M`~> +pAY*mo`"pls8E#trr<&trr<&trr<&ns82itrrE&uquHcs!!*#u!!)ut!s&B$!;c]s!<3&urrW9$ +rrE#t"9AK%!<<#urVult!WN0!rr<&us8;rfrrE+MJcGEC!!'>)!W[b$JcC<$JcC<$^Ai]~> +pAY*mo`"mkm/R"aT`>#lJcGEC!!'>)rW%NLJcC<$JcEIaJ,~> +pAY*mo`"mkm/R"aT`>#lJcGEC!!'>)rW%NLJcC<$JcEIaJ,~> +pAY*mo`"mkm/R"aT`5#mJ:[anrr<&)rrN1NJ:[`Os+13$s1\M`~> +pAY*mo`"mkL]@ASJcGEC!!'>)rW%NLJcC<$JcEIaJ,~> +pAY*mo`"mkL]@ASJcGEC!!'>)rW%NLJcC<$JcEIaJ,~> +pAY*mo`"mkL]7ATJ:[anrr<&)rrN1NJ:[`Os+13$s1\M`~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +p]#dEo`0RCp&>!lZN'n(JcC<$JcC<$^Ai]~> +p]#dEo`0RCp&>!lZN'n(JcC<$JcC<$^Ai]~> +p]#dEp&G)CJcGEC!!'>)!W[b$JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&>!lZN'n(JcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QBl!4)V+!.]TNs+13$s+13as*t~> +pAY*mJcGQGrr@WMp&D/pJcC<$JcC<$^Ai]~> +pAY*mJcGQGrr@WMp&D/pJcC<$JcC<$^Ai]~> +pAY*mJcGQG!<@Y$s7QDr!<7S#s+13$s+13as*t~> +pAY*mJcGQGrr@WMo)Grpnc,fnli3ITe,QWPgAeAWOoKq~> +pAY*mJcGQGrr@WMo)Grpnc,fnli3ITe,QWPgAeAWOoKq~> +pAY*mJcGQG!<@Y$s762pJFWZD!:0ZT!7UtP!8@IW!0R;3~> +pAY*mJcGQGrr@WMo)Grpnc&RhZMsn)li-qbT)Scje,KCJZMsn)gA_-QZMsn)OoKq~> +pAY*mJcGQGrr@WMo)Grpnc&RhZMsn)li-qbT)Scje,KCJZMsn)gA_-QZMsn)OoKq~> +pAY*mJcGQG!<@Y$s762pJFWX>!4)V)!:0Xb!1s2j!7UrJ!4)V)!8@GQ!4)V)!0R;3~> +pAY*mJcGQGrr@WMcN!kDkl1V_ir8uYZMsn)li-qbT)Scje,KCJZMsn)gA_-QZMsn)OoKq~> +pAY*mJcGQGrr@WMcN!kDkl1V_ir8uYZMsn)li-qbT)Scje,KCJZMsn)gA_-QZMsn)OoKq~> +pAY*mJcGQG!<@Y$s3CZDpuq_Xpu;;R!4)V)!:0Xb!1s2j!7UrJ!4)V)!8@GQ!4)V)!0R;3~> +pAY*mJcGQGrr@WMdf91EkPkM^ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrr@WMdf91EkPkM^ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s3grEpuhYWpu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrr@WMf)P[Krr3$"rrD9_rW)!Y!!)TirrDWiqZ,:KrW)?c!!)rsrrDBbrrE&urrDNf +rrD]kquHKkrrDrrrrDusrW(LK!!)iprrDBbrrE&urrDNfrrDHdrW(aR!!)rsrrDZjq#KRWrW!0& +!!*'!!!)ZkrW&/^J,~> +pAY*mJcGQGrr@WMf)P[Krr3$"rrD9_rW)!Y!!)TirrDWiqZ,:KrW)?c!!)rsrrDBbrrE&urrDNf +rrD]kquHKkrrDrrrrDusrW(LK!!)iprrDBbrrE&urrDNfrrDHdrW(aR!!)rsrrDZjq#KRWrW!0& +!!*'!!!)ZkrW&/^J,~> +pAY*mJcGQG!<@Y$s475Kq#:9pq#C-Qs8MfRrr<&is8N)is8)fKrrN1NJF3@:!;uls!:0[b!<3#u +!:Tsf!;- +pAY*mJcGQGrr@ZNrrDZjrrD?arW)iq!W`6#oDegjp](3mj8T)Zo)J^ir;Zcsrr;uurr;uuk5YG] +nc/RgmJd.dr;Zcsr;Zcsrr;uup](6nrr;uun,NCfo`+pks8W*!q#C?oqu?Zrr;Z`rec,ULq>^Hp +r;Zcsrr;uup](6nrr;uun,NCfmJm.ch#@?Sr;Zcsr;Zcsrr;uur;ZcsjT#5[qZ$Qqo`+mjPQ-.~> +pAY*mJcGQGrr@ZNrrDZjrrD?arW)iq!W`6#oDegjp](3mj8T)Zo)J^ir;Zcsrr;uurr;uuk5YG] +nc/RgmJd.dr;Zcsr;Zcsrr;uup](6nrr;uun,NCfo`+pks8W*!q#C?oqu?Zrr;Z`rec,ULq>^Hp +r;Zcsrr;uup](6nrr;uun,NCfmJm.ch#@?Sr;Zcsr;Zcsrr;uur;ZcsjT#5[qZ$Qqo`+mjPQ-.~> +pAY*mJcGQG!<@Y%s8Vlcs8VlZs8MfjrrVuqq"+Ocq"OgfpuDAS!:p0i!;uls!<3#u!<3#u!9X=] +!:g'j!.]Uerr<&ss8N)ss8N)us8N)ns8N)us8N)fs8N)ks8N*!s8N)os8N)rs8N)srrN1NJCXZ" +!;ZZp!;uls!<3#u!;HNn!<3#u!:Tsf!:Bdf!.]UTrr<&ss8N)ss8N)us8N)ss8N)[s8N)qs8N)k +rrN1NJ +pAY*mJcGQGrr@]O!s&B$!;6?n!<3&Zs8N'"rrDZj!W`6#q#:HsrrE'!jSo2[o)J^irVufr!<;ut +s8W*!rVultrr2rurr;rtrr;uu"TJH%s8W#to)J[hmJd.dr;ZcsrVufr!<;ut!ri6#rr3*$s8N'! +rr;uurr;osrVufrrr;uurr2rurr;oss8W*!s8W*!!ri6#rr3*$s8N'!rr;lrr;Z`rec,ULq>^Hp +rVufr!<;ut!ri6#rr3*$s8N'!rr;uurr;osrVufrrr;uurr2rurr;osq#C +pAY*mJcGQGrr@]O!s&B$!;6?n!<3&Zs8N'"rrDZj!W`6#q#:HsrrE'!jSo2[o)J^irVufr!<;ut +s8W*!rVultrr2rurr;rtrr;uu"TJH%s8W#to)J[hmJd.dr;ZcsrVufr!<;ut!ri6#rr3*$s8N'! +rr;uurr;osrVufrrr;uurr2rurr;oss8W*!s8W*!!ri6#rr3*$s8N'!rr;lrr;Z`rec,ULq>^Hp +rVufr!<;ut!ri6#rr3*$s8N'!rr;uurr;osrVufrrr;uurr2rurr;osq#C +pAY*mJcGQG!<@Y&rr`&rs7l9e!r2fjj8]/S!WVrdrrVuqq"Xjlq#C-hpuMGT!:p0i!<)rr!!*&t +!<<*!!<)rt!<2uu!<3#t!<3#u!!N<%!<<)t!:p-k!.]Uerr<&ss8N)ts8;ots8;p!rr<&urr`?% +rr<&us8N)us8;rrs8;rss8N)urr<&us8;rts8N*!s8N'#rr<&urr`?%rr<&us82lprrN1NJCXZ" +!;ZZp!<)rr!!*&t!!<0#!<3!$!<<'!!<3#u!<3#s!<)rr!<3#u!<2uu!<3#s!;QQq!.]UTrr<&s +s8N)ts8;ots8;rrs8N)ts8N'#rrE)t!<)rr!<<)u!<<)s!!`H'!<<'!!<3#t!;c]s!.]T`s*t~> +pAY*mJcGQGrr@`P!!)ut!!)Zk!!)*[!!*#u!!)Qh!!)fo"9AH%rrD-[!!)TirrDusrrE&urrE&u +rrE#trrE&u#lt#*!!*$!!<<)u!!`H'!<<'!!:g*g!:Bdd!;uls!;uls!<3#u!<<*!!<3!$!<<'! +!<3#u!<<*!!<<*!!<<*!!<<*!!<<*!!<<'!!<3#u!;uls!<<'%!<<'!!<3!,!<<'!!<<'!!<<'! +!;ulr!7h)L!;ZZp!;uls!<3#u!<<*!!<3!$!<<'!!<3#u!<<*!!<<*!!<<*!!<<*!!<<*!!<<'! +!<3#u!;6Bk!8RSS!;uls!;uls!<3#u!;uls!<)rs!;uls!<<*!!<)rt!!`H'!<<'!!<3#u!<<*! +!<<*!!!<0#!;lfq!0dG5~> +pAY*mJcGQGrr@`P!!)ut!!)Zk!!)*[!!*#u!!)Qh!!)fo"9AH%rrD-[!!)TirrDusrrE&urrE&u +rrE#trrE&u#lt#*!!*$!!<<)u!!`H'!<<'!!:g*g!:Bdd!;uls!;uls!<3#u!<<*!!<3!$!<<'! +!<3#u!<<*!!<<*!!<<*!!<<*!!<<*!!<<'!!<3#u!;uls!<<'%!<<'!!<3!,!<<'!!<<'!!<<'! +!;ulr!7h)L!;ZZp!;uls!<3#u!<<*!!<3!$!<<'!!<3#u!<<*!!<<*!!<<*!!<<*!!<<*!!<<'! +!<3#u!;6Bk!8RSS!;uls!;uls!<3#u!;uls!<)rs!;uls!<<*!!<)rt!!`H'!<<'!!<3#u!<<*! +!<<*!!!<0#!;lfq!0dG5~> +pAY*mJcGQG!<@Y'rrDimrrDidrrDiTrrDinrrDiarrDihrri,sq#C-Mrr<&is8N)ss8N)us8N)u +s8N)ts8N)urs8]*rr<'!!!*'!rW!0&!!*'!!!)Qh!W[b$mJd.dr;Zcsr;Zcsrr;uus8W*!rr3*$ +s8N'!rr;uus8W*!s8W*!s8W*!s8W*!s8W*!s8N'!rr;uur;Zcss8N3%s8N'!rr3B,s8N'!s8N'! +s8N'!r;QfuJ:Q2M!!)iprrDusrrE&urrE*!rrE&u"9AK%!!*#urrE*!rrE*!rrE*!rrE*!rrE*! +rrE*!!!*#urrD`l!W[b$h#@?Sr;Zcsr;Zcsrr;uur;ZcsrVuisr;Zcss8W*!rVult#6+Z's8N'! +rr;uus8W*!s8W*!!ri6#qu6]tJ:Nj`J,~> +pAY*mJcGQGrr@`P!!)ut!!)Zk!!)-\!!*#u!!)Ng!!)ip"p"]'!<<'!jo5;\o)J^ir;Zcsrr;uu +rr;lrs8W*!!WN0!s8N'/rr<'!rr<'!rr<'!rr<&hs8E#crr<&ss8N)ss8N)us8N)us8N'"rrE&u +q>gQqrrE*!rrE*!rrE*!rrE*!rr<*"!<)rs!<)rr!<)rt!!3*"rr;uus8W*!s8W*!r;Z`rec,UL +q>^Hpr;Zcsrr;uurr;uu!WN0!s7u`qs8N*!s8N*!s8N*!s8N*!s8N'"rrE#trW)ZlrW(aR!!)rs +rrDusrrE&urrDusrrE#trrDrrrrE*!rrE#trr<9'!!*'!!!*#urrE*!rrE*!rr<-#!!)orrW&/^ +J,~> +pAY*mJcGQGrr@`P!!)ut!!)Zk!!)-\!!*#u!!)Ng!!)ip"p"]'!<<'!jo5;\o)J^ir;Zcsrr;uu +rr;lrs8W*!!WN0!s8N'/rr<'!rr<'!rr<'!rr<&hs8E#crr<&ss8N)ss8N)us8N)us8N'"rrE&u +q>gQqrrE*!rrE*!rrE*!rrE*!rr<*"!<)rs!<)rr!<)rt!!3*"rr;uus8W*!s8W*!r;Z`rec,UL +q>^Hpr;Zcsrr;uurr;uu!WN0!s7u`qs8N*!s8N*!s8N*!s8N*!s8N'"rrE#trW)ZlrW(aR!!)rs +rrDusrrE&urrDusrrE#trrDrrrrE*!rrE#trr<9'!!*'!!!*#urrE*!rrE*!rr<-#!!)orrW&/^ +J,~> +pAY*mJcGQG!<@Y'rrDimrrDidrrDiUrrDinrrDi`rrDiirs&8us7lWopuVMU!:p0i!;uls!<3#u +!<3#r!<<*!!!3*"rr;uu%fZM/s8N'!s8N'!s8N'!nc&XjJ:R%e!!)rsrrDusrrE&urrE&urr<*" +!<3#p!<<*!!<<*!!<<*!!<<*!!<<*!!!3*"rVuisrVufrrVult!WN0!s8N*!s8N*!s8N)srrN1N +JCXZ"!;ZZp!;uls!<3#u!<3#u!!3*"rr;fps8W*!s8W*!s8W*!s8W*!s8W*!!WN/us8E#lrrN1N +JDC/)!;uls!;uls!<3#u!;uls!<)rt!;lfr!<<*!!<)rt!!`H'!<<'!!<3#u!<<*!!<<*!!!<0# +!;lct!.]T`s*t~> +pAY*mJcGQGrr@`P!!)ut!!)Zk!!)3^rrE#t!!)Ng!!)ip"p"]'!<<'!jo5;\o)J^ir;Zcsrr;uu +rr;uur;Zcs!WN0!s82lss8N*!s8N*!s8N)hs8E#crr<&ss8N)ss8N)us8N)us8N'"rrE&urrE&u +rrE*!rrE*!rrE*!rrE*!rrE*!r;ccqrW)rtrrE*!!!*#urr<*"!<3#u!<<*!!<<*!!;ulr!7h)L +!;ZZp!;uls!<3#u!<3#u!!3*"rr;uurr;uus8W*!s8W*!s8W*!s8W*!s8W#tr;Z`rp](3mh#@?S +r;Zcsr;Zcsrr;uur;ZcsrVultrVufrs8W*!rVucqs8W*!rr;uus8W*!s8Vusqu?WqPQ-.~> +pAY*mJcGQGrr@`P!!)ut!!)Zk!!)3^rrE#t!!)Ng!!)ip"p"]'!<<'!jo5;\o)J^ir;Zcsrr;uu +rr;uur;Zcs!WN0!s82lss8N*!s8N*!s8N)hs8E#crr<&ss8N)ss8N)us8N)us8N'"rrE&urrE&u +rrE*!rrE*!rrE*!rrE*!rrE*!r;ccqrW)rtrrE*!!!*#urr<*"!<3#u!<<*!!<<*!!;ulr!7h)L +!;ZZp!;uls!<3#u!<3#u!!3*"rr;uurr;uus8W*!s8W*!s8W*!s8W*!s8W#tr;Z`rp](3mh#@?S +r;Zcsr;Zcsrr;uur;ZcsrVultrVufrs8W*!rVucqs8W*!rr;uus8W*!s8Vusqu?WqPQ-.~> +pAY*mJcGQG!<@Y'rrDimrrDidrrDiWs8VlmrrDi`rrDiirs&8us7lWopuVMU!:p0i!;uls!<3#u +!<3#u!;uls!!3*"rr;lrs8W*!s8W*!s8W*!nc&XjJ:R%e!!)rsrrDusrrE&urrE&urr<*"!<3#u +!<3#u!<<*!!<<*!!<<*!!<<*!!<<)t!;ulr!<3#u!<<'!!<3#u!!3*"rr;uus8W*!s8W*!r;Qfu +J:Q2M!!)iprrDusrrE&urrE&urr<*"!<3#u!<3#u!<<*!!<<*!!<<*!!<<*!!<<)t!;ulr!;HKp +!.]UTrr<&ss8N)ss8N)us8N)ss8N)ts8N)ts8;rts8N)ts82lss8N)us8N*!s8N*!s82lorrN1N +J +pAY*mJcGQGrr@`P!!)ut!!)Zk!!)6_!!)rs!!)Kf!!)ip"p"]'!<<'!jo5;\o)J^ir;Zcsrr;uu +rr;uur;Zcs!WN0!s8N)ss8N*!s8N*!s8N)hs8E#crr<&ss8N)ss8N)us8N)us8N'"rrE&urrE&u +rrE*!rrE*!rrE*!rrE*!rrE*!rr<-#!!)rsrW)uurrE*!rrE*!rr<*"!<3#u!<<*!!<<*!!;ulr +!7h)L!;ZZp!;uls!<3#u!<3#u!!3*"rr;uurr;uus8W*!s8W*!s8W*!s8W*!s8W*!!ri6#r;Z`r +q#C +pAY*mJcGQGrr@`P!!)ut!!)Zk!!)6_!!)rs!!)Kf!!)ip"p"]'!<<'!jo5;\o)J^ir;Zcsrr;uu +rr;uur;Zcs!WN0!s8N)ss8N*!s8N*!s8N)hs8E#crr<&ss8N)ss8N)us8N)us8N'"rrE&urrE&u +rrE*!rrE*!rrE*!rrE*!rrE*!rr<-#!!)rsrW)uurrE*!rrE*!rr<*"!<3#u!<<*!!<<*!!;ulr +!7h)L!;ZZp!;uls!<3#u!<3#u!!3*"rr;uurr;uus8W*!s8W*!s8W*!s8W*!s8W*!!ri6#r;Z`r +q#C +pAY*mJcGQG!<@Y'rrDimrrDidrrDiXrrDilrrDi_rrDiirs&8us7lWopuVMU!:p0i!;uls!<3#u +!<3#u!;uls!!3*"rr;uur;Zcss8W*!s8W*!nc&XjJ:R%e!!)rsrrDusrrE&urrE&urr<*"!<3#u +!<3#u!<<*!!<<*!!<<*!!<<*!!<<*!!!<0#!;ulr!<<*!!<<*!!<<*!!!3*"rr;uus8W*!s8W*! +r;QfuJ:Q2M!!)iprrDusrrE&urrE&urr<*"!<3#u!<3#u!<<*!!<<*!!<<*!!<<*!!<<*!!!<0# +!;ulr!;QQq!.]UTrr<&ss8N)ss8N)us8N)ss8N)ts8N)us8N''rr<'!rr<&ts8N)ss8N)us8N*! +s8N*!s8N)orrN1NJ +pAY*mJcGQGrr@`P!!)ut!!)Zk!!)9`!!)rs!!)He!!)lq!!*#u!!*#u!!)0]!!)TirrDusrrE&u +rrE&urrDrrrrE#trrDusrrE*!rrE*!rrDThrW)?c!!)rsrrDusrrE&urrE#trrE#trrE&urrE*! +rrE*!rrE*!rrE*!rrE*!rrE*!rrDusrrE*!rrE*!rrE&urrE#trrE*!rr<'!rW)lrrW(LK!!)ip +rrDusrrE&urrE#trrE#trrE&urrE*!rrE*!rrE*!rrE*!rrE*!rrE*!rrDusrrDiorW(aR!!)rs +rrDusrrE&urrDusrrE#trrE&urr<9'!!*'!!!)utrrDusrrE&urrE*!rrE*!rrDiorW&/^J,~> +pAY*mJcGQGrr@`P!!)ut!!)Zk!!)9`!!)rs!!)He!!)lq!!*#u!!*#u!!)0]!!)TirrDusrrE&u +rrE&urrDrrrrE#trrDusrrE*!rrE*!rrDThrW)?c!!)rsrrDusrrE&urrE#trrE#trrE&urrE*! +rrE*!rrE*!rrE*!rrE*!rrE*!rrDusrrE*!rrE*!rrE&urrE#trrE*!rr<'!rW)lrrW(LK!!)ip +rrDusrrE&urrE#trrE#trrE&urrE*!rrE*!rrE*!rrE*!rrE*!rrE*!rrDusrrDiorW(aR!!)rs +rrDusrrE&urrDusrrE#trrE&urr<9'!!*'!!!)utrrDusrrE&urrE*!rrE*!rrDiorW&/^J,~> +pAY*mJcGQG!<@Y'rrDimrrDidrrDiYrrDilrrDi^rrDijrrDinrrDinrrDiVrr<&is8N)ss8N)u +s8N)us8N)rs8N)ts8N)ss8N*!s8N*!s8N)hrrN1NJF3@:!;uls!;uls!<3#u!<)rt!<)rt!<3#u +!<<*!!<<*!!<<*!!<<*!!<<*!!<<*!!;uls!<<*!!<<*!!<3#u!<)rt!<<*!!!*&u!;uiu!.]UM +rr<&ps8N)ss8N)us8N)ts8N)ts8N)us8N*!s8N*!s8N*!s8N*!s8N*!s8N*!s8N)ss8N)orrN1N +JDC/)!;uls!;uls!<3#u!;uls!<)rt!<3#u!!`H'!<<'!!<)rt!;uls!<3#u!<<*!!<<*!!;QQq +!.]T`s*t~> +pAY*mJcGQGrr@]O!s&B$!;$3j!:0[b!;lcr!:Kje!;c]q!<2uu!<2uu!9X:]!:p0e!<<)u!<<)u +!!*&r!<<*!!;ulq!<<*!!<<*!!<3#t!;$6i!:Bdd!;ulo!<<)u!<<)u!<<*!!<)rt!<3#u!<3#s +!<)rr!<3#u!<3#u!!*&t!<3#r!<)rt!<)rt!<3#u!!<0#!;ulr!7h)L!;ZZl!<<)u!<<)u!<<*! +!<)rt!<3#u!<3#s!<)rr!<3#u!<3#u!!*&t!;HNm!8RSS!;ulo!<<)u!<<)u!<3#u!<)rt!<)rq +!<<)t!<<)t!<<*!!<3#u!<<*!!<3#s!;lfq!0dG5~> +pAY*mJcGQGrr@]O!s&B$!;$3j!:0[b!;lcr!:Kje!;c]q!<2uu!<2uu!9X:]!:p0e!<<)u!<<)u +!!*&r!<<*!!;ulq!<<*!!<<*!!<3#t!;$6i!:Bdd!;ulo!<<)u!<<)u!<<*!!<)rt!<3#u!<3#s +!<)rr!<3#u!<3#u!!*&t!<3#r!<)rt!<)rt!<3#u!!<0#!;ulr!7h)L!;ZZl!<<)u!<<)u!<<*! +!<)rt!<3#u!<3#s!<)rr!<3#u!<3#u!!*&t!;HNm!8RSS!;ulo!<<)u!<<)u!<3#u!<)rt!<)rq +!<<)t!<<)t!<<*!!<3#u!<<*!!<3#s!;lfq!0dG5~> +pAY*mJcGQG!<@Y&rr`&rs7l3c!;Pm[s7lKk!;Q!^!;QEj!;QQn!;QQn!;P^V!!)TiqZ-ZrrW)uu +rVuruqZ-ZrrrDusr;cltrrE*!rrE&urW)Qi!W[b$mJd.dr;ZWos8W&us8W&us8W*!rVultrr;uu +rr;osrVufrrr;uurr;uu!<;utrr;lrrVultrVultrr;uu!ri6#r;QfuJ:Q2M!!)ipqZ-ZrrW)uu +rW)uurrE#trrE&urrE&ur;cfrr;cisrrE&urr<'!r;cTl!W[b$h#@?Sr;ZWos8W&us8W&urr;uu +rVultrVucqs8W#ts8W#ts8W*!rr;uus8W*!rr;osqu6]tJ:Nj`J,~> +pAY*mJcGQGrr@ZNrrE#t!!*#u!!)utquH0b!!)lq!!)KfquH]q!!)ut!!)ut!!)3^!!'>)rW)?c +!!'A*rrDKerW(LK!!'>)rW(aR!!'>)rW&/^J,~> +pAY*mJcGQGrr@ZNrrE#t!!*#u!!)utquH0b!!)lq!!)KfquH]q!!)ut!!)ut!!)3^!!'>)rW)?c +!!'A*rrDKerW(LK!!'>)rW(aR!!'>)rW&/^J,~> +pAY*mJcGQG!<@Y%s8VlmrrDinrrDims8;Z[rrDijrrDi_s8;ZjrrDimrrDimrrDiWrr<&)rrN1N +JF3@:!42_*!:Kjg!.]UMrr<&)rrN1NJDC/)!4)V+!.]T`s*t~> +pAY*mJcGQGrr@WMh#@?Sh>[HTrVlitrVlitkPkM^ZN'n(mJd.d[/^.+mJm.cec,ULZN'n(h#@?S +ZN'n(PQ-.~> +pAY*mJcGQGrr@WMh#@?Sh>[HTrVlitrVlitkPkM^ZN'n(mJd.d[/^.+mJm.cec,ULZN'n(h#@?S +ZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s4mVSptbrMq#13mq#13mpuhYW!4)V+!.]Uerr<&+s8N)drrN1NJCXZ"!4)V+ +!.]UTrr<&)rrN1NJ +pAY*mJcGQGrr@WMhZ*TUfDbgNir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrr@WMhZ*TUfDbgNir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s5*eUpt,NGpu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrr@WMhu +pAY*mJcGQGrr@WMhu +pAY*mJcGQG!<@Y$s53hVpsoBEpu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrr@WMi;WcWeGfLKir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrr@WMi;WcWeGfLKir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s5 +pAY*mJcGQGrr@WMirB#Ye,KCJir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrr@WMirB#Ye,KCJir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s5O(Yps]6Cpu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrr@WMj8T)ZdJj1Hir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrr@WMj8T)ZdJj1Hir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s5X+ZpsK*Apu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrr@WMjSo2[d/O(Gir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrr@WMjSo2[d/O(Gir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s5a1[psB$@pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrr@WMk5YG]ci3tFir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrr@WMk5YG]ci3tFir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s5s@]ps8s?pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrr@WMkPkM^c2RbDir?.]mJiUTec2cPh#FMWPQ-.~> +pAY*mJcGQGrr@WMkPkM^c2RbDir?.]mJiUTec2cPh#FMWPQ-.~> +pAY*mJcGQG!<@Y$s6'C^ps&g=pu;=X!<7T:s-s)hJCX\(!<7T)s0)M'J +pAY*mJcGQGrr@WMkl1V_bl7YCir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrr@WMkl1V_bl7YCir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s60I_prra +pAY*mJcGQGrr@WMlMpkabPqPBir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrr@WMlMpkabPqPBir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s6BXapri[;pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrr@WMli-qbao;>@ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrr@WMli-qbao;>@ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s6K[bprWO9pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrr@WMm/I%caSu5?ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrr@WMm/I%caSu5?ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s6TacprNI8pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrr@WMmf3:ea8Z,>ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrr@WMmf3:ea8Z,>ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s6fpeprEC7pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrr@WMn,E@f`W#o +pAY*mJcGQGrr@WMn,E@f`W#o +pAY*mJcGQG!<@Y$s6osfpr375pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrr@WMnG`Ig`;]f;ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrr@WMnG`Ig`;]f;ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s7$$gpr*14pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrr@WMo)J^i_uB]:ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrr@WMo)J^i_uB]:ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s763ipr!+3pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrr@WMoD\dj_>aK8ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrr@WMoD\dj_>aK8ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s7?6jpqct1pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrr@WMo`"mk_#FB7ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrr@WMo`"mk_#FB7ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s7H +pAY*mJcGQGrr@WMpAb-m^]+96ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrr@WMpAb-m^]+96ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s7ZKmpqQh/pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrr@WMp\t3n^&J'4ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrr@WMp\t3n^&J'4ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s7cNnpq?\-pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrr@WMq#: +pAY*mJcGQGrr@WMq#: +pAY*mJcGQG!<@Y$s7lTopq6V,pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrr@WMqZ$Qq]Dhj2ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrr@WMqZ$Qq]Dhj2ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s8)cqpq-P+pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrr@WMqu6Wr\c2X0ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrr@WMqu6Wr\c2X0ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s82frpppD)pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrr@WMr;Q`s\GlO/ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrr@WMr;Q`s\GlO/ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s8;lsppg>(pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrr@WMrr;uu\,QF.ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrr@WMrr;uu\,QF.ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s8N&upp^8'pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrr@WMs8N'![Jp4,ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrr@WMs8N'![Jp4,ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Y$s8W*!ppL,%pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrr@WM!WN/,rr<&Yrr<&)s8E#crr<%js8E#Krr<&)s8E#Rrr<&)s8E"^s*t~> +pAY*mJcGQGrr@WM!WN/,rr<&Yrr<&)s8E#crr<%js8E#Krr<&)s8E#Rrr<&)s8E"^s*t~> +pAY*mJcGQG!<@Y$rrN/p[/U.$ir8uYZMst+J:R%e!!&Vj!W[b$ec,ULZMst+J:QGT!!'>)!W[b$ +PQ-.~> +pAY*mJcGQGrr@ZNrrBD*!!)$Y!!'>)rW)?c!!&VjrW(LK!!'>)rW(aR!!'>)rW&/^J,~> +pAY*mJcGQGrr@ZNrrBD*!!)$Y!!'>)rW)?c!!&VjrW(LK!!'>)rW(aR!!'>)rW&/^J,~> +pAY*mJcGQG!<@Y%s8Vl#rrDiRrr<&)rrN1NJF3@:!1s2l!.]UMrr<&)rrN1NJDC/)!4)V+!.]T` +s*t~> +pAY*mJcGQGrr@]O!!';(!!)$Y!!'>)rW)?c!!&VjrW(LK!!'>)rW(aR!!'>)rW&/^J,~> +pAY*mJcGQGrr@]O!!';(!!)$Y!!'>)rW)?c!!&VjrW(LK!!'>)rW(aR!!'>)rW&/^J,~> +pAY*mJcGQG!<@Y&rrDi!rrDiRrr<&)rrN1NJF3@:!1s2l!.]UMrr<&)rrN1NJDC/)!4)V+!.]T` +s*t~> +pAY*mJcGQGrr@`P!!'8'!!)$Y!!'>)rW)?c!!&VjrW(LK!!'>)rW(aR!!'>)rW&/^J,~> +pAY*mJcGQGrr@`P!!'8'!!)$Y!!'>)rW)?c!!&VjrW(LK!!'>)rW(aR!!'>)rW&/^J,~> +pAY*mJcGQG!<@Y'rrDhurrDiRrr<&)rrN1NJF3@:!1s2l!.]UMrr<&)rrN1NJDC/)!4)V+!.]T` +s*t~> +pAY*mJcGQGrr@fRrrB8&!!)$YXoRkhRK1n +pAY*mJcGQGrr@fRrrB8&!!)$YXoRkhRK1n +pAY*mJcGQG!<@Y)s8VktrrDiRs0)M'JF3B,!<7T"s0)M'JDC1/!<7S5s*t~> +pAY*mJcGQGrr@iS!!'/$!!)$Y!!'>)rW)?c!!&VjrW(LK!!'>)rW(aR!!'>)rW&/^J,~> +pAY*mJcGQGrr@iS!!'/$!!)$Y!!'>)rW)?c!!&VjrW(LK!!'>)rW(aR!!'>)rW&/^J,~> +pAY*mJcGQG!<@Y*rrDhrrrDiRrr<&)rrN1NJF3@:!1s2l!.]UMrr<&)rrN1NJDC/)!4)V+!.]T` +s*t~> +pAY*mJcGQGrr@lT!!',#!!)$Y!!'>)rW)?c!!&VjrW(LK!!'>)rW(aR!!'>)rW&/^J,~> +pAY*mJcGQGrr@lT!!',#!!)$Y!!'>)rW)?c!!&VjrW(LK!!'>)rW(aR!!'>)rW&/^J,~> +pAY*mJcGQG!<@Y+rrDhqrrDiRrr<&)rrN1NJF3@:!1s2l!.]UMrr<&)rrN1NJDC/)!4)V+!.]T` +s*t~> +pAY*mJcGQGrr@rVrrB,"!!)$Y!!'>)rW)?c!!&VjrW(LK!!'>)rW(aR!!'>)rW&/^J,~> +pAY*mJcGQGrr@rVrrB,"!!)$Y!!'>)rW)?c!!&VjrW(LK!!'>)rW(aR!!'>)rW&/^J,~> +pAY*mJcGQG!<@Y-s8VkprrDiRrr<&)rrN1NJF3@:!1s2l!.]UMrr<&)rrN1NJDC/)!4)V+!.]T` +s*t~> +pAY*mJcGQGrr@uW!!'"u!!)$Y!!'>)rW)?c!!&VjrW(LK!!'>)rW(aR!!'>)rW&/^J,~> +pAY*mJcGQGrr@uW!!'"u!!)$Y!!'>)rW)?c!!&VjrW(LK!!'>)rW(aR!!'>)rW&/^J,~> +pAY*mJcGQG!<@Y.rrDhnrrDiRrr<&)rrN1NJF3@:!1s2l!.]UMrr<&)rrN1NJDC/)!4)V+!.]T` +s*t~> +pAY*mJcGQGrrCFGJH5QIl2L_`ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrrCFGJH5QIl2L_`ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Yss+(1I!9sL`pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1N +J +pAY*mJcGQGrrCFG!!%TMrVlitl2L_`ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrrCFG!!%TMrVlitl2L_`ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Ysrr<%Ms8Drt!9sL`pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&) +rrN1NJ +pAY*mJcGQGrrCFG!!%TMrVlitl2L_`ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrrCFG!!%TMrVlitl2L_`ir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Ysrr<%Ms8Drt!9sL`pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&) +rrN1NJ +pAY*mJcGQGrrCFG!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrrCFG!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Ysrr<%Ms8Ds!!.]UcrrDiRrr<&)rrN1NJF3@:!1s2l!.]UMrr<&)rrN1NJDC/) +!4)V+!.]T`s*t~> +pAY*mJcGQGrrCFG!!(dRrrDEcq#J;3rW)9a!!)$Y!!'>)rW)?c!!&VjrW(LK!!'>)rW(aR!!'>) +rW&/^J,~> +pAY*mJcGQGrrCFG!!(dRrrDEcq#J;3rW)9a!!)$Y!!'>)rW)?c!!&VjrW(LK!!'>)rW(aR!!'>) +rW&/^J,~> +pAY*mJcGQG!<@Ysrr<&Rs8N)cs7lZ3rrN1NJF!48pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+ +!.]UTrr<&)rrN1NJ +pAY*mJcGQGrrCFG!!(dRrrDusrrE&urrD`lrrBh6rW)9a!!)$Y!!'>)rW)?c!!&VjrW(LK!!'>) +rW(aR!!'>)rW&/^J,~> +pAY*mJcGQGrrCFG!!(dRrrDusrrE&urrD`lrrBh6rW)9a!!)$Y!!'>)rW)?c!!&VjrW(LK!!'>) +rW(aR!!'>)rW&/^J,~> +pAY*mJcGQG!<@Ysrr<&Rs8N)ss8N)us8N)ls8N)6rrN1NJF!48pu;;R!4)V+!.]Uerr<%jrrN1N +JCXZ"!4)V+!.]UTrr<&)rrN1NJ +pAY*mJcGQGrrCFG!!(dRrrE#tr;Zitr;Zp!!!*#u!!)utrrE#trr<-#!<;utrVufrs8W&ug&M$O +li-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrrCFG!!(dRrrE#tr;Zitr;Zp!!!*#u!!)utrrE#trr<-#!<;utrVufrs8W&ug&M$O +li-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Ysrr<&Rs8N)ts8;ots8;p!rr<&urr<&ts8N)ts8N'#rrE)t!<)rr!<<)u!87AR +!.]UcrrDiRrr<&)rrN1NJF3@:!1s2l!.]UMrr<&)rrN1NJDC/)!4)V+!.]T`s*t~> +pAY*mJcGQGrrCFG!!(dRrrDusrrE&urrE*!rrE&u!!)utrrE#trW)lrrrE*!rrE#trr<-#!!(aQ +rW)9a!!)$Y!!'>)rW)?c!!&VjrW(LK!!'>)rW(aR!!'>)rW&/^J,~> +pAY*mJcGQGrrCFG!!(dRrrDusrrE&urrE*!rrE&u!!)utrrE#trW)lrrrE*!rrE#trr<-#!!(aQ +rW)9a!!)$Y!!'>)rW)?c!!&VjrW(LK!!'>)rW(aR!!'>)rW&/^J,~> +pAY*mJcGQG!<@Ysrr<&Rs8N)ss8N)us8N*!s8N)urr<&ts8N)ts8E#rs8N*!s8N)ts8N'#rr<&Q +rrN1NJF!48pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1NJ +pAY*mJcGQGrrCFG!!(dRrrDusrrE&urrE&urr<*"!;uls!<)rt!;lfr!<<*!!<)rt!!<0#!8@JP +!:0Xb!94"Y!4)Y(!:Bdd!1s5i!7h)L!4)Y(!8RSS!4)Y(!0dG5~> +pAY*mJcGQGrrCFG!!(dRrrDusrrE&urrE&urr<*"!;uls!<)rt!;lfr!<<*!!<)rt!!<0#!8@JP +!:0Xb!94"Y!4)Y(!:Bdd!1s5i!7h)L!4)Y(!8RSS!4)Y(!0dG5~> +pAY*mJcGQG!<@Ysrr<&Rs8N)ss8N)us8N)us8N'"rrDusrrE#trrDrrrrE*!rrE#trr<-#!!(aQ +!W[b$li-t[ir8uYZMst+J:R%e!!&Vj!W[b$ec,ULZMst+J:QGT!!'>)!W[b$PQ-.~> +pAY*mJcGQGrrCFG!!(dRrrDusrrE&urrE&urr<*"!;uls!<)rt!<)rr!<<*!!<)rq!8@JP!:0Xb +!94"Y!4)Y(!:Bdd!1s5i!7h)L!4)Y(!8RSS!4)Y(!0dG5~> +pAY*mJcGQGrrCFG!!(dRrrDusrrE&urrE&urr<*"!;uls!<)rt!<)rr!<<*!!<)rq!8@JP!:0Xb +!94"Y!4)Y(!:Bdd!1s5i!7h)L!4)Y(!8RSS!4)Y(!0dG5~> +pAY*mJcGQG!<@Ysrr<&Rs8N)ss8N)us8N)us8N'"rrDusrrE#trrE#tr;cltrrE#tquGIN!W[b$ +li-t[ir8uYZMst+J:R%e!!&Vj!W[b$ec,ULZMst+J:QGT!!'>)!W[b$PQ-.~> +pAY*mJcGQGrrCFG!!(dRrrDusrrE&urrE&urr<*"!;uls!<)rt!<3#u!!`H'!<<'!!<)rt!8%8M +!:0Xb!94"Y!4)Y(!:Bdd!1s5i!7h)L!4)Y(!8RSS!4)Y(!0dG5~> +pAY*mJcGQGrrCFG!!(dRrrDusrrE&urrE&urr<*"!;uls!<)rt!<3#u!!`H'!<<'!!<)rt!8%8M +!:0Xb!94"Y!4)Y(!:Bdd!1s5i!7h)L!4)Y(!8RSS!4)Y(!0dG5~> +pAY*mJcGQG!<@Ysrr<&Rs8N)ss8N)us8N)us8N'"rrDusrrE#trrE&urr<9'!!*'!!!)utrrC[N +!W[b$li-t[ir8uYZMst+J:R%e!!&Vj!W[b$ec,ULZMst+J:QGT!!'>)!W[b$PQ-.~> +pAY*mJcGQGrrCFG!!(dRrrDusrrE&urrE#trrDrrrrE#trrE&urr<9'!!*'!!!)utrrC[NrW)9a +!!)$Y!!'>)rW)?c!!&VjrW(LK!!'>)rW(aR!!'>)rW&/^J,~> +pAY*mJcGQGrrCFG!!(dRrrDusrrE&urrE#trrDrrrrE#trrE&urr<9'!!*'!!!)utrrC[NrW)9a +!!)$Y!!'>)rW)?c!!&VjrW(LK!!'>)rW(aR!!'>)rW&/^J,~> +pAY*mJcGQG!<@Ysrr<&Rs8N)ss8N)us8N)ts8N)rs8N)ts8N)us8N''rr<'!rr<&ts8N)NrrN1N +JF!48pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1NJ +pAY*mJcGQGrrCFG!!(dRqZ-ZrrW)uurW)uurrDrrrrE#trrE#tquHcsr;cltr;bROrW)9a!!)$Y +!!'>)rW)?c!!&VjrW(LK!!'>)rW(aR!!'>)rW&/^J,~> +pAY*mJcGQGrrCFG!!(dRqZ-ZrrW)uurW)uurrDrrrrE#trrE#tquHcsr;cltr;bROrW)9a!!)$Y +!!'>)rW)?c!!&VjrW(LK!!'>)rW(aR!!'>)rW&/^J,~> +pAY*mJcGQG!<@Ysrr<&Rs8)frs8E#us8E#us8N)rs8N)ts8N)ts82lss8;rts8;rOrrN1NJF!48 +pu;;R!4)V+!.]Uerr<%jrrN1NJCXZ"!4)V+!.]UTrr<&)rrN1NJ +pAY*mJcGQGrrCFG!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrrCFG!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Ysrr<%Ms8Ds!!.]UcrrDiRrr<&)rrN1NJF3@:!1s2l!.]UMrr<&)rrN1NJDC/) +!4)V+!.]T`s*t~> +pAY*mJcGQGrrCFG!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrrCFG!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Ysrr<%Ms8Ds!!.]UcrrDiRrr<&)rrN1NJF3@:!1s2l!.]UMrr<&)rrN1NJDC/) +!4)V+!.]T`s*t~> +pAY*mJcGQGrrDHdrrDZjrrDcm!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?S +ZN'n(PQ-.~> +pAY*mJcGQGrrDHdrrDZjrrDcm!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?S +ZN'n(PQ-.~> +pAY*mJcGQG!<@Z;s8Vlcs8Vlfrr<%Ms8Ds!!.]UcrrDiRrr<&)rrN1NJF3@:!1s2l!.]UMrr<&) +rrN1NJDC/)!4)V+!.]T`s*t~> +pAY*mJcGQGrrDKe!s&B$!;6?n!<3&mrr<%Ms8Dus!:0Xb!94"Y!4)Y(!:Bdd!1s5i!7h)L!4)Y( +!8RSS!4)Y(!0dG5~> +pAY*mJcGQGrrDKe!s&B$!;6?n!<3&mrr<%Ms8Dus!:0Xb!94"Y!4)Y(!:Bdd!1s5i!7h)L!4)Y( +!8RSS!4)Y(!0dG5~> +pAY*mJcGQG!<@Z)!W[b$PQ-.~> +pAY*mJcGQGrrDNf!!)ut!!)Zk!!)`m!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n( +h#@?SZN'n(PQ-.~> +pAY*mJcGQGrrDNf!!)ut!!)Zk!!)`m!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n( +h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Z=rrDimrrDidrrDifrr<%Ms8Ds!!.]UcrrDiRrr<&)rrN1NJF3@:!1s2l!.]UM +rr<&)rrN1NJDC/)!4)V+!.]T`s*t~> +pAY*mJcGQGrrDNf!!)ut!!)Zk!!)`m!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n( +h#@?SZN'n(PQ-.~> +pAY*mJcGQGrrDNf!!)ut!!)Zk!!)`m!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n( +h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Z=rrDimrrDidrrDifrr<%Ms8Ds!!.]UcrrDiRrr<&)rrN1NJF3@:!1s2l!.]UM +rr<&)rrN1NJDC/)!4)V+!.]T`s*t~> +pAY*mJcGQGrrDNf!!)ut!!)Zk!!)`m!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n( +h#@?SZN'n(PQ-.~> +pAY*mJcGQGrrDNf!!)ut!!)Zk!!)`m!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n( +h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Z=rrDimrrDidrrDifrr<%Ms8Ds!!.]UcrrDiRrr<&)rrN1NJF3@:!1s2l!.]UM +rr<&)rrN1NJDC/)!4)V+!.]T`s*t~> +pAY*mJcGQGrrDNf!!)ut!!)Zk!!)`m!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n( +h#@?SZN'n(PQ-.~> +pAY*mJcGQGrrDNf!!)ut!!)Zk!!)`m!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n( +h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Z=rrDimrrDidrrDifrr<%Ms8Ds!!.]UcrrDiRrr<&)rrN1NJF3@:!1s2l!.]UM +rr<&)rrN1NJDC/)!4)V+!.]T`s*t~> +pAY*mJcGQGrrDNf!!)ut!!)Zk!!)`m!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n( +h#@?SZN'n(PQ-.~> +pAY*mJcGQGrrDNf!!)ut!!)Zk!!)`m!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n( +h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Z=rrDimrrDidrrDifrr<%Ms8Ds!!.]UcrrDiRrr<&)rrN1NJF3@:!1s2l!.]UM +rr<&)rrN1NJDC/)!4)V+!.]T`s*t~> +pAY*mJcGQGrrDKe!s&B$!;$3j!;?GD!;QW\rr<&Yrr<&)s8E#crr<%js8E#Krr<&)s8E#Rrr<&) +s8E"^s*t~> +pAY*mJcGQGrrDKe!s&B$!;$3j!;?GD!;QW\rr<&Yrr<&)s8E#crr<%js8E#Krr<&)s8E#Rrr<&) +s8E"^s*t~> +pAY*mJcGQG!<@Z)!W[b$mJd.dT)SilJ:Q2M!!'>) +!W[b$h#@?SZMst+J:Nj`J,~> +pAY*mJcGQGrrDHdrrE#t!!*#u!!)utquHNl!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,UL +ZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrrDHdrrE#t!!*#u!!)utquHNl!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,UL +ZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Z;s8VlmrrDinrrDims8;Zerr<%Ms8Ds!!.]UcrrDiRrr<&)rrN1NJF3@:!1s2l +!.]UMrr<&)rrN1NJDC/)!4)V+!.]T`s*t~> +pAY*mJcGQGrrCFG!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrrCFG!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Ysrr<%Ms8Ds!!.]UcrrDiRrr<&)rrN1NJF3@:!1s2l!.]UMrr<&)rrN1NJDC/) +!4)V+!.]T`s*t~> +pAY*mJcGQGrrCFG!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQGrrCFG!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n(PQ-.~> +pAY*mJcGQG!<@Ysrr<%Ms8Ds!!.]UcrrDiRrr<&)rrN1NJF3@:!1s2l!.]UMrr<&)rrN1NJDC/) +!4)V+!.]T`s*t~> +pAY*mJcGQGrrCjSrrDfn!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n( +PQ-.~> +pAY*mJcGQGrrCjSrrDfn!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n( +PQ-.~> +pAY*mJcGQG!<@Z*s8Vlgrr<%Ms8Ds!!.]UcrrDiRrr<&)rrN1NJF3@:!1s2l!.]UMrr<&)rrN1N +JDC/)!4)V+!.]T`s*t~> +pAY*mJcGQGrrCdQrrDlp!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n( +PQ-.~> +pAY*mJcGQGrrCdQrrDlp!!%TMrVuisli-qbir8uYZN'n(mJd.dT)\ciec,ULZN'n(h#@?SZN'n( +PQ-.~> +pAY*mJcGQG!<@Z(s8Vlirr<%Ms8Ds!!.]UcrrDiRrr<&)rrN1NJF3@:!1s2l!.]UMrr<&)rrN1N +JDC/)!4)V+!.]T`s*t~> +pAY*mJcGQGrrC^OrW)lr!!%TMrVuisli-qbir?.]mJiUTec2cPh#FMWPQ-.~> +pAY*mJcGQGrrC^OrW)lr!!%TMrVuisli-qbir?.]mJiUTec2cPh#FMWPQ-.~> +pAY*mJcGQG!<@Z&s8Mfkrr<%Ms8Ds!!.]UcrrDiRs0)M'JF3B,!<7T"s0)M'JDC1/!<7S5s*t~> +pAY*mJcGQGrrCULrrE&u!!%TMrVuisli-qbhuBq]lMmCTdf6QPg&J;WPQ-.~> +pAY*mJcGQGrrCULrrE&u!!%TMrVuisli-qbhuBq]lMmCTdf6QPg&J;WPQ-.~> +pAY*mJcGQG!<@Z#s8Vlnrr<%Ms8Ds!!.]UcrrDiOs07+>s.+\gs07+-s07* +pAY*mJcGQGrrCOJrr<*"!.k1Ks8E#arr<&Vs02Rhs.'/ +pAY*mJcGQGrrCOJrr<*"!.k1Ks8E#arr<&Vs02Rhs.'/ +pAY*mJcGQG!<@Z!s8Viprr@WMrVlp!J:Qtc!;PIOZ%2->SUf/gZ%1O-Z%/& +pAY*mJcGQGblE=prVuisli-qb\c2X0S,WHgJcG6>rrA/\q>c*HlMlA~> +pAY*mJcGQGblE=prVuisli-qb\c2X0S,WHgJcG6>rrA/\q>c*HlMlA~> +pAY*mJcGQG!<@Ysp](;Fs8Ds!!.]UcrrDi)rrDh`rrDhFs7$'gplPOPpjrJ1s*t~> +pAY*mJcGQGrrCOJrr<*"!.k1Ks8E#arr<&0s8N(hrr<%Ms763g!13`^!<)rt!.k17s*t~> +pAY*mJcGQGrrCOJrr<*"!.k1Ks8E#arr<&0s8N(hrr<%Ms763g!13`^!<)rt!.k17s*t~> +pAY*mJcGQG!<@Z!s8Viprr@WMrVlp!J:Qtc!;O,)s7i,a!;M-Fo)J[`QiHpWrVuomJcG!7J,~> +pAY*mJcGQGrrCULrrE&u!!%TMrVuisli-qb\c;U.TE"ljJcGECrr<-#!!&#YrW%NLkl6/~> +pAY*mJcGQGrrCULrrE&u!!%TMrVuisli-qb\c;U.TE"ljJcGECrr<-#!!&#YrW%NLkl6/~> +pAY*mJcGQG!<@Z#s8Vlnrr<%Ms8Ds!!.]UcrrDi)s8D_bs8MeEs7QElp]CKjpl5=QpjrJ/s*t~> +pAY*mJcGQGrrC^OrW)lr!!((>!!) +pAY*mJcGQGrrC^OrW)lr!!((>!!) +pAY*mJcGQG!<@Z&s8Mfkrr<&>rr<&arr<&GrrN1NJF!48pr<@6q"F^jq#C-hpoOMqq"OgfplPOU +q!7tZq#C?qq#C-!s8VlJs8VlorrDhFs60K5~> +pAY*mJcGQGrrCdQrrDlp!!((>!!)BcquG1FrW)9a!!((>!W`6#pAY9rrrE*!!3Z>'!<3&orr`?% +!<3%^rrN3#!:Bgd!<)p!!<3&/rrN3#!8RVS!<2uu!.k15s*t~> +pAY*mJcGQGrrCdQrrDlp!!((>!!)BcquG1FrW)9a!!((>!W`6#pAY9rrrE*!!3Z>'!<3&orr`?% +!<3%^rrN3#!:Bgd!<)p!!<3&/rrN3#!8RVS!<2uu!.k15s*t~> +pAY*mJcGQG!<@Z(s8Vlirr<&>rr<&cs82lFrrN1NJF!48prEC9q#C-_rrr2tq#CBhY5\Rus7lBh +"Si#ls7hcW!r2fjmJm4]rVlros7j,(!r2fjh#IELrr2unJcFp5J,~> +pAY*mJcGQGrrCjSrrDfn!!)fo!!)or!s&?$!<)rs!<3!'!<3$!s8N'!rVuisrr2rurVuiss8N0$ +rr<&ts8E#trrW9$!!)rsrrCFGrW)9a!!(" +pAY*mJcGQGrrCjSrrDfn!!)fo!!)or!s&?$!<)rs!<3!'!<3$!s8N'!rVuisrr2rurVuiss8N0$ +rr<&ts8E#trrW9$!!)rsrrCFGrW)9a!!(" +pAY*mJcGQG!<@Z*s8Vlgrr<&orr<&rrrW9$!!)utrW)rt#6=c(!<<'!!<)rs!<2uu!<)rs!<<'$ +!<3$!rVuisrr3'#rr<&ss8N)GrrN1NJF!48pr375q"F^lq#CBhs8VkrrrDihrri,sq#C,NrrDi_ +s8Vllrr`&rs7j&&!;PCM!;QNm!;M-Fk5Tr~> +pAY*mJcGQGrrCFG!!)fo!!)orrrE*!!s&B$!<3!.!<<'!!<<'!!<<'!s8N)urr<&rrr<&ss8N*! +rrW9$rrE&u"9AK%!!)lq!W`6#dJs1Gli-qb`W#o +pAY*mJcGQGrrCFG!!)fo!!)orrrE*!!s&B$!<3!.!<<'!!<<'!!<<'!s8N)urr<&rrr<&ss8N*! +rrW9$rrE&u"9AK%!!)lq!W`6#dJs1Gli-qb`W#o +pAY*mJcGQG!<@Ysrr<&orr<&rs8N*!rrW9$rrE&u%KQP/!!*'!!!*'!!<<'!rr2ruqu6Wrr;Zcs +s8N0$s8N)urr`?%rr<&qrrN3#!7CfJ!.]UcrrDi5rrDifrr`&rs7lTns7iet!;QBi#5J5uq#CBh +OoGFVl2Lk\s8Vl%rrDiOs8VlmrrDhFs5j92~> +pAY*mJcGQGrrCFG!!)fo!!)or!!*#u!!)or!s&B$!<2uu!<3!#!<<'!rr2ruqu6Wrr;Q`srr2ru +qu6`us8N)=s8E#arr<& +pAY*mJcGQGrrCFG!!)fo!!)or!!*#u!!)or!s&B$!<2uu!<3!#!<<'!rr2ruqu6Wrr;Q`srr2ru +qu6`us8N)=s8E#arr<& +pAY*mJcGQG!<@Ysrr<&orr<&rrr<&urr<&rrrW9$rrE&u!!*#u!s&B$!<2uu!;lcr!;uis!<2uu +!;lcu!<<'!`r?)?J:Qtc!;OP5!;Q6e"8Morq#13mposbuq"apoq#CBhs8VkVrrDiZrrDinrrDi% +rrDiQs8VllrrDhFs5a31~> +pAY*mJcGQGrrCFG!!)orq>gNp!!*#u!!*#ur;clt!!*#u!!*#u!W`9#quHWo!!)rs!!*#u!!*#u +r;clt!!(%=rW)9a!!(" +pAY*mJcGQGrrCFG!!)orq>gNp!!*#u!!*#ur;clt!!*#u!!*#u!W`9#quHWo!!)rs!!*#u!!*#u +r;clt!!(%=rW)9a!!(" +pAY*mJcGQG!<@Ysrr<&rs7u`prr<&urr<&us8;rtrr<&urr<&urrN3#s82lorr<&srr<&urr<&u +s8;rtrr<&=rrN1NJF!48pr375q"=Xhq#CBhr;QclZ2Xh!q>UZos8Vlos7h`V!;Pm[!;QNm!;Nu% +!;PUSs7lHj!;M-FjSs`~> +pAY*mJcGQGrrCFG!!)fo!!)or!!*#u!s&B$!<3!#!<<'!rr2rurr3'#s8N)nrr<&srr<&urrW9$ +rrE&u!s&B$!65' +pAY*mJcGQGrrCFG!!)fo!!)or!!*#u!s&B$!<3!#!<<'!rr2rurr3'#s8N)nrr<&srr<&urrW9$ +rrE&u!s&B$!65' +pAY*mJcGQG!<@Ysrr<&orr<&rrr<&urrW9$rrE&u!s&B$!<2uu!<3!#!<<'!p\t3nr;Q`srr3'# +s8N)urrW9$rrC(=!W[b$li-t[`W#r5p&>$err2unXT&:qqYpQjrr2unrr2unP5bOWm/I(\rVllm +[/U.$jo>AUq>UHiJcFd1J,~> +pAY*mJcGQGrrCFG!!)fo!!)or!!*#u!s&B$!<3!#!<<'!rr2rurr3'#s8N)nrr<&srr<&urrW9$ +rrE&u!s&B$!65' +pAY*mJcGQGrrCFG!!)fo!!)or!!*#u!s&B$!<3!#!<<'!rr2rurr3'#s8N)nrr<&srr<&urrW9$ +rrE&u!s&B$!65' +pAY*mJcGQG!<@Ysrr<&orr<&rrr<&urrW9$rrE&u!s&B$!<2uu!<3!#!<<'!p\t3nr;Q`srr3'# +s8N)urrW9$rrC(=!W[b$li-t[`W#r5p&>$erVllmXoACrqYpQjrr2unrr2unP5bOWmJd1]r;Qcl +[/U.$k5PGVq#:?hJcFa0J,~> +pAY*mJcGQGrrCFG!!)fo!!)or!!*#u!!*#uqu?ct!<2uu!<2uu!<3#s!<<'!!<)rs!<<'!!<2uu +!<3#r!!3*"`rH# +pAY*mJcGQGrrCFG!!)fo!!)or!!*#u!!*#uqu?ct!<2uu!<2uu!<3#s!<<'!!<)rs!<<'!!<2uu +!<3#r!!3*"`rH# +pAY*mJcGQG!<@Ysrr<&orr<&rrr<&urr<&us82itrrE&u!!*#u!!*#ur;clt!!)utrW)uu!!*#u +!!*#uqu?ct!65$?!.]UcrrDi7s8;ZdrrDimrrDhts8;ZjrrDimrrDimrrDhZs8;Z]rrDilrrDi% +s8;ZWs8VkFs4@:$~> +pAY*mJcGQGrrCFG!!%TMrVuisli-qb\GlO/r;Q`sVuH_srVlitrVlitJc>cN!;lcr!1X#g!.k1# +s*t~> +pAY*mJcGQGrrCFG!!%TMrVuisli-qb\GlO/r;Q`sVuH_srVlitrVlitJc>cN!;lcr!1X#g!.k1# +s*t~> +pAY*mJcGQG!<@Ysrr<%Ms8Ds!!.]UcrrDi(rrDilrrDhlrrDimrrDimrrDhFrrN/pqu6ZkS,`N` +JcF:#J,~> +pAY*mJcGQGrrCFG!!%TMrVuisli-qb\GlO/r;Q`sUAk2nJcGZJ!!&;arr@WMe,Op~> +pAY*mJcGQGrrCFG!!%TMrVuisli-qb\GlO/r;Q`sUAk2nJcGZJ!!&;arr@WMe,Op~> +pAY*mJcGQG!<@Ysrr<%Ms8Ds!!.]UcrrDi(rrDilrrDhgrrDhFs8;lspm(mZpjrIos*t~> +pAY*mJcGQGrrCFG!!%TMrVuisli-qbZ2Xe(U]1;oJcG]K!!&>brr@WMdJn^~> +pAY*mJcGQGrrCFG!!%TMrVuisli-qbZ2Xe(U]1;oJcG]K!!&>brr@WMdJn^~> +pAY*mJcGQG!<@Ysrr<%Ms8Ds!!.]UcrrDi!rrDhhrrDhFs8Drtpm1s[pjrIms*t~> +pAY*mJcGQGrrCFG!!%TMrVuisli-qbZ2Xe(U]1;oJcG`L!!&>b!!%TMci8L~> +pAY*mJcGQGrrCFG!!%TMrVuisli-qbZ2Xe(U]1;oJcG`L!!&>b!!%TMci8L~> +pAY*mJcGQG!<@Ysrr<%Ms8Ds!!.]UcrrDi!rrDhhrrDhFs8N#upm1p[pjrIks*t~> +pAY*mJcGQGrrCFG!!%TMrVuisli-qbYl=\'V#LDpJcGcM!!&Acrr@WMcMrC~> +pAY*mJcGQGrrCFG!!%TMrVuisli-qbYl=\'V#LDpJcGcM!!&Acrr@WMcMrC~> +pAY*mJcGQG!<@Ysrr<%Ms8Ds!!.]UcrrDhurrDhirrDhFs8W*!pm;$\pjrIjs*t~> +pAY*mJcGQGrrCFG!!%TMrVuisli-qbYl=\'V#LDpJc>cN!1 +pAY*mJcGQGrrCFG!!%TMrVuisli-qbYl=\'V#LDpJc>cN!1 +pAY*mJcGQG!<@Ysrr<%Ms8Ds!!.]UcrrDhurrDhirrDhFrrN/pR/d3]JcEsoJ,~> +pAY*mJcGQGrrCFG!!)3^!!)6_!!)?b!!)Ng!s&B$!9a@^!;ZZo!:0Xb!3cD&!2]\q!.k.M!1Ele +!.k0ms*t~> +pAY*mJcGQGrrCFG!!)3^!!)6_!!)?b!!)Ng!s&B$!9a@^!;ZZo!:0Xb!3cD&!2]\q!.k.M!1Ele +!.k0ms*t~> +pAY*mJcGQG!<@Ysrr<&^rr<&_rr<&brr<&grrW9$rrD6^!!)ip!W[b$li-t[YQ"UtV>gPjJc>`F +RK*<^JcEmmJ,~> +pAY*mJcGQGrrCFG!!(:D!!)rs!!*#u!!)]l!!)ut!!*#u!!)`m!!)Wj!!)foquHWorW)9a!!'5& +!!&kq!!%WN!!&Jfrr@WMaT$b~> +pAY*mJcGQGrrCFG!!(:D!!)rs!!*#u!!)]l!!)ut!!*#u!!)`m!!)Wj!!)foquHWorW)9a!!'5& +!!&kq!!%WN!!&Jfrr@WMaT$b~> +pAY*mJcGQG!<@Ysrr<&Drr<&srr<&urr<&lrr<&trr<&urr<&mrr<&jrr<&os82lorrN1NJF!48 +poj\tpndujpk&MGpmV6_pjrIds*t~> +pAY*mJcGQGrrCFG!!)Zk! +pAY*mJcGQGrrCFG!!)Zk! +pAY*mJcGQG!<@Ysrr<&krrE-"rW)rt#6=c(!<3'!!;uis!;c`p!<2uu!<2uu!<)rs!!*&u!!3*" +r;R!%s8N*!!<<)u!!*&u!<<'*!<3$!rrE*!!<<#urr2rurr3!!s8E#ts8E#ts8N)prrN1NJF!48 +poaVspnn&kpk/SHpm_<`pjrIbs*t~> +pAY*mJcGQGrrCFG!!)ZkrrE&u"9AK%!!*#u!s&B$!<2uu!;c]q!<2uu!;lcr!;uis!<2uu!<2uu +!<2uu!<3!"!<3&urr<&urr<&us8N)urr`?%rr<&urrW9$rrE&u!s&B$!<2uu!<3!%!<<'!rrDoq +rW)9a!!'/$!!&qs!!%]P!!&Mg!!%TM`;b>~> +pAY*mJcGQGrrCFG!!)ZkrrE&u"9AK%!!*#u!s&B$!<2uu!;c]q!<2uu!;lcr!;uis!<2uu!<2uu +!<2uu!<3!"!<3&urr<&urr<&us8N)urr`?%rr<&urrW9$rrE&u!s&B$!<2uu!<3!%!<<'!rrDoq +rW)9a!!'/$!!&qs!!%]P!!&Mg!!%TM`;b>~> +pAY*mJcGQG!<@Ysrr<&ks8N)urr`?%rr<&urrW9$rrE&u!!)lq!!*#u!!)or!!)rs!!*#u!!*#u +!!*#u!!*#u!W`6#rr2rurr2rurr;uurr3*$s8N'!rr3'#s8N)urrW9$rrE&u!!*#u"T\T&!<3&q +rrN1NJF!48poXPrpo",lpk8YIpm_9`pjrI`s*t~> +pAY*mJcGQGrrCFG!!)Zk!!)ut!s&B$!<)p"!<<'!rr2rupAY*mqu6Wrr;Q`srr2rurr2rurr3'# +s8N)urrW9$rrE&u!!*#u!!)ut!s&B$!<)p"!<<'!rr3'#s8N)urr<&urr<&ls8E#arr<&$rr<%s +rr<%Qrr<%hs8N(Ms24ke~> +pAY*mJcGQGrrCFG!!)Zk!!)ut!s&B$!<)p"!<<'!rr2rupAY*mqu6Wrr;Q`srr2rurr2rurr3'# +s8N)urrW9$rrE&u!!*#u!!)ut!s&B$!<)p"!<<'!rr3'#s8N)urr<&urr<&ls8E#arr<&$rr<%s +rr<%Qrr<%hs8N(Ms24ke~> +pAY*mJcGQG!<@Ysrr<&krr<&trrW9$rrE#t!s&B$!<2uu!;?Em!;lcr!;uis!<2uu!<2uu!<3!# +!<<'!rr3'#s8N)urr<&urr<&trrW9$rrE#t!s&B$!<3!#!<<'!rr2rurr2rup&>'nJ:Qtc!;N\r +!;NJl!;M9J!;N)as7h0F_uG5~> +pAY*mJcGQGrrCFG!!)orquHcs!!)ut!s&B$!<)ot!<3!"!<3&os8;rprr<&srr<&urr<&trrN3# +!<2uu!<3!#!<<'!rr2rurr2rurVls"s8N)trrW9$rrE&u!s&B$!<3#r!;6Bk!:0Xb!3H2#!3#nt +!/CLR!1j/i!.k0ds*t~> +pAY*mJcGQGrrCFG!!)orquHcs!!)ut!s&B$!<)ot!<3!"!<3&os8;rprr<&srr<&urr<&trrN3# +!<2uu!<3!#!<<'!rr2rurr2rurVls"s8N)trrW9$rrE&u!s&B$!<3#r!;6Bk!:0Xb!3H2#!3#nt +!/CLR!1j/i!.k0ds*t~> +pAY*mJcGQG!<@Ysrr<&rs82lsrr<&trrW9$rrE#t!!*#u!W`6#q#C9mqu6Wrr;Q`srr2rurVlp! +rrE&u!!*#u!s&B$!<2uu!<2uu!<)p"!<<'!rVls"s8N)urrW9$rrE&uquHEi!W[b$li-t[XT&:q +W;ckmLAq8KScA`bJcERdJ,~> +pAY*mJcGQGrrCFG!!)Zk!!)ut!s&B$!<)ot!<3!"!<3&prr<&urr<&rrr<&srr<&urr<&trr`?% +!<<)q!!3*"rr2rurr2rurVls"s8N)trrW9$rrE&u!s&B$!<2uu!:g*g!:0Xb!3H2#!3#nt!/LRS +!1s5j!.k0bs*t~> +pAY*mJcGQGrrCFG!!)Zk!!)ut!s&B$!<)ot!<3!"!<3&prr<&urr<&rrr<&srr<&urr<&trr`?% +!<<)q!!3*"rr2rurr2rurVls"s8N)trrW9$rrE&u!s&B$!<2uu!:g*g!:0Xb!3H2#!3#nt!/LRS +!1s5j!.k0bs*t~> +pAY*mJcGQG!<@Ysrr<&krr<&trrW9$rrE#t!!*#u!W`6#q>UEprr2ruqu6Wrr;Q`srr2rurVm!# +rrE*!q>^Qr!<2uu!<2uu!<)p"!<<'!rVls"s8N)urrW9$rrE&u!!)Qh!W[b$li-t[XT&:qW;ckm +L]7ALT)\icJcELbJ,~> +pAY*mJcGQGrrCFG!!)ZkrrE&u!s&B$!<)ot!<3!"!<3&prr<&urr<&rrr<&srr<&urr<&trriE& +!<<'!r;QfurrE&u!!*#u!!)ut"9AK%!!*#u$3:,+!<<'!!<<'!rr2runc/Rgli-qbX8`/"WW)qu +M#RGTTE"okJcEF`J,~> +pAY*mJcGQGrrCFG!!)ZkrrE&u!s&B$!<)ot!<3!"!<3&prr<&urr<&rrr<&srr<&urr<&trriE& +!<<'!r;QfurrE&u!!*#u!!)ut"9AK%!!*#u$3:,+!<<'!!<<'!rr2runc/Rgli-qbX8`/"WW)qu +M#RGTTE"okJcEF`J,~> +pAY*mJcGQG!<@Ysrr<&ks8N)urrW9$rrE#t!!*#u!W`6#q>UEprr2ruqu6Wrr;Q`srr2rurVm$$ +rrE*!!;uiu!<3&urr<&urr<&trr`?%rr<&ursAc+rrE*!!!*'!!<2uu!:g'j!.]UcrrDhprrDhn +rrDhMrrDhds8VkFs1SG_~> +pAY*mJcGQGrrCFG!!)Zk! +pAY*mJcGQGrrCFG!!)Zk! +pAY*mJcGQG!<@Ysrr<&krrE-"rW)rt!!)ut!!)ut!!)cnqu?ct!<3#r!<<'!!<2uu!<)p"!<<'! +qYpTsrrE&u!s&B$!<)p#!<<'!s8E#ss8N'"rrE&u!!*#ur;cNj!W[b$li-t[X8`1pWW)tnM>mSN +TDnodJcE@^J,~> +pAY*mJcGQGrrCFG!!)Zk!!)Bcr;`YnrW)9a!!'&!!!'&!!!%oV!!&\lrr@WM])R9~> +pAY*mJcGQGrrCFG!!)Zk!!)Bcr;`YnrW)9a!!'&!!!'&!!!%oV!!&\lrr@WM])R9~> +pAY*mJcGQG!<@Ysrr<&krr<&cs8;qnrrN1NJF!48po=>opo=>opko(Opn7ZepjrIVs*t~> +pA][Dp&NJ>!!)Zk!!%rWrW)9a!!'&!!!'&!!!%rW!!&_mrr@WM\Gq'~> +pA][Dp&NJ>!!)Zk!!%rWrW)9a!!'&!!!'&!!!%rW!!&_mrr@WM\Gq'~> +pA][DpAb2Dd/O(Go`"mkMuNhYJ:Qtc!;NSo!;NSo!;MKP!;N8fs7h0F\Gq'~> +oDa@Aq#JeA!!%TMrVuisli-qbWW)quX8`/"N;ikXUAt5nJcE1YJ,~> +oDa@Aq#JeA!!%TMrVuisli-qbWW)quX8`/"N;ikXUAt5nJcE1YJ,~> +oDaAlq1-il!!%TMrVlp!J:Qtc!;NPn!;NVp!;MNQ!;N;gs7h0F[f:j~> +oDa@Aq#JeA!!%TMrVuisli-qbW;chtXT&8#NW/tYU]:>oJcE+WJ,~> +oDa@Aq#JeA!!%TMrVuisli-qbW;chtXT&8#NW/tYU]:>oJcE+WJ,~> +oDaAlq1-il!!%TMrVlp!J:Qtc!;NMm!;NYq!;MQR!;N>hs7h0F[/YX~> +PQ1X_WrE&!JcG]KrW)9a!!&tt!!',#!!&&Z!!&eo!!%TMZN#F~> +PQ1X_WrE&!JcG]KrW)9a!!&tt!!',#!!&&Z!!&eo!!%TMZN#F~> +PQ1[XWrE&!JcG]K!W[b$li-t[W;ckmXT&:qNrK+SU]1>hJcE%UJ,~> +PQ1X_WrE&!JcG]KrW)9a!!&qs!!'/$!!&)[!!&hprr@WMZ2]=~> +PQ1X_WrE&!JcG]KrW)9a!!&qs!!'/$!!&)[!!&hprr@WMZ2]=~> +PQ1[XWrE&!JcG]K!W[b$li-t[VuHblXoACrO8f4TV#UJiJcE"TJ,~> +PQ([arrB,"!!%TMrVuisli-qbVuH_sXoAA$OT,:\V>pPqJcDqRJ,~> +PQ([arrB,"!!%TMrVuisli-qbVuH_sXoAA$OT,:\V>pPqJcDqRJ,~> +PQ(^Zs7iYp!!%TMrVlp!J:Qtc!;NJl!;N\r!;MZU!;NDjs7h0FYQ'+~> +YQ+V&oDegjp\t9prrB,"!!%TMrVuisli-qbVZ-VrY5\J%OoGC]VZ6YrJcDkPJ,~> +YQ+V&oDegjp\t9prrB,"!!%TMrVuisli-qbVZ-VrY5\J%OoGC]VZ6YrJcDkPJ,~> +YQ+XtoDejcp\t +Yl=e*s8N)lrrN3#!;QTo!<<'!!3H2#!.k1Ks8E#arr<%rrr<&%rr<%^rr<%ss8N(Ms/Z0M~> +Yl=e*s8N)lrrN3#!;QTo!<<'!!3H2#!.k1Ks8E#arr<%rrr<&%rr<%^rr<%ss8N(Ms/Z0M~> +Yl=h#s8VlerrVuqq"Xmhq#C?opoOJq!.k1KrrN1NJF!48pnn&kpoaVsplbXWpo"/lpjrIGs*t~> +Z2Xe(rVlito`"mkq#C?os8N'!XT&8#JcG]KrW)9a!!&kq!!'5&!!&5_!!&qs!!%TMWW.J~> +Z2Xe(rVlito`"mkq#C?os8N'!XT&8#JcG]KrW)9a!!&kq!!'5&!!&5_!!&qs!!%TMWW.J~> +Z2Xh!rVllmo`"pdq#CBhs8N)oXT&8#JcG]K!W[b$li-t[V>gPjYQ"UtPQ(XXVuHblJcD_LJ,~> +Z2Xe(rVlito`"mkq>UKrrrE&u!!'/$JH5KGli-qbV>gMqYQ"S&PlC^`W;lktJcD\KJ,~> +Z2Xe(rVlito`"mkq>UKrrrE&u!!'/$JH5KGli-qbV>gMqYQ"S&PlC^`W;lktJcD\KJ,~> +Z2Xh!rVllmo`"pdq>UNks7lTn!;N\rJH5QIs+'G8!;NDj!;Nbt!;MfY!;NMms7h0FW;hA~> +Z2Xe(rVlito`"mkq>UKrrrE&u!!'/$!!%TMrVuisli-qbV#LDpYl=\'Q2^gaWW2tuJcDVIJ,~> +Z2Xe(rVlito`"mkq>UKrrrE&u!!'/$!!%TMrVuisli-qbV#LDpYl=\'Q2^gaWW2tuJcDVIJ,~> +Z2Xh!rVllmo`"pdq>UNks7lTn!;N\r!!%TMrVlp!J:Qtc!;NAi!;Neu!;MiZ!;NPns7h0FVZ2/~> +Z2Xe(rVlito`"mkq>UKrrrE&u!!'/$!!%TMrVuisli-qbV#LDpYl=\'QN$pbWrN)!JcDPGJ,~> +Z2Xe(rVlito`"mkq>UKrrrE&u!!'/$!!%TMrVuisli-qbV#LDpYl=\'QN$pbWrN)!JcDPGJ,~> +Z2Xh!rVllmo`"pdq>UNks7lTn!;N\r!!%TMrVlp!J:Qtc!;NAi!;Neu!;Ml[!;NSos7h0FV#Pr~> +Z2Xe(rVlito`"mkqYpWts8N)trr<&%rr<%Ms8Dus!:0Xb!2KPo!3uP(!13]c!3?/"!.k0Es*t~> +Z2Xe(rVlito`"mkqYpWts8N)trr<&%rr<%Ms8Dus!:0Xb!2KPo!3uP(!13]c!3?/"!.k0Es*t~> +Z2Xh!rVllmo`"pdqYpZms8VlmrrDhsrr<%Ms8Ds!!.]UcrrDhhrrDi!rrDh\rrDhps8VkFs.]OD~> +Yl=e*s8N)jrr<&qrrW9$rrE#t!!'2%!!%TMrVuisli-qbUAk2nZMsn)R/[-dX8`/"i;\?.ciEY: +J,~> +Yl=e*s8N)jrr<&qrrW9$rrE#t!!'2%!!%TMrVuisli-qbUAk2nZMsn)R/[-dX8`/"i;\?.ciEY: +J,~> +Yl=h#s8VlcrrDijrr`&rs7lQm!;N_s!!%TMrVlp!J:Qtc!;N;g!;Nl"!;Mr]!;NVp!;PLPJH4$s +o)F4~> +YQ+V&rVlitrr2rurVucqrVlitrr2rur;Q`sYQ"S&JcG]KrW)9a!!&bn!!'>)!!&Ge!!',#rrCsV +!!%TMdf0:Io)F4~> +YQ+V&rVlitrr2rurVucqrVlitrr2rur;Q`sYQ"S&JcG]KrW)9a!!&bn!!'>)!!&Ge!!',#rrCsV +!!%TMdf0:Io)F4~> +YQ+XtrVllmrr2unrVufjrVllmrr2unr;QclYQ"S&JcG]K!W[b$li-t[UAk5gZMsq"RK!9^XT/=q +hu +Qi@$crr2rur;Q`sYQ"S&JcG]KrW)9a!!&_m!!'A*!!&Jf!!'/$rrCmT!!%TMdf0:Io)F4~> +Qi@$crr2rur;Q`sYQ"S&JcG]KrW)9a!!&_m!!'A*!!&Jf!!'/$rrCmT!!%TMdf0:Io)F4~> +Qi@'\rr2unr;QclYQ"S&JcG]K!W[b$li-t[U&P,fZi:%#Rf[HTJcF0u!!)TiJ,~> +PQ(U_WW)quJcG]KrW)9a!!&_m!!'A*!!&Mg!!'2%rrCgR!!%TMdf9:Ho`'F~> +PQ(U_WW)quJcG]KrW)9a!!&_m!!'A*!!&Mg!!'2%rrCgR!!%TMdf9:Ho`'F~> +PQ(XXWW)quJcG]K!W[b$li-t[U&P,fZi:%#S,WK`Y5eOsg]%6RJcF0u!W[b$o`'F~> +PQ(U_WW)quJcG]KrW)9a!!&\l!!'D+!!&Ph!!'5&rrCaP!!)$YrrDBbqZ,ORrrDNfr;at>rW)Tj +J,~> +PQ(U_WW)quJcG]KrW)9a!!&\l!!'D+!!&Ph!!'5&rrCaP!!)$YrrDBbqZ,ORrrDNfr;at>rW)Tj +J,~> +PQ(XXWW)quJcG]K!W[b$li-t[T`5#e[/U.$SGrTaYQ+Xtg&D$PirB#Yli6h^huE]Vn,N=dao;DB +J:R:lJ,~> +PQ(U_WW)quJcG]KrW)9a!!&\l!!'D+!!&Si!!'5&!!(XN!!)$YrrDusrrE&urrDfnrrD!WrrDBb +rrE&u"9AK%!!)cnrrCdQrW)TjJ,~> +PQ(U_WW)quJcG]KrW)9a!!&\l!!'D+!!&Si!!'5&!!(XN!!)$YrrDusrrE&urrDfnrrD!WrrDBb +rrE&u"9AK%!!)cnrrCdQrW)TjJ,~> +PQ(XXWW)quJcG]K!W[b$li-t[T`5#e[/U.$Sc8]bYQ"UtfDbgNirB#Yr;Zcsrr;uup](6ni;`fW +li6tbrr3*$s8N'!p](6ngA_3SJ:R:lJ,~> +PQ(U_WW)quJcG]KrW)9a!!&Yk!!'G,!!&Vj!!'8'rrCXM!!)$YrrE#tr;Zitr;Zp!!!*#u"9AK% +!!)utrrE#t!W`9#rW)osr;[*&!!*'!!!*&t!<<*!!<3#s!<3#u!!`H'!<<'!!;ulq!<<)t!<<)t +!<3#t!9*tW!;-;A~> +PQ(U_WW)quJcG]KrW)9a!!&Yk!!'G,!!&Vj!!'8'rrCXM!!)$YrrE#tr;Zitr;Zp!!!*#u"9AK% +!!)utrrE#t!W`9#rW)osr;[*&!!*'!!!*&t!<<*!!<3#s!<3#u!!`H'!<<'!!;ulq!<<)t!<<)t +!<3#t!9*tW!;-;A~> +PQ(XXWW)quJcG]K!W[b$li-t[TDnod[Jp7%T)SfcYlFauf)G^MirB#YrVufr!<;ut!ri6#rr3*$ +s8N'!rVultrVlp!s8W&urVufr#QFc(s8N'!s8;rts8N)us8;rss8N''rr<'!rr<&ss8;rts8;rt +s8;rss8E#WrrN1NJFrkl~> +PQ(U_WW)quJcG]KrW)9a!!&Yk!!'G,!!&Yk!!';(rrCRK!!)$YrrDusrrE&urrE*!rrE&u"9AK% +!!)rsrrE*!$NU5,!!*$!!<<'!!<)rt!<<*!!<<*!!<3#u!<<*!!<<*!!<<)u!!E6$!<<#ur;Zcs +qu?Zrs8W*!rr;uu!ri6#irAuXo`'F~> +PQ(U_WW)quJcG]KrW)9a!!&Yk!!'G,!!&Yk!!';(rrCRK!!)$YrrDusrrE&urrE*!rrE&u"9AK% +!!)rsrrE*!$NU5,!!*$!!<<'!!<)rt!<<*!!<<*!!<3#u!<<*!!<<*!!<<)u!!E6$!<<#ur;Zcs +qu?Zrs8W*!rr;uu!ri6#irAuXo`'F~> +PQ(XXWW)quJcG]K!W[b$li-t[TDnod[Jp7%TDnodZ2ak!eGfLKirB#Yr;Zcsrr;uus8W*!rr3*$ +s8N'!r;Zcss8NH,s8N'!rr<'!rr<&ts8N*!s8N*!s8N)us8N*!s8N*!s8N*!s8E!#rr<'!rW)lr +rrDrrrrE*!rrE&urr<-#!!)$Y!W[b$o`'F~> +PQ(U_WW)quJcG]KrW)9a!!&Vj!!'J-!!&\l!!'>)rrCLI!!)$YrrDusrrE&urrE&urr<*"!<3#r +!<3#t!<3#u!!`H'!<<'!!<)rt!<<*!!<<*!!<3#u!<<*!!<<*!!<<*!!<<*!!<<)t!<3#u!;lfr +!<<*!!<3#u!!<0#!94%X!;-;A~> +PQ(U_WW)quJcG]KrW)9a!!&Vj!!'J-!!&\l!!'>)rrCLI!!)$YrrDusrrE&urrE&urr<*"!<3#r +!<3#t!<3#u!!`H'!<<'!!<)rt!<<*!!<<*!!<3#u!<<*!!<<*!!<<*!!<<*!!<<)t!<3#u!;lfr +!<<*!!<3#u!!<0#!94%X!;-;A~> +PQ(XXWW)quJcG]K!W[b$li-t[T)Sfc[f6@&T`5#eZN't"df0:IirB#Yr;Zcsrr;uurr;uu!WN0! +s82lrs8E#ts8N''rr<'!rr<&ts8N*!s8N*!s8N)us8N*!s8N*!s8N*!s8N*!s8N*!s8;rss8N)r +s8N*!s8N)us8N'#rr<&YrrN1NJFrkl~> +PQ(U_WW)quJcG]KrW)9a!!&Si!!'M.!!&_m!!'A*rrCFG!!)$YrrDusrrE&urrE&urr<*"!<3#u +!;lfq!<3#r!<<*!!<)rt!<<*!!<<*!!<3#u!<<*!!<<*!!<<*!!<<*!!<)rs!<<*!!<)rr!<<*! +!<3#r!94%X!;-;A~> +PQ(U_WW)quJcG]KrW)9a!!&Si!!'M.!!&_m!!'A*rrCFG!!)$YrrDusrrE&urrE&urr<*"!<3#u +!;lfq!<3#r!<<*!!<)rt!<<*!!<<*!!<3#u!<<*!!<<*!!<<*!!<<*!!<)rs!<<*!!<)rr!<<*! +!<3#r!94%X!;-;A~> +PQ(XXWW)quJcG]K!W[b$li-t[Sc8]b\,QI'U&P,fZiC(#d/O(GirB#Yr;Zcsrr;uurr;uu!WN0! +s8N)rs8E#ts82lss8N)ts8N*!s8N*!s8N)us8N*!s8N*!s8N*!s8N*!s8N)ts8E#us8N)ts8;rt +s8N)us82lVrrN1NJFrkl~> +PQ(U_WW)quJcG]KrW)9a!!&Si!!'M.!!&bn!!'D+rrC@E!!)$YrrDusrrE&urrE&urr<*"!<3#u +!;lfq!<3#u!;uls!<)rt!<<*!!<<*!!<3#u!<<*!!<<*!!<<*!!<<*!!;uls!<<*!!<3#u!!`H' +!<<'!!<3#u!8mhU!;-;A~> +PQ(U_WW)quJcG]KrW)9a!!&Si!!'M.!!&bn!!'D+rrC@E!!)$YrrDusrrE&urrE&urr<*"!<3#u +!;lfq!<3#u!;uls!<)rt!<<*!!<<*!!<3#u!<<*!!<<*!!<<*!!<<*!!;uls!<<*!!<3#u!!`H' +!<<'!!<3#u!8mhU!;-;A~> +PQ(XXWW)quJcG]K!W[b$li-t[Sc8]b\,QI'UAk5g[/^1$cMmkEirB#Yr;Zcsrr;uurr;uu!WN0! +s8N)rs8E#ts8N)ss8N)ts8N*!s8N*!s8N)us8N*!s8N*!s8N*!s8N*!s8N)ss8N*!s8N)us8N'' +rr<'!rr<&us8N)VrrN1NJFrkl~> +PQ(U_WW)quJcG]KrW)9a!!&Ph!!'P/!!&eo!!'D+!!(7C!!)$YrrDusrrE&urrE#trrE#trrDus +#QXo)!!*'!!!)rsrrE#trr<'!rW)uurrE&urrE*!rrE*!rrE*!rrE*!rr<*"!<3#u!<<*!!<3#u +!!`H'!<<'!!<3#u!8mhU!;-;A~> +PQ(U_WW)quJcG]KrW)9a!!&Ph!!'P/!!&eo!!'D+!!(7C!!)$YrrDusrrE&urrE#trrE#trrDus +#QXo)!!*'!!!)rsrrE#trr<'!rW)uurrE&urrE*!rrE*!rrE*!rrE*!rr<*"!<3#u!<<*!!<3#u +!!`H'!<<'!!<3#u!8mhU!;-;A~> +PQ(XXWW)quJcG]K!W[b$li-t[SGrTa\GlR(U]1>h[/U.$bl7YCirB#Yr;Zcsrr;uurVultrVult +r;R$&s8N'!s8N'!r;ZcsrVult!<<#us8W*!rr;uus8W*!s8W*!s8W*!s8W*!!WN0!s8N*!s8N)u +s8N''rr<'!rr<&us8N)VrrN1NJFrkl~> +PQ(U_WW)quJcG]KrW)9a!!&Ph!!'P/!!&hp!!'G,rrC7B!!)$YqZ-ZrrW)uurW)uurrE#tq>gKo +rrE*!r;cisr;cltrr<-#!!*#urW!$"!!*#ur;cisrrE*!rrE*!r;cfrrW)uuquHcsrW)uur;bjW +rW)TjJ,~> +PQ(U_WW)quJcG]KrW)9a!!&Ph!!'P/!!&hp!!'G,rrC7B!!)$YqZ-ZrrW)uurW)uurrE#tq>gKo +rrE*!r;cisr;cltrr<-#!!*#urW!$"!!*#ur;cisrrE*!rrE*!r;cfrrW)uuquHcsrW)uur;bjW +rW)TjJ,~> +PQ(XXWW)quJcG]K!W[b$li-t[SGrTa\GlR(V#LGi[K$:%bPqPBirAlUs8W&us8W&us8W*!rVu]o +rVults8W#trr;oss8W*!!ri6#rr;rt!ri6#rr;osrr;uus8W*!s8W#trVuiss8Vuss8W&us8W#t +ir9&[J:R:lJ,~> +PQ(U_WW)quJcG]KrW)9a!!&Mg!!'S0!!&kq!!'J-rrC1@!!%TMdf9:Ho`'F~> +PQ(U_WW)quJcG]KrW)9a!!&Mg!!'S0!!&kq!!'J-rrC1@!!%TMdf9:Ho`'F~> +PQ(XXWW)quJcG]K!W[b$li-t[S,WK`\c2[)V>gPj[f?C&ao;>@JcF0u!W[b$o`'F~> +PQ(U_WW)quJcG]KrW)9a!!&Mg!!'S0!!&nr!!'M.rrC+>!!%TMdf9:Ho`'F~> +PQ(U_WW)quJcG]KrW)9a!!&Mg!!'S0!!&nr!!'M.rrC+>!!%TMdf9:Ho`'F~> +PQ(XXWW)quJcG]K!W[b$li-t[S,WK`\c2[)VZ-Yk\,ZL'a8Z,>JcF0u!W[b$o`'F~> +PQ(U_WW)quJcG]KrW)9a!!&Jf!!'V1!!&qs!!'P/rrC% +PQ(U_WW)quJcG]KrW)9a!!&Jf!!'V1!!&qs!!'P/rrC% +PQ(XXWW)quJcG]K!W[b$li-t[Rf +PQ(U_WW)quJcG]KrW)9a!!&Jf!!'V1!!&tt!!'P/!!'q:!!%TMdf9:Ho`'F~> +PQ(U_WW)quJcG]KrW)9a!!&Jf!!'V1!!&tt!!'P/!!'q:!!%TMdf9:Ho`'F~> +PQ(XXWW)quJcG]K!W[b$li-t[Rf +PQ(U_WW)quJcG]KrW)9a!!&Ge!!'Y2!!'"u!!'S0rrBq9!!%TMdf9:Ho`'F~> +PQ(U_WW)quJcG]KrW)9a!!&Ge!!'Y2!!'"u!!'S0rrBq9!!%TMdf9:Ho`'F~> +PQ(XXWW)quJcG]K!W[b$li-t[RK!9^]Dhm+WW)tn\c;^)_Z'T9JcF0u!W[b$o`'F~> +PQ(U_WW)quJcG]KrW)9a!!&Ge!!'Y2!!'&!!!'V1rrBk7!!%TMdf9:Ho`'F~> +PQ(U_WW)quJcG]KrW)9a!!&Ge!!'Y2!!'&!!!'V1rrBk7!!%TMdf9:Ho`'F~> +PQ(XXWW)quJcG]K!W[b$li-t[RK!9^]Dhm+WrE(o])Vg*_#FB7JcF0u!W[b$o`'F~> +PQ(U_WW)quJcG]KrW)9a!!&Dd!!'\3!!')"!!'Y2rrBe5!!%TMdf9:Ho`'F~> +PQ(U_WW)quJcG]KrW)9a!!&Dd!!'\3!!')"!!'Y2rrBe5!!%TMdf9:Ho`'F~> +PQ(XXWW)quJcG]K!W[b$li-t[R/[0]]`/!,X8`1p]Dqp+^Ae05JcF0u!W[b$o`'F~> +PQ(U_WW)quJcG]KrW)9a!!&Ac!!'_4!!',#!!'\3rrB_3!!%TMdf9:Ho`'F~> +PQ(U_WW)quJcG]KrW)9a!!&Ac!!'_4!!',#!!'\3rrB_3!!%TMdf9:Ho`'F~> +PQ(XXWW)quJcG]K!W[b$li-t[Qi@'\^&J*-XT&:q]`8$,]`.s3JcF0u!W[b$o`'F~> +PQ(U_WW)quJcG]KrW)9a!!&Ac!!'_4!!'/$!!'\3!!'V1!!%TMdf9:Ho`'F~> +PQ(U_WW)quJcG]KrW)9a!!&Ac!!'_4!!'/$!!'\3!!'V1!!%TMdf9:Ho`'F~> +PQ(XXWW)quJcG]K!W[b$li-t[Qi@'\^&J*-XoACr]`/!,])Ma1JcF0u!W[b$o`'F~> +PQ(U_WW)quJcG]KrW)9a!!&>b!!'b5!!'2%!!'_4rrBV0JH3sqo`'F~> +PQ(U_WW)quJcG]KrW)9a!!&>b!!'b5!!'2%!!'_4rrBV0JH3sqo`'F~> +PQ(XXWW)quJcG]K!W[b$li-t[QN$s[^Ae3.Y5\Ls^&S--\c73\ciF)ro`'F~> +PQ(U_WW)quJcG]KrW)9a!!&>b!!'b5!!'8'rrBe5rrBP.!!%TMdf9:Ho`'F~> +PQ(U_WW)quJcG]KrW)9a!!&>b!!'b5!!'8'rrBe5rrBP.!!%TMdf9:Ho`'F~> +PQ(XXWW)quJcG]K!W[b$li-t[QN$s[^Ae3.YlFau^An6.\,QF.JcF0u!W[b$o`'F~> +PQ(U_WW)quJcG]KrW)9a!!&;a!!'e6!!';(!!'b5rrBJ,!!%TMdf9:Ho`'F~> +PQ(U_WW)quJcG]KrW)9a!!&;a!!'e6!!';(!!'b5rrBJ,!!%TMdf9:Ho`'F~> +PQ(XXWW)quJcG]K!W[b$li-t[Q2^jZ^]+ +PQ(U_WW)quJcG]KrW)9a!!&;a!!'e6!!'>)!!'e6rrBD*!!%TMdf9:Ho`'F~> +PQ(U_WW)quJcG]KrW)9a!!&;a!!'e6!!'>)!!'e6rrBD*!!%TMdf9:Ho`'F~> +PQ(XXWW)quJcG]K!W[b$li-t[Q2^jZ^]+ +PQ(U_WW)quJcG]KrW)9a!!&8`!!'h7!!'A*!!'e6!!';(!!%TMdf9:Ho`'F~> +PQ(U_WW)quJcG]KrW)9a!!&8`!!'h7!!'A*!!'e6!!';(!!%TMdf9:Ho`'F~> +PQ(XXWW)quJcG]K!W[b$li-t[PlCaY_#FE0Zi:%#^]+ +PQ(U_WW)quJcG]KrW)9a!!&8`!!'h7!!'D+!!'h7rrB;'!!%TMdf9:Ho`'F~> +PQ(U_WW)quJcG]KrW)9a!!&8`!!'h7!!'D+!!'h7rrB;'!!%TMdf9:Ho`'F~> +PQ(XXWW)quJcG]K!W[b$li-t[PlCaY_#FE0[/U.$_#OH0Yl=\'JcF0u!W[b$o`'F~> +PQ(U_WW)quJcG]KrW)9a!!&5_!!'k8!!'G,!!'k8rrB5%!!%TMdf9:Ho`'F~> +PQ(U_WW)quJcG]KrW)9a!!&5_!!'k8!!'G,!!'k8rrB5%!!%TMdf9:Ho`'F~> +PQ(XXWW)quJcG]K!W[b$li-t[PQ(XX_>aN1[Jp7%_>jQ1Y5\J%JcF0u!W[b$o`'F~> +PQ(U_WW.MLq#Ka\!!&2^!!'n9!!'J-!!'n9rrB/#!!%TMdf9:Ho`'F~> +PQ(U_WW.MLq#Ka\!!&2^!!'n9!!'J-!!'n9rrB/#!!%TMdf9:Ho`'F~> +PQ(XXWW.MLqZ-YHli-t[P5bOW_Z'W2[f6@&_Z0Z2XT&8#JcF0u!W[b$o`'F~> +PQ(U_VZ22IquH'_!!&2^!!'n9!!'M.!!'q:rrB)!!!%TMdf9:Ho`'F~> +PQ(U_VZ22IquH'_!!&2^!!'n9!!'M.!!'q:rrB)!!!%TMdf9:Ho`'F~> +PQ(XXVZ23tr.+,5!;M`W!;OG2!;O&'!;OJ3s7iVo!!%TMdf0@KJ:R:lJ,~> +PQ(U_VZ22IquH'_!!&/]!!'q:!!'P/!!'q:!!&tt!!%TMdf9:Ho`'F~> +PQ(U_VZ22IquH'_!!&/]!!'q:!!'P/!!'q:!!&tt!!%TMdf9:Ho`'F~> +PQ(XXVZ23tr.+,5!;M]V!;OJ3!;O)(!;OJ3!;NMm!!%TMdf0@KJ:R:lJ,~> +PQ(U_JcEC_!!(FH!!&/]!!'q:!!'S0!!'t;rrAts!!%TMdf9:Ho`'F~> +PQ(U_JcEC_!!(FH!!&/]!!'q:!!'S0!!'t;rrAts!!%TMdf9:Ho`'F~> +PQ(XXJcEC_!;OtA!;M]V!;OJ3!;O,)!;OM4s7iMl!!%TMdf0@KJ:R:lJ,~> +PQ(U_JcEC_!!(FH!!&,\!!'t;!!'V1!!(" +PQ(U_JcEC_!!(FH!!&,\!!'t;!!'V1!!(" +PQ(XXJcEC_!;OtA!;MZU!;OM4!;O/*!;OP5s7iGj!!%TMdf0@KJ:R:lJ,~> +PQ(U_JcEF`rW(CH!!&,\!!'t;!!'Y2!!(%=rrAho!!)9`!!)HeqZ,UT!!)Qh!!)rs!!)Ti!!(7C +rW)TjJ,~> +PQ(U_JcEF`rW(CH!!&,\!!'t;!!'Y2!!(%=rrAho!!)9`!!)HeqZ,UT!!)Qh!!)rs!!)Ti!!(7C +rW)TjJ,~> +PQ(XXJcEF`rqOqA!;MZU!;OM4!;O2+!;OS6s7iAh!!)9`!!)HeqZ,UT!!)Qh!!)rs!!)Ti!!(7C +!W[b$o`'F~> +PQ(U_JcEjlrrDfnrW(CH!!&)[!!("rrAbm!!)Qh!!)lq!!)rs!!*#u!!)fo!!(sW +!!)EdrrE&urrDWi!!(7CrW)TjJ,~> +PQ(U_JcEjlrrDfnrW(CH!!&)[!!("rrAbm!!)Qh!!)lq!!)rs!!*#u!!)fo!!(sW +!!)EdrrE&urrDWi!!(7CrW)TjJ,~> +PQ(XXJcEjls7l?grqOqA!;MWT!;OP5!;O5,!;OV7s7i;f!!)Qh!!)lq!!)rs!!*#u!!)fo!!(sW +!!)EdrrE&urrDWi!!(7C!W[b$o`'F~> +PQ(U_JcEmm!W`6#q#:HsrrE'!e,KCJO8f1[`W#o<^&J'4a8Z,>TDnlkq#: +PQ(U_JcEmm!W`6#q#:HsrrE'!e,KCJO8f1[`W#o<^&J'4a8Z,>TDnlkq#: +PQ(XXJcEmm!r2fjq#:Kls7lWhe,KFCO8f4T`W#r5^&J*-a8Z/7TDnlkq#: +PQ(U_JcEgk!!)fo"9AH%rrCOJ!!&&Z!!(%=!!'b5!!(+?rrAYj!!)fo!!)or!!)lq!!)rs!!*#u +!!*#u!!*#u!W`6#qYpTsrrE&u!!*#u!s&B$!;uis!<3!#!<<'!rr3'#s8N)urt#21rr<'!rrE*! +!<3'!!<3&urr<&urrW9$rrE&urrE*!!!*#u!!(LJrW)TjJ,~> +PQ(U_JcEgk!!)fo"9AH%rrCOJ!!&&Z!!(%=!!'b5!!(+?rrAYj!!)fo!!)or!!)lq!!)rs!!*#u +!!*#u!!*#u!W`6#qYpTsrrE&u!!*#u!s&B$!;uis!<3!#!<<'!rr3'#s8N)urt#21rr<'!rrE*! +!<3'!!<3&urr<&urrW9$rrE&urrE*!!!*#u!!(LJrW)TjJ,~> +PQ(XXJcEgk!;Q?h"Si#ls7k(C!;MTS!;OS6!;O;.!;OY8s7i2c!!)fo!!)or!!)lq!!)rs!!*#u +!!*#u!!*#u!W`6#qYpTsrrE&u!!*#u!s&B$!;uis!<3!#!<<'!rr3'#s8N)urt#21rr<'!rrE*! +!<3'!!<3&urr<&urrW9$rrE&urrE*!!!*#u!!(LJ!W[b$o`'F~> +PQ(U_JcEgk!!)ip"p"]'!<<'!eGfLKNrK(Z`r?#=^]+96aoDA@SGrQhq#: +PQ(U_JcEgk!!)ip"p"]'!<<'!eGfLKNrK(Z`r?#=^]+96aoDA@SGrQhq#: +PQ(XXJcEgk!;QBi#5J5uq#CBheGfODNrK+S`r?&6^]+ +PQ(U_JcEgk!!)ip"p"]'!<<'!eGfLKNW/tYa8Z,>_#FB7b5_JARf +PQ(U_JcEgk!!)ip"p"]'!<<'!eGfLKNW/tYa8Z,>_#FB7b5_JARf +PQ(XXJcEgk!;QBi#5J5uq#CBheGfODNW0"Ra8Z/7_#FE0b5_M:Rf +PQ(U_JcEgk!!)ip"p"]'!<<'!eGfLKN;ikXaSu5?_>aK8bQ%SBR/[-dq#: +PQ(U_JcEgk!!)ip"p"]'!<<'!eGfLKN;ikXaSu5?_>aK8bQ%SBR/[-dq#: +PQ(XXJcEgk!;QBi#5J5uq#CBheGfODN;inQaSu88_>aN1bQ%V;R/[-dq#: +PQ(U_JcEgk!!)lq!!*#u!!*#u!!(RL!!%uX!!(+?!!'n9!!(7CrrAAb!!)fo!!)or!!)lq!!)rs +!!*#u!!)ut"T\Q&s8N)qrrN3#!<2uu!;lcr!;uj%!<<'!!<<'!rr3'#s8N)urrW9$rrE&u#lt#* +!<<'!s8N)urr<&urrW9$rrE&urrE*!!!(@FrW)TjJ,~> +PQ(U_JcEgk!!)lq!!*#u!!*#u!!(RL!!%uX!!(+?!!'n9!!(7CrrAAb!!)fo!!)or!!)lq!!)rs +!!*#u!!)ut"T\Q&s8N)qrrN3#!<2uu!;lcr!;uj%!<<'!!<<'!rr3'#s8N)urrW9$rrE&u#lt#* +!<<'!s8N)urr<&urrW9$rrE&urrE*!!!(@FrW)TjJ,~> +PQ(XXJcEgk!;QEj!;QQn!;QQn!;P+E!;MNQ!;OY8!;OG2!;Oe +PQ(U_JcEgk!!)lq!!*#u!!*#u!!(RL!!%rW!!(.@!!'q:!!(7C!!&8`!!)fo!!)lq!s&B$!<3#r +!<<'!!<2uu!<)ot!<3#q!!3*"rr2rurr;osrr;rtrr;uu!WN0!rrW9$rrE&urW)rt!!*#u!s&B$ +!;uis!<)rs!<)rs!!3*"rr;ose,TCIo`'F~> +PQ(U_JcEgk!!)lq!!*#u!!*#u!!(RL!!%rW!!(.@!!'q:!!(7C!!&8`!!)fo!!)lq!s&B$!<3#r +!<<'!!<2uu!<)ot!<3#q!!3*"rr2rurr;osrr;rtrr;uu!WN0!rrW9$rrE&urW)rt!!*#u!s&B$ +!;uis!<)rs!<)rs!!3*"rr;ose,TCIo`'F~> +PQ(XXJcEgk!;QEj!;QQn!;QQn!;P+E!;MKP!;O\9!;OJ3!;Oe +PQ(U_JcEmmquH]q!!)ut!!)ut!!(UM!!%rW!!(.@!!'t;!!(:DrrA8_!!%TMdf9:Ho`'F~> +PQ(U_JcEmmquH]q!!)ut!!)ut!!(UM!!%rW!!(.@!!'t;!!(:DrrA8_!!%TMdf9:Ho`'F~> +PQ(XXJcEmmr:p6j!;QNm!;QNm!;P.F!;MKP!;O\9!;OM4!;Oh=s7hfX!!%TMdf0@KJ:R:lJ,~> +PQ(U_JcERd!!)ut!!)ut!!(UM!!%oV!!(1A!!(" +PQ(U_JcERd!!)ut!!)ut!!(UM!!%oV!!(1A!!(" +PQ(XXJcERd!;QNm!;QNm!;P.F!;MHO!;O_:!;OP5!;Ok>s7h`V!!%TMdf0@KJ:R:lJ,~> +PQ(U_JcEC_!!(FH!!%oV!!(1A!!(%=!!(@FrrA,[!!%TMdf9:Ho`'F~> +PQ(U_JcEC_!!(FH!!%oV!!(1A!!(%=!!(@FrrA,[!!%TMdf9:Ho`'F~> +PQ(XXJcEC_!;OtA!;MHO!;O_:!;OS6!;On?s7hZT!!%TMdf0@KJ:R:lJ,~> +PQ(U_JcEC_!!(FH!!%lU!!(4B!!((>!!(CGrrA&Y!!%TMdf9:Ho`'F~> +PQ(U_JcEC_!!(FH!!%lU!!(4B!!((>!!(CGrrA&Y!!%TMdf9:Ho`'F~> +PQ(XXJcEC_!;OtA!;MEN!;Ob;!;OV7!;Oq@s7hTR!!%TMdf0@KJ:R:lJ,~> +PQ(U_JcEC_!!(FH!!%lU!!(4B!!(+?!!(CG!!%rW!!%TMdf9:Ho`'F~> +PQ(U_JcEC_!!(FH!!%lU!!(4B!!(+?!!(CG!!%rW!!%TMdf9:Ho`'F~> +PQ(XXJcEC_!;OtA!;MEN!;Ob;!;OY8!;Oq@!;MKP!!%TMdf0@KJ:R:lJ,~> +PQ(U_JcEC_!!(FH!!%iT!!(7C!!(.@!!(FHrr@rV!!%TMdf9:Ho`'F~> +PQ(U_JcEC_!!(FH!!%iT!!(7C!!(.@!!(FHrr@rV!!%TMdf9:Ho`'F~> +PQ(XXJcEC_!;OtA!;MBM!;Oe +PQ(U_JcEC_!!(FH!!%fS!!(:D!!(1A!!(IIrr@lT!!)0]!!)HeqZ,UT!!)QhquHQm!!)'Z!!)-\ +rW)TjJ,~> +PQ(U_JcEC_!!(FH!!%fS!!(:D!!(1A!!(IIrr@lT!!)0]!!)HeqZ,UT!!)QhquHQm!!)'Z!!)-\ +rW)TjJ,~> +PQ(XXJcEC_!;OtA!;M?L!;Oh=!;O_:!;P"Bs7hEM!!)0]!!)HeqZ,UT!!)QhquHQm!!)'Z!!)-\ +!W[b$o`'F~> +PQ(U_JcEC_!!(FH!!%fS!!(:D!!(4B!!(LJrr@fR!!)0]!!)rs!!*#u!!)fo!!(sW!!)He!!)Zk +!!)'Z!!)-\rW)TjJ,~> +PQ(U_JcEC_!!(FH!!%fS!!(:D!!(4B!!(LJrr@fR!!)0]!!)rs!!*#u!!)fo!!(sW!!)He!!)Zk +!!)'Z!!)-\rW)TjJ,~> +PQ(XXJcEC_!;OtA!;M?L!;Oh=!;Ob;!;P%Cs7h?K!!)0]!!)rs!!*#u!!)fo!!(sW!!)He!!)Zk +!!)'Z!!)-\!W[b$o`'F~> +PQ(U_JcEC_!!(FH!!%cR!!(=E!!(7C!!(OKrr@`P!!)fo!!)or!s&?$!<2uu!<2uu!<)rs!!*&u +!!3*"r;Zcsqu6Wrrr2rurr;rtrVuiss8N'!rr3!!s8E#urr<&us8E#trrrK'!!*'!!;lcr!<3!$ +!<<'!s8E#trs&Q(!!*'!!!)utrW)osrW!!!!<3#t!:0[a!;-;A~> +PQ(U_JcEC_!!(FH!!%cR!!(=E!!(7C!!(OKrr@`P!!)fo!!)or!s&?$!<2uu!<2uu!<)rs!!*&u +!!3*"r;Zcsqu6Wrrr2rurr;rtrVuiss8N'!rr3!!s8E#urr<&us8E#trrrK'!!*'!!;lcr!<3!$ +!<<'!s8E#trs&Q(!!*'!!!)utrW)osrW!!!!<3#t!:0[a!;-;A~> +PQ(XXJcEC_!;OtA!;M!;Oe +PQ(U_JcEC_!!(FH!!%cR!!(=E!!(:D!!(OK!!%WN!!)fo!!)orrrE*!!!)or!!)rs!!*#u!!*#u +!!*#u!W`6#qYpTsrrE&u!!*#u!s&B$!;uis!<3!#!<<'!rr3'#s8N)ursAc+rr<'!rrE*!!;uis +!<3!$!<<'!!<3!.!<<'!!<<'!!<<'!s8N)urrW9$rrE&urrE*!!!*#u!!)BcrW)TjJ,~> +PQ(U_JcEC_!!(FH!!%cR!!(=E!!(:D!!(OK!!%WN!!)fo!!)orrrE*!!!)or!!)rs!!*#u!!*#u +!!*#u!W`6#qYpTsrrE&u!!*#u!s&B$!;uis!<3!#!<<'!rr3'#s8N)ursAc+rr<'!rrE*!!;uis +!<3!$!<<'!!<3!.!<<'!!<<'!!<<'!s8N)urrW9$rrE&urrE*!!!*#u!!)BcrW)TjJ,~> +PQ(XXJcEC_!;OtA!;M!;Oh=!;P(D!;M0G!!)fo!!)orrrE*!!!)or!!)rs!!*#u!!*#u +!!*#u!W`6#qYpTsrrE&u!!*#u!s&B$!;uis!<3!#!<<'!rr3'#s8N)ursAc+rr<'!rrE*!!;uis +!<3!$!<<'!!<3!.!<<'!!<<'!!<<'!s8N)urrW9$rrE&urrE*!!!*#u!!)Bc!W[b$o`'F~> +PQ(U_JcEC_!!(FH!!%`Q!!(@F!!(=E!!(RLrr@WM!!)fo!!)or!!*#u!!)or!!)rs!!*#u!!*#u +!!*#u! +PQ(U_JcEC_!!(FH!!%`Q!!(@F!!(=E!!(RLrr@WM!!)fo!!)or!!*#u!!)or!!)rs!!*#u!!*#u +!!*#u! +PQ(XXJcEC_!;OtA!;M9J!;On?!;Ok>!;P+Es7h0F!!)fo!!)or!!*#u!!)or!!)rs!!*#u!!*#u +!!*#u! +PQ(U_JcEC_!!(FH!!%`Q!!(@F!!(@F!!(UMrr@WMs8N'!qu?Kmrr2rurr2ruqu6Wrr;Q`srr2ru +rVm$$rrE*!!;ZWp!<)rq!<<'!!;uis!<3!#!<<'!rr3'#s8N)urrW9$rrE&u!!)rs!s&B$!<3!# +!<<'!rVls"s8N)urr<&urrW9$rrE&u!s&B$!<)p!!<<)s!:9ab!;-;A~> +PQ(U_JcEC_!!(FH!!%`Q!!(@F!!(@F!!(UMrr@WMs8N'!qu?Kmrr2rurr2ruqu6Wrr;Q`srr2ru +rVm$$rrE*!!;ZWp!<)rq!<<'!!;uis!<3!#!<<'!rr3'#s8N)urrW9$rrE&u!!)rs!s&B$!<3!# +!<<'!rVls"s8N)urr<&urrW9$rrE&u!s&B$!<)p!!<<)s!:9ab!;-;A~> +PQ(XXJcEC_!;OtA!;M9J!;On?!;On?!;P.Fs7h0Fs8N'!qu?Kmrr2rurr2ruqu6Wrr;Q`srr2ru +rVm$$rrE*!!;ZWp!<)rq!<<'!!;uis!<3!#!<<'!rr3'#s8N)urrW9$rrE&u!!)rs!s&B$!<3!# +!<<'!rVls"s8N)urr<&urrW9$rrE&u!s&B$!<)p!!<<)s!:9^e!.]Uls*t~> +PQ(U_JcEC_!!(FH!!%]P!!(CG!!(CG!!(XNrr@WMrVlitq#: +PQ(U_JcEC_!!(FH!!%]P!!(CG!!(CG!!(XNrr@WMrVlitq#: +PQ(XXJcEC_!;OtA!;M6I!;Oq@!;Oq@!;P1Gs7h0FrVlitq#: +PQ(U_JcEC_!!(FH!!%]P!!(CG!!(FH!!([Orr@WMqu6Wrq#: +PQ(U_JcEC_!!(FH!!%]P!!(CG!!(FH!!([Orr@WMqu6Wrq#: +PQ(XXJcEC_!;OtA!;M6I!;Oq@!;OtA!;P4Hs7h0Fqu6Wrq#: +PQ(U_JcEC_!!(FH!!%ZO!!(FH!!(II!!([O!!%TMq>UEpq#: +PQ(U_JcEC_!!(FH!!%ZO!!(FH!!(II!!([O!!%TMq>UEpq#: +PQ(XXJcEC_!;OtA!;M3H!;OtA!;P"B!;P4H!;M-Fq>UEpq#: +PQ(U_JcF@%M?'dY!!(FH!!(LJ!!(^Prr@WMq#: +PQ(U_JcF@%M?'dY!!(FH!!(LJ!!(^Prr@WMq#: +PQ(XXJcF@%M?'dY!;OtA!;P%C!;P7Is7h0Fq#: +PQ(U_JcF@%!!%uX!!',#!!(II!!(OK!!(aQrr@WMpAY*mJcF0urW)TjJ,~> +PQ(U_JcF@%!!%uX!!',#!!(II!!(OK!!(aQrr@WMpAY*mJcF0urW)TjJ,~> +PQ(XXJcF@%!!%uX!!',#!;P"B!;P(D!;P:Js7h0FpAY*mJcF0u!W[b$o`'F~> +PQ(U_JcF@%!!%uX!!')"!!(LJ!!(RL!!(dRrr@WMo`"mkJcF0urW)TjJ,~> +PQ(U_JcF@%!!%uX!!')"!!(LJ!!(RL!!(dRrr@WMo`"mkJcF0urW)TjJ,~> +PQ(XXJcF@%!!%uX!!')"!;P%C!;P+E!;P=Ks7h0Fo`"mkJcF0u!W[b$o`'F~> +PQ(U_JcF@%!!%uXrW')#!!(LJ!!(UM!!(gSrr@WMo)A[iJcF0urW)TjJ,~> +PQ(U_JcF@%!!%uXrW')#!!(LJ!!(UM!!(gSrr@WMo)A[iJcF0urW)TjJ,~> +PQ(XXJcF@%!!%uX!W[b$XoACre,KFCf)GaFh#IELJcG<@!!%TMdf0@KJ:R:lJ,~> +PQ(U_JcF@%!!)Ngq#KOVr;ah:rW'&"!!(OK!!(XN!!(gS!!%TMnG`IgJcF0urW)TjJ,~> +PQ(U_JcF@%!!)Ngq#KOVr;ah:rW'&"!!(OK!!(XN!!(gS!!%TMnG`IgJcF0urW)TjJ,~> +PQ(XXJcF@%!!)Ngq#KOVr;ah:!W[b$XT&:qeGfODfDbjGh#@BLJcG6>!!%TMdf0@KJ:R:lJ,~> +PQ(U_JcF@%!!)EdrrD*ZrrE&u!!)KfrrDQgrrDHdrW'&"!!(OK!!([O!!(jTrr@WMn,E@fJcF0u +rW)TjJ,~> +PQ(U_JcF@%!!)EdrrD*ZrrE&u!!)KfrrDQgrrDHdrW'&"!!(OK!!([O!!(jTrr@WMn,E@fJcF0u +rW)TjJ,~> +PQ(XXJcF@%!!)EdrrD*ZrrE&u!!)KfrrDQgrrDHd!W[b$XT&:qeGfODf`(sHh>dNMJcG3=!!%TM +df0@KJ:R:lJ,~> +PQ(U_JcF@%!!)EdrrE#trr<-#!<;utrVufrs8W&urr;uuq>^Bnrr;uu"TJH%s8W#trr;rts8W*! +rVucqmf37dX8`/"ec,ULg&D$PhZ*TUJcG-;!!(CG!!)]lq>gQq!!&JfrW)TjJ,~> +PQ(U_JcF@%!!)EdrrE#trr<-#!<;utrVufrs8W&urr;uuq>^Bnrr;uu"TJH%s8W#trr;rts8W*! +rVucqmf37dX8`/"ec,ULg&D$PhZ*TUJcG-;!!(CG!!)]lq>gQq!!&JfrW)TjJ,~> +PQ(XXJcF@%!!)EdrrE#trr<-#!<;utrVufrs8W&urr;uuq>^Bnrr;uu"TJH%s8W#trr;rts8W*! +rVucqmf*=gJ:O^#!;P+E!;P7I!;PFNs7h0FmJd.dd/O(Gp&Fjgs8N'!Rf +PQ(U_JcF@%!!)EdrrE#trW)lrrrE*!rrE#trr<9'!!*'!!!)lqrrE*!rrE*!rW!0&!!*'!!!*#u +rr +PQ(U_JcF@%!!)EdrrE#trW)lrrrE*!rrE#trr<9'!!*'!!!)lqrrE*!rrE*!rW!0&!!*'!!!*#u +rr +PQ(XXJcF@%!!)EdrrE#trW)lrrrE*!rrE#trr<9'!!*'!!!)lqrrE*!rrE*!rW!0&!!*'!!!*#u +rr +PQ(U_JcF@%!!)EdrrE#trrDrrrrE*!rrE#trr<9'!!*'!!!)lqrrE*!rrE*!rrE*!rrE*!rrE&u +rr<-#!!*#urW)rtrrDHdrW&tu!!(UM!!(dR!!(sWrr@WMl2L_`q#: +PQ(U_JcF@%!!)EdrrE#trrDrrrrE*!rrE#trr<9'!!*'!!!)lqrrE*!rrE*!rrE*!rrE*!rrE&u +rr<-#!!*#urW)rtrrDHdrW&tu!!(UM!!(dR!!(sWrr@WMl2L_`q#: +PQ(XXJcF@%!!)EdrrE#trrDrrrrE*!rrE#trr<9'!!*'!!!)lqrrE*!rrE*!rrE*!rrE*!rrE&u +rr<-#!!*#urW)rtrrDHd!W[b$WrE(of)GaFg]%9Ki;`iPJcG!7!!)fo!!)lqrW)rt"T\Q&!<<)u +!<<'"!<<#ur;Zcsrr2rurVuis!<<#urVlitr;R*(s8N*!!!*'!!!)utrW'#!!W[b$o`'F~> +PQ(U_JcF@%!!)EdrrE#trrE#tr;cltrrE#tquHcsrrDoqrrE*!rrE*!rrE*!rrE*!rrE&uquH`r +rW)rtrrDHdrW&tu!!(UM!!(gS!!(sW!!%TMkPkM^q#: +PQ(U_JcF@%!!)EdrrE#trrE#tr;cltrrE#tquHcsrrDoqrrE*!rrE*!rrE*!rrE*!rrE&uquH`r +rW)rtrrDHdrW&tu!!(UM!!(gS!!(sW!!%TMkPkM^q#: +PQ(XXJcF@%!!)EdrrE#trrE#tr;cltrrE#tquHcsrrDoqrrE*!rrE*!rrE*!rrE*!rrE&uquH`r +rW)rtrrDHd!W[b$WrE(of)GaFh#@BLi;WfPJcFp5!!)fo!!)or!!*#u$3:,+!!*'!!<<'!rr;uu +s8N'!r;Q`srVlitr;Q`srr2rur;Q`sr;R6,s8N'!s8N'!s8N*!rrE&u!!',#!W[b$o`'F~> +PQ(U_JcF@%!!)EdrrE#trrE&urr<9'!!*'!!!)utrrDusrrDoqrrE*!rrE*!rrE*!rrE*!rrE&u +rrDrrrW)rtrrDHdrW&qt!!(XN!!(jT!!)!Xrr@WMk5PD]q#: +PQ(U_JcF@%!!)EdrrE#trrE&urr<9'!!*'!!!)utrrDusrrDoqrrE*!rrE*!rrE*!rrE*!rrE&u +rrDrrrW)rtrrDHdrW&qt!!(XN!!(jT!!)!Xrr@WMk5PD]q#: +PQ(XXJcF@%!!)EdrrE#trrE&urr<9'!!*'!!!)utrrDusrrDoqrrE*!rrE*!rrE*!rrE*!rrE&u +rrDrrrW)rtrrDHd!W[b$WW)tnfDbjGh>[KMiW&rQJcFm4!!)fo!!)or!!*#u!s&B$!<3!#!<<'! +rr2rurVlitrr2rur;Q`sr;Q`srr2rur;Q`sr;Qj!s8N)urr<&urrW9$rrE&u!!',#!W[b$o`'F~> +f`-L&joDL_!!)EdrrE#trrE&urr<9'!!*'!!!)utrrDrrrrE&u'EJ15!!*'!!!*'!!!*'!!!*'! +!!*#urrDus#QXo)!!*'!!!)EdrW&ns!!([O!!(mU!!)$Yrr@WMjSo2[qu?Kmrr;lrs8N'!rr3'# +s8N)urr<&trr<&urr<&srr<&srr<&urr<&srr<&srrW9$rrE&u!!*#u!W`9#quEhurW)TjJ,~> +f`-L&joDL_!!)EdrrE#trrE&urr<9'!!*'!!!)utrrDrrrrE&u'EJ15!!*'!!!*'!!!*'!!!*'! +!!*#urrDus#QXo)!!*'!!!)EdrW&ns!!([O!!(mU!!)$Yrr@WMjSo2[qu?Kmrr;lrs8N'!rr3'# +s8N)urr<&trr<&urr<&srr<&srr<&urr<&srr<&srrW9$rrE&u!!*#u!W`9#quEhurW)TjJ,~> +f`-L&joDL_!!)EdrrE#trrE&urr<9'!!*'!!!)utrrDrrrrE&u'EJ15!!*'!!!*'!!!*'!!!*'! +!!*#urrDus#QXo)!!*'!!!)Ed!W[b$W;ckmf`(sHhZ!TNirB&RJcFg2!!)orq>gNpquHcs!!*#u +!s&B$!<2uu!<)ot!<2uu!;uis!;uis!<2uu!;uis!;uj!!<<'!rr2rurr3$"s8VusXT&>%J:R:l +J,~> +f`(pOJcFs6!!',#!!)EdrrE#trrE#tquHcsr;cltr;cfrr;cfrr;cisrrE*!rrE&urW)uur;Zlu +!<)rt!<<)u!:Tse!3#nt!8.;O!8meV!9=+Z!.k10rr<&orr<&rrr<&rrr<&urrW9$rrE&u!!)rs +!W`6#qu6Wrr;Q`srr2rur;Q`sr;Qj!s8N)urr<&urrW9$rrB"trW)TjJ,~> +f`(pOJcFs6!!',#!!)EdrrE#trrE#tquHcsr;cltr;cfrr;cfrr;cisrrE*!rrE&urW)uur;Zlu +!<)rt!<<)u!:Tse!3#nt!8.;O!8meV!9=+Z!.k10rr<&orr<&rrr<&rrr<&urrW9$rrE&u!!)rs +!W`6#qu6Wrr;Q`srr2rur;Q`sr;Qj!s8N)urr<&urrW9$rrB"trW)TjJ,~> +f`(pOJcFs6!!',#!!)EdrrE#trrE#tquHcsr;cltr;cfrr;cfrr;cisrrE*!rrE&urW)uur;Zlu +!<)rt!<<)u!:Tph!.]TurrDiHrrDiOrrDiSs8VkFs5O%Y!;QQo!;lcr!;lcr!<3!#!<<'!rr2ru +r;QfurrDrr!!)rs!!*#u!!)rs!!)rs!s&B$!<2uu!<3!#!<<'!W;co!J:R:lJ,~> +f`(pOJcFs6!!',#!!%uXrW&kr!!(^P!!(sW!!)*[rr@WMi;WcWq#: +f`(pOJcFs6!!',#!!%uXrW&kr!!(^P!!(sW!!)*[rr@WMi;WcWq#: +f`(pOJcFs6!!',#!!%uX!W[b$VuHblg&D'Ii;WfPjT#8TJcF[.!!)fo!!)or!!)or!!*#u!s&B$ +!<2uu!;uiu!<3&rrr<&srr<&urr<&srr<&srrW9$rrE&u!!*#u!s&B$!3#o!!.]Uls*t~> +f`(pOJcFs6rW',$!!%uXrW&kr!!(^P!!)!X!!)-\rr@WMhZ!QUq#: +f`(pOJcFs6rW',$!!%uXrW&kr!!(^P!!)!X!!)-\rr@WMhZ!QUq#: +f`(pOJcFs6!W[b$Y5\J%N;iqZJ:OQt!;P7I!;POQ!;P[Us7h0FhZ!QUq#: +f`(pOkPt>Xdf97G^&S'3Y5\J%N;rkWVZ-VrgA_-Qir8uYjo5;\JcFO*!!([O!!%TMr;Z`ro`'F~> +f`(pOkPt>Xdf97G^&S'3Y5\J%N;rkWVZ-VrgA_-Qir8uYjo5;\JcFO*!!([O!!%TMr;Z`ro`'F~> +f`(pOkPt>Xdf97G^&J-6J:Og&!!%uX!W[b$VZ-YkgA_0Jir9#Rjo5>UJcFO*!!([O!!%TMr;Qfu +J:R:lJ,~> +f`(pOjT#5[f)PaMrVultrr2run,NCfnGiLgjo>;[Y5\J%N;rkWVZ-VrgA_-Qj8T)Zk5YG]JcFL) +!!(^P!!%TMqu?Wqo`'F~> +f`(pOjT#5[f)PaMrVultrr2run,NCfnGiLgjo>;[Y5\J%N;rkWVZ-VrgA_-Qj8T)Zk5YG]JcFL) +!!(^P!!%TMqu?Wqo`'F~> +f`(pOjT#5[f)PaMrVultrr2run,NCfnGiLgjo5A^J:Og&!!%uX!W[b$VZ-YkgA_0Jj8T,Sk5YJV +JcFL)!!(^P!!%TMqu6]tJ:R:lJ,~> +f`(pOjT#5[rVult!ri9#r;cfrr;cltrW)osr;cisrW)uur;cltrrDlpr;cisrr<3%!!*'!r;cis +rW)uurrE#tquGmZrW',$!!%uXrW&ep!!(dR!!)*[!!)3^rr@WMg&D$PJcF0urW)TjJ,~> +f`(pOjT#5[rVult!ri9#r;cfrr;cltrW)osr;cisrW)uur;cltrrDlpr;cisrr<3%!!*'!r;cis +rW)uurrE#tquGmZrW',$!!%uXrW&ep!!(dR!!)*[!!)3^rr@WMg&D$PJcF0urW)TjJ,~> +f`(pOjT#5[rVult!ri9#r;cfrr;cltrW)osr;cisrW)uur;cltrrDlpr;cisrr<3%!!*'!r;cis +rW)uurrE#tquGmZ!W[b$Y5\J%N;iqZJ:OKr!;P=K!;PXT!;PaWs7h0Fg&D$PJcF0u!W[b$o`'F~> +f`(pOjT#5[rVuisr;Zcss8W*!rVult#6+Z's8N'!r;Zcs#6+Z's8N'!rr;uuqZ$Qqs8W*!s8W&u +#6+Z's8N'!rr;uu%K?D.s8N'!s8N*!rr<&\s8E#$rr<%Xs8E"prr<&Rrr<&\rr<&_s8N(Ms4@8N +!.k0us8E#js*t~> +f`(pOjT#5[rVuisr;Zcss8W*!rVult#6+Z's8N'!r;Zcs#6+Z's8N'!rr;uuqZ$Qqs8W*!s8W&u +#6+Z's8N'!rr;uu%K?D.s8N'!s8N*!rr<&\s8E#$rr<%Xs8E"prr<&Rrr<&\rr<&_s8N(Ms4@8N +!.k0us8E#js*t~> +f`(pOjT#5[rVuisr;Zcss8W*!rVult#6+Z's8N'!r;Zcs#6+Z's8N'!rr;uuqZ$Qqs8W*!s8W&u +#6+Z's8N'!rr;uu%K?D.s8N'!s8N*!rr<&\rrN1NJ?JnP!0$pZ!.]TrrrDiKrrDiUrrDiXs8VkF +s4@8N!.k0urrN1NJFrkl~> +f`(pOjT#5[rVultqu?Zrs8W*!rVult"TJH%s8W&urVult#6+Z's8N'!rr;uuqZ$Qqs8W*!s8W*! +s8W*!s8W*!rr;uu!ri6#rr;rtrr;uujo>;[Y5\J%N;rkWV#LDph#@?Sk5PD]l2Ub`JcF:#!!%TM +df9:Ho`'F~> +f`(pOjT#5[rVultqu?Zrs8W*!rVult"TJH%s8W&urVult#6+Z's8N'!rr;uuqZ$Qqs8W*!s8W*! +s8W*!s8W*!rr;uu!ri6#rr;rtrr;uujo>;[Y5\J%N;rkWV#LDph#@?Sk5PD]l2Ub`JcF:#!!%TM +df9:Ho`'F~> +f`(pOjT#5[rVultqu?Zrs8W*!rVult"TJH%s8W&urVult#6+Z's8N'!rr;uuqZ$Qqs8W*!s8W*! +s8W*!s8W*!rr;uu!ri6#rr;rtrr;uujo5A^J:Og&!!%uX!W[b$V#LGih#@BLk5PGVl2UeYJcF:# +!!%TMdf0@KJ:R:lJ,~> +f`(pOjT#5[rVultrVufrs8W*!rVucqrr;rtrr;lrs8W*!rr;uuqZ$Qqs8W*!s8W*!s8W*!s8W*! +rr;lrrr;rtrr;uujo>;[Y5\J%N;rkWV#LDph#@?SkPkM^l2L_`JcF4!!!%TMdf9:Ho`'F~> +f`(pOjT#5[rVultrVufrs8W*!rVucqrr;rtrr;lrs8W*!rr;uuqZ$Qqs8W*!s8W*!s8W*!s8W*! +rr;lrrr;rtrr;uujo>;[Y5\J%N;rkWV#LDph#@?SkPkM^l2L_`JcF4!!!%TMdf9:Ho`'F~> +f`(pOjT#5[rVultrVufrs8W*!rVucqrr;rtrr;lrs8W*!rr;uuqZ$Qqs8W*!s8W*!s8W*!s8W*! +rr;lrrr;rtrr;uujo5A^J:Og&!!%uX!W[b$V#LGih#@BLkPkPWl2LbYJcF4!!!%TMdf0@KJ:R:l +J,~> +f`(pOjT#5[rVultrr;uu#6+Z's8N'!rVultqZ$Nps8W*!r;Zcsrr;uuqZ$Qqs8W*!s8W*!s8W*! +s8W*!rr;uuqu?Wqrr;uujo>;[Y5\J%N;rkWU]1;oh>[HTkl1V_lMpkaJcF0u!!)?b!!(dR!!)]l +q>gQq!!&nrrW)TjJ,~> +f`(pOjT#5[rVultrr;uu#6+Z's8N'!rVultqZ$Nps8W*!r;Zcsrr;uuqZ$Qqs8W*!s8W*!s8W*! +s8W*!rr;uuqu?Wqrr;uujo>;[Y5\J%N;rkWU]1;oh>[HTkl1V_lMpkaJcF0u!!)?b!!(dR!!)]l +q>gQq!!&nrrW)TjJ,~> +f`(pOjT#5[rVultrr;uu#6+Z's8N'!rVultqZ$Nps8W*!r;Zcsrr;uuqZ$Qqs8W*!s8W*!s8W*! +s8W*!rr;uuqu?Wqrr;uujo5A^J:Og&!!%uX!W[b$U]1>hh>[KMkl1YXlMpnZJcF0u!!)?b!!(dR +!!)]lq>gQq!!&nr!W[b$o`'F~> +f`(pOjT#5[rVultrr;uu#6+Z's8N'!rVultq>^Hps8W*!r;ZcsrVultrr3Z4s8N'!s8N'!s8N'! +s8N'!s8N'!rr;uur;R$&s8N'!s8N'!jo>;[Y5a:XUAk2nhZ!QUl2L_`li6tbJcF*s!!)?b!!(dR +!!)rs!!*#u!!)rs!!&\lrW)TjJ,~> +f`(pOjT#5[rVultrr;uu#6+Z's8N'!rVultq>^Hps8W*!r;ZcsrVultrr3Z4s8N'!s8N'!s8N'! +s8N'!s8N'!rr;uur;R$&s8N'!s8N'!jo>;[Y5a:XUAk2nhZ!QUl2L_`li6tbJcF*s!!)?b!!(dR +!!)rs!!*#u!!)rs!!&\lrW)TjJ,~> +f`(pOjT#5[rVultrr;uu#6+Z's8N'!rVultq>^Hps8W*!r;ZcsrVultrr3Z4s8N'!s8N'!s8N'! +s8N'!s8N'!rr;uur;R$&s8N'!s8N'!jo5A^J:Og&M?*[,UAk5ghZ!TNl2LbYli7"[JcF*s!!)?b +!!(dR!!)rs!!*#u!!)rs!!&\l!W[b$o`'F~> +f`(pOjT#5[rVultrVucqs8W#ts8W#ts8W#trVufrrr;rtrr;osrVufrrr;uus8W*!rr;rts8W#t +!WN/us8N*!s8E#]s8E#$rr<%Xs8E"mrr<&Urr<&arr<&cs8N(Ms3CWE!;QQo!;c`p!<<'$!<3$! +rVuisrr3'#rr<&ts8;rss8E#trr<&urr<&ts8Duus8E#srr<&srsAc+rrE'!!<<'!!<)rs!4W"- +!;-;A~> +f`(pOjT#5[rVultrVucqs8W#ts8W#ts8W#trVufrrr;rtrr;osrVufrrr;uus8W*!rr;rts8W#t +!WN/us8N*!s8E#]s8E#$rr<%Xs8E"mrr<&Urr<&arr<&cs8N(Ms3CWE!;QQo!;c`p!<<'$!<3$! +rVuisrr3'#rr<&ts8;rss8E#trr<&urr<&ts8Duus8E#srr<&srsAc+rrE'!!<<'!!<)rs!4W"- +!;-;A~> +f`(pOjT#5[rVultrVucqs8W#ts8W#ts8W#trVufrrr;rtrr;osrVufrrr;uus8W*!rr;rts8W#t +!WN/us8N*!s8E#]rrN1NJ?JnP!0$pZ!.]TorrDiNrrDiZrrDi\s8VkFs3CWE!;QQo!;c`p!<<'$ +!<3$!rVuisrr3'#rr<&ts8;rss8E#trr<&urr<&ts8Duus8E#srr<&srsAc+rrE'!!<<'!!<)rs +!4Vt0!.]Uls*t~> +f`(pOJcFs6rW',$!!%uXrW&Yl!!(pV!!)?b!!)Edrr@WMbl7YCq#: +f`(pOJcFs6rW',$!!%uXrW&Yl!!(pV!!)?b!!)Edrr@WMbl7YCq#: +f`(pOJcFs6!W[b$Y5\J%N;iqZJ:O?n!;PIO!;Pm[!;Ps]s7h0Fbl7YCq#: +f`(pOJcFs6rW',$!!%uXrW&Yl!!(pV!!)Bc!!)Ed!!%TMb5VGAq#: +f`(pOJcFs6rW',$!!%uXrW&Yl!!(pV!!)Bc!!)Ed!!%TMb5VGAq#: +f`(pOJcFs6!W[b$Y5\J%N;iqZJ:O?n!;PIO!;Pp\!;Ps]!;M-Fb5VGAq#: +f`(pOJcFs6rW',$!!%uXrW&Vk!!(sW!!)Ed!!)Herr@WMao;>@qu?Kmrr2rur;Q`srr2rurr;os +s8N'!rr3'#s8N)urrN3#s82lorr<&srr<&urr<&srr<&srrW9$rrE&u!!*#u!W`9#quF8,rW)Tj +J,~> +f`(pOJcFs6rW',$!!%uXrW&Vk!!(sW!!)Ed!!)Herr@WMao;>@qu?Kmrr2rur;Q`srr2rurr;os +s8N'!rr3'#s8N)urrN3#s82lorr<&srr<&urr<&srr<&srrW9$rrE&u!!*#u!W`9#quF8,rW)Tj +J,~> +f`(pOJcFs6!W[b$Y5\J%N;iqZJ:O@qu?Kmrr2rur;Q`srr2ru +rr;oss8N'!rr3'#s8N)urrN3#s82lorr<&srr<&urr<&srr<&srrW9$rrE&u!!*#u!W`9#quF8, +!W[b$o`'F~> +f`(pOJcFs6rW',$!!%uXrW&Vk!!(sW!!)He!!)Kfrr@WMa8Z,>q#: +f`(pOJcFs6rW',$!!%uXrW&Vk!!(sW!!)He!!)Kfrr@WMa8Z,>q#: +f`(pOJcFs6!W[b$Y5\J%N;iqZJ:Oq#: +f`(pOJcFs6rW',$!!%uXrW&Sj!!)!X!!)Kf!!)Ngrr@WM`W#o +f`(pOJcFs6rW',$!!%uXrW&Sj!!)!X!!)Kf!!)Ngrr@WM`W#o +f`(pOJcFs6!W[b$Y5\J%N;iqZJ:O9l!;POQ!;Q$_!;Q'`s7h0F`W#o +f`(pOJcFs6rW',$!!%uXrW&Sj!!)!X!!)Ng!!)Qhrr@WM_uB]:q#: +f`(pOJcFs6rW',$!!%uXrW&Sj!!)!X!!)Ng!!)Qhrr@WM_uB]:q#: +f`(pOJcFs6!W[b$Y5\J%N;iqZJ:O9l!;POQ!;Q'`!;Q*as7h0F_uB]:q#: +f`(pOJcFs6rW',$!!%uXrW'\4JH5]MJcF[.!!(II!!%WNrW)TjJ,~> +f`(pOJcFs6rW',$!!%uXrW'\4JH5]MJcF[.!!(II!!%WNrW)TjJ,~> +f`(pOJcFs6!W[b$Y5\J%N;iqZJ:PB6JH5]MJcF[.!!(II!!%WN!W[b$o`'F~> +f`(pOJcFs6rW',$!!%uXrW'\4!!%TM!!%TMi;WcWf)P[KJcG]Lo`'F~> +f`(pOJcFs6rW',$!!%uXrW'\4!!%TM!!%TMi;WcWf)P[KJcG]Lo`'F~> +f`(pOJcFs6!W[b$Y5\J%N;iqZJ:PB6!!%TM!!%TMi;WcWf)P[KJc>cOJ:R:lJ,~> +f`(pOJcFs6rW',$!!%uXrW'\4!!%TM!!%TMi;WcWJcF0urW)TjJ,~> +f`(pOJcFs6rW',$!!%uXrW'\4!!%TM!!%TMi;WcWJcF0urW)TjJ,~> +f`(pOJcFs6!W[b$Y5\J%N;iqZJ:PB6!!%TM!!%TMi;WcWJcF0u!W[b$o`'F~> +g&HU'irH7^!!%uXrW'\4!!%TMrW%NLir8uYJcF0urW)TjJ,~> +g&HU'irH7^!!%uXrW'\4!!%TMrW%NLir8uYJcF0urW)TjJ,~> +g&HU'jT,=2Y5\J%N;iqZJ:PB6!!%TM!W[b$JcFa0!!%TMdf0@KJ:R:lJ,~> +f`(pOJcFs6rW',$!!)0]!!)?b!!)Ng!s&B$!9a@^!:Tse!5AI5!;- +f`(pOJcFs6rW',$!!)0]!!)?b!!)Ng!s&B$!9a@^!:Tse!5AI5!;- +f`(pOJcFs6!W[b$Y5\J%k5PD]li-qbnG`Rjs8N)^rr<&frrN1NJA2$`!;- +f`(pOJcFs6rW',$!!)0]!!)rs!!*#u!!)]l!!)ut!!*#u!!)`m!!)Wj!!)foquH9erW'\4!!)Qh +rrD-[rrDoqrrDfnrrE&u!!)KfrrDQgrrDWirW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6rW',$!!)0]!!)rs!!*#u!!)]l!!)ut!!*#u!!)`m!!)Wj!!)foquH9erW'\4!!)Qh +rrD-[rrDoqrrDfnrrE&u!!)KfrrDQgrrDWirW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6!W[b$Y5\J%k5PD]r;Q`srr2rup&>!lrVlitrr2rupAY*moD\djq#C6lnc&XjJ:PB6 +!!)QhrrD-[rrDoqrrDfnrrE&u!!)KfrrDQgrrDWi!W[b$JcFa0!!%TMdf0@KJ:R:lJ,~> +f`(pOJcFs6rW',$!!)fo!!)lqrW)rt!!*#u!!)utrVururW!!!!;uj%!<<'!rrE*!rVururW)uu +#lsu*!<3'!rrE)u!<2uu!<3!!!<<#urr;rtrr;uun,N@e^Ae05nc/UhrVult!ri9#r;cfrr;clt +rW)uuqu?s$!!*'!!!*#urW)rtrrDlpr;cisrr<3%!!*'!r;cisrW)uurrE#tquH?grW%NLir8uY +kPkM^mf34cg&LsMZ2ae'o`'F~> +f`(pOJcFs6rW',$!!)fo!!)lqrW)rt!!*#u!!)utrVururW!!!!;uj%!<<'!rrE*!rVururW)uu +#lsu*!<3'!rrE)u!<2uu!<3!!!<<#urr;rtrr;uun,N@e^Ae05nc/UhrVult!ri9#r;cfrr;clt +rW)uuqu?s$!!*'!!!*#urW)rtrrDlpr;cisrr<3%!!*'!r;cisrW)uurrE#tquH?grW%NLir8uY +kPkM^mf34cg&LsMZ2ae'o`'F~> +f`(pOJcFs6!W[b$Y5\J%q#: +f`(pOJcFs6rW',$!!)fo!!)or!!*#u!!)or!!)rs!!*#u!!*#u!!*#u!!*#u!W`6#rr2rurr2ru +rr;uurr3*$s8N'!rr3'#s8N)urrW9$rrE&u!!*#u"T\T&!<3&gs8E#4rr<&hs8N)ts8E#rs8N*! +s8N)ts8N''rr<'!rr<&us8N*!s8N*!s8N''rr<'!rr<&qs8N*!s8N*!s8E!&rr<'!rr<&us8N'. +rr<'!rr<'!rrE*!!!)TirW%NLir8uYkPkM^r;Q`srr2ruq#: +f`(pOJcFs6rW',$!!)fo!!)or!!*#u!!)or!!)rs!!*#u!!*#u!!*#u!!*#u!W`6#rr2rurr2ru +rr;uurr3*$s8N'!rr3'#s8N)urrW9$rrE&u!!*#u"T\T&!<3&gs8E#4rr<&hs8N)ts8E#rs8N*! +s8N)ts8N''rr<'!rr<&us8N*!s8N*!s8N''rr<'!rr<&qs8N*!s8N*!s8E!&rr<'!rr<&us8N'. +rr<'!rr<'!rrE*!!!)TirW%NLir8uYkPkM^r;Q`srr2ruq#: +f`(pOJcFs6!W[b$Y5\J%q#: +f`(pOJcFs6rW',$!!)fo!!)cn!!)or!!)rs!!*#u!!*#u!!*#u!s&B$!<3!#!<<'!rr2rurr2ru +rVls"s8N)trrW9$rrE&u!s&B$!<2uu!<2uu!:0[a!5AI5!:g*h!<)rt!;lfr!<<*!!<)rt!!`H' +!<<'!!<3#u!<<*!!<<*!!!`H'!<<'!!;c`q!<<*!!<<*!!<<*!!<<*!!<3#u!!<0#!<3#t!<3#u +!:p0h!.k10rr<&orr<&qs8E#urr<&urr<&ts8Duus8E!!rrDusrrE&u#QXo)!<3$!s8W&urVuis +rr;rtrVuisrr;rt!WN/ts8E#ts8E#us8E#urr<&urr<&us8E#Bs8E#js*t~> +f`(pOJcFs6rW',$!!)fo!!)cn!!)or!!)rs!!*#u!!*#u!!*#u!s&B$!<3!#!<<'!rr2rurr2ru +rVls"s8N)trrW9$rrE&u!s&B$!<2uu!<2uu!:0[a!5AI5!:g*h!<)rt!;lfr!<<*!!<)rt!!`H' +!<<'!!<3#u!<<*!!<<*!!!`H'!<<'!!;c`q!<<*!!<<*!!<<*!!<<*!!<3#u!!<0#!<3#t!<3#u +!:p0h!.k10rr<&orr<&qs8E#urr<&urr<&ts8Duus8E!!rrDusrrE&u#QXo)!<3$!s8W&urVuis +rr;rtrVuisrr;rt!WN/ts8E#ts8E#us8E#urr<&urr<&us8E#Bs8E#js*t~> +f`(pOJcFs6!W[b$Y5\J%q#: +f`(pOJcFs6rW',$!!)orq>gKor;c`p!!)rs!!*#u!!)ut!W`6#rr2rurr3'#s8N)urr<&urr<&t +rrW9$rrE#t!s&B$!<3!#!<<'!rr;lrli6qa^Ae05nc/UhrVultrVufrs8W*!rVucqs8W*!rr;uu +s8W*!s8Vuss8W*!qZ$Qqs8W*!s8W*!s8W*!s8W*!rr;lrrr;rtrr;uuo)J[hJcFa0!!)fo!!)or +!!)fo!!)rs!!*#u!!*#u!!*#u!W`6#rr3*$s8N'!rr2rurr3'#s8N)srr<&urrW9$rrDus!!)rs +!!)rs!!*#u!!*#u!s&B$!<2uu!<3!#!<<'!aoD>?o`'F~> +f`(pOJcFs6rW',$!!)orq>gKor;c`p!!)rs!!*#u!!)ut!W`6#rr2rurr3'#s8N)urr<&urr<&t +rrW9$rrE#t!s&B$!<3!#!<<'!rr;lrli6qa^Ae05nc/UhrVultrVufrs8W*!rVucqs8W*!rr;uu +s8W*!s8Vuss8W*!qZ$Qqs8W*!s8W*!s8W*!s8W*!rr;lrrr;rtrr;uuo)J[hJcFa0!!)fo!!)or +!!)fo!!)rs!!*#u!!*#u!!*#u!W`6#rr3*$s8N'!rr2rurr3'#s8N)srr<&urrW9$rrDus!!)rs +!!)rs!!*#u!!*#u!s&B$!<2uu!<3!#!<<'!aoD>?o`'F~> +f`(pOJcFs6!W[b$Y5\J%qu?KmrVufrqu6Wrr;Q`srr2rurVlp!rrE&u!!*#u!s&B$!<2uu!<2uu +!<)p"!<<'!rVls"s8N)urrW9$rrE&uquH'_!W[b$^Ae05nc/UhrVultrVufrs8W*!rVucqs8W*! +rr;uus8W*!s8Vuss8W*!qZ$Qqs8W*!s8W*!s8W*!s8W*!rr;lrrr;rtrr;uuo)AakJ:N4Nir8uY +q#: +f`(pOJcFs6rW',$!!)fo!!)or!!*#u!!)or!!)rs!!*#u!!)ut"9AH%s8Voq!WN0!rr<&urr<&t +rrW9$rrE#t!s&B$!<3!#!<<'!rr2rukPtM]^Ae05nc/UhrVultrr;uu#6+Z's8N'!rVultr;Zcs +rr;uus8W*!s8W*!r;ZcsqZ$Qqs8W*!s8W*!s8W*!s8W*!rr;uuqu?Wqrr;uuo)J[hJcFa0!!)fo +!!)or!!)fo!!)rs!!*#u!!*#u!!*#u! +f`(pOJcFs6rW',$!!)fo!!)or!!*#u!!)or!!)rs!!*#u!!)ut"9AH%s8Voq!WN0!rr<&urr<&t +rrW9$rrE#t!s&B$!<3!#!<<'!rr2rukPtM]^Ae05nc/UhrVultrr;uu#6+Z's8N'!rVultr;Zcs +rr;uus8W*!s8W*!r;ZcsqZ$Qqs8W*!s8W*!s8W*!s8W*!rr;uuqu?Wqrr;uuo)J[hJcFa0!!)fo +!!)or!!)fo!!)rs!!*#u!!*#u!!*#u! +f`(pOJcFs6!W[b$Y5\J%q#:^Qr!<2uu!<2uu +!<)p"!<<'!rVls"s8N)urrW9$rrE&u!!)3^!W[b$^Ae05nc/UhrVultrr;uu#6+Z's8N'!rVult +r;Zcsrr;uus8W*!s8W*!r;ZcsqZ$Qqs8W*!s8W*!s8W*!s8W*!rr;uuqu?Wqrr;uuo)AakJ:N4N +ir8uYq#: +f`(pOJcFs6rW)ZlrrCXMrrD]k!!)`m!!)fo!!)or!!*#u!!)or!!)rs!!*#u!!)ut"T\Q&s8N)s +rrN3#!<2uu!<2uu!<)p#!<<'!!<3!*!<<'!s8N'!s8N)urr<&^s8E#4rr<&hs8N)ts8N)us8N'' +rr<'!rr<&ts8N)ss8N)us8N*!s8N*!s8N)rs8N)urtGJ5rr<'!rr<'!rr<'!rr<'!rr<&us8N)s +rs/W)rr<'!rr<&is8E"Ls5O%Y!;lfm!<)rt!;c]q!;uis!<2uu!<)p$!<3'!rrDrr!!)ut!!*#u +!s&B$!;ulp!<3#u!<)rt!;lcu!<<'!rVufrs8N'!rr2rurr2rurr;uubQ%PAo`'F~> +f`(pOJcFs6rW)ZlrrCXMrrD]k!!)`m!!)fo!!)or!!*#u!!)or!!)rs!!*#u!!)ut"T\Q&s8N)s +rrN3#!<2uu!<2uu!<)p#!<<'!!<3!*!<<'!s8N'!s8N)urr<&^s8E#4rr<&hs8N)ts8N)us8N'' +rr<'!rr<&ts8N)ss8N)us8N*!s8N*!s8N)rs8N)urtGJ5rr<'!rr<'!rr<'!rr<'!rr<&us8N)s +rs/W)rr<'!rr<&is8E"Ls5O%Y!;lfm!<)rt!;c]q!;uis!<2uu!<)p$!<3'!rrDrr!!)ut!!*#u +!s&B$!;ulp!<3#u!<)rt!;lcu!<<'!rVufrs8N'!rr2rurr2rurr;uubQ%PAo`'F~> +f`(pOJcFs6!W[b$pAb0ff)PdFo`"pdpAY*mq#: +f`(pOJcFs6rW)]m!W`6#fDbpQs8N)ns82llrr<&orr<&qs82itrrE&uquHcs!!*#u!!)ut!s&B$ +!;c]s!<3&urrW9$rrE#t"9AK%!<<#urVult!WN0!rr<&us8;r`s8E#4rr<&hs8N)ts8N)ts82ls +s8;rts8;rts8N)us8N*!s8N)us8;rrs8;rrs8;rss8N*!s8N)us8E#us8;ourrE#trrE*!rW)Tj +rW%NLir8uYq#: +f`(pOJcFs6rW)]m!W`6#fDbpQs8N)ns82llrr<&orr<&qs82itrrE&uquHcs!!*#u!!)ut!s&B$ +!;c]s!<3&urrW9$rrE#t"9AK%!<<#urVult!WN0!rr<&us8;r`s8E#4rr<&hs8N)ts8N)ts82ls +s8;rts8;rts8N)us8N*!s8N)us8;rrs8;rrs8;rss8N*!s8N)us8E#us8;ourrE#trrE*!rW)Tj +rW%NLir8uYq#: +f`(pOJcFs6!W[b$p\t +f`(pOJcFs6rW)Wk!!([O!!)ut!!)cnrrDcm!!%uXrW'\4!!%TMrW%NLir8uYq#: +f`(pOJcFs6rW)Wk!!([O!!)ut!!)cnrrDcm!!%uXrW'\4!!%TMrW%NLir8uYq#: +f`(pOJcFs6!W[b$p&>$ef`(sHrVllmp](9gpAY*mN;iqZJ:PB6!!%TM!W[b$JcFa0!!)fo!!)fo +!!)or!!)rs!!*#u!!)ut"T\Q&s8N)rrr<&trr<&urrW9$rrDus!!)fo!!)rs!!)rs!s&B$!<2uu +!<3!#!<<'!rr3*$s8N'!r;Q`sbl7_EJ:R:lJ,~> +f`(pOJcFs6rW)Wk!!([O!!)ut!!)cn!W`6#p\t3nN;rkW^Ae05JcG]LJcFa0!!)fo!!)orrW)rt +!!*#uquHcs!!*#u!!)ut!!*#u!!)or!!)rsrW)osrW)rtr;cltrW)rtrW)uur;cfr!!*#uquHcs +!!*#urr<0$!<<)u!6bEA!;-;A~> +f`(pOJcFs6rW)Wk!!([O!!)ut!!)cn!W`6#p\t3nN;rkW^Ae05JcG]LJcFa0!!)fo!!)orrW)rt +!!*#uquHcs!!*#u!!)ut!!*#u!!)or!!)rsrW)osrW)rtr;cltrW)rtrW)uur;cfr!!*#uquHcs +!!*#urr<0$!<<)u!6bEA!;-;A~> +f`(pOJcFs6!W[b$p&>$ef`(sHrVllmp\tcOJ:N4Nir8uYq#: +f`(pOk5PD]li-qbnG`Rjs8N)^rr<&Bs8E#krr<&Orr<&trr<&arr<%Xs8E#4rr<%Ms8E"Ls5O%Y +!.k0us8E#js*t~> +f`(pOk5PD]li-qbnG`Rjs8N)^rr<&Bs8E#krr<&Orr<&trr<&arr<%Xs8E#4rr<%Ms8E"Ls5O%Y +!.k0us8E#js*t~> +f`(pOk5PD]li-qbnG`Rjs8N)^rr<&BrrN1NJG&pBpt5THq#13mq!.kZ!0$pZ!.]U6rr<%MrrN1N +J:[a[rr<%Ms3goK!.]Uls*t~> +f`(pOk5PD]r;Q`srr2rup&>!lrVlitrr2rupAY*moD\djq#C6lc2[bCp&>!lf`(pOrVlitlMgha +N;rkW^Ae05JcG]LJcFa0!!%TMdf9:Ho`'F~> +f`(pOk5PD]r;Q`srr2rup&>!lrVlitrr2rupAY*moD\djq#C6lc2[bCp&>!lf`(pOrVlitlMgha +N;rkW^Ae05JcG]LJcFa0!!%TMdf9:Ho`'F~> +f`(pOk5PD]r;Q`srr2rup&>!lrVlitrr2rupAY*moD\djq#C6lc2RhFJ:R=m!;P4H!;QNm!;PjZ +!!%uX!W[b$^Ae05Jc>cOJ:N4Nir8uYJcF0u!W[b$o`'F~> +f`(pOq#: +f`(pOq#: +f`(pOq#:$ef`(sHrVllmlMghaN;iqZJ:PB6!!%TM!W[b$ +JcFa0!!%TMdf0@KJ:R:lJ,~> +f`(pOq#: +f`(pOq#: +f`(pOq#:cOJ:N4Nir8uYJcF0u!W[b$o`'F~> +f`(pOq#:s8E#ms82lLs8N)trr<&urr<&hrr<&Srr<&brr<&grrW9$rrD6^ +!!)iprW'\4!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOq#:s8E#ms82lLs8N)trr<&urr<&hrr<&Srr<&brr<&grrW9$rrD6^ +!!)iprW'\4!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOq#:rrN1NJG9*Apt5WHq#13mq#:9nq!n@a!8RSS!:0Xb!:^!j!<<'! +kPkM^q>UKrJ:PB6!!%TM!W[b$JcFa0!!%TMdf0@KJ:R:lJ,~> +f`(pOqu?KmrVufrqu6Wrr;Q`srr2rurVlp!rrE&u!!*#u!s&B$!<2uu!<2uu!<)p"!<<'!rVls" +s8N)urrW9$rrE&uquFe;rW',$!!)Qh!!)Ed!!)rs!!*#u!!)]l!!)ut!!*#u!!)`m!!)Wj!!)fo +quHWorW'\4!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOqu?KmrVufrqu6Wrr;Q`srr2rurVlp!rrE&u!!*#u!s&B$!<2uu!<2uu!<)p"!<<'!rVls" +s8N)urrW9$rrE&uquFe;rW',$!!)Qh!!)Ed!!)rs!!*#u!!)]l!!)ut!!*#u!!)`m!!)Wj!!)fo +quHWorW'\4!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOqu?KmrVufrqu6Wrr;Q`srr2rurVlp!rrE&u!!*#u!s&B$!<2uu!<2uu!<)p"!<<'!rVls" +s8N)urrW9$rrE&uquFe;!W[b$Y5\J%nc&RhmJd.dr;Q`srr2rup&>!lrVlitrr2rupAY*moD\dj +q#C6lqu6]tJ:PB6!!%TM!W[b$JcFa0!!%TMdf0@KJ:R:lJ,~> +f`(pOq#:^Qr!<2uu!<2uu!<)p"!<<'!rVls" +s8N)urrW9$rrE&u!!'q:rW',$!!)fo!!)rsrW)`nrW)rt!!*#u!!)utrVururW!!!!;uj%!<<'! +rrE*!rVururW)uu#lsu*!<3'!rrE)u!<2uu!<3!!!<<#urr;rtrr;uuq>^EopAb-mk5YG]o`"mk +pAY*mJcG]LJcFa0!!%TMdf9:Ho`'F~> +f`(pOq#:^Qr!<2uu!<2uu!<)p"!<<'!rVls" +s8N)urrW9$rrE&u!!'q:rW',$!!)fo!!)rsrW)`nrW)rt!!*#u!!)utrVururW!!!!;uj%!<<'! +rrE*!rVururW)uu#lsu*!<3'!rrE)u!<2uu!<3!!!<<#urr;rtrr;uuq>^EopAb-mk5YG]o`"mk +pAY*mJcG]LJcFa0!!%TMdf9:Ho`'F~> +f`(pOq#:^Qr!<2uu!<2uu!<)p"!<<'!rVls" +s8N)urrW9$rrE&u!!'q:!W[b$Y5\J%q#:cOJ:N4Nir8uYJcF0u!W[b$o`'F~> +f`(pOq#:%!;QQo!;lcr!;QQo!<2uu!;lcr!;uis!<2uu!<2uu!<2uu +!<3!"!<3&urr<&urr<&us8N)urr`?%rr<&urrW9$rrE&u!s&B$!<2uu!<3!%!<<'!rrDoqrW)]m +!W`6#kPkVas8N)ns82lls+(1K!.k10rr<%Ms3grH!;-;A~> +f`(pOq#:%!;QQo!;lcr!;QQo!<2uu!;lcr!;uis!<2uu!<2uu!<2uu +!<3!"!<3&urr<&urr<&us8N)urr`?%rr<&urrW9$rrE&u!s&B$!<2uu!<3!%!<<'!rrDoqrW)]m +!W`6#kPkVas8N)ns82lls+(1K!.k10rr<%Ms3grH!;-;A~> +f`(pOq#: +f`(pOq#: +f`(pOq#: +f`(pOq#: +f`(pOJcFs6rW',$!!)orq>gNp!!)cnr;c`p!!)rs!!*#u!!)ut!W`6#rr2rurr3'#s8N)urr<&u +rr<&trrW9$rrE#t!s&B$!<3!#!<<'!rr;lrp&G!kp&>!lkl1V_rVlitp\t9prrDfn!!%TMrW%NL +ir8uYJcF0urW)TjJ,~> +f`(pOJcFs6rW',$!!)orq>gNp!!)cnr;c`p!!)rs!!*#u!!)ut!W`6#rr2rurr3'#s8N)urr<&u +rr<&trrW9$rrE#t!s&B$!<3!#!<<'!rr;lrp&G!kp&>!lkl1V_rVlitp\t9prrDfn!!%TMrW%NL +ir8uYJcF0urW)TjJ,~> +f`(pOJcFs6!W[b$Y5\J%qu?Kmrr2rup](0lqu6Wrr;Q`srr2rurVlp!rrE&u!!*#u!s&B$!<2uu +!<2uu!<)p"!<<'!rVls"s8N)urrW9$rrE&uquHEi!W[b$p&>$ekl1YXrVllmp\t +f`(pOJcFs6rW',$!!)fo!!)or!!)fo!!*#u!!)or!!)rs!!*#u!!)ut"9AH%s8Voq!WN0!rr<&u +rr<&trrW9$rrE#t!s&B$!<3!#!<<'!rr2runc/Rgp&>!lkl1V_rVlitlMghaJcG]LJcFa0JH3sq +o`'F~> +f`(pOJcFs6rW',$!!)fo!!)or!!)fo!!*#u!!)or!!)rs!!*#u!!)ut"9AH%s8Voq!WN0!rr<&u +rr<&trrW9$rrE#t!s&B$!<3!#!<<'!rr2runc/Rgp&>!lkl1V_rVlitlMghaJcG]LJcFa0JH3sq +o`'F~> +f`(pOJcFs6!W[b$Y5\J%q#:^Qr!<2uu +!<2uu!<)p"!<<'!rVls"s8N)urrW9$rrE&u!!)Qh!W[b$p&>$ekl1YXrVllmlMghaJc>cOJ:N4N +ir=Q0ciF)ro`'F~> +f`(pOJcFs6rW',$!!)fo!!)or!!)fo!!*#u!!)or!!)rs!!*#u!!)ut"T\Q&s8N)srrN3#!<2uu +!<2uu!<)p#!<<'!!<3!*!<<'!s8N'!s8N)urr<&hs8E#krr<&_rr<&trr<&arr<%Ms8E"Ls5O%Y +!.k0us8E#js*t~> +f`(pOJcFs6rW',$!!)fo!!)or!!)fo!!*#u!!)or!!)rs!!*#u!!)ut"T\Q&s8N)srrN3#!<2uu +!<2uu!<)p#!<<'!!<3!*!<<'!s8N'!s8N)urr<&hs8E#krr<&_rr<&trr<&arr<%Ms8E"Ls5O%Y +!.k0us8E#js*t~> +f`(pOJcFs6!W[b$Y5\J%q#: +f`(pOJcFs6WW;ho!!)lq!!)foqu?ct!<3#r!<<'!!<2uu!<)p"!<<'!qYpTsrrE&u!s&B$!<)p# +!<<'!s8E#ss8N'"rrE&u!!*#ur;cNjrW)Wk!!)6_!!)ut!!) +f`(pOJcFs6WW;ho!!)lq!!)foqu?ct!<3#r!<<'!!<2uu!<)p"!<<'!qYpTsrrE&u!s&B$!<)p# +!<<'!s8E#ss8N'"rrE&u!!*#ur;cNjrW)Wk!!)6_!!)ut!!) +f`(pOJcFs6!W[b$Y4qtsq#:'nJ:R=m!;PdX!;QNm!;PjZ!!%TM!W[b$JcFa0 +!!%TMdf0@KJ:R:lJ,~> +f`(pOJcFs6rW',$!!)Edr;`YnrW)Wk!!)3^!s&B$!9sL`!.k1L!.k10rr<%Ms3grH!;-;A~> +f`(pOJcFs6rW',$!!)Edr;`YnrW)Wk!!)3^!s&B$!9sL`!.k1L!.k10rr<%Ms3grH!;-;A~> +f`(pOJcFs6!W[b$Y5\J%mJm+bV#LJrJ:R=m!;PaW"8Morq!%eY!.k.O!.]TNs5O%Y!.k0urrN1N +JFrkl~> +f`(pOJcFs6rW',$!!%uXrW)]mquGs\rrE#t!!*#u!!)Qh!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6rW',$!!%uXrW)]mquGs\rrE#t!!*#u!!)Qh!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6!W[b$Y5\J%N;iqZJ:RCor:oLUs7lQm!;QQn!;Q*a!!%TM!W[b$JcFa0!!%TMdf0@K +J:R:lJ,~> +f`(pOf)G^Mli-qbnG`Rjs8N)^rr<&Rs8E#$rr<%Xs8E#4rr<%Ms8E"Ls5O%Y!.k0us8E#js*t~> +f`(pOf)G^Mli-qbnG`Rjs8N)^rr<&Rs8E#$rr<%Xs8E#4rr<%Ms8E"Ls5O%Y!.k0us8E#js*t~> +f`(pOf)G^Mli-qbnG`Rjs8N)^rr<&RrrN1NJ?JnP!0$pZ!.]U6rr<%MrrN1NJ:[a[rr<%Ms3goK +!.]Uls*t~> +f`(pOnc&RhkPkM^r;Q`srr2rup&>!lrVlitrr2rupAY*moD\djq#C6lh>dHSY5\J%N;rkW^Ae05 +JcG]LJcFa0!!%TMdf9:Ho`'F~> +f`(pOnc&RhkPkM^r;Q`srr2rup&>!lrVlitrr2rupAY*moD\djq#C6lh>dHSY5\J%N;rkW^Ae05 +JcG]LJcFa0!!%TMdf9:Ho`'F~> +f`(pOnc&RhkPkM^r;Q`srr2rup&>!lrVlitrr2rupAY*moD\djq#C6lh>[NVJ:Og&!!%uX!W[b$ +^Ae05Jc>cOJ:N4Nir8uYJcF0u!W[b$o`'F~> +f`(pOq#: +f`(pOq#: +f`(pOq#: +f`(pOq#:%!0$sW!5AI5!.k1L!.k10 +rr<%Ms3grH!;-;A~> +f`(pOq#:%!0$sW!5AI5!.k1L!.k10 +rr<%Ms3grH!;-;A~> +f`(pOq#: +f`(pOq#: +f`(pOq#: +f`(pOq#: +f`(pOqu?Kmrr2rurVultp](0lqu6Wrr;Q`srr2rurVlp!rrE&u!!*#u!s&B$!<2uu!<2uu!<)p" +!<<'!rVls"s8N)urrW9$rrE&uquG@KrW',$!!%uXrW'\4!!)Qh!!)'Z!!(sW!!)rs!!*#u!!)rs +!!)'ZrW%NLir8uYJcF0urW)TjJ,~> +f`(pOqu?Kmrr2rurVultp](0lqu6Wrr;Q`srr2rurVlp!rrE&u!!*#u!s&B$!<2uu!<2uu!<)p" +!<<'!rVls"s8N)urrW9$rrE&uquG@KrW',$!!%uXrW'\4!!)Qh!!)'Z!!(sW!!)rs!!*#u!!)rs +!!)'ZrW%NLir8uYJcF0urW)TjJ,~> +f`(pOqu?Kmrr2rurVultp](0lqu6Wrr;Q`srr2rurVlp!rrE&u!!*#u!s&B$!<2uu!<2uu!<)p" +!<<'!rVls"s8N)urrW9$rrE&uquG@K!W[b$Y5\J%N;iqZJ:PB6!!)Qh!!)'Z!!(sW!!)rs!!*#u +!!)rs!!)'Z!W[b$JcFa0!!%TMdf0@KJ:R:lJ,~> +f`(pOq#:UEprr2ruqu6Wrr;Q`srr2rurVm!#rrE*!q>^Qr!<2uu!<2uu!<)p" +!<<'!rVls"s8N)urrW9$rrE&u!!(LJrW',$!!%uXrW'\4!!)fo!!)rsrW)uu$3:,+!<3$!s8N'! +rVuisrVuis!<<#urr;rtrr33'rr<'!rr<&urrE-"rW)rt!!*#u!!)utrVururW)os!!)rs$3:,+ +!<3$!s8N'!rVuisqZ$NpJcFa0!!%TMdf9:Ho`'F~> +f`(pOq#:UEprr2ruqu6Wrr;Q`srr2rurVm!#rrE*!q>^Qr!<2uu!<2uu!<)p" +!<<'!rVls"s8N)urrW9$rrE&u!!(LJrW',$!!%uXrW'\4!!)fo!!)rsrW)uu$3:,+!<3$!s8N'! +rVuisrVuis!<<#urr;rtrr33'rr<'!rr<&urrE-"rW)rt!!*#u!!)utrVururW)os!!)rs$3:,+ +!<3$!s8N'!rVuisqZ$NpJcFa0!!%TMdf9:Ho`'F~> +f`(pOq#:UEprr2ruqu6Wrr;Q`srr2rurVm!#rrE*!q>^Qr!<2uu!<2uu!<)p" +!<<'!rVls"s8N)urrW9$rrE&u!!(LJ!W[b$Y5\J%N;iqZJ:PB6!!)fo!!)rsrW)uu$3:,+!<3$! +s8N'!rVuisrVuis!<<#urr;rtrr33'rr<'!rr<&urrE-"rW)rt!!*#u!!)utrVururW)os!!)rs +$3:,+!<3$!s8N'!rVuisqYpTsJ:N4Nir8uYJcF0u!W[b$o`'F~> +f`(pOq#:UEprr2ruqu6Wrr;Q`srr2rurVm$$rrE*!!;uiu!<3&urr<&urr<&t +rr`?%rr<&ursAc+rrE*!!!*'!!<2uu!7UuI!3Z>%!0$rh!;QQo!;lcr!<3!.!<<'!!<<'!!<<'! +s8N)urrW9$rrDus!!*#u!!*#u%flY0!!*'!!!*'!!<<'!!<2uu!;lcr!;uis!<2uu!;uis!;uj, +!<<'!!<<'!!<<'!s8N)urr<&rs8E"Ls5O%Y!.k0us8E#js*t~> +f`(pOq#:UEprr2ruqu6Wrr;Q`srr2rurVm$$rrE*!!;uiu!<3&urr<&urr<&t +rr`?%rr<&ursAc+rrE*!!!*'!!<2uu!7UuI!3Z>%!0$rh!;QQo!;lcr!<3!.!<<'!!<<'!!<<'! +s8N)urrW9$rrDus!!*#u!!*#u%flY0!!*'!!!*'!!<<'!!<2uu!;lcr!;uis!<2uu!;uis!;uj, +!<<'!!<<'!!<<'!s8N)urr<&rs8E"Ls5O%Y!.k0us8E#js*t~> +f`(pOq#:UEprr2ruqu6Wrr;Q`srr2rurVm$$rrE*!!;uiu!<3&urr<&urr<&t +rr`?%rr<&ursAc+rrE*!!!*'!!<2uu!7UrL!.]U&rr<%XrrN1NJA1dY!;QQo!;lcr!<3!.!<<'! +!<<'!!<<'!s8N)urrW9$rrDus!!*#u!!*#u%flY0!!*'!!!*'!!<<'!!<2uu!;lcr!;uis!<2uu +!;uis!;uj,!<<'!!<<'!!<<'!s8N)urr<&rrrN1NJ:[a[rr<%Ms3goK!.]Uls*t~> +f`(pOq#: +f`(pOq#: +f`(pOq#: +f`(pOkPtJ\LB%5QY5\J%N;rkW^Ae05qu?Kmrr2rurr3'#s8N)urr<&urrN3#s82lrs8N)urr<&t +s8;rtrr<&urr<&urrW9$rrE#t!!)or!!)rs!!*#u!!)rs!!)rs!s&B$!<2uu!<3!"!<<)s!;lfq +!.k10rr<%Ms3grH!;-;A~> +f`(pOkPtJ\LB%5QY5\J%N;rkW^Ae05qu?Kmrr2rurr3'#s8N)urr<&urrN3#s82lrs8N)urr<&t +s8;rtrr<&urr<&urrW9$rrE#t!!)or!!)rs!!*#u!!)rs!!)rs!s&B$!<2uu!<3!"!<<)s!;lfq +!.k10rr<%Ms3grH!;-;A~> +f`(pOkPtJ\LAq;TJ:Og&!!%uX!W[b$^Ae05qu?Kmrr2rurr3'#s8N)urr<&urrN3#s82lrs8N)u +rr<&ts8;rtrr<&urr<&urrW9$rrE#t!!)or!!)rs!!*#u!!)rs!!)rs!s&B$!<2uu!<3!"!<<)s +!;lct!.]TNs5O%Y!.k0urrN1NJFrkl~> +f`(pOJcFs6rW',$!!%uXrW'\4!!)fo!!)or!!*#u!s&B$!<2uu!<3!#!<<'!q#:Ers8N)urr<&u +rrW9$rrE&u!!*#u!s&B$!<)ot!;lcr!;uis!<2uu!;uis!;uj!!<<'!rr2rurr3'#s8N)ns8E"L +s5O%Y!.k0us8E#js*t~> +f`(pOJcFs6rW',$!!%uXrW'\4!!)fo!!)or!!*#u!s&B$!<2uu!<3!#!<<'!q#:Ers8N)urr<&u +rrW9$rrE&u!!*#u!s&B$!<)ot!;lcr!;uis!<2uu!;uis!;uj!!<<'!rr2rurr3'#s8N)ns8E"L +s5O%Y!.k0us8E#js*t~> +f`(pOJcFs6!W[b$Y5\J%N;iqZJ:PB6!!)fo!!)or!!*#u!s&B$!<2uu!<3!#!<<'!q#:Ers8N)u +rr<&urrW9$rrE&u!!*#u!s&B$!<)ot!;lcr!;uis!<2uu!;uis!;uj!!<<'!rr2rurr3'#s8N)n +rrN1NJ:[a[rr<%Ms3goK!.]Uls*t~> +f`(pOJcFs6rW',$!!%uXrW'\4!!)fo!!)or!!*#u!s&B$!<2uu!<3!#!<<'!q#:Ers8N)urr<&u +rrW9$rrE&u!!*#u"9AK%!!*#u!!)or!!)rs!!*#u!!)rs!!)rs!s&B$!<2uu!<3!#!<<'!p](3m +JcFa0!!%TMdf9:Ho`'F~> +f`(pOJcFs6rW',$!!%uXrW'\4!!)fo!!)or!!*#u!s&B$!<2uu!<3!#!<<'!q#:Ers8N)urr<&u +rrW9$rrE&u!!*#u"9AK%!!*#u!!)or!!)rs!!*#u!!)rs!!)rs!s&B$!<2uu!<3!#!<<'!p](3m +JcFa0!!%TMdf9:Ho`'F~> +f`(pOJcFs6!W[b$Y5\J%N;iqZJ:PB6!!)fo!!)or!!*#u!s&B$!<2uu!<3!#!<<'!q#:Ers8N)u +rr<&urrW9$rrE&u!!*#u"9AK%!!*#u!!)or!!)rs!!*#u!!)rs!!)rs!s&B$!<2uu!<3!#!<<'! +p\t9pJ:N4Nir8uYJcF0u!W[b$o`'F~> +f`(pOJcFs6rW',$!!%uXrW'\4!!)fo!!)lq"p"]'!<<'!rr2rurr2rurr;oss8W&urVlitrr;lr +!WN0!rr<&urr`?%rrE)u!<2uu!<3#r!<<'!!<2uu!<)ot!;uj!!<<'!rr2rurr2rurr;osqu?Wq +JcFa0!!%TMdf9:Ho`'F~> +f`(pOJcFs6rW',$!!%uXrW'\4!!)fo!!)lq"p"]'!<<'!rr2rurr2rurr;oss8W&urVlitrr;lr +!WN0!rr<&urr`?%rrE)u!<2uu!<3#r!<<'!!<2uu!<)ot!;uj!!<<'!rr2rurr2rurr;osqu?Wq +JcFa0!!%TMdf9:Ho`'F~> +f`(pOJcFs6!W[b$Y5\J%N;iqZJ:PB6!!)fo!!)lq"p"]'!<<'!rr2rurr2rurr;oss8W&urVlit +rr;lr!WN0!rr<&urr`?%rrE)u!<2uu!<3#r!<<'!!<2uu!<)ot!;uj!!<<'!rr2rurr2rurr;os +qu6]tJ:N4Nir8uYJcF0u!W[b$o`'F~> +f`(pOJcFs6rW',$!!%uXrW'\4!!'\3!!((>rW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6rW',$!!%uXrW'\4!!'\3!!((>rW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6!W[b$Y5\J%N;iqZJ:PB6!!'\3!!((>!W[b$JcFa0!!%TMdf0@KJ:R:lJ,~> +f`(pOJcFs6rW',$!!%uXrW'\4!!'\3!!((>rW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6rW',$!!%uXrW'\4!!'\3!!((>rW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6!W[b$Y5\J%N;iqZJ:PB6!!'\3!!((>!W[b$JcFa0!!%TMdf0@KJ:R:lJ,~> +f`(pOmJd.daT)):s8N'!mJd.dg]%6Rr;Z`rY5\J%N;rkW^Ae05JcG]LJcFa0!!%TMdf9:Ho`'F~> +f`(pOmJd.daT)):s8N'!mJd.dg]%6Rr;Z`rY5\J%N;rkW^Ae05JcG]LJcFa0!!%TMdf9:Ho`'F~> +f`(pOmJd.daT)):s8N'!mJd.dg]%6Rr;QfuJ:Og&!!%uX!W[b$^Ae05Jc>cOJ:N4Nir8uYJcF0u +!W[b$o`'F~> +f`(pOnc&Rh_>aK8kPkM^q#: +f`(pOnc&Rh_>aK8kPkM^q#: +f`(pOnc&Rh_>aK8kPkM^q#:cOJ:N4Nir8uYJcF0u +!W[b$o`'F~> +f`(pOq#: +f`(pOq#: +f`(pOq#: +f`(pOq#:UEpr;R6,s8N'!s8N'!s8N*!rrE&u%06G.!<<'!!<<'!s8N)urr<&urr`?%rr<&urr<&urrN3# +!<3!#!<<'!r;Z`rY5\J%N;rkW^Ae05JcG]LJcFa0!!%TMdf9:Ho`'F~> +f`(pOq#:UEpr;R6,s8N'!s8N'!s8N*!rrE&u%06G.!<<'!!<<'!s8N)urr<&urr`?%rr<&urr<&urrN3# +!<3!#!<<'!r;Z`rY5\J%N;rkW^Ae05JcG]LJcFa0!!%TMdf9:Ho`'F~> +f`(pOq#:UEpr;R6,s8N'!s8N'!s8N*!rrE&u%06G.!<<'!!<<'!s8N)urr<&urr`?%rr<&urr<&urrN3# +!<3!#!<<'!r;QfuJ:Og&!!%uX!W[b$^Ae05Jc>cOJ:N4Nir8uYJcF0u!W[b$o`'F~> +f`(pOq#: +f`(pOq#: +f`(pOq#: +f`(pOqu?Kmrr2rurr3'#s8N)urr<&urrN3#s82lls8N)urr<&trr<&us8;rtrr<&urr<&prr<&s +rrW9$rrE&u!!*#u!W`9#quHcs!s&B$!<3!#!<<'!rr;lrs8N'!r;QfurrE&ur;clt!!)rsrW',$ +!!%uXrW'\4!!)0]!!)rs!!*#u!!)]l!!)ut!!*#u!!)`m!!)Wj!!)foquGmZrW%NLir8uYJcF0u +rW)TjJ,~> +f`(pOqu?Kmrr2rurr3'#s8N)urr<&urrN3#s82lls8N)urr<&trr<&us8;rtrr<&urr<&prr<&s +rrW9$rrE&u!!*#u!W`9#quHcs!s&B$!<3!#!<<'!rr;lrs8N'!r;QfurrE&ur;clt!!)rsrW',$ +!!%uXrW'\4!!)0]!!)rs!!*#u!!)]l!!)ut!!*#u!!)`m!!)Wj!!)foquGmZrW%NLir8uYJcF0u +rW)TjJ,~> +f`(pOqu?Kmrr2rurr3'#s8N)urr<&urrN3#s82lls8N)urr<&trr<&us8;rtrr<&urr<&prr<&s +rrW9$rrE&u!!*#u!W`9#quHcs!s&B$!<3!#!<<'!rr;lrs8N'!r;QfurrE&ur;clt!!)rs!W[b$ +Y5\J%N;iqZJ:PB6!!)0]!!)rs!!*#u!!)]l!!)ut!!*#u!!)`m!!)Wj!!)foquGmZ!W[b$JcFa0 +!!%TMdf0@KJ:R:lJ,~> +f`(pOq#: +f`(pOq#: +f`(pOq#: +f`(pOq#:UEp +r;Qj!s8N)urr<&urrW9$rrDrr!s&B$!<3!#!<<'!rr2ruqu6Wrr;Qp#rrE*!!<3!#!<<'!r;Z`r +Y5\J%N;rkW^Ae05q#: +f`(pOq#:UEp +r;Qj!s8N)urr<&urrW9$rrDrr!s&B$!<3!#!<<'!rr2ruqu6Wrr;Qp#rrE*!!<3!#!<<'!r;Z`r +Y5\J%N;rkW^Ae05q#: +f`(pOq#:UEp +r;Qj!s8N)urr<&urrW9$rrDrr!s&B$!<3!#!<<'!rr2ruqu6Wrr;Qp#rrE*!!<3!#!<<'!r;Qfu +J:Og&!!%uX!W[b$^Ae05q#: +f`(pOq#: +f`(pOq#: +f`(pOq#: +f`(pOf`1mMq>UEpT)\ciY5\J%N;rkW^Ae05qu?KmrVufrqu6Wrr;Q`srr2rurVlp!rrE&u!!*#u +!s&B$!<2uu!<2uu!<)p"!<<'!rVls"s8N)urrW9$rrE&uquG[TrW%NLir8uYJcF0urW)TjJ,~> +f`(pOf`1mMq>UEpT)\ciY5\J%N;rkW^Ae05qu?KmrVufrqu6Wrr;Q`srr2rurVlp!rrE&u!!*#u +!s&B$!<2uu!<2uu!<)p"!<<'!rVls"s8N)urrW9$rrE&uquG[TrW%NLir8uYJcF0urW)TjJ,~> +f`(pOf`1mMq>UEpT)SilJ:Og&!!%uX!W[b$^Ae05qu?KmrVufrqu6Wrr;Q`srr2rurVlp!rrE&u +!!*#u!s&B$!<2uu!<2uu!<)p"!<<'!rVls"s8N)urrW9$rrE&uquG[T!W[b$JcFa0!!%TMdf0@K +J:R:lJ,~> +f`(pObl7YCT)\ciY5\J%N;rkW^Ae05q#:^Qr +!<2uu!<2uu!<)p"!<<'!rVls"s8N)urrW9$rrE&u!!(gSrW%NLir8uYJcF0urW)TjJ,~> +f`(pObl7YCT)\ciY5\J%N;rkW^Ae05q#:^Qr +!<2uu!<2uu!<)p"!<<'!rVls"s8N)urrW9$rrE&u!!(gSrW%NLir8uYJcF0urW)TjJ,~> +f`(pObl7YCT)SilJ:Og&!!%uX!W[b$^Ae05q#:^Qr!<2uu!<2uu!<)p"!<<'!rVls"s8N)urrW9$rrE&u!!(gS!W[b$JcFa0!!%TMdf0@KJ:R:l +J,~> +f`(pOJcFs6rW',$!!%uXrW'\4!!)fo!!)or!!*#u!!)or!!)rs!!*#u!!)ut"T\Q&s8N)srrN3# +!<2uu!<2uu!<)p#!<<'!!<3!*!<<'!s8N'!s8N)urr<&Ss8E"Ls5O%Y!.k0us8E#js*t~> +f`(pOJcFs6rW',$!!%uXrW'\4!!)fo!!)or!!*#u!!)or!!)rs!!*#u!!)ut"T\Q&s8N)srrN3# +!<2uu!<2uu!<)p#!<<'!!<3!*!<<'!s8N'!s8N)urr<&Ss8E"Ls5O%Y!.k0us8E#js*t~> +f`(pOJcFs6!W[b$Y5\J%N;iqZJ:PB6!!)fo!!)or!!*#u!!)or!!)rs!!*#u!!)ut"T\Q&s8N)s +rrN3#!<2uu!<2uu!<)p#!<<'!!<3!*!<<'!s8N'!s8N)urr<&SrrN1NJ:[a[rr<%Ms3goK!.]Ul +s*t~> +f`(pOJcFs6rW',$!!%uXrW'\4!!)fo!!)lqqu?ct!<3#r!<<'!!<2uu!<)p"!<<'!qYpTsrrE&u +!s&B$!<)p#!<<'!s8E#ss8N'"rrE&u!!*#ur;bdUrW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6rW',$!!%uXrW'\4!!)fo!!)lqqu?ct!<3#r!<<'!!<2uu!<)p"!<<'!qYpTsrrE&u +!s&B$!<)p#!<<'!s8E#ss8N'"rrE&u!!*#ur;bdUrW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6!W[b$Y5\J%N;iqZJ:PB6!!)fo!!)lqqu?ct!<3#r!<<'!!<2uu!<)p"!<<'!qYpTs +rrE&u!s&B$!<)p#!<<'!s8E#ss8N'"rrE&u!!*#ur;bdU!W[b$JcFa0!!%TMdf0@KJ:R:lJ,~> +f`(pOJcFs6rW',$!!%uXrW'\4!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6rW',$!!%uXrW'\4!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6!W[b$Y5\J%N;iqZJ:PB6!!%TM!W[b$JcFa0!!%TMdf0@KJ:R:lJ,~> +f`(pOJcFs6rW',$!!%uXrW'\4!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6rW',$!!%uXrW'\4!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6!W[b$Y5\J%N;iqZJ:PB6!!%TM!W[b$JcFa0!!%TMdf0@KJ:R:lJ,~> +f`(pO^]433!<;lqli-qbcN!kDY5\J%N;rkW^Ae05JcG]LJcFa0!!%TMdf9:Ho`'F~> +f`(pO^]433!<;lqli-qbcN!kDY5\J%N;rkW^Ae05JcG]LJcFa0!!%TMdf9:Ho`'F~> +f`(pO^]433!<;lqli-qbcMmqGJ:Og&!!%uX!W[b$^Ae05Jc>cOJ:N4Nir8uYJcF0u!W[b$o`'F~> +f`(pO_#FB7q#: +f`(pO_#FB7q#: +f`(pO_#FB7q#:cOJ:N4Nir8uYJcF0u!W[b$o`'F~> +f`(pOq#: +f`(pOq#: +f`(pOq#:cOJ:N4Nir8uYJcF0u!W[b$o`'F~> +f`(pOq#: +f`(pOq#: +f`(pOq#:cOJ:N4Nir8uYJcF0u +!W[b$o`'F~> +f`(pOq#: +f`(pOq#: +f`(pOq#: +f`(pOqu?Kmrr2rurVls"s8N)trrW9$rrE&u!W`9#quHcs!!*#u!W`9#quHWo!!)rs!!)ut!!)rs +!!)utquHcsquFn>rW',$!!%uXrW'\4!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOqu?Kmrr2rurVls"s8N)trrW9$rrE&u!W`9#quHcs!!*#u!W`9#quHWo!!)rs!!)ut!!)rs +!!)utquHcsquFn>rW',$!!%uXrW'\4!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOqu?Kmrr2rurVls"s8N)trrW9$rrE&u!W`9#quHcs!!*#u!W`9#quHWo!!)rs!!)ut!!)rs +!!)utquHcsquFn>!W[b$Y5\J%N;iqZJ:PB6!!%TM!W[b$JcFa0!!%TMdf0@KJ:R:lJ,~> +f`(pOq#: +f`(pOq#: +f`(pOq#:cOJ:N4Nir8uYJcF0u!W[b$o`'F~> +f`(pOq#:%!0$sW!5AI5!.k1L!.k10rr<%Ms3grH!;-;A~> +f`(pOq#:%!0$sW!5AI5!.k1L!.k10rr<%Ms3grH!;-;A~> +f`(pOq#: +f`(pOq#: +f`(pOq#: +f`(pOq#: +f`(pOnc&Rhp&>!lLB%5QY5\J%N;rkW^Ae05JcG]LJcFa0!!%TMdf9:Ho`'F~> +f`(pOnc&Rhp&>!lLB%5QY5\J%N;rkW^Ae05JcG]LJcFa0!!%TMdf9:Ho`'F~> +f`(pOnc&Rhp&>!lLAq;TJ:Og&!!%uX!W[b$^Ae05Jc>cOJ:N4Nir8uYJcF0u!W[b$o`'F~> +f`(pOnc&Rhp&>!lLB%5QY5\J%N;rkW^Ae05JcG]LJcFa0!!%TMdf9:Ho`'F~> +f`(pOnc&Rhp&>!lLB%5QY5\J%N;rkW^Ae05JcG]LJcFa0!!%TMdf9:Ho`'F~> +f`(pOnc&Rhp&>!lLAq;TJ:Og&!!%uX!W[b$^Ae05Jc>cOJ:N4Nir8uYJcF0u!W[b$o`'F~> +f`(pOJcFs6rW',$L]G0h!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6rW',$L]G0h!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6!W[b$Y5a@Zs+%i`!!%TM!W[b$JcFa0!!%TMdf0@KJ:R:lJ,~> +f`(pOJcFs6rW'#!MZCKk!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6rW'#!MZCKk!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6!W[b$X8e*.^Ae05Jc>cOJ:N4Nir8uYJcF0u!W[b$o`'F~> +f`(pOJcFs6rW'#!MZCKk!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6rW'#!MZCKk!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6!W[b$X8e*.^Ae05Jc>cOJ:N4Nir8uYJcF0u!W[b$o`'F~> +f`(pOJcFs6rW%NLo)A[iK)YfNJcG]LJcFa0!!%TMdf9:Ho`'F~> +f`(pOJcFs6rW%NLo)A\TK)YfNJcG]LJcFa0!!%TMdf9:Ho`'F~> +f`(pOJcFs6!W[b$JcG<@!'l/9!!%TM!W[b$JcFa0!!%TMdf0@KJ:R:lJ,~> +f`(pOJcFs6rW%NLo)A[iK)YfNJcG]LJcFa0!!%TMdf9:Ho`'F~> +f`(pOJcFs6rW%NLo)A\TK)YfNJcG]LJcFa0!!%TMdf9:Ho`'F~> +f`(pOJcFs6!W[b$JcG<@!'l/9!!%TM!W[b$JcFa0!!%TMdf0@KJ:R:lJ,~> +f`(pOJcFs6rW%NLoD\jlrr@]O!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6rW%NLoD\kWs$25:!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6!W[b$JcG?A!^QcNKDtoOJc>cOJ:N4Nir8uYJcF0u!W[b$o`'F~> +f`(pOJcFs6rW%NLoD\jlrr@]O!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6rW%NLoD\kWs$25:!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6!W[b$JcG?A!^QcNKDtoOJc>cOJ:N4Nir8uYJcF0u!W[b$o`'F~> +f`(pOJcFs6rW%NLoD\jlrr@]O!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6rW%NLoD\kWs$25:!!%TMrW%NLir8uYJcF0urW)TjJ,~> +f`(pOJcFs6!W[b$JcG?A!^QcNKDtoOJc>cOJ:N4Nir8uYJcF0u!W[b$o`'F~> +g&HU'irFZ1o`"mkrr2ruK`;#PJcG]LJcFa0JH3sqo`'F~> +g&HU'irFZ1o`"nVrr2s`K`;#PJcG]LJcFa0JH3sqo`'F~> +g&HU'jT,=2JcGBB!'pP`!'l5;!!%TM!W[b$JcFa0JH4$ss+'bAJ,~> +f`(pOJcFs6rW%NLo`"mkrr2ruK`;#PJcG]LJcFX-JH4'to`'F~> +f`(pOJcFs6rW%NLo`"nVrr2s`K`;#PJcG]LJcFX-JH4'to`'F~> +f`(pOJcFs6!W[b$JcGBB!'pP`!'l5;!!%TM!W[b$JcFX-JUl-uo`'F~> +f`(pOJcFs6rW%NLo`"mkrr2ruK`;#PJcG]LJcFX-JH4'to`'F~> +f`(pOJcFs6rW%NLo`"nVrr2s`K`;#PJcG]LJcFX-JH4'to`'F~> +f`(pOJcFs6!W[b$JcGBB!'pP`!'l5;!!%TM!W[b$JcFX-JUl-uo`'F~> +f`(pOJcFs6rW%NLp&>!lr;Q`sL&V,QJcG]LJcCT,!!&MgJ,~> +f`(pOJcFs6rW%NLp&>"Wr;Qa^L&V,QJcG]LJcCT,!!&MgJ,~> +f`(pOJcFs6!W[b$JcGEC!'pJ^!'l8 +f`(pOJcFs6rW%NLp&>!lr;Q`sL&V,QJcG]LJcCT,!!&MgJ,~> +f`(pOJcFs6rW%NLp&>"Wr;Qa^L&V,QJcG]LJcCT,!!&MgJ,~> +f`(pOJcFs6!W[b$JcGEC!'pJ^!'l8 +f`(pOJcFs6rW%NLp&>!lr;Q`sL&V,QJcG]LJcCW-rW&JgJ,~> +f`(pOJcFs6rW%NLp&>"Wr;Qa^L&V,QJcG]LJcCW-rW&JgJ,~> +f`(pOJcFs6!W[b$JcGEC!'pJ^!'l8 +f`(pOJcFs6rW%NLpAY*mqYpNqLAq5RJcG]LJcDPGrrD]k!!)cnrW&JgJ,~> +f`(pOJcFs6rW%NLpAY+XqYpO\LAq5RJcG]LJcDPGrrD]k!!)cnrW&JgJ,~> +f`(pOJcFs6!W[b$JcGHD!'pD\!'l;=!!%TM!W[b$JcDPGs7l6d!;Q +f`(pOJcFs6rW%NLpAY*mqYpNqLAq5RJcG]LJcDSH!s&B$!;HNk!;c]u!<3'!!1j.?~> +f`(pOJcFs6rW%NLpAY+XqYpO\LAq5RJcG]LJcDSH!s&B$!;HNk!;c]u!<3'!!1j.?~> +f`(pOJcFs6!W[b$JcGHD!'pD\!'l;=!!%TM!W[b$JcDSH"8Morq"Ogdq"k!nq#C-hpmqG8~> +f`(pOJcFs6rW%NLpAY*mqYpNqLAq5RJcG]LJcDVI!!)ut!!)cnrrDio"9AH%rrAViJ,~> +f`(pOJcFs6rW%NLpAY+XqYpO\LAq5RJcG]LJcDVI!!)ut!!)cnrrDio"9AH%rrAViJ,~> +f`(pOJcFs6!W[b$JcGHD!'pD\!'l;=!!%TM!W[b$JcDVI!;QNm!;Q +f`(pOJcFs6rW%NLp\t3nq#:SJcG]LJcDVI!!)ut!!)cn!W`6#qYpa"s8N*!rrAYjJ,~> +f`(pOJcFs6rW%NLp\t4Yq#:=ZL]7>SJcG]LJcDVI!!)ut!!)cn!W`6#qYpa"s8N*!rrAYjJ,~> +f`(pOJcFs6!W[b$JcGKE!'p>Z!'l>>!!%TM!W[b$JcDVI!;QNm!;Q +f`(pOJcFs6rW%NLp\t3nq#:SJcG]LJcDVI!!)ut!!)Ed"p"]'!<<'!T)X<~> +f`(pOJcFs6rW%NLp\t4Yq#:=ZL]7>SJcG]LJcDVI!!)ut!!)Ed"p"]'!<<'!T)X<~> +f`(pOJcFs6!W[b$JcGKE!'p>Z!'l>>!!%TM!W[b$JcDVI!;QNm!;Ps]#5J5uq#CBhT)X<~> +f`(pOJcFs6rW%NLp\t3nq#:SJcG]LJcDVI!!)ut!!)Ed"p"]'!<<'!T)X<~> +f`(pOJcFs6rW%NLp\t4Yq#:=ZL]7>SJcG]LJcDVI!!)ut!!)Ed"p"]'!<<'!T)X<~> +f`(pOJcFs6!W[b$JcGKE!'p>Z!'l>>!!%TM!W[b$JcDVI!;QNm!;Ps]#5J5uq#CBhT)X<~> +f`(pOJcFs6rW%NLq#: +f`(pOJcFs6rW%NLq#:=ZpAY+XM#RGTJcG]LJcDVI!!)ut!!)He!!*#u!!*#u!!&YkJ,~> +f`(pOJcFs6!W[b$JcGNF!'p8X!'lA?!!%TM!W[b$JcDVI!;QNm!;Q!^!;QQn!;QQn!;N2dJ,~> +f`(pOJcFs6rW%NLq#BsdM#RGTJcG]LJcDSH!s&B$!:Bdd!<2uu!<2uu!2':A~> +f`(pOJcFs6rW%NLq#BtOM#RGTJcG]LJcDSH!s&B$!:Bdd!<2uu!<2uu!2':A~> +f`(pOJcFs6!W[b$JcGNFoK\64!!%TM!W[b$JcDSH"8Morq!J(]q#:9nq#:9npn.S:~> +f`(pOJcFs6rW%NLo)A[iK)YfNJcG]LJcDPGrrE#t!!*#u!!)`m!!)ut!!)ut!!&\lJ,~> +f`(pOJcFs6rW%NLo)A\TK)YfNJcG]LJcDPGrrE#t!!*#u!!)`m!!)ut!!)ut!!&\lJ,~> +f`(pOJcFs6!W[b$JcG<@!'l/9!!%TM!W[b$JcDPGs7lQm!;QQn!;Q9f!;QNm!;QNm!;N5eJ,~> +f`(pOJcFs6rW%NLo)A[iK)YfNJcG]LJcCc1!!)ut!!)ut!!&\lJ,~> +f`(pOJcFs6rW%NLo)A\TK)YfNJcG]LJcCc1!!)ut!!)ut!!&\lJ,~> +f`(pOJcFs6!W[b$JcG<@!'l/9!!%TM!W[b$JcCc1!;QNm!;QNm!;N5eJ,~> +f`(pOJcFs6rW%NLo)A[iK)YfNJcG]LJcCT,!!&MgJ,~> +f`(pOJcFs6rW%NLo)A\TK)YfNJcG]LJcCT,!!&MgJ,~> +f`(pOJcFs6!W[b$JcG<@!'l/9!!%TM!W[b$JcCT,!;N&`J,~> +f`(pOJcFs6rW%NLo)A[iK)YfNJcG]LJcCT,!!&MgJ,~> +f`(pOJcFs6rW%NLo)A\TK)YfNJcG]LJcCT,!!&MgJ,~> +f`(pOJcFs6!W[b$JcG<@!'l/9!!%TM!W[b$JcCT,!;N&`J,~> +f`(pOJcFs6rW%NLo)A[iK)YfNJcG]LJcCT,!!&MgJ,~> +f`(pOJcFs6rW%NLo)A\TK)YfNJcG]LJcCT,!!&MgJ,~> +f`(pOJcFs6!W[b$JcG<@!'l/9!!%TM!W[b$JcCT,!;N&`J,~> +f`(pOJcFs6rW%NLo)A[iK)YfNJcG]LJcCT,!!&MgJ,~> +f`(pOJcFs6rW%NLo)A\TK)YfNJcG]LJcCT,!!&MgJ,~> +f`(pOJcFs6!W[b$JcG<@!'l/9!!%TM!W[b$JcCT,!;N&`J,~> +f`(pOJcFs6rW%NLo)A[iK)YfNJcG]LJcCT,!!&MgJ,~> +f`(pOJcFs6rW%NLo)A\TK)YfNJcG]LJcCT,!!&MgJ,~> +f`(pOJcFs6!W[b$JcG<@!'l/9!!%TM!W[b$JcCT,!;N&`J,~> +f`(pOJcFs6rW%NLo)A[iK)YfNJcG]LJcCT,!!&MgJ,~> +f`(pOJcFs6rW%NLo)A\TK)YfNJcG]LJcCT,!!&MgJ,~> +f`(pOJcFs6!W[b$JcG<@!'l/9!!%TM!W[b$JcCT,!;N&`J,~> +f`(pOJcFs6rW'M/JH4'tlMlD8r;_EKM>mPUS,\!~> +f`(pOJcFs6rW'M/JH4'tlMlD8r;_EKM>mPUS,\!~> +f`(pOJcFs6!W[b$\c73\d/`J3JH5]Ms+#\#M>mSNS,\!~> +f`(pOJcFs6rW'M/!!%TMe,KCJkPp)5!!%TMM>mPUS,\!~> +f`(pOJcFs6rW'M/!!%TMe,KCJkPp)5!!%TMM>mPUS,\!~> +f`(pOJcFs6!W[b$\c2X0JcF4!!!)3^JUd`NJcCT,!;N&`J,~> +f`(pOJcFs6rW'M/!!%TMe,KCJkPp)5!!%TMM>mPUS,\!~> +f`(pOJcFs6rW'M/!!%TMe,KCJkPp)5!!%TMM>mPUS,\!~> +f`(pOJcFs6!W[b$\c2X0JcF4!!!)3^JUd`NJcCT,!;N&`J,~> +f`(pOJcFs6rW'M/!!%TMe,TCIU]1;oJcC<$ci3tFS,\!~> +f`(pOJcFs6rW'M/!!%TMe,TCIU]1 +f`(pOJcFs6!W[b$\c2X0JcF4!!W[b$U]1 +f`(pOJcFs6rW'M/!!(1Aq#KUXr;a)%rW&_n!!%TMJcF'r!!&MgJ,~> +f`(pOJcFs6rW'M/!!(1Aq#KUXr;a)%rW&_n!'l,8JcF'r!!&MgJ,~> +f`(pOJcFs6!W[b$\c2X0b5_8;kPtJ\Yl=b)J:OEp!'l,8JcF'r!;N&`J,~> +f`(pOJcFs6rW'M/!!((>rrD0\rrE&u"9AK%!!)cnrrBn8rW&bo!W`6#JcC<$d/O(GS,\!~> +f`(pOJcFs6rW'M/!!((>rrD0\rrE&u"9AK%!!)cnrrBn8rW&bo!^QcNJcC<$d/O(GS,\!~> +f`(pOJcFs6!W[b$\c2X0a8c/>jo>>\rr3*$s8N'!p](6n_>aQ:J:OHq!^QcNJcC<$d/O+@S,\!~> +f`(pOJcFs6rW'M/!!((>rrE#trr<-#!<;utrVufrs8W&us8W*!r;Z]qs8W#ts8W#trr;rtaT)5> +V#LJrrr@WMJcF*s!!&MgJ,~> +f`(pOJcFs6rW'M/!!((>rrE#trr<-#!<;utrVufrs8W&us8W*!r;Z]qs8W#ts8W#trr;rtaT)5> +V#LK]s$2/8JcF*s!!&MgJ,~> +f`(pOJcFs6!W[b$\c2X0a8c/>rVult!ri9#r;cfrr;cltrW)uurrDusr;cltr;cltr;cisrW(%> +!W[b$V#LK]s$2/8JcF*s!;N&`J,~> +f`(pOJcFs6rW'M/!!((>rrE#trW)lrrrE*!rrE#trr<0$!!*&u!;uls!;lfr!<<*!!<3#u!!<0# +!6P9?!2TVr!<3%Ms+13srr<%gs*t~> +f`(pOJcFs6rW'M/!!((>rrE#trW)lrrrE*!rrE#trr<0$!!*&u!;uls!;lfr!<<*!!<3#u!!<0# +!6P9?!2TVr5lX*#s+13srr<%gs*t~> +f`(pOJcFs6!W[b$\c2X0a8c/>rVuisr;Zcss8W*!rVult"9/?$s8E#rs8N)rs8N*!s8N)us8N'# +rr<&@rrN1NJ>E2H5lX*#s+13srrDh`s*t~> +f`(pOJcFs6rW'M/!!((>rrE#trrDrrrrE*!rrE#trr<3%!!*'!r;cisrrDrrrrE*!rrE&urr<-# +!!(.@rW&ep!!*#u!!%TMJcF-t!!&MgJ,~> +f`(pOJcFs6rW'M/!!((>rrE#trrDrrrrE*!rrE#trr<3%!!*'!r;cisrrDrrrrE*!rrE&urr<-# +!!(.@rW&ep!'pP`!'l,8JcF-t!!&MgJ,~> +f`(pOJcFs6!W[b$\c2X0a8c/>rVultqu?Zrs8W*!rVult"TJH%s8W#trr;uuqu?Zrs8W*!rr;uu +!ri6#ao;DBJ:OKr!'pP`!'l,8JcF-t!;N&`J,~> +f`(pOJcFs6rW'M/!!((>rrE#trrE#tr;cltrrE#tquH]qrW)uurrE#tr;cltrrE&uquFk=rW&ep +!!*#u!!%TMJcF-t!!&MgJ,~> +f`(pOJcFs6rW'M/!!((>rrE#trrE#tr;cltrrE#tquH]qrW)uurrE#tr;cltrrE&uquFk=rW&ep +!'pP`!'l,8JcF-t!!&MgJ,~> +f`(pOJcFs6!W[b$\c2X0a8c/>rVultrVufrs8W*!rVucqrVuiss8W*!rVufrs8W*!rr;lrao;DB +J:OKr!'pP`!'l,8JcF-t!;N&`J,~> +f`(pOJcFs6rW'M/!!((>rrE#trrE&urr<9'!!*'!!!)utrrDlprrE*!rrE&urr<9'!!*'!!!*#u +rrC(=rW&ep!!*#u!!%TMJcF-t!!&MgJ,~> +f`(pOJcFs6rW'M/!!((>rrE#trrE&urr<9'!!*'!!!)utrrDlprrE*!rrE&urr<9'!!*'!!!*#u +rrC(=rW&ep!'pP`!'l,8JcF-t!!&MgJ,~> +f`(pOJcFs6!W[b$\c2X0a8c/>rVultrr;uu#6+Z's8N'!rVultq>^Hps8W*!rr;uu#6+Z's8N'! +rr;uu`r?)?J:OKr!'pP`!'l,8JcF-t!;N&`J,~> +f`-L&j8caj!!((>rrE#trrE&urr<9'!!*'!!!)utrrE#t!!*#urrE*!rrE&urr<9'!!*'!!!*#u +rrC(=rW&hq!!)rs!!%TMXT*hOblID9J,~> +f`-L&j8caj!!((>rrE#trrE&urr<9'!!*'!!!)utrrE#t!!*#urrE*!rrE&urr<9'!!*'!!!*#u +rrC(=rW&hq!'pJ^!'l,8XT*hOblID9J,~> +f`-L&joGF3\c2X0a8c/>rVultrr;uu#6+Z's8N'!rVultrVlitrr;uus8W*!rr;uu#6+Z's8N'! +rr;uu`r?)?J:ONs!'pJ^!'l,8XT*hOblID9J,~> +ec11#k5`'m!!((>rrE#trrE#tquHcsr;cltr;cltr;cfrrW)uuquHcsrW)uur;at>rW&hq!!)rs +!!%TMXT&8#JcF'r!!)ZkJ,~> +ec11#k5`'m!!((>rrE#trrE#tquHcsr;cltr;cltr;cfrrW)uuquHcsrW)uur;at>rW&hq!'pJ^ +!'l,8XT&8#JcF'r!!)ZkJ,~> +ec12NkCC,C!!((>rrE#trrE#tquHcsr;cltr;cltr;cfrrW)uuquHcsrW)uur;at>!W[b$VZ-W] +r;Qa^JcDhO!!%TMci3tFo`'F~> +ec11#k5`'m!!%TMe,TCIVZ-Vrr;Q`sJcDhO!!%TMci3tFo`'F~> +ec11#k5`'m!!%TMe,TCIVZ-W]r;Qa^JcDhO!!%TMci3tFo`'F~> +ec12NkCC,C!!%TMe,KILJ:ONs!'pJ^!'l,8XT&8#JcF'r!!)ZkJ,~> +N;rnXJcG$8!!%TMe,TCIVuH_sqYpNqJcDkP!!%TMci +N;roCJcG$8!!%TMe,TCIVuH`^qYpO\JcDkP!!%TMci +N;roCJcG$8!!%TMe,KILJ:OQt!'pD\!'l,8XoAA$JcF'r!W[b$pA]X~> +N;rnXJcG$8!!%TMe,TCIVuH_sqYpNqJcDkP!!(aQrrDBbquG@Kr;a\6rW)ZlJ,~> +N;roCJcG$8!!%TMe,TCIVuH`^qYpO\JcDkP!!(aQrrDBbquG@Kr;a\6rW)ZlJ,~> +N;roCJcG$8!!%TMe,KILJ:OQt!'pD\!'l,8XoAA$gAh0Qli6k_fDkdL_>aQ:J:R@nJ,~> +N;iqZrr@WMli-qbJcF4!rW&kr!!)lq!!%TMXoAA$gAh0Qr;Zcsrr;uup](6ns8W*!g&M'Prr3*$ +s8N'!p](6ndf9:HpA]X~> +N;irEs$2/8li-qbJcF4!rW&kr!'pD\!'l,8XoAA$gAh0Qr;Zcsrr;uup](6ns8W*!g&M'Prr3*$ +s8N'!p](6ndf9:HpA]X~> +N;irEs$2/8li-qbJcF4!!W[b$VuH`^qYpO\JcDkP!!(aQrrDusrrE&urrDfnrrE*!rrCaPrrE&u +"9AK%!!)cnrrCLI!W[b$pA]X~> +N;iqZrr@WMli-qbJcF4!rW&ns!!)fo!!%TMY5\J%gAh0QrVufr!<;ut!ri6#rr3K/s8N'!s8N'! +rr<'!!<<)t!<)rr!<<)u!<)rr!<3#s!!<0#!;ulq!<<)t!<<)t!<3#t!87DO!;?GC~> +N;irEs$2/8li-qbJcF4!rW&ns!'p>Z!'l,8Y5\J%gAh0QrVufr!<;ut!ri6#rr3K/s8N'!s8N'! +rr<'!!<<)t!<)rr!<<)u!<)rr!<3#s!!<0#!;ulq!<<)t!<<)t!<3#t!87DO!;?GC~> +N;irEs$2/8li-qbJcF4!!W[b$W;ci_q#:=ZJcDnQ!!(aQrrE#tr;Zitr;Zp!!!*#u%flY0!!*'! +!!*$!!<3'!s8;rrs8;rts8E#ss8;rss8;p!rr<&ss8;rts8;rts8;rss8E#OrrN1NJG0"n~> +N;iqZrr@WMli-qbJcF4!rW&ns!!)fo!!%TMY5\J%gAh0Qr;Zcsrr;uus8W*!rr39)s8N'!s8N'! +s8E#us8N*!s8N*!s8N)ts8N''rr<'!rr<&ss8N)ts8E#rs8N)rs8N*!s8N)us8N'#rr<&Qs8E#l +s*t~> +N;irEs$2/8li-qbJcF4!rW&ns!'p>Z!'l,8Y5\J%gAh0Qr;Zcsrr;uus8W*!rr39)s8N'!s8N'! +s8E#us8N*!s8N*!s8N)ts8N''rr<'!rr<&ss8N)ts8E#rs8N)rs8N*!s8N)us8N'#rr<&Qs8E#l +s*t~> +N;irEs$2/8li-qbJcF4!!W[b$W;ci_q#:=ZJcDnQ!!(aQrrDusrrE&urrE*!rrE&u#lt#*!!*'! +!!*&u!<<*!!<<*!!<<*!!<)rt!!`H'!<<'!!;uls!<)rs!;uls!;lfr!<<*!!<3#u!!<0#!8@GS +!.]Uns*t~> +NW/tYrr2ruJcG*:!!%TMe,TCIW;chtq#: +NW/uDrr2s`JcG*:!!%TMe,TCIW;ci_q#:=ZJcDnQ!!(aQrrDusrrE&urrE&urr<*"!<3#r!<<*! +!<3#u!<<*!!<<*!!<)rt!!N<%!<<)u!<)rs!<)rr!<3#u!;lfr!<<*!!<3#u!!<0#!8@JP!;?GC~> +NW/uDrr2s`JcG*:!!%TMe,KILJ:OTu!'p>Z!'l,8Y5\J%gAh0Qr;Zcsrr;uurr;uu!WN0!s82ls +s8N)us8N*!s8N*!s8N)ts8N'%rr<'!s8E#ss8E#ss8;rss8N)rs8N*!s8N)us8N'#rr<&QrrN1N +JG0"n~> +NW/tYrr2ruJcG*:!!%TMe,TCIWW)qupAY*mJcDqR!!(aQrrDusrrE&urrE&urr<*"!<3#u!;uls +!<3#u!<<*!!<<*!!<)rq!<3#t!<)rs!;ulr!<<*!!<)rr!<<*!!<3#r!8@JP!;?GC~> +NW/uDrr2s`JcG*:!!%TMe,TCIWW)r`pAY+XJcDqR!!(aQrrDusrrE&urrE&urr<*"!<3#u!;uls +!<3#u!<<*!!<<*!!<)rq!<3#t!<)rs!;ulr!<<*!!<)rr!<<*!!<3#r!8@JP!;?GC~> +NW/uDrr2s`JcG*:!!%TMe,KILJ:OX!!'p8X!'l,8YQ"S&gAh0Qr;Zcsrr;uurr;uu!WN0!s8N)s +s8N)us8N*!s8N*!s8N)ts82lrs8E#ss8E#rs8E#us8N)ts8;rts8N)us82lNrrN1NJG0"n~> +NW/tYrr2ruJcG*:!!%TMe,TCIWW2SjJcDqR!!(aQrrDusrrE&urrE&urr<*"!<3#u!;uls!<3#u +!<<*!!<<*!!<)rt!;c`p!<)rs!;uls!<<*!!<3#u!!`H'!<<'!!<3#u!8%8M!;?GC~> +NW/uDrr2s`JcG*:!!%TMe,TCIWW2TUJcDqR!!(aQrrDusrrE&urrE&urr<*"!<3#u!;uls!<3#u +!<<*!!<<*!!<)rt!;c`p!<)rs!;uls!<<*!!<3#u!!`H'!<<'!!<3#u!8%8M!;?GC~> +NW/uDrr2s`JcG*:!!%TMe,KILJ:OX!oK\!-YQ"S&gAh0Qr;Zcsrr;uurr;uu!WN0!s8N)ss8N)u +s8N*!s8N*!s8N)ts8N)qs8E#ss8E#rs8N*!s8N)us8N''rr<'!rr<&us8N)NrrN1NJG0"n~> +NrK(Zr;Q`sJcG-;JH4!rU]1;oJcD_L!!(aQrrDusrrE&urrE#trrE#trrDusrrE&urrE*!rrE*! +rrE#trrDlprrDusrr<*"!<3#u!<<*!!<3#u!!`H'!<<'!!<3#u!8%8M!;?GC~> +NrK)Er;Qa^JcG-;JH4!rU]1 +NrK)Er;Qa^JcG-;JH4'ts+$mE!'l,8WW)qugAh0Qr;Zcsrr;uurVultrVultr;Zcsrr;uus8W*! +s8W*!rVultq>^Hpr;Zcs!WN0!s8N*!s8N)us8N''rr<'!rr<&us8N)NrrN1NJG0"n~> +NrK(Zr;Q`sJcG-;!!%TMe,TCIU]1;oJcD_L!!(aQqZ-ZrrW)uurW)uurrE#trrDusrrE#tr;cfr +r;cltr;cltr;cisr;cisr;cfrrW)uuquHcsrW)uur;bROrW)ZlJ,~> +NrK)Er;Qa^JcG-;!!%TMe,TCIU]1 +NrK)Er;Qa^JcG-;!!%TMe,KILJ:OEp!'l,8WW)qugAh$Ms8W&us8W&us8W*!rVultr;ZcsrVufr +rVufrs8W#ts8W#trr;osrr;osrVuiss8Vuss8W&us8W#tgA_3SJ:R@nJ,~> +NrK(Zr;Q`sJcG-;!!%TMe,TCIU]1;oJcD_L!!%TMci +NrK)Er;Qa^JcG-;!!%TMe,TCIU]1 +NrK)Er;Qa^JcG-;!!%TMe,KILJ:OEp!'l,8WW)quJcF'r!W[b$pA]X~> +O8f1[qYpNqJcG0 +O8f2FqYpO\JcG0 +O8f2FqYpO\JcG0 +O8f1[qYpNqJcG0 +O8f2FqYpO\JcG0 +O8f2FqYpO\JcG0 +O8f1[qYpNqJcG0 +O8f2FqYpO\JcG0 +O8f2FqYpO\JcG0 +OT,:\q#: +OT,;Gq#:=ZJcG3=!!%TMe,TCIU]1 +OT,;Gq#:=ZJcG3=!!%TMe,KILJ:OEp!'l,8WW)quJcF'r!W[b$pA]X~> +OT,:\q#: +OT,;Gq#:=ZJcG3=!!%TMe,TCIU]1 +OT,;Gq#:=ZJcG3=!!%TMe,KILJ:OEp!'l,8WW)quJcF'r!W[b$pA]X~> +OT,:\q#: +OT,;Gq#:=ZJcG3=!!%TMe,TCIU]1 +OT,;Gq#:=ZJcG3=!!%TMe,KILJ:OEp!'l,8WW)quJcF'r!W[b$pA]X~> +OoGC]pAY*mJcG6>!!%TMe,TCIU]1;oJcD_L!!%TMci +OoGDHpAY+XJcG6>!!%TMe,TCIU]1 +OoGDHpAY+XJcG6>!!%TMe,KILJ:OEp!'l,8WW)quJcF'r!W[b$pA]X~> +OoP%RJcG6>!!%TMe,TCIU]1;oJcD_L!!%TMci +OoP&=JcG6>!!%TMe,TCIU]1 +OoP&=JcG6>!!%TMe,KILJ:OEp!'l,8WW)quJcF'r!W[b$pA]X~> +N;ikXJcG!7!!%TMe,TCIU]1;oJcD_LJH3jnpA]X~> +N;ilCJcG!7!!%TMe,TCIU]1 +N;ilCJcG!7!!%TMe,KILJ:OEp!'l,8WW.MLblIcopA]X~> +N;ikXJcG!7!!';(quHcs!!)rs!!)Ti!!)rsq>g +N;ilCJcG!7!!';(quHcs!!)rs!!)Ti!!)rsq>g +N;ilCJcG!7!!';(quHcs!!)rs!!)Ti!!)rsq>g +N;ikXJcG!7!!'>)!!)lq!!)rs!!)Ti!!)ip!!)]l!!)lq!!)ipquH*`rW&_n!!%TMWW)quJcF'r +rW)ZlJ,~> +N;ilCJcG!7!!'>)!!)lq!!)rs!!)Ti!!)ip!!)]l!!)lq!!)ipquH*`rW&_n!'l,8WW)quJcF'r +rW)ZlJ,~> +N;ilCJcG!7!!'>)!!)lq!!)rs!!)Ti!!)ip!!)]l!!)lq!!)ipquH*`!W[b$U]1 +N;ikXJcG!7!!)fo!!)or!UEpr;Q`srr;rtrVuiss8N0$rr<&srr<&rs8E#trrE-"rW)rt!!*#urW)rtrrD?arW&_n +!!%TMWW)quJcF'rrW)ZlJ,~> +N;ilCJcG!7!!)fo!!)or!UEpr;Q`srr;rtrVuiss8N0$rr<&srr<&rs8E#trrE-"rW)rt!!*#urW)rtrrD?arW&_n +!'l,8WW)quJcF'rrW)ZlJ,~> +N;ilCJcG!7!!)fo!!)or!UEpr;Q`srr;rtrVuiss8N0$rr<&srr<&rs8E#trrE-"rW)rt!!*#urW)rtrrD?a!W[b$ +U]1 +N;ikXJcG!7!!)fo!!)orrrE&u"9AK%!!*#u!!*#u!s&B$!;uis!<3!#!<<'!r;Q`sr;Q`srr3'# +s8N)orr<&prr<&srrW9$rrE&u!s&B$!;uls!<<'!!<)ot!;uis!<3!$!<<'!!<3!&!<<'!s8N)u +rriE&rrE'!li6qaU]1;oJcD_L!!%TMci +N;ilCJcG!7!!)fo!!)orrrE&u"9AK%!!*#u!!*#u!s&B$!;uis!<3!#!<<'!r;Q`sr;Q`srr3'# +s8N)orr<&prr<&srrW9$rrE&u!s&B$!;uls!<<'!!<)ot!;uis!<3!$!<<'!!<3!&!<<'!s8N)u +rriE&rrE'!li6qaU]1 +N;ilCJcG!7!!)fo!!)orrrE&u"9AK%!!*#u!!*#u!s&B$!;uis!<3!#!<<'!r;Q`sr;Q`srr3'# +s8N)orr<&prr<&srrW9$rrE&u!s&B$!;uls!<<'!!<)ot!;uis!<3!$!<<'!!<3!&!<<'!s8N)u +rriE&rrE'!li."dJ:OEp!'l,8WW)quJcF'r!W[b$pA]X~> +N;ikXJcG!7!!)fo!!)or!!)ut!s&B$!<)ot!<3!#!<<'!r;Q`srr3'#s8N)srr<&srr<&urrW9$ +rrDio!!)ipq>gEm!s&B$!;uis!<2uu!<)ot!;QQr!<<'!rVm'%s8N*!rrE&u!!)0]rW&_n!!%TM +WW)quJcF'rrW)ZlJ,~> +N;ilCJcG!7!!)fo!!)or!!)ut!s&B$!<)ot!<3!#!<<'!r;Q`srr3'#s8N)srr<&srr<&urrW9$ +rrDio!!)ipq>gEm!s&B$!;uis!<2uu!<)ot!;QQr!<<'!rVm'%s8N*!rrE&u!!)0]rW&_n!'l,8 +WW)quJcF'rrW)ZlJ,~> +N;ilCJcG!7!!)fo!!)or!!)ut!s&B$!<)ot!<3!#!<<'!r;Q`srr3'#s8N)srr<&srr<&urrW9$ +rrDio!!)ipq>gEm!s&B$!;uis!<2uu!<)ot!;QQr!<<'!rVm'%s8N*!rrE&u!!)0]!W[b$U]1 +N;ikXJcG!7!!)orq>gNp!!)ut!s&B$!<)ot!<3!#!<<'!r;ZZprr;uurVultrr;lrrr;uuqYpNq +r;Qj!s8N)srr<&us8;rss8N)urr<&urr<&trr<&rs8;rtrr<&trriE&rrE*!quGmZrW) +N;ilCJcG!7!!)orq>gNp!!)ut!s&B$!<)ot!<3!#!<<'!r;ZZprr;uurVultrr;lrrr;uuqYpNq +r;Qj!s8N)srr<&us8;rss8N)urr<&urr<&trr<&rs8;rtrr<&trriE&rrE*!quGmZrW) +N;ilCJcG!7!!)orq>gNp!!)ut!s&B$!<)ot!<3!#!<<'!r;ZZprr;uurVultrr;lrrr;uuqYpNq +r;Qj!s8N)srr<&us8;rss8N)urr<&urr<&trr<&rs8;rtrr<&trriE&rrE*!quGmZ!W[b$m/MV: +NW@N-!!%TMci4%HJ:R@nJ,~> +N;ikXJcG!7!!)fo!!)or!!)ut!s&B$!<)ot!<3!#!<<'!r;Q`sq#: +N;ilCJcG!7!!)fo!!)or!!)ut!s&B$!<)ot!<3!#!<<'!r;Q`sq#: +N;ilCJcG!7!!)fo!!)or!!)ut!s&B$!<)ot!<3!#!<<'!r;Q`sq#: +N;ikXJcG!7!!)fo!!)orrrE&u!s&B$!<)ot!<3!#!<<'!r;Q`sq#: +N;ilCJcG!7!!)fo!!)orrrE&u!s&B$!<)ot!<3!#!<<'!r;Q`sq#: +N;ilCJcG!7!!)fo!!)orrrE&u!s&B$!<)ot!<3!#!<<'!r;Q`sq#: +N;ikXJcG!7!!)fo!!)or! +N;ilCJcG!7!!)fo!!)or! +N;ilCJcG!7!!)fo!!)or! +N;ikXJcG!7!!)Qh!!%TMjT#2Zm/I%cZiBh$k5YD\#6+Z's8N'!q#C9mR/d-ceGfLKJcF'rrW)Zl +J,~> +N;ilCJcG!7!!)Qh!!%TMjT#2Zm/I%cZiBh$k5YD\#6+Z's8N'!q#C9mR/d-ceGfLKJcF'rrW)Zl +J,~> +N;ilCJcG!7!!)Qh!!%TMjSo8]J:R"d!!'A*q#KRWrW!0&!!*'!!!)for;`5b!W[b$eGfLKJcF'r +!W[b$pA]X~> +N;ikXJcG!7!!)Qh!!%TMjT#2Zm/I%cYlF_'jT#5[qZ$Qqq>^Hprr3*$s8N'!p](6nWW2qteGfLK +JcF'rrW)ZlJ,~> +N;ilCJcG!7!!)Qh!!%TMjT#2Zm/I%cYlF_'jT#5[qZ$Qqq>^Hprr3*$s8N'!p](6nWW2qteGfLK +JcF'rrW)ZlJ,~> +N;ilCJcG!7!!)Qh!!%TMjSo8]J:R"d!!'8'rrD-[rrDoqrrDlprrE&u"9AK%!!)cnrrB%u!W[b$ +eGfLKJcF'r!W[b$pA]X~> +N;ikXJcG!7!!%TMe,TCIm/I%cYlF_'rVult!ri9#r;cfrr;cltrW)uuqu?s$!!*'!!!*#urW)uu +rrDusr;cltr;cltr;cisrW'2&rW(IJ!!)9`!!)lq!!)Ed!!%`QrW)ZlJ,~> +N;ilCJcG!7!!%TMe,TCIm/I%cYlF_'rVult!ri9#r;cfrr;cltrW)uuqu?s$!!*'!!!*#urW)uu +rrDusr;cltr;cltr;cisrW'2&rW(IJ!!)9`!!)lq!!)Ed!!%`QrW)ZlJ,~> +N;ilCJcG!7!!%TMe,KILJ:R"d!!'8'rrE#trr<-#!<;utrVufrs8W&us8Vus#6+Z's8N'!rr;rt +s8W*!r;Z]qs8W#ts8W#trr;rtYl=b)J:Q/L!!)9`!!)lq!!)Ed!!%`Q!W[b$pA]X~> +N;ikXJcG!7!!%TMe,TCIm/I%cYlF_'rVuisr;Zcss8W*!rVult#6+Z's8N'!rr;uus8W*!s8W*! +"9/?$s8E#rs8N)rs8N*!s8N)us8N'#rr<&(s8E#Jrr<&Xrr<&Zrr<%[s8E#ls*t~> +N;ilCJcG!7!!%TMe,TCIm/I%cYlF_'rVuisr;Zcss8W*!rVult#6+Z's8N'!rr;uus8W*!s8W*! +"9/?$s8E#rs8N)rs8N*!s8N)us8N'#rr<&(s8E#Jrr<&Xrr<&Zrr<%[s8E#ls*t~> +N;ilCJcG!7!!%TMe,KILJ:R"d!!'8'rrE#trW)lrrrE*!rrE#trr<9'!!*'!!!*#urrE*!rrE*! +rr<0$!!*&u!;uls!;lfr!<<*!!<3#u!!<0#!3uP*!.]ULrr<&Xrr<&Zrr<%[rrN1NJG0"n~> +N;ikXJcG!7!!%TMe,TCIm/I%cYlF_'rVultqu?Zrs8W*!rVult#6+Z's8N'!rr;uus8W*!s8W*! +"TJH%s8W#trr;uuqu?Zrs8W*!rr;uu!ri6#Z2ae'eGfLKq#: +N;ilCJcG!7!!%TMe,TCIm/I%cYlF_'rVultqu?Zrs8W*!rVult#6+Z's8N'!rr;uus8W*!s8W*! +"TJH%s8W#trr;uuqu?Zrs8W*!rr;uu!ri6#Z2ae'eGfLKq#: +N;ilCJcG!7!!%TMe,KILJ:R"d!!'8'rrE#trrDrrrrE*!rrE#trr<9'!!*'!!!*#urrE*!rrE*! +rr<3%!!*'!r;cisrrDrrrrE*!rrE&urr<-#!!';(!W[b$eGfLKq#: +N;ikXJcG!7!!%TMe,TCIm/I%cYlF_'rVultrVufrs8W*!rVucqs8W*!rr;uus8W*!s8VusrVuis +s8W*!rVufrs8W*!rr;lrZ2ae'eGfLKq#: +N;ilCJcG!7!!%TMe,TCIm/I%cYlF_'rVultrVufrs8W*!rVucqs8W*!rr;uus8W*!s8VusrVuis +s8W*!rVufrs8W*!rr;lrZ2ae'eGfLKq#: +N;ilCJcG!7!!%TMe,KILJ:R"d!!'8'rrE#trrE#tr;cltrrE#tquHcsrrE&urrE*!rrE*!quH]q +rW)uurrE#tr;cltrrE&uquF#%!W[b$eGfLKq#: +N;ikXJcG!7!!) +N;ilCJcG!7!!) +N;ilCJcG!7!!)^Hps8W*!rr;uu#6+Z's8N'!rr;uuY5\P'J:Q/L!!)fo!!)or!!)ut"p"]'!<<'! +rVlitqu6Wrrr3'#s8N)urrrK'rrE*!!<3!#!<<'!O8f7]J:R@nJ,~> +])ST,QN$pblMghag&D$Pir8uY[/^+*m/I%cYlF_'rVultrr;uu#6+Z's8N'!rVultr;Zcsrr;uu +s8W*!s8W*!rVlitrr;uus8W*!rr;uu#6+Z's8N'!rr;uuY5eJ$eGfLKqu?Kmrr2rurVm'%s8N*! +rrE#t!!)or!!*#u!s&B$!<3!&!<<'!s8N)urrW9$rrA,[rW)ZlJ,~> +])ST,QN$pblMghag&D$Pir8uY[/^+*m/I%cYlF_'rVultrr;uu#6+Z's8N'!rVultr;Zcsrr;uu +s8W*!s8W*!rVlitrr;uus8W*!rr;uu#6+Z's8N'!rr;uuY5eJ$eGfLKqu?Kmrr2rurVm'%s8N*! +rrE#t!!)or!!*#u!s&B$!<3!&!<<'!s8N)urrW9$rrA,[rW)ZlJ,~> +])ST,QN$pblMghag&D$Pir8uY[/U1-J:R"d!!'8'rrE#trrE&urr<9'!!*'!!!)utrrDusrrE&u +rrE*!rrE*!rrE#t!!*#urrE*!rrE&urr<9'!!*'!!!*#urrB5%!W[b$eGfLKqu?Kmrr2rurVm'% +s8N*!rrE#t!!)or!!*#u!s&B$!<3!&!<<'!s8N)urrW9$rrA,[!W[b$pA]X~> +])Ma1VuH_sQN$pbq#: +])Ma1VuH_sQN$pbq#: +])Ma1VuH_sQN$pbq#: +])Ma1VuH_sQN$pbq#:UEprr3'#s8N)urrN3#!<3!*!<<'!!<<'! +s8N)qrr<&urrW9$rrE&u%06G.!<<'!!<<'!s8N)+s8E#brr<%Ms,d9[!7_#K!;QQo!;lfr!<3!& +!<<'!s8N)us8N)rrt,82rr<'!rrE*!!!*'!!<<'!rr3'#s8N([s8E#ls*t~> +])Ma1VuH_sQN$pbq#:UEprr3'#s8N)urrN3#!<3!*!<<'!!<<'! +s8N)qrr<&urrW9$rrE&u%06G.!<<'!!<<'!s8N)+s8E#brr<%Ms,d9[!7_#K!;QQo!;lfr!<3!& +!<<'!s8N)us8N)rrt,82rr<'!rrE*!!!*'!!<<'!rr3'#s8N([s8E#ls*t~> +])Ma1VuH_sQN$pbq#:UEprr3'#s8N)urrN3#!<3!*!<<'!!<<'! +s8N)qrr<&urrW9$rrE&u%06G.!<<'!!<<'!s8N)+rrN1NJF*:9!.k03rrN1NJCOT!!;QQo!;lfr +!<3!&!<<'!s8N)us8N)rrt,82rr<'!rrE*!!!*'!!<<'!rr3'#s8N([rrN1NJG0"n~> +])Ma1VuQ_rR/[-dq#: +])Ma1VuQ_rR/[-dq#: +])Ma1VuHeuJ:O$e!!)fo!!)or!!*#u!s&B$!<)ot!;ZWp!<3!#!<<'!rr3$"rrE&u!s&B$!<3!# +!<<'!qYpNqrr3'#s8N)urrrK'rrE*!!<3!#!<<'![/U1-J:R"d!!%TMOT,@^J:Q/L!!)fo!!)or +! +])Ma1rVuZneGoIIjT#2ZR/[-dqu?Kmrr2rurr3'#s8N)trr<&ps82lrrr`?%!<<)s!<<'!!<3!# +!<<'!qYpNqrr3'#s8N)urrrK'rrE*!!<3!#!<<'![/^+*m/I%cJcCi3rW(IJ!!)Qh!!)3^!!%TM +qu?WqpA]X~> +])Ma1rVuZneGoIIjT#2ZR/[-dqu?Kmrr2rurr3'#s8N)trr<&ps82lrrr`?%!<<)s!<<'!!<3!# +!<<'!qYpNqrr3'#s8N)urrrK'rrE*!!<3!#!<<'![/^+*m/I%cJcCi3rW(IJ!!)Qh!!)3^!!%TM +qu?WqpA]X~> +])Ma1rVuZneGoIIjSo8]J:O$e!!)orq>gNp!!*#u!s&B$!<)ot!;ZZm!<3!$!<3'!s82lsrr<&u +rrW9$rrDoq!!*#u!s&B$!<3!&!<<'!s8N)urrW9$rrBG+!W[b$m/I%cJcCi3!W[b$eGfLKnc&Rh +kPkM^JcGWI!W[b$pA]X~> +])Ma1qZ$Qqf)PaMs8W*!rr3*$s8N'!p](6np&G!kR/[-dq#: +])Ma1qZ$Qqf)PaMs8W*!rr3*$s8N'!p](6np&G!kR/[-dq#: +])Ma1qZ$Qqf)PaMs8W*!rr3*$s8N'!p](6np&>'nJ:O$e!!)fo!!)or!!*#u!s&B$!<)ot!;ZWp +!;c^!!<3'!rrDrr!!*#u!s&B$!;c]q!<3!#!<<'!rr30&s8N*!rrE&u!s&B$!4;b-!.]Udrr<%M +s,d6^!.]ULrr<&hrr<&bs8;qKs8)`s!.]Uns*t~> +])Ma1qZ$QqrVult!ri9#r;cfrr;cltrW)osr;cisrW)uur;Zp!!!)rsr;cltr;cltr;cisrW)lr +rW&>c!!)fo!!)or!!*#u"9AK%!!*#u!!)ip!!)lq"T\Q&s8N)rrr<&urrW9$rrDoq&HMk2!!*'! +!<<'!!<<'!s8N)urrW9$rrBG+rW) +])Ma1qZ$QqrVult!ri9#r;cfrr;cltrW)osr;cisrW)uur;Zp!!!)rsr;cltr;cltr;cisrW)lr +rW&>c!!)fo!!)or!!*#u"9AK%!!*#u!!)ip!!)lq"T\Q&s8N)rrr<&urrW9$rrDoq&HMk2!!*'! +!<<'!!<<'!s8N)urrW9$rrBG+rW) +])Ma1qZ$QqrVult!ri9#r;cfrr;cltrW)osr;cisrW)uur;Zp!!!)rsr;cltr;cltr;cisrW)lr +!W[b$R/[-dq#:UEpqYp^!rrE*!!;lcr!<3!#!<<'!qYq--s8N'! +s8N*!rr<'!rrE*!!<3!#!<<'![/U1-J:R"d!!%TMOT,@^J:Q/L!!%TMci4%HJ:R@nJ,~> +])Ma1qZ$QqrVuisr;Zcss8W*!rVult#6+Z's8N'!r;Zcs#lal)s8N'!s8W&ur;Zcsqu?Zrs8W*! +rr;uu!ri6#rVuisR/[-dq#: +])Ma1qZ$QqrVuisr;Zcss8W*!rVult#6+Z's8N'!r;Zcs#lal)s8N'!s8W&ur;Zcsqu?Zrs8W*! +rr;uu!ri6#rVuisR/[-dq#: +])Ma1qZ$QqrVuisr;Zcss8W*!rVult#6+Z's8N'!r;Zcs#lal)s8N'!s8W&ur;Zcsqu?Zrs8W*! +rr;uu!ri6#rVlp!J:O$e!!)fo!!)or!!*#u"9AK%!<<#up](0lrVlitrVufrs8N'!rr2rurr3'# +s8N)ts8N'"rrE&urr<<(!<<'!s8N)urr<&urr<&,rrN1NJF*:9!.k03rrN1NJCOT!!.k0rrrN1N +JG0"n~> +])Ma1qZ$QqrVultqu?Zrs8W*!rVult"TJH%s8W&urVult#6+Z's8N'!rr;osrr;uuqu?Zrs8W*! +rr;uu!ri6#rVuisR/[-dirArWf`(pOT`=ukm/I%cJcCi3rW(IJ!!%TMci +])Ma1qZ$QqrVultqu?Zrs8W*!rVult"TJH%s8W&urVult#6+Z's8N'!rr;osrr;uuqu?Zrs8W*! +rr;uu!ri6#rVuisR/[-dirArWf`(pOT`=ukm/I%cJcCi3rW(IJ!!%TMci +])Ma1qZ$QqrVultqu?Zrs8W*!rVult"TJH%s8W&urVult#6+Z's8N'!rr;osrr;uuqu?Zrs8W*! +rr;uu!ri6#rVlp!J:O$e!!)$Yr;bLM!!&\l!W[b$m/I%cJcCi3!W[b$eGfLKJcF'r!W[b$pA]X~> +])Ma1qZ$QqrVultrVufrs8W*!rVucqrr;rtrr;lrs8W*!r;Z`rs8W*!rVufrs8W*!rr;lrrVuis +R/[-d\c;U.TE"ljm/I%cJcCi3rW(IJ!!%TMci +])Ma1qZ$QqrVultrVufrs8W*!rVucqrr;rtrr;lrs8W*!r;Z`rs8W*!rVufrs8W*!rr;lrrVuis +R/[-d\c;U.TE"ljm/I%cJcCi3rW(IJ!!%TMci +])Ma1qZ$QqrVultrVufrs8W*!rVucqrr;rtrr;lrs8W*!r;Z`rs8W*!rVufrs8W*!rr;lrrVlp! +J:O$e!!'S0r;`Ji!W[b$m/I%cJcCi3!W[b$eGfLKJcF'r!W[b$pA]X~> +])Ma1qZ$QqrVultrr;uu#6+Z's8N'!rVultqZ$Nps8W*!r;Zcsqu?Zrs8W*!rr;uu#6+Z's8N'! +rr;uuqZ$NpR/[-dJcF4!rW) +])Ma1qZ$QqrVultrr;uu#6+Z's8N'!rVultqZ$Nps8W*!r;Zcsqu?Zrs8W*!rr;uu#6+Z's8N'! +rr;uuqZ$NpR/[-dJcF4!rW) +])Ma1qZ$QqrVultrr;uu#6+Z's8N'!rVultqZ$Nps8W*!r;Zcsqu?Zrs8W*!rr;uu#6+Z's8N'! +rr;uuqYpTsJ:O$e!!%TMe,KILJ:R"d!!%TMOT,@^J:Q/L!!)!X!!)lq!!)Ed!!&#Y!W[b$pA]X~> +])Ma1qZ$QqrVultrr;uu#6+Z's8N'!rVultq>^Hps8W*!r;Zcss8N'!rr;uus8W*!rr;uu#6+Z' +s8N'!rr;uuqZ$NpR/[-dJcF4!rW) +])Ma1qZ$QqrVultrr;uu#6+Z's8N'!rVultq>^Hps8W*!r;Zcss8N'!rr;uus8W*!rr;uu#6+Z' +s8N'!rr;uuqZ$NpR/[-dJcF4!rW) +])Ma1qZ$QqrVultrr;uu#6+Z's8N'!rVultq>^Hps8W*!r;Zcss8N'!rr;uus8W*!rr;uu#6+Z' +s8N'!rr;uuqYpTsJ:O$e!!%TMe,KILJ:R"dJH1]1s+&W!!!(^P!!)'Z!!&Ac!W[b$pA]X~> +])Ma1qZ$QqrVultrVucqs8W#ts8W#ts8W#trVufrrr;rt!<;utrVuiss8Vuss8W&us8W#trVuis +R/[-dJcF4!rW) +])Ma1qZ$QqrVultrVucqs8W#ts8W#ts8W#trVufrrr;rt!<;utrVuiss8Vuss8W&us8W#trVuis +R/[-dJcF4!rW) +])Ma1qZ$QqrVultrVucqs8W#ts8W#ts8W#trVufrrr;rt!<;utrVuiss8Vuss8W&us8W#trVlp! +J:O$e!!%TMe,KILJ:R"d!!%TMOT,@^J:Q/L!!)fo!!)or! +])Ma1VuQ_rR/[-dJcF4!rW) +])Ma1VuQ_rR/[-dJcF4!rW) +])Ma1VuHeuJ:O$e!!%TMe,KILJ:R"d!!%TMOT,@^J:Q/L!!)fo!!)orrrE&u"9AK%!!*#u"p"]' +!<<'!rr;uuqu6Wrrr3'#s8N)urs\u.rrE*!!!*'!!<<'!Qi@*eJ:R@nJ,~> +])Ma1VuQ_rR/[-ddJj1Hg]%6RmJd.d_#OB6m/I%cJcCi3rW(IJ!!)fo!!)or!!)ut!s&B$!<)p% +!<<'!s8N)trr<&rrr<&urrW9$rrE&u"p"]'!<<'!rr3'#s8N(cs8E#ls*t~> +])Ma1VuQ_rR/[-ddJj1Hg]%6RmJd.d_#OB6m/I%cJcCi3rW(IJ!!)fo!!)or!!)ut!s&B$!<)p% +!<<'!s8N)trr<&rrr<&urrW9$rrE&u"p"]'!<<'!rr3'#s8N(cs8E#ls*t~> +])Ma1VuHeuJ:O$e!!(FH!!(dR!!)Ed!!'h7!W[b$m/I%cJcCi3!W[b$eGfLKq#: +])Ma1VuQ_rR/[-da8Z,>jo5;\j8T)Zb5_G@m/I%cJcCi3rW(IJ!!)orq>gNp!!)ut!s&B$!<)p% +!<<'!s8N)trr<&rrr<&urrW9$rrE&u"p"]'!<<'!rr3'#s8N(cs8E#ls*t~> +])Ma1VuQ_rR/[-da8Z,>jo5;\j8T)Zb5_G@m/I%cJcCi3rW(IJ!!)orq>gNp!!)ut!s&B$!<)p% +!<<'!s8N)trr<&rrr<&urrW9$rrE&u"p"]'!<<'!rr3'#s8N(cs8E#ls*t~> +])Ma1VuHeuJ:O$e!!((>!!)-\!!)'Z!!(1A!W[b$m/I%cJcCi3!W[b$eGfLKqu?Kmrr2rurVls" +s8N)trrrK'rrE*!!<)ot!;lcr!<3!#!<<'!rr30&s8N*!rrE&u!s&B$!13]e!.]Uns*t~> +])Ma1VuQ_rR/[-dq#: +])Ma1VuQ_rR/[-dq#: +])Ma1VuHeuJ:O$e!!)fo!!)lqrW)rtrW)uu!!)rs! +])Ma1VuQ_rR/[-dq#: +])Ma1VuQ_rR/[-dq#: +])Ma1VuHeuJ:O$e!!)fo!!)or!!)rs!!*#u!s&B$!<3!"!<3&urr<&prsAc+rr<'!rrE*!!<2uu +!<3!$!<<'!!<2uu!<3!"!<3&urrW9$rrDrr!!*#u!s&B$!<3!-!<<'!s8N'!s8N*!rrC4A!W[b$ +m/I%cJcCi3!W[b$nc/XaoDejcpAY*mq#: +])Ma1VuQ_rR/[-dq#: +])Ma1VuQ_rR/[-dq#: +])Ma1VuHeuJ:O$e!!)fo!!)or!!)fo!s&B$!<3!"!<3&urr<&prrW9$rrE&u!s&B$!<2uu!<3!# +!<<'!rVlitrr2rur;Qj!s8N)rrr<&urrW9$rrE&u"p"]'!<<'!rr3'#s8N)ArrN1NJF*:9!.k03 +rrN1NJF`^Bq#CBhp&>*gs7l +])Ma1VuQ_rR/[-dqu?KmrVultrVufrrr3*$rrE*!quHQm!s&B$!<3!#!<<'!rr;lrs8N'!r;Qfu +rrE&ur;clt!!)or!!*#u!s&B$!<3!&!<<'!s8N)urrW9$rrC4ArW) +])Ma1VuQ_rR/[-dqu?KmrVultrVufrrr3*$rrE*!quHQm!s&B$!<3!#!<<'!rr;lrs8N'!r;Qfu +rrE&ur;clt!!)or!!*#u!s&B$!<3!&!<<'!s8N)urrW9$rrC4ArW) +])Ma1VuHeuJ:O$e!!)orq>gKorrE#tr;cis"9AH%s8Vusq>UNss8N)urrW9$rrE&uquHcs!!)rs +!W`6#rr;oss8N'!qu6Wrrr3'#s8N)urrrK'rrE*!!<3!#!<<'!b5VMCJ:R"d!!%TMOT,@^J:R7k +!;QNm!;Q3d!;Q9f!!)Qh!!)lq!!)3^!!%ZO!W[b$pA]X~> +])Ma1VuQ_rR/[-dq#:*os8N)urrW9$rrE&u!!)or!!)rs +"T\Q&s8N)urrW9$rrDrr!!*#u!s&B$!<3!&!<<'!s8N)urrW9$rrC4ArW) +])Ma1VuQ_rR/[-dq#:*os8N)urrW9$rrE&u!!)or!!)rs +"T\Q&s8N)urrW9$rrDrr!!*#u!s&B$!<3!&!<<'!s8N)urrW9$rrC4ArW) +])Ma1VuHeuJ:O$e!!)fo!!)fo!s&B$!<2uu!<3!%!<3'!rrD`l!s&B$!<3!#!<<'!rr2ruqu6Wr +r;Qp#rrE*!!<3!#!<<'!qu6Wrrr3'#s8N)urrrK'rrE*!!<3!#!<<'!b5VMCJ:R"d!!%TMOT,@^ +J:R7k!;QNm!;Q3d!;Q9f!!)Qh!!)lq!!)?br;_HL!W[b$pA]X~> +])Ma1VuQ_rR/[-dq#:*os8N)urrW9$rrE&u!!)or!!)rs +"T\Q&s8N)urrW9$rrDrr&HMk2!!*'!!<<'!!<<'!s8N)urrW9$rrC4ArW) +])Ma1VuQ_rR/[-dq#:*os8N)urrW9$rrE&u!!)or!!)rs +"T\Q&s8N)urrW9$rrDrr&HMk2!!*'!!<<'!!<<'!s8N)urrW9$rrC4ArW) +])Ma1VuHeuJ:O$e!!)fo!!)fo!s&B$!<2uu!<3!%!<3'!rrD`l!s&B$!<3!#!<<'!rr2ruqu6Wr +r;Qp#rrE*!!<3!#!<<'!qu76.s8N'!s8N*!rr<'!rrE*!!<3!#!<<'!b5VMCJ:R"d!!%TMOT,@^ +J:R7k!;QNm!;Q3d!;Q9f!!%TMci4%HJ:R@nJ,~> +]DnT*R/[-dq#:UNss8N)urr<&urr<&us8;rtrr<&rrr<&ts82j" +rrE*!!<)rt!!3*"rr;uu#QFf(rrE*!!<2uu!<2uu!6bEA!:9^c!.k03s8E#irr<&trr<&krr<&m +rr<%Ms3L`E!;?GC~> +]DnT*R/[-dq#:UNss8N)urr<&urr<&us8;rtrr<&rrr<&ts82j" +rrE*!!<)rt!!3*"rr;uu#QFf(rrE*!!<2uu!<2uu!6bEA!:9^c!.k03s8E#irr<&trr<&krr<&m +rr<%Ms3L`E!;?GC~> +]DnZ,s+$L:!!)fo!!)orrW)osquH`r!!)utr;cZn!s&B$!<2uu!<2uu!<3#s!<<'!!;lcr!<)rq +!!N<%s8N)ts8N'"rrE&urr<<(!<<'!s8N)urr<&urr<&BrrN1NJF*:9!.k03rrN1NJFid@q#13m +q"4Rdq"F^f!.k0rrrN1NJG0"n~> +])Ma1VuQ_rR/[-dfDkdLc2RbD[f?=,m/I%cJcG3=!!)Her;bONquH!]! +])Ma1VuQ_rR/[-dfDkdLc2RbD[f?=,m/I%cJcG3=!!)Her;bONquH!]! +])Ma1VuHeuJ:O$e!!(XNr;b+B!!'J-!W[b$m/I%cJcG3=!!)Her;bONquH!]"9AH%J:R7k!;QNm +!;Q3d!;Q9f!!%TMci4%HJ:R@nJ,~> +])Ma1VuQ_rR/[-dU]:8m[K$4+m/I%cbl7YC^]+96p\t3noD\djr;Q`srr2ruq#: +])Ma1VuQ_rR/[-dU]:8m[K$4+m/I%cbl7YC^]+96p\t3noD\djr;Q`srr2ruq#: +])Ma1VuHeuJ:O$e!!&eor;a8*!W[b$m/I%cbl7YC^]+96p\t3noD\djr;Q`srr2ruq#: +])Ma1VuQ_rR/[-dJcF4!rW)UHqs8E#trriE&!!*'!rW)osrW)rtrW)osrW)rtrW)`nrVururW)rtrW)uurW)rtrW)rt +!!*#u!!)utrVururW!!!!;uls!<3!(!<<'!rr<'!s8E#ss8E#ts8E#ss8E#ts8E!!rrDusrW)rt +rW)uurW)rtrW)rtrr<'!rW)KgrrE#t!!*#u!!)utquHNl!!(OK!!)3^!!)9`!!)]lq>gQq!!((> +rW)ZlJ,~> +])Ma1VuQ_rR/[-dJcF4!rW)UHqs8E#trriE&!!*'!rW)osrW)rtrW)osrW)rtrW)`nrVururW)rtrW)uurW)rtrW)rt +!!*#u!!)utrVururW!!!!;uls!<3!(!<<'!rr<'!s8E#ss8E#ts8E#ss8E#ts8E!!rrDusrW)rt +rW)uurW)rtrW)rtrr<'!rW)KgrrE#t!!*#u!!)utquHNl!!(OK!!)3^!!)9`!!)]lq>gQq!!((> +rW)ZlJ,~> +])Ma1VuHeuJ:O$e!!%TMe,KILJ:R"d!!)fo!!)lqrW)uu!!*#u%06G.!<3$!rrE'!!<<)u!<3!% +!<3$!s8W&uq>UHqs8E#trriE&!!*'!rW)osrW)rtrW)osrW)rtrW)`nrVururW)rtrW)uurW)rt +rW)rt!!*#u!!)utrVururW!!!!;uls!<3!(!<<'!rr<'!s8E#ss8E#ts8E#ss8E#ts8E!!rrDus +rW)rtrW)uurW)rtrW)rtrr<0$!.]Uis8VlmrrDinrrDims8;Zerr<&Krr<&^rr<&`rr<&ls7u`q +rr<&>rrN1NJG0"n~> +])Ma1VuQ_rR/[-dJcF4!rW) +])Ma1VuQ_rR/[-dJcF4!rW) +])Ma1VuHeuJ:O$e!!%TMe,KILJ:R"d!!)fo!!)or!!)rs!!*#u"9AK%!!*#urrE&u!!*#u$3:,+ +!!*'!!<<'!q#C?orr3*$s8N'!rr2rurr3'#s8N)srr<&urrW9$rrDus!!)`m!!)rs!!*#u!!*#u +!s&B$!<2uu!<2uu!;lcr!;uis!<2uu!<2uu!<3!"!<3&urr`?%rr<&urr<&urrW9$rrDus!!*#u +!s&B$!;uis!;uis!;uis!<2uu!<3!#!<<'!rr2rurr36(s8N*!!!%P"eGfLKf`(pOkPkM^jo5;\ +r;Q`srr2rur;Q`s_>aQ:J:R@nJ,~> +])Ma1VuQ_rR/[-dJcF4!rW)N!;?GC~> +])Ma1VuQ_rR/[-dJcF4!rW)N!;?GC~> +])Ma1VuHeuJ:O$e!!%TMe,KILJ:R"d!!)fo!!)or!!)rs!!*#u!s&B$!<)ot!<)ot!<3!#!<<'! +rr3'#s8N)orr<&trrW9$rrE#t!!*#u!s&B$!;uis!<3!#!<<'!r;Q`spAY*mr;Q`sqYpWts8N)u +rr<&urr<&rrr<&srr<&urr<&urr<&urrE-"r;cis!!)ut!!*#u!s&B$!;uis!<3!#!<<'!r;Q`s +qu?Zrrr2ruqYpWts8N)urr<&urr<&srrN1NJCOT!!;QQo!;c`p!<<'&!<3$!s8W&urVuiss8W&u +s8N'!rr;rtrr3'#rr<&ps8E#ursAc+rrE'!!<<'!!<)rs!<2uu!<2uu!<)rs!!*&u!<)ot!;uj( +!<<'!rr<'!rr<&ts8E#NrrN1NJG0"n~> +])Ma1VuQ_rR/[-dJcF4!rW)gNp!!)rs!!*#u!s&B$!<)ot!<)rq!<<'!!<3!#!<<'! +q#: +])Ma1VuQ_rR/[-dJcF4!rW)gNp!!)rs!!*#u!s&B$!<)ot!<)rq!<<'!!<3!#!<<'! +q#: +])Ma1VuHeuJ:O$e!!%TMe,KILJ:R"d!!)orq>gNp!!)rs!!*#u!s&B$!<)ot!<)rq!<<'!!<3!# +!<<'!q#: +])Ma1VuQ_rR/[-dL]@8PrVuislMghaqYpNqq>^Eom/I%cq#:UEpq#: +])Ma1VuQ_rR/[-dL]@8PrVuislMghaqYpNqq>^Eom/I%cq#:UEpq#: +])Ma1VuHeuJ:O$e!!%fSquH]qrW)6`!!)lq!!)ip!W[b$m/I%cq#: +])Ma1VuQ_rR/[-df)G^Mrr2ru\GlO/q>UEprr2ruli-qbr;ZZpqu?Wqm/I%cq#: +])Ma1VuQ_rR/[-df)G^Mrr2ru\GlO/q>UEprr2ruli-qbr;ZZpqu?Wqm/I%cq#: +])Ma1VuHeuJ:O$e!!(UM!!*#u!!'P/!!)ip!!*#u!!)?b!!)rsquHWo!W[b$m/I%cq#:gNp!!)rs +!!)utquH`rr;clt!!*#u!s&B$!<3!#!<<'!rr2ruq>UEprr3'#s8N)urr<&urrN3#s82lorr<&s +rr<&urr<&srr<&srrW9$rrE&u!!*#u!W`9#quGFM!W[b$pA]X~> +])Ma1VuQ_rR/[-dq#: +])Ma1VuQ_rR/[-dq#: +])Ma1VuHeuJ:O$e!!)fo!!)lqrW)uu!!)rs!Qs7lTn!!)fo!!)or!!)rs!!)ut!!)or!!*#u!s&B$!<3!#!<<'!rr3'#s8N)u +rr<&prr<&urrW9$rrE&u!!*#u!s&B$!;HKn!;uis!<2uu!;uis!;uj!!<<'!rr2rurr3'#s8N)L +rrN1NJG0"n~> +])Ma1VuQ_rR/[-dq#:`MrW(RMrr<*" +!;QQo!;lcr!;uis!<)ot!;lcr!<3!#!<<'!rr3'#s8N)urrW9$rrE&u!!)ip!!*#u!s&B$!<2uu +!<3!#!<<'!p\t3nr;Q`srr2rur;Q`sr;Qj!s8N)urr<&urrW9$rrCULrW)ZlJ,~> +])Ma1VuQ_rR/[-dq#:`MrW(RMrr<*" +!;QQo!;lcr!;uis!<)ot!;lcr!<3!#!<<'!rr3'#s8N)urrW9$rrE&u!!)ip!!*#u!s&B$!<2uu +!<3!#!<<'!p\t3nr;Q`srr2rur;Q`sr;Qj!s8N)urr<&urrW9$rrCULrW)ZlJ,~> +])Ma1VuHeuJ:O$e!!)fo!!)or!!*#u!s&B$!<3!"!<3&ursAc+rr<'!rrE*!!<3!#!<<'!r;Z`r +rr3'#s8N)urr<&ps8N*!rrW9$rrE&u%KQP/!!*'!!!*'!!<<'!rr3'#s8N)orr<&prr<&srrW9$ +rrE&u!s&B$!<3!$!<<'!!<3!#!<<'!rr3$"rrDoq!W[b$m/I%caT)2=s8N'!ec5RJJc>iP!.]UO +s8ViprrDio!!)or!!)rs!!)ut!!)or!!*#u!s&B$!<3!#!<<'!rr3'#s8N)urr<&prr<&urrW9$ +rrE&u!!*#u!s&B$!;HKn!;uis!<2uu!;uis!;uj!!<<'!rr2rurr3'#s8N)LrrN1NJG0"n~> +])Ma1VuQ_rR/[-dq#:UEprr2ruqu6`us8N)urr<&urrW9$rrE&u!s&B$!;QQo!;ZWp!;uj!!<<'! +rr2ruqu6`us8N)trrN3#!;$6i!:9^c!5ea9!.k0ss3Lc@rr<&qs8E#urr<&ss8;rss82lsrrW9$ +rrE&urW)rt!!*#u!!)fo"p"]'!<<'!rr2rurr2rurr;oss8N'!rr;lrs8N'!rr2rurVlitr;Qj! +s8N)urr<&urr<&us8;rNs8E#ls*t~> +])Ma1VuQ_rR/[-dq#:UEprr2ruqu6`us8N)urr<&urrW9$rrE&u!s&B$!;QQo!;ZWp!;uj!!<<'! +rr2ruqu6`us8N)trrN3#!;$6i!:9^c!5ea9!.k0ss3Lc@rr<&qs8E#urr<&ss8;rss82lsrrW9$ +rrE&urW)rt!!*#u!!)fo"p"]'!<<'!rr2rurr2rurr;oss8N'!rr;lrs8N'!rr2rurVlitr;Qj! +s8N)urr<&urr<&us8;rNs8E#ls*t~> +])Ma1VuHeuJ:O$e!!)fo!!)or!!*#u!s&B$!<3!"!<3&urrW9$rrE&u!s&B$!<2uu!<2uu!<3!" +!<3&trrW9$rrE&u!!)ip!!*#u!!)or!s&B$!<2uu!<3!#!<<'!rr3'#s8N)orr<&prr<&srrW9$ +rrE&u!!)or!s&B$!<)p!!<3&jrrN1NJF*:9!5ea9!.k0srrN1NJCO>o!;QQo!;c`p!<<'!!;ulq +!<3#r!<<'$!<<'!rr;rtrr2rurr2ruq#:Nus8N*!rrE&u!!*#u!!*#ur;clt!!*#uquHcs!!*#u +!!)ut!!)rs!s&B$!<2uu!<2uu!<3#s!87AR!.]Uns*t~> +])Ma1VuQ_rR/[-dqu?Kmrr;lrrr3*$rrE*!quHcs!!*#u!s&B$!<2uu!<2uu!<3!"!<3&trrN3# +s82lmrr<&urr<&us8;rtrr<&urr<&urrN3#s82lrs8N)qrr<&srrW9$rrDus!s&B$!<2uu!<3#s +!<<'!!<)rs!;$6i!:9^c!.k03s8E#Ms8N'"rrBt:r;_u[rW)ZlJ,~> +])Ma1VuQ_rR/[-dqu?Kmrr;lrrr3*$rrE*!quHcs!!*#u!s&B$!<2uu!<2uu!<3!"!<3&trrN3# +s82lmrr<&urr<&us8;rtrr<&urr<&urrN3#s82lrs8N)qrr<&srrW9$rrDus!s&B$!<2uu!<3#s +!<<'!!<)rs!;$6i!:9^c!.k03s8E#Ms8N'"rrBt:r;_u[rW)ZlJ,~> +])Ma1VuHeuJ:O$e!!)orq>gNpquH`r"9AH%s8Vuss8N'!rr3'#s8N)urr<&urr<&urrN3#!<)p! +!<<)s!;ZWp!<2uu!<3#s!<<'!!<2uu!<3!"!<<)s!<3#u!;c]q!;uj!!<<'!r;Qj!s8N)urr<&u +s8;rtrr<&ts8E#irrN1NJF*:9!.k03rrN1NJCji$p]:Bp_uKZ8OoGI_J:R@nJ,~> +])Ma1VuQ_rR/[-dq#:!lrr3'#s8N)urrW9$rrE&u!!*#u!s&B$!;QQo!;lcr!;uj!!<<'!r;Qj!s8N)urrW9$rrE&u +!s&B$!<)p"!<<'!o`+mjm/I%cJcCi3rW(XOrrE&u!!%TMci +])Ma1VuQ_rR/[-dq#:!lrr3'#s8N)urrW9$rrE&u!!*#u!s&B$!;QQo!;lcr!;uj!!<<'!r;Qj!s8N)urrW9$rrE&u +!s&B$!<)p"!<<'!o`+mjm/I%cJcCi3rW(XOrrE&u!!%TMci +])Ma1VuHeuJ:O$e!!)fo!!)or!!)lq"T\Q&s8N)rrr<&urrW9$rrE&u!!)ut"T\Q&s8N)trrW9$ +rrD`l!!*#u!s&B$!<3!#!<<'!rr2rurr3'#s8N)orr<&rrr<&srrW9$rrDus!s&B$!<3!#!<<'! +rr3'#s8N)trrW9$rrD]k!W[b$m/I%cJcCi3!W[b$g&M*Irr2ruJcF'r!W[b$pA]X~> +])Ma1VuQ_rR/[-dq#: +])Ma1VuQ_rR/[-dq#: +])Ma1VuHeuJ:O$e!!)fo!!)or!!)lq"T\Q&s8N)rrr<&urrW9$rrE&u!!)ut"p"Z's8N'!rr3'# +s8N)lrr<&urrW9$rrE&u!s&B$!<2uu!<3!#!<<'!q#: +])Ma1VuQ_rR/[-dq#: +])Ma1VuQ_rR/[-dq#: +])Ma1VuHeuJ:O$e!!)fo!!)lqr;cfr!!)utr;clt!!*#u!!*#u!!*#u!!)ut!!*#u!UEpJcF'r!W[b$pA]X~> +])Ma1VuQ_rR/[-dbl7YCrr2ruoDeaha8Z,>gAh-Pm/I%cJcCi3rW(mVrrDfn!!%TMci +])Ma1VuQ_rR/[-dbl7YCrr2ruoDeaha8Z,>gAh-Pm/I%cJcCi3rW(mVrrDfn!!%TMci +])Ma1VuHeuJ:O$e!!(7C!!*#u!!)Wjr;an +])Ma1VuQ_rR/[-dc2RbDrVlit[/^.+h#I?Rm/I%cJcCi3rW(IJ!!%TMci +])Ma1VuQ_rR/[-dc2RbDrVlit[/^.+h#I?Rm/I%cJcCi3rW(IJ!!%TMci +])Ma1VuHeuJ:O$e!!(:D!!)ut!!'D+rrCjS!W[b$m/I%cJcCi3!W[b$eGfLKJcF'r!W[b$pA]X~> +])Ma1VuQ_rR/[-dJcF4!rW) +])Ma1VuQ_rR/[-dJcF4!rW) +])Ma1VuHeuJ:O$e!!%TMe,KILJ:R"d!!%TMOT,@^J:Q/L!!)Qh!!)$Y!!)3^!!)9`!!)]lq>gQq +!!(1A!W[b$pA]X~> +])Ma1VuQ_rR/[-dJcF4!rW) +])Ma1VuQ_rR/[-dJcF4!rW) +])Ma1VuHeuJ:O$e!!%TMe,KILJ:R"d!!%TMOT,@^J:Q/L!!(RL!!)3^!!)-\!!)rs!!*#u!!)rs +!!'t;!W[b$pA]X~> +])Ma1VuQ_rR/[-dJcF4!rW)^Eos8NE+s8N*!!!*'!!!)utrW)rt!!*#u!!)utrVururW)os!!)rs$3:,+ +!<3$!s8N'!rVuisg].6QpA]X~> +])Ma1VuQ_rR/[-dJcF4!rW)^Eos8NE+s8N*!!!*'!!!)utrW)rt!!*#u!!)utrVururW)os!!)rs$3:,+ +!<3$!s8N'!rVuisg].6QpA]X~> +])Ma1VuHeuJ:O$e!!%TMe,KILJ:R"dJH1]1s+&W!!!)fo!!)or"p"]'!<3$!rVuisrr;rtrr3!! +s82lsrr<&us8E#trrW9$!!)iprW)uu$3:,+!<3$!s8N'!rVuisrr2rurr2rurVuis!<<#urVlit +r;R*(s8N*!!!*'!!!)utrW(^Q!W[b$pA]X~> +])Ma1VuQ_rR/[-dJcF4!rW)UEprr3H.s8N'!s8N'!s8N*!rrE&u!!)or!!)rs!!*#u +!!)rs!!)rs%KQP/!!*'!!!*'!!<<'!rr2ruh#I?RpA]X~> +])Ma1VuQ_rR/[-dJcF4!rW)UEprr3H.s8N'!s8N'!s8N*!rrE&u!!)or!!)rs!!*#u +!!)rs!!)rs%KQP/!!*'!!!*'!!<<'!rr2ruh#I?RpA]X~> +])Ma1VuHeuJ:O$e!!%TMe,KILJ:R"d!!%TMOT,@^J:Q/L!!)fo!!)or$3:,+!!*'!!<<'!r;Q`s +rr3*$s8N'!rr2rurr3'#s8N)urs&Q(rr<'!rrDlp!!*#u%KQP/!!*'!!!*'!!<<'!rr2ruqu6Wr +r;Q`srr2rur;Q`sr;R6,s8N'!s8N'!s8N*!rrE&u!!(gS!W[b$pA]X~> +])Ma1VuQ_rR/[-ddf0CLs8N)Ds82lqs8E#`rr<&qrr<&]s8E#brr<%Ms,d9[!7_#K!;QQo!;lcu +!<<'!rr3'#s8N)srr<&urrW9$rrE#t!!*#u!s&B$!<3!#!<<'!rr2ruq>UEprr3'#s8N)urr<&u +rrW9$rrE&u!!)or!!)rs!!*#u!!)rs!!)rs!s&B$!<2uu!<3!#!<<'!rr2ruh#I?RpA]X~> +])Ma1VuQ_rR/[-ddf0CLs8N)Ds82lqs8E#`rr<&qrr<&]s8E#brr<%Ms,d9[!7_#K!;QQo!;lcu +!<<'!rr3'#s8N)srr<&urrW9$rrE#t!!*#u!s&B$!<3!#!<<'!rr2ruq>UEprr3'#s8N)urr<&u +rrW9$rrE&u!!)or!!)rs!!*#u!!)rs!!)rs!s&B$!<2uu!<3!#!<<'!rr2ruh#I?RpA]X~> +])Ma1VuHeuJ:O$e!!(II!s&B$!6tQA!<)rs!:'Ra!;c]q!9X:_!.]Udrr<%Ms,d6^!.]ULrr<&o +rr<&rrrW9$rrE&u!s&B$!;uis!<3!#!<<'!rVlitrr3'#s8N)urrW9$rrE&u!!)ip!!*#u!s&B$ +!<2uu!<3!#!<<'!rr2ruqu6Wrr;Q`srr2rur;Q`sr;Qj!s8N)urr<&urrW9$rrE&u!!(gS!W[b$ +pA]X~> +]DnT*R/[-ddf0CLs8N)Err<&prr<&urr<&brr<&ss82l\s8E#brr<%Ms,d9[!7_#K!;lfm!<3!# +!<<'!rr2rurr;uurr;lrs8N'!rVlitrr3'#s8N)urrW9$rrE&u!!)ip!!*#u!s&B$!<2uu!<3!" +!<<)s!;lcr!;uis!<2uu!;uis!;uj!!<<'!rr2rurr3$"s8Vush#I?RpA]X~> +]DnT*R/[-ddf0CLs8N)Err<&prr<&urr<&brr<&ss82l\s8E#brr<%Ms,d9[!7_#K!;lfm!<3!# +!<<'!rr2rurr;uurr;lrs8N'!rVlitrr3'#s8N)urrW9$rrE&u!!)ip!!*#u!s&B$!<2uu!<3!" +!<<)s!;lcr!;uis!<2uu!;uis!;uj!!<<'!rr2rurr3$"s8Vush#I?RpA]X~> +]DnZ,s+$L:!!(II!s&B$!7(TE!;ZWp!<2uu!:0Xb!;ulp!9jFa!.]Udrr<%Ms,d6^!.]ULrr<&r +s7u`prrW9$rrE&u!!*#urrE&uquHcs!!)ut!!*#u!s&B$!<3!#!<<'!rr2ruq>UEprr3'#s8N)u +rr<&urrN3#s82lorr<&srr<&urr<&srr<&srrW9$rrE&u!!*#u!W`9#quGOP!W[b$pA]X~> +])Ma1VuQ_rR/[-dq#:N!;?GC~> +])Ma1VuQ_rR/[-dq#:N!;?GC~> +])Ma1VuHeuJ:O$e!!)fo!!)lqrW!!!!;uit!<<#urr;rtrr;rtrr3'#s8N)prrW9$!!)utrW)rt +#6=c(!<<'!!<)rs!<)rs!<<'!!<2uu!;ZWp!;uj!!<<'!rr2rurr;rtrr3-%rr<'!!<3!$!<<'! +!9X:_!.]Udrr<%Ms,d6^!.]ULrr<&orr<&rrrW9$rrE&u!!)rs!s&B$!;lcr!<)ot!<3!#!<<'! +rr3'#s8N)urr<&prr<&urrW9$rrE&u!!*#u!s&B$!;HKn!;uis!<2uu!;uis!;uj!!<<'!rr2ru +rr3'#s8N)OrrN1NJG0"n~> +])Ma1VuQ_rR/[-dq#: +])Ma1VuQ_rR/[-dq#: +])Ma1VuHeuJ:O$e!!)fo!!)or!!)ut!!)rsrrDus!!)rs!!*#u"p"]'!<<'!q>^Hps8N0$s8N)u +rsf&/rr<'!rr<'!rrE*!!<3!#!<<'!q#:UEpr;Qj!s8N)urrW9$rrE&u"9AK%!!*#u!s&B$ +!<3!"!<3&^rrN1NJF*:9!.k03rrN1NJCOT!!;QQo!;lcu!<<'!rr2rur;Qj!s8N)rrr<&trr<&u +rrW9$rrE&u!s&B$!<2uu!;ZWp!<3!#!<<'!rr2rurr3'#s8N)nrr<&srr<&urr<&srr<&srrW9$ +rrE&u!!*#u!s&B$!8.;Q!.]Uns*t~> +])Ma1VuQ_rR/[-dq#:UEprr2ruqu6`us8N)u +rr<&urrW9$rrE&u!s&B$!;QQo!;ZWp!;uj!!<<'!rr2ruqu6`us8N)trrN3#!9!nV!:9^c!.k03 +s8E#Jrr<&orr<&rrrW9$rrE&u!W`9#rW)osr;clt!!)rs!s&B$!<3#t!<2uu!<2uu!;QQu!<<'! +s8N)urr<&urr<&us8;rtrr<&us82lsrr<&urr<&trr<&srrW9$rrE&u!!*#u!!*#ur;bXQrW)Zl +J,~> +])Ma1VuQ_rR/[-dq#:UEprr2ruqu6`us8N)u +rr<&urrW9$rrE&u!s&B$!;QQo!;ZWp!;uj!!<<'!rr2ruqu6`us8N)trrN3#!9!nV!:9^c!.k03 +s8E#Jrr<&orr<&rrrW9$rrE&u!W`9#rW)osr;clt!!)rs!s&B$!<3#t!<2uu!<2uu!;QQu!<<'! +s8N)urr<&urr<&us8;rtrr<&us82lsrr<&urr<&trr<&srrW9$rrE&u!!*#u!!*#ur;bXQrW)Zl +J,~> +])Ma1VuHeuJ:O$e!!)fo!!)or!!)rs!!*#u!W`6#r;Q`sq#:Nus8N*!rrDlp!!*#u!!)or!s&B$ +!<2uu!<3!#!<<'!rr3'#s8N)orr<&prr<&srrW9$rrE&u!!)or!s&B$!<)p!!<3&WrrN1NJF*:9 +!.k03rrN1NJCOT!!;QQo!;lcu!<<'!rr3$"s8W&urVufrs8N'!r;Qj!s8N)us8E#trr<&urr<&o +rrrK'rrE*!!<2uu!<2uu!<3#s!<<'!!<3#r!<<'!!<2uu!<)ot!;uj!!<<'!rr2rurr2rurr;os +h#@EUJ:R@nJ,~> +])Ma1VuQ_rR/[-dqu?KmrVultrr2rurr3*$s8N'!rr2ruqu?Tps8N0$s8N)prr<&urr<&us8;rt +rr<&urr<&urrN3#s82lrs8N)qrr<&srrW9$rrDus!s&B$!<2uu!<3#s!<<'!!<)rs!9!nV!:9^c +!.k03s8E#Jrr<&7s8;q^s8E#ls*t~> +])Ma1VuQ_rR/[-dqu?KmrVultrr2rurr3*$s8N'!rr2ruqu?Tps8N0$s8N)prr<&urr<&us8;rt +rr<&urr<&urrN3#s82lrs8N)qrr<&srrW9$rrDus!s&B$!<2uu!<3#s!<<'!!<)rs!9!nV!:9^c +!.k03s8E#Jrr<&7s8;q^s8E#ls*t~> +])Ma1VuHeuJ:O$e!!)orq>gKorrE&u!!*#u"9AK%!!*#u!!)orr;clt!s&B$!;ZWp!<2uu!<3#s +!<<'!!<2uu!<3!"!<<)s!<3#u!;c]q!;uj!!<<'!r;Qj!s8N)urr<&us8;rtrr<&ts8E#VrrN1N +JF*:9!.k03rrN1NJCOT!!5SX5!0mKb!.]Uns*t~> +])Ma1VuQ_rR/[-dq#: +])Ma1VuQ_rR/[-dq#: +])Ma1VuHeuJ:O$e!!)fo!!)fo!!*#u!W`6#r;Qj!s8N)srr<&urrrK'rrE*!!;ZWp!<3!#!<<'! +rr3'#s8N)urr<&urrW9$rrDio!!)or!!)rs!s&B$!;uj!!<<'!rr3'#s8N)urrW9$rrE#t!s&B$ +!9*qZ!.]Udrr<%Ms,d6^!.]ULrr<%Ms3L]H!.]Uns*t~> +])Ma1VuQ_rR/[-dq#: +])Ma1VuQ_rR/[-dq#: +])Ma1VuHeuJ:O$e!!)fo!!)fo!!*#u!W`6#r;Qj!s8N)srr<&urrrK'rrE*!!;ZWp!<3!#!<<'! +rr3'#s8N)urr<&urrW9$rrDio!!)lq!!)ut!!*#u!!*#u!!*#u#6=f(!!*'!!<3!#!<<'!rVlit +rr2ruir9&[J:R"d!!%TMOT,@^J:Q/L!!%TMci4%HJ:R@nJ,~> +])Ma1VuQ_rR/[-dq#: +])Ma1VuQ_rR/[-dq#: +])Ma1VuHeuJ:O$e!!)fo!!)orrW)lr!!*#urW)osrW)rtqu?m"!<<'!q>UEprr2rurr;lr!WN0! +rr<&urr<&us8;rts8E#trr<&ss82lqs8E#rs8N'"rrE&uqu?ct!<)ot!<)ot!9=(\!.]Udrr<%M +s,d6^!.]ULrr<%Ms3L]H!.]Uns*t~> +])Ma1VuQ_rR/[-dl2L_`jo>8Za8Z,>a8c,=m/I%cJcCi3rW(IJ!!%TMci +])Ma1VuQ_rR/[-dl2L_`jo>8Za8Z,>a8c,=m/I%cJcCi3rW(IJ!!%TMci +])Ma1VuHeuJ:O$e!!)9`!!)-\r;an!W[b$m/I%cJcCi3!W[b$eGfLKJcF'r!W[b$pA]X~> +])Ma1VuQ_rR/[-dlMghaV>pPqaoD>?m/I%cJcCi3rW(IJ!!%TMci +])Ma1VuQ_rR/[-dlMghaV>pPqaoD>?m/I%cJcCi3rW(IJ!!%TMci +])Ma1VuHeuJ:O$e!!) +])Ma1VuQ_rR/[-dJcF4!rW) +])Ma1VuQ_rR/[-dJcF4!rW) +])Ma1VuHeuJ:O$e!!%TMe,KILJ:R"d!!%TMOT,@^J:Q/L!!(4BquH]qrW)6`!!'"u!W[b$pA]X~> +])Ma1VuQ_rR/[-dJcF4!rW)UEprr2ruli-qbWW2qtpA]X~> +])Ma1VuQ_rR/[-dJcF4!rW)UEprr2ruli-qbWW2qtpA]X~> +])Ma1VuHeuJ:O$e!!%TMe,KILJ:R"d!!%TMOT,@^J:Q/L!!(7C!!)ip!!*#u!!)?b!!'"u!W[b$ +pA]X~> +])Ma1VuQ_rR/[-dJcF4!rW) +])Ma1VuQ_rR/[-dJcF4!rW) +])Ma1VuHeuJ:O$e!!%TMe,KILJ:R"d!!%TMOT,@^J:Q/L!!)fo!!)or!s&?$!<)rs!<3!'!<3$! +s8N'!rVuisrr2rurr2ruq>UEpr;Qj!s8N)urr<&us8E#trriE&!!*$!rr2ruXoAG&J:R@nJ,~> +])Ma1VuQ_rR/[-dJcF4!rW) +])Ma1VuQ_rR/[-dJcF4!rW) +])Ma1VuHeuJ:O$e!!%TMe,KILJ:R"d!!%TMOT,@^J:Q/L!!)fo!!)orrrE*!!s&B$!<3!.!<<'! +!<<'!!<<'!s8N)urr<&rrr<&prr<&srrW9$rrE&u!s&B$!<3!$!<<'!!<3!#!<<'!XT&>%J:R@n +J,~> +])Ma1VuQ_rR/[-dVuQYprVuislMghaqYpNqg&M$Om/I%cJcCi3rW(IJ!!)fo!!)or!!*#u!!)or +!s&B$!<2uu!<3!#!<<'!rr2ruqu6Wrq>UEpr;Qj!s8N)urr<&rrrW9$rrE#t!W`6#X8i/!pA]X~> +])Ma1VuQ_rR/[-dVuQYprVuislMghaqYpNqg&M$Om/I%cJcCi3rW(IJ!!)fo!!)or!!*#u!!)or +!s&B$!<2uu!<3!#!<<'!rr2ruqu6Wrq>UEpr;Qj!s8N)urr<&rrrW9$rrE#t!W`6#X8i/!pA]X~> +])Ma1VuHeuJ:O$e!!&qsquH]qrW)6`!!)lq!!(^P!W[b$m/I%cJcCi3!W[b$eGfLKq#: +])Ma1VuQ_rR/[-dnc&Rh\c2X0q>UEprr2ruli-qbr;ZZpg].6Qm/I%cJcCi3rW(IJ!!)orq>gNp +!!*#u!!*#ur;clt!!*#u!!*#u!W`9#quHWo!!)rs!s&B$!;uj!!<<'!rr2rurr;oss8N'!rVuis +X8i/!pA]X~> +])Ma1VuQ_rR/[-dnc&Rh\c2X0q>UEprr2ruli-qbr;ZZpg].6Qm/I%cJcCi3rW(IJ!!)orq>gNp +!!*#u!!*#ur;clt!!*#u!!*#u!W`9#quHWo!!)rs!s&B$!;uj!!<<'!rr2rurr;oss8N'!rVuis +X8i/!pA]X~> +])Ma1VuHeuJ:O$e!!)Qh!!'S0!!)ip!!*#u!!)?b!!)rsquGLO!W[b$m/I%cJcCi3!W[b$eGfLK +qu?Kmrr2rurr2rurr;oss8N'!rr2rurr3$"s8Vusqu6Wrr;Qj!s8N)srrW9$rrE&u!!*#ur;clt +!!)utrW'#!!W[b$pA]X~> +])Ma1VuQ_rR/[-dq#:UEpr;Qj!s8N)urr<&us8E#trriE&!!*$!rr3*$s8N'!g&M$Om/I%c +JcCi3rW(IJ!!)fo!!)or!!*#u!s&B$!<3!#!<<'!rr2rurr3'#s8N)nrr<&srrW9$rrDus!s&B$ +!<3!#!<<'!rr3'#s8N)trrW9$rrB/#rW)ZlJ,~> +])Ma1VuQ_rR/[-dq#:UEpr;Qj!s8N)urr<&us8E#trriE&!!*$!rr3*$s8N'!g&M$Om/I%c +JcCi3rW(IJ!!)fo!!)or!!*#u!s&B$!<3!#!<<'!rr2rurr3'#s8N)nrr<&srrW9$rrDus!s&B$ +!<3!#!<<'!rr3'#s8N)trrW9$rrB/#rW)ZlJ,~> +])Ma1VuHeuJ:O$e!!)fo!!)rsrW)uu"T\Q&!<<)u!<3!!!<<#uq#:Errr<&ts8E#trs&Q(!!*'! +!!)utrW)osrW)uu!!*#u!!)ip!!)rs!s&B$!<2uu!<3#t!<3!%!<3$!rrE&u"9AK%!!(^P!W[b$ +m/I%cJcCi3!W[b$eGfLKq#:%J:R@nJ,~> +])Ma1VuQ_rR/[-dq#:^Hps8N0$s8N)ursf&/rr<'! +rr<'!rrE*!!<3!#!<<'!q#:UEpr;Qj!s8N)urrW9$rrE&u"9AK%!!*#u!s&B$!<3!"!<3&Q +s8E#brr<%Ms,d9[!7_#K!;QQo!;lcr!<3!#!<<'!rr3'#s8N)urr<&urrW9$rrDcm!!)ut!!*#u +!!*#u!!*#u#6=f(!!*'!!<3!#!<<'!rVlitrr2ruXoJA#pA]X~> +])Ma1VuQ_rR/[-dq#:^Hps8N0$s8N)ursf&/rr<'! +rr<'!rrE*!!<3!#!<<'!q#:UEpr;Qj!s8N)urrW9$rrE&u"9AK%!!*#u!s&B$!<3!"!<3&Q +s8E#brr<%Ms,d9[!7_#K!;QQo!;lcr!<3!#!<<'!rr3'#s8N)urr<&urrW9$rrDcm!!)ut!!*#u +!!*#u!!*#u#6=f(!!*'!!<3!#!<<'!rVlitrr2ruXoJA#pA]X~> +])Ma1VuHeuJ:O$e!!)fo!!)or!!*#urrE&u!!*#u"9AK%!!*#u!!)iprrE*!!s&B$!<3!.!<<'! +!<<'!!<<'!s8N)urrW9$rrDio!!)ip!!)rs!s&B$!<3!#!<<'!rr3*$s8N'!rr3'#s8N)urrN3# +!8@GS!.]Udrr<%Ms,d6^!.]ULrr<&orr<&rrr<&urrW9$rrE&u!s&B$!<2uu!<3!#!<<'!pAY*m +rVlitrr2rurr2rurr33's8N'!s8N)urrW9$rrE#t!!*#u!!'/$!W[b$pA]X~> +])Ma1VuQ_rR/[-dq#:UNss8N)trr<&prr<&urr<&rrrW9$rrE&u!!*#u!s&B$ +!<3!#!<<'!q#:UEpr;Qj!s8N)urr<&rrrW9$rrE#t!W`6#e,TCIm/I%cJcCi3rW(IJ!!)fo +!!)or!!*#u!!*#uqu?ct!<2uu!<2uu!<3#s!<<'!!;ulp!<)rs!;uls!!3*"rr;lr!WN/urr<&t +rr<&%s8E#ls*t~> +])Ma1VuQ_rR/[-dq#:UNss8N)trr<&prr<&urr<&rrrW9$rrE&u!!*#u!s&B$ +!<3!#!<<'!q#:UEpr;Qj!s8N)urr<&rrrW9$rrE#t!W`6#e,TCIm/I%cJcCi3rW(IJ!!)fo +!!)or!!*#u!!*#uqu?ct!<2uu!<2uu!<3#s!<<'!!;ulp!<)rs!;uls!!3*"rr;lr!WN/urr<&t +rr<&%s8E#ls*t~> +])Ma1VuHeuJ:O$e!!)fo!!)or!!*#u!!)ip!s&B$!<)ot!;ZWp!<2uu!;lcu!<<'!rr2rurr3'# +s8N)urrW9$rrDio!!)ip!!)rs!s&B$!<2uu!;lcu!<<'!rVlp!rrCOJ!W[b$m/I%cJcCi3!W[b$ +eGfLKq#:'!.]Uns*t~> +])Ma1VuQ_rR/[-dqu?Kmrr2rurr2rur;Z]qs8N'!rVlitq>UEprr2rurr;oss8N'!rr2rurr3$" +s8Vusrr;uuqYpNqr;Qj!s8N)srrW9$rrE&u!!*#ur;clt!!)utrW(FIrW) +])Ma1VuQ_rR/[-dqu?Kmrr2rurr2rur;Z]qs8N'!rVlitq>UEprr2rurr;oss8N'!rr2rurr3$" +s8Vusrr;uuqYpNqr;Qj!s8N)srrW9$rrE&u!!*#ur;clt!!)utrW(FIrW) +])Ma1VuHeuJ:O$e!!)orq>gNp!!*#u!!)rsr;clt!!)ut!!)ip!!*#u!!*#ur;clt!!*#u!!*#u +!W`9#quH`rrrDoq!!)rs!s&B$!;uj!!<<'!rr2rurr;oss8N'!rVuise,KILJ:R"d!!%TMOT,@^ +J:Q/L!!'e6!!&2^!W[b$pA]X~> +])Ma1VuQ_rR/[-dq#: +])Ma1VuQ_rR/[-dq#: +])Ma1VuHeuJ:O$e!!)fo!!)or!!*#u!!)ut!!*#u!s&B$!<)ot!;ZWp!<3!#!<<'!rr3'#s8N)u +rr<&urrW9$rrDio!!)or!!)rs!s&B$!;uj!!<<'!rr3'#s8N)urrW9$rrE#t!s&B$!7_#M!.]Ud +rr<%Ms,d6^!.]ULrr<&5s8N(`rrN1NJG0"n~> +])Ma1VuQ_rR/[-dq#:UEprr3'#s8N)urrW9$rrE&u +!!*#u!s&B$!;QQo!;c]q!<)ot!<2uu!<2uu!<3!'!<<'!!<<'!rr3'#s8N)trr<&urr<&Ls8E#b +rr<%Ms,d9[!7_#K!.k0rs8E#ls*t~> +])Ma1VuQ_rR/[-dq#:UEprr3'#s8N)urrW9$rrE&u +!!*#u!s&B$!;QQo!;c]q!<)ot!<2uu!<2uu!<3!'!<<'!!<<'!rr3'#s8N)trr<&urr<&Ls8E#b +rr<%Ms,d9[!7_#K!.k0rs8E#ls*t~> +])Ma1VuHeuJ:O$e!!)fo!!)or!!*#u!!)ut!!*#u"9AK%!!*#u!!)ip!!*#u!s&B$!<3!#!<<'! +rr2rurr3'#s8N)orr<&qrr<&trr<&urr<&urr<&urs&Q(rr<'!rrE&u!s&B$!<)ot!<2uu!7h)N +!.]Udrr<%Ms,d6^!.]ULrr<%Ms3L]H!.]Uns*t~> +])Ma1VuQ_rR/[-dq#: +])Ma1VuQ_rR/[-dq#: +])Ma1VuHeuJ:O$e!!)fo!!)lq!s&B$!;ulp!!<0#s8E#nrr<&urr<&us82itrrE&u!!*#u!!*#u +r;cltrW)rt!!)rsquH]qrW)lrrr<*"!<3#r!!3*"rVlitrVlitf)GdOJ:R"d!!%TMOT,@^J:Q/L +!!%TMci4%HJ:R@nJ,~> +])Ma1VuQ_rR/[-diVrlXqZ$Koa8Z,>])Va0m/I%cJcCi3rW(IJ!!%TMci +])Ma1VuQ_rR/[-diVrlXqZ$Koa8Z,>])Va0m/I%cJcCi3rW(IJ!!%TMci +])Ma1VuHeuJ:O$e!!)!X!!)lqr;an +])Ma1VuQ_rR/[-diVrlX]Dqm2]`7s2m/I%cJcCi3rW(IJ!!%TMci +])Ma1VuQ_rR/[-diVrlX]Dqm2]`7s2m/I%cJcCi3rW(IJ!!%TMci +])Ma1VuHeuJ:O$e!!)!X!!'Y2rrB_3!W[b$m/I%cJcCi3!W[b$eGfLKJcF'r!W[b$pA]X~> +])Ma1VuQ_rR/[-dJcF4!rW)!lkPtG[rVuislMgha]Dqj1 +pA]X~> +])Ma1VuQ_rR/[-dJcF4!rW)!lkPtG[rVuislMgha]Dqj1 +pA]X~> +])Ma1VuHeuJ:O$e!!%TMe,KILJ:R"d!!%TMOT,@^J:Q/L!!)9`!!)lq!!)]l!!)3^quH]qrW)6` +!!'Y2!W[b$pA]X~> +])Ma1VuQ_rR/[-dJcF4!rW)UEpjSo2[q>UEprr2ruli-qb]Dqj1 +pA]X~> +])Ma1VuQ_rR/[-dJcF4!rW)UEpjSo2[q>UEprr2ruli-qb]Dqj1 +pA]X~> +])Ma1VuHeuJ:O$e!!%TMe,KILJ:R"d!!%TMOT,@^J:Q/L!!)!X!!)ip!!)*[!!)ip!!*#u!!)?b +!!'Y2!W[b$pA]X~> +])Ma1VuQ_rR/[-dJcF4!rW)UEpr;Qj!s8N)urr<&us8E#trriE&!!*$!rr2ru^]495 +pA]X~> +])Ma1VuQ_rR/[-dJcF4!rW)UEpr;Qj!s8N)urr<&us8E#trriE&!!*$!rr2ru^]495 +pA]X~> +])Ma1VuHeuJ:O$e!!%TMe,KILJ:R"d!!%TMOT,@^J:Q/L!!)fo!!)or! +])Ma1VuQ_rR/[-dJcF4!rW)UEpr;Qj!s8N)urrW9$rrE&u"9AK%!!*#u!s&B$!5AL4! +;?GC~> +])Ma1VuQ_rR/[-dJcF4!rW)UEpr;Qj!s8N)urrW9$rrE&u"9AK%!!*#u!s&B$!5AL4! +;?GC~> +])Ma1VuHeuJ:O$e!!%TMe,KILJ:R"d!!%TMOT,@^J:Q/L!!)fo!!)orrrE&u"p"]'!<<'!rr;uu +q>UEprr3H.s8N'!s8N'!s8N*!rrE&u!!)or!!)ip!!)rs!s&B$!<3!#!<<'!rr3*$s8N'!rr3'# +s8N)5rrN1NJG0"n~> +])Ma1VuQ_rR/[-dnc&Rh_#O<4rVuislMghaqYpNqdJs1Gm/I%cJcCi3rW(IJ!!)fo!!)or!!)ut +"p"]'!<<'!rVlitq>UEprr3'#s8N)urr<&urrW9$rrE&u!!)or!!)ip!!)rs!s&B$!<2uu!;lcu +!<<'!rVlp!rrBb4rW)ZlJ,~> +])Ma1VuQ_rR/[-dnc&Rh_#O<4rVuislMghaqYpNqdJs1Gm/I%cJcCi3rW(IJ!!)fo!!)or!!)ut +"p"]'!<<'!rVlitq>UEprr3'#s8N)urr<&urrW9$rrE&u!!)or!!)ip!!)rs!s&B$!<2uu!;lcu +!<<'!rVlp!rrBb4rW)ZlJ,~> +])Ma1VuHeuJ:O$e!!)Qh!!'h7quH]qrW)6`!!)lq!!(FH!W[b$m/I%cJcCi3!W[b$eGfLKq#:UEpr;Qj!s8N)u +rr<&rrrW9$rrE#t!W`6#^&J-6J:R@nJ,~> +])Ma1VuQ_rR/[-dYl=\'q>UEprr2ruli-qbr;ZZpe,TCIm/MV:Mu_B-!!)orq>gNp!!)ut"p"]' +!<<'!rVlitq>UEprr3'#s8N)urr<&urrN3#s82lorr<&srrW9$rrDus!s&B$!<2uu!<3#s!<<'! +!<)rs!58F3!;?GC~> +])Ma1VuQ_rR/[-dYl=\'q>UEprr2ruli-qbr;ZZpe,TCIm/MV:Mu_B-!!)orq>gNp!!)ut"p"]' +!<<'!rVlitq>UEprr3'#s8N)urr<&urrN3#s82lorr<&srrW9$rrDus!s&B$!<2uu!<3#s!<<'! +!<)rs!58F3!;?GC~> +])Ma1VuHeuJ:O$e!!'8'!!)ip!!*#u!!)?b!!)rsquG4G!W[b$m/MV:NWB*0eGfLKqu?Kmrr2ru +rVm'%s8N*!rrE#t!!)ip!!*#u!s&B$!<2uu!<3!"!<<)s!;lcr!;uj!!<<'!r;Qj!s8N)urr<&u +s8;rtrr<&ts8E#3rrN1NJG0"n~> +])Ma1VuQ_rR/[-dq#: +])Ma1VuQ_rR/[-dq#: +])Ma1VuHeuJ:O$e!!)fo!!)or#QXo)!<3$!s8W&u!WN/qrrW9$!!)utrW)rt#6=c(!<<'!!<)rs +!<)rs!<<'!!<2uu!;ZWp!;uj!!<<'!rr2rurr;rtrr3-%rr<'!!<3!$!<<'!!7CfJ!.]Uas+,]] +JCOT!!;QQo!;lcr!<)p%!<<'!s8N)trr<&prr<&urrW9$rrE&u!!*#u!s&B$!;HKn!;uj!!<<'! +r;Qj!s8N)urrW9$rrE&u!s&B$!<)p"!<<'!^Ae67J:R@nJ,~> +])Ma1VuQ_rR/[-dq#:^Hps8N0$s8N)ursf&/rr<'!rr<'!rrE*! +!<3!#!<<'!q#:UEpr;Qj!s8N)urrW9$rrE&u"9AK%!!*#u!s&B$!<3!"!<3&Is8E#_s+(02 +!7_#K!;QQo!;lfr!<3!&!<<'!s8N)us8N)prr<&urrW9$rrE&u!!*#u!s&B$!;?Em!<)ot!<2uu +!<2uu!<3!'!<<'!!<<'!rr3'#s8N)trr<&urr<&6s8E#ls*t~> +])Ma1VuQ_rR/[-dq#:^Hps8N0$s8N)ursf&/rr<'!rr<'!rrE*! +!<3!#!<<'!q#:UEpr;Qj!s8N)urrW9$rrE&u"9AK%!!*#u!s&B$!<3!"!<3&Is8E#_s+(02 +!7_#K!;QQo!;lfr!<3!&!<<'!s8N)us8N)prr<&urrW9$rrE&u!!*#u!s&B$!;?Em!<)ot!<2uu +!<2uu!<3!'!<<'!!<<'!rr3'#s8N)trr<&urr<&6s8E#ls*t~> +])Ma1VuHeuJ:O$e!!)fo!!)or"9AK%!!*#u!!*#urrDlprrE*!!s&B$!<3!.!<<'!!<<'!!<<'! +s8N)urrW9$rrDio!!)ip!!)rs!s&B$!<3!#!<<'!rr3*$s8N'!rr3'#s8N)urrN3#!7LlK!.]Ua +s+,]]JCOT!!;QQo!;lfr!<3!&!<<'!s8N)us8N)prr<&urrW9$rrE&u!!*#u!s&B$!;?Em!<)ot +!<2uu!<2uu!<3!'!<<'!!<<'!rr3'#s8N)trr<&urr<&6rrN1NJG0"n~> +])SN*R/[-dq#:UEpr;Qj!s8N)urr<&rrrW9$rrE#t!W`6#bQ%PAJcG$8!!%TMir8uYq#: +])SN*R/[-dq#:UEpr;Qj!s8N)urr<&rrrW9$rrE#t!W`6#bQ%PAJcG$8!'l,8ir8uYq#: +])ST,s+$L:!!)fo!!)or!s&B$!<)ot!<)ot!;ZWp!<2uu!;lcu!<<'!rr2rurr3'#s8N)urrW9$ +rrDio!!)ip!!)rs!s&B$!<2uu!;lcu!<<'!rVlp!rrC7B!W[b$JcG$8!'l,8ir8uYq#: +\,W<*R/[-dqu?Kmrr3'#s8N)trr<&trr<&prr<&urr<&us8;rtrr<&urr<&urrN3#s82lrs8N)q +rr<&srrW9$rrDus!s&B$!<2uu!<3#s!<<'!!<)rs!6bEA!.k18rr<%Ms5O%Y!:g'h!:Tsd!7_#K +!2TYo!;?GC~> +\,W<*R/[-dqu?Kmrr3'#s8N)trr<&trr<&prr<&urr<&us8;rtrr<&urr<&urrN3#s82lrs8N)q +rr<&srrW9$rrDus!s&B$!<2uu!<3#s!<<'!!<)rs!6bEA!.k18rr><8s5O%Y!:g'h!:Tsd!7_#K +!2TYo!;?GC~> +\,W=UR/[-dqu?Kmrr3'#s8N)trr<&trr<&prr<&urr<&us8;rtrr<&urr<&urrN3#s82lrs8N)q +rr<&srrW9$rrDus!s&B$!<2uu!<3#s!<<'!!<)rs!6bBD!.]TNs6BUa5_8sprr<&hrr<&fs8;rI +rr<%prrN1NJG0"n~> +\,W<*R/[-dq#: +\,W<*R/[-dq#: +\,W=UR/[-dq#: +MuWeWJcG'9!!)fo!!)or!s&B$!<)ot!<3#u!;ZWp!<3!#!<<'!rr3'#s8N)urr<&urrW9$rrDio +!!)lq!!)ut!!*#u!!*#u!!*#u#6=f(!!*'!!<3!#!<<'!rVlitrr2ruc2[bCJcG'9!W`6#JcFd1 +!!%TMci +MuWfBJcG'9!!)fo!!)or!s&B$!<)ot!<3#u!;ZWp!<3!#!<<'!rr3'#s8N)urr<&urrW9$rrDio +!!)lq!!)ut!!*#u!!*#u!!*#u#6=f(!!*'!!<3!#!<<'!rVlitrr2ruc2[bCJcG'9!^QcNJcFd1 +!!%TMci +MuWfBJcG'9!!)fo!!)or!s&B$!<)ot!<3#u!;ZWp!<3!#!<<'!rr3'#s8N)urr<&urrW9$rrDio +!!)lq!!)ut!!*#u!!*#u!!*#u#6=f(!!*'!!<3!#!<<'!rVlitrr2ruc2RhFJ:N4Nli.#Os$2/8 +j8T)ZJcF'r!W[b$pA]X~> +MuWeWJcG'9!!)fo!!)or!s&B$!;ulr!!3*"q>UEprr2rurr;lr!WN0!rr<&urr<&us8;rts8E#t +rr<&ss82lqs8E#rs8N'"rrE&uqu?ct!<)ot!<)ot!7(WD!.k19rrN3#!.k11rr<%Ms3L`E!;?GC~> +MuWfBJcG'9!!)fo!!)or!s&B$!;ulr!!3*"q>UEprr2rurr;lr!WN0!rr<&urr<&us8;rts8E#t +rr<&ss82lqs8E#rs8N'"rrE&uqu?ct!<)ot!<)ot!7(WD!.k19rrPIc5_8sqrr<%Ms3L`E!;?GC~> +MuWfBJcG'9!!)fo!!)or!s&B$!;ulr!!3*"q>UEprr2rurr;lr!WN0!rr<&urr<&us8;rts8E#t +rr<&ss82lqs8E#rs8N'"rrE&uqu?ct!<)ot!<)ot!7(TG!.]TNs6K[d5lX*#s5X+Z!.k0rrrN1N +JG0"n~> +MuNhYrr@WMm/I%cjSo8]s8W#ta8Z,>ZN'n(JcG*:!!*#u!!%TMjSo2[JcF'rrW)ZlJ,~> +MuNiDs$2/8m/I%cjSo8]s8W#ta8Z,>ZN'n(JcG*:!'pP`!'l,8jSo2[JcF'rrW)ZlJ,~> +MuNiDs$2/8m/I%cjSo8]s8W#ta8Z,>ZMst+J:N4Nm/I&Nrr2s`JcFg2!!%TMci4%HJ:R@nJ,~> +MuNhYrr@WMm/I%cjSo2[_#OE7[/^+*JcG*:!!*#u!!%TMjSo2[JcF'rrW)ZlJ,~> +MuNiDs$2/8m/I%cjSo2[_#OE7[/^+*JcG*:!'pP`!'l,8jSo2[JcF'rrW)ZlJ,~> +MuNiDs$2/8m/I%cjSo2[_#OE7[/U1-J:N4Nm/I&Nrr2s`JcFg2!!%TMci4%HJ:R@nJ,~> +MuNhYrr@WMm/I%cJcF4!rW%NLm/I%crr2ruJcFg2!!(4B!!(OK!!)]lquHZp!!)-\!!)6_rW)Zl +J,~> +MuNiDs$2/8m/I%cJcF4!rW%NLm/I&Nrr2s`JcFg2!!(4B!!(OK!!)]lquHZp!!)-\!!)6_rW)Zl +J,~> +MuNiDs$2/8m/I%cJcF4!!W[b$JcG*:!'pP`!'l,8jSo2[bPqPBeGfLKp&Fpir;Q`sjo5;\kl1\a +J:R@nJ,~> +N;ikXrr2ruJcG-;!!%TMe,TCIJcG-;!!)rs!!%TMjo5;\ci3tFiVrlXnc&RhpAY*mp\t3nkPtG[ +lMph`pA]X~> +N;ilCrr2s`JcG-;!!%TMe,TCIJcG-;!'pJ^!'l,8jo5;\ci3tFiVrlXnc&RhpAY*mp\t3nkPtG[ +lMph`pA]X~> +N;ilCrr2s`JcG-;!!%TMe,KILJ:N4NmJd/Or;Qa^JcFj3!!(@F!!)!X!!)Qh!!)`m!!)cn!!)3^ +quH$^!W[b$pA]X~> +N;ikXrr2ruJcG-;!!%TMe,TCIJcG-;!!)rs!!%TMjo5;\q#: +N;ilCrr2s`JcG-;!!%TMe,TCIJcG-;!'pJ^!'l,8jo5;\q#: +N;ilCrr2s`JcG-;!!%TMe,KILJ:N4NmJd/Or;Qa^JcFj3!!)fo!!)lqrW)rt!!*#u!!*#urW)os +rW)uu!!*#u! +N;ikXrr2ruJcG-;!!%TMe,TCIJcG-;!!)rs!!%TMjo5;\q#: +N;ilCrr2s`JcG-;!!%TMe,TCIJcG-;!'pJ^!'l,8jo5;\q#: +N;ilCrr2s`JcG-;!!%TMe,KILJ:N4NmJd/Or;Qa^JcFj3!!)fo!!)or!!*#u!!*#u!W`6#rr2ru +rr3'#s8N)srr<&urrW9$rrE&u!s&B$!<3!'!<<'!!<<'!q>UEpr;Q`srr2rurr3'#s8N)srrW9$ +rrDoq!!)cn!W`6#rr;uurr;uurr2rurr3$"rrDusrr<*"!9sLb!.]Uns*t~> +NW/tYr;Q`sJcG0 +NW/uDr;Qa^JcG0 +NW/uDr;Qa^JcG0UEpr;Q`sqYpWts8N)srrN3#!;ZWp +!;QQo!<3!#!<<'!rVlitq>UNss8N)urr<&\rrN1NJG0"n~> +NW/tYr;Q`sJcG0;[pA]X~> +NW/uDr;Qa^JcG0;[pA]X~> +NW/uDr;Qa^JcG0gNpquH]q!!)utquHcs!!)rs +!!*#u!s&B$!<3!#!<<'!rr3'#s8N)urr<&os8N)urr<&ts8;rtrr<&ss8E#orr<&srr<&urr<&u +rrW9$rrE#t!!)rsr;clt!!*#u!!)-\!W[b$pA]X~> +NW/tYr;Q`sJcG0 +NW/uDr;Qa^JcG0 +NW/uDr;Qa^JcG0^Qr!<)ot!<)ot!<2uu!<3!"!<3&[rrN1NJG0"n~> +NrK(ZqYpNqJcG3=!!%TMe,TCIJcG3=!!)fo!!%TMkPkM^q#: +NrK)EqYpO\JcG3=!!%TMe,TCIJcG3=!'p>Z!'l,8kPkM^q#: +NrK)EqYpO\JcG3=!!%TMe,KILJ:N4Nn,EAQq#:=ZJcFp5!!)fo!!)or!!)lq!W`6#rr2ruqu6Wr +r;R!%s8N'!s8N)urrW9$rrE&u!s&B$!<2uu!;?Ep!<<'!rr2rurr3'#s8N)srr<&urr<&qrr<&t +rrW9$rrDus!W`6#rVlitrVlitrr2rurr3$"rrD-[!W[b$pA]X~> +NrK(ZqYpNqJcG3=JH4!rJcG3=!!)fo!!%TMkPkM^q#: +NrK)EqYpO\JcG3=JH4!rJcG3=!'p>Z!'l,8kPkM^q#: +NrK)EqYpO\JcG3=JH4'ts+#\#n,EAQq#:=ZJcFp5!!)fo!!)lqr;clt!!*#u!!*#ur;cisrW)rt +rr<*"!<3!#!<<'!rr;rtrr2rurr2ruq>^EorVlitrr;lrs8W&us8N'!rVlp!rrDusqu?ct!;c`q +!<)ot!;ulp!<2uu!9=(\!.]Uns*t~> +NrK(ZqYpNqJcG3=!!%TMe,TCIJcG3=!!)fo!!%TMkPkM^])V^/\c2X0j8])YpA]X~> +NrK)EqYpO\JcG3=!!%TMe,TCIJcG3=!'p>Z!'l,8kPkM^])V^/\c2X0j8])YpA]X~> +NrK)EqYpO\JcG3=!!%TMe,KILJ:N4Nn,EAQq#:=ZJcFp5!!'V1r;aD.!!)'Z!W[b$pA]X~> +O8f1[q#:!!%TMe,TCIJcG6>!!)`m!!%TMkl1V_JcG3=!!)$YrW)ZlJ,~> +O8f2Fq#:=ZJcG6>!!%TMe,TCIJcG6>!'p8X!'l,8kl1V_JcG3=!!)$YrW)ZlJ,~> +O8f2Fq#:=ZJcG6>!!%TMe,KILJ:N4NnG`JRpAY+XJcFs6!!%TMn,E@fir9&[J:R@nJ,~> +O8f1[q#:!!%TMe,TCIJcG6>oDjIBkl1V_JcF'rrW)ZlJ,~> +O8f2Fq#:=ZJcG6>!!%TMe,TCIJcG6>oK\!-kl1V_JcF'rrW)ZlJ,~> +O8f2Fq#:=ZJcG6>!!%TMe,KILJ:N4NnGi,GJcFs6!!%TMci4%HJ:R@nJ,~> +O8f1[q#:!!%TMe,TCIJcG$8!!%TMir8uYJcF'rrW)ZlJ,~> +O8f2Fq#:=ZJcG6>!!%TMe,TCIJcG$8!'l,8ir8uYJcF'rrW)ZlJ,~> +O8f2Fq#:=ZJcG6>!!%TMe,KILJ:N4NlMgiLJcFa0!!%TMci4%HJ:R@nJ,~> +OT,:\pAY*mJcG9?!!%TMe,TCIJcG$8!!%TMir8uYJcF'rrW)ZlJ,~> +OT,;GpAY+XJcG9?!!%TMe,TCIJcG$8!'l,8ir8uYJcF'rrW)ZlJ,~> +OT,;GpAY+XJcG9?!!%TMe,KILJ:N4NlMgiLJcFa0!!%TMci4%HJ:R@nJ,~> +OT4qQJcG9?!!%TMe,TCIJcG$8!!%TMir8uYJcF'rrW)ZlJ,~> +OT4r +OT4r +MuNbWJcG$8!!%TMe,TCIJcG$8!!%TMir8uYdJj1Hmf3.aiVrlXnc/Lel2L_`kl:V^pA]X~> +MuNcBJcG$8!!%TMe,TCIJcG$8!'l,8ir8uYdJj1Hmf3.aiVrlXnc/Lel2L_`kl:V^pA]X~> +MuNcBJcG$8!!%TMe,KILJ:N4NlMgiLJcFa0!!(FH!!)HeqZ,UT!!)QhquH!]!!)6_!W[b$pA]X~> +MuNbWJcG$8!!%TMe,TCIJcG$8!!%TMir8uYli-qbp\t3noD\djr;Q`srr2ruq#: +MuNcBJcG$8!!%TMe,TCIJcG$8!'l,8ir8uYli-qbp\t3noD\djr;Q`srr2ruq#: +MuNcBJcG$8!!%TMe,KILJ:N4NlMgiLJcFa0!!)?b!!)cn!!)Wj!!)rs!!*#u!!)fo!!(sW!!)He +!!)or!!)cn!!)foquH$^!W[b$pA]X~> +MuNbWJcG$8!!%TMe,TCIJcG$8!!%TMir8uYq#: +MuNcBJcG$8!!%TMe,TCIJcG$8!'l,8ir8uYq#: +MuNcBJcG$8!!%TMe,KILJ:N4NlMgiLJcFa0!!)fo!!)lqrVururW)rtrW)uurW)rtrW)rt!!*#u +!!)utrVururW!!!!;uls!;lcr!<2uu!<3#t!<)rs!<<'!!<3!!!<<#us8N'!rr;rtrr30&rr<'! +rrDusrW)rtrW)uurW)rtrW)rtrrD9_!W[b$pA]X~> +MuNbWJcG$8!!%TMe,TCIJcG$8!!%TMir8uYq#: +MuNcBJcG$8!!%TMe,TCIJcG$8!'l,8ir8uYq#: +MuNcBJcG$8!!%TMe,KILJ:N4NlMgiLJcFa0!!)fo!!)or!!)rs!!*#u!!*#u!s&B$!<2uu!<2uu +!;lcr!;uis!<2uu!<2uu!<3!"!<3&qrrN3#!<2uu!<3!#!<<'!r;Q`srr3'#s8N)urrW9$rrE&u +$3:,+!!*'!!<<'!r;Q`srr2rurr3'#s8N)urr<&urriE&rrE'!l2LebJ:R@nJ,~> +MuNbWJcG$8!!%TMe,TCIJcG$8!!%TMir8uYq#: +MuNcBJcG$8!!%TMe,TCIJcG$8!'l,8ir8uYq#: +MuNcBJcG$8!!%TMe,KILJ:N4NlMgiLJcFa0!!)fo!!)or!!)rs!!)lq!s&B$!<2uu!<2uu!;lcr +!;uis!<2uu!<2uu!<3!!!<;rsrr3$"rrE&u!!*#u!s&B$!;uis!<3!#!<<'!rr3'#s8N)urrW9$ +rrE&u!!*#urrE&u!!)lq!s&B$!<2uu!<2uu!9F.]!.]Uns*t~> +MuNbWJcG$8!!%TMe,TCIJcG$8!!%TMir8uYqu?KmrVultrr2rurVufrs8N'!rr;lrqu6Wrr;Q`s +rr2rurVm$$rrE*!!;ZWp!<)rq!<<'!!;uis!<3!#!<<'!rr3'#s8N)urrW9$rrE&u!!)rs!s&B$ +!<)rr!<<'!!<3#r!9F1Z!;?GC~> +MuNcBJcG$8!!%TMe,TCIJcG$8!'l,8ir8uYqu?KmrVultrr2rurVufrs8N'!rr;lrqu6Wrr;Q`s +rr2rurVm$$rrE*!!;ZWp!<)rq!<<'!!;uis!<3!#!<<'!rr3'#s8N)urrW9$rrE&u!!)rs!s&B$ +!<)rr!<<'!!<3#r!9F1Z!;?GC~> +MuNcBJcG$8!!%TMe,KILJ:N4NlMgiLJcFa0!!)orq>gKorrE&u!!)utr;clt!!*#uquHWo!!)rs +!!*#u!!)ut"T\Q&s8N)prr<&ts82lsrr<&srr<&urrW9$rrE&u!s&B$!<3!#!<<'!rr2rur;Qj! +s8N)ts8;rtrr<&us82lXrrN1NJG0"n~> +MuNbWJcG$8!!%TMe,TCIJcG$8!!%TMir8uYq#: +MuNcBJcG$8!!%TMe,TCIJcG$8!'l,8ir8uYq#: +MuNcBJcG$8!!%TMe,KILJ:N4NlMgiLJcFa0!!)fo!!)fo!s&B$!<2uu!<3!#!<<'!rr2rup\t3n +r;Q`srr2rurVm$$rrE*!!;c]s!<3&urr<&rrr<&srr<&urrW9$rrE&u!s&B$!<3!#!<<'!rr2ru +r;Qj!s8N)urr<&urrW9$rrE&u!!(sW!W[b$pA]X~> +MuNbWJcG$8!!%TMe,TCIJcG$8!!%TMir8uYq#: +MuNcBJcG$8!!%TMe,TCIJcG$8!'l,8ir8uYq#: +MuNcBJcG$8!!%TMe,KILJ:N4NlMgiLJcFa0!!)fo!!)fo!s&B$!<2uu!<3!#!<<'!rr2rup\t3n +r;Q`srr2rurVm$$rrE*!!;c]s!<3&urr<&rrr<&srs&Q(rr<'!rrE&u!s&B$!<3!#!<<'!rr2ru +r;Qj!s8N)urr<&urrW9$rrE&u!!(sW!W[b$pA]X~> +MuNbWJcG$8!!%TMe,TCIJcG$8!!%TMir8uYq#: +MuNcBJcG$8!!%TMe,TCIJcG$8!'l,8ir8uYq#: +MuNcBJcG$8!!%TMe,KILJ:N4NlMgiLJcFa0!!)fo!!)orrW)os!!*#uquHcs!!*#ur;clt!!*#u +quHcs!!*#u!!)ut!!*#uqZ$Zs!<2uu!<3#s!<3#t!<3#u!!3*"rr3'#s8N)us8E#trr<&urrE-" +r;cfr!!*#uquHcs!!*#ur;bpY!W[b$pA]X~> +MuNbWJcG$8!!%TMe,TCIJcG$8!!%TMir8uYJcF'rrW)ZlJ,~> +MuNcBJcG$8!!%TMe,TCIJcG$8!'l,8ir8uYJcF'rrW)ZlJ,~> +MuNcBJcG$8!!%TMe,KILJ:N4NlMgiLJcFa0!!%TMci4%HJ:R@nJ,~> +cMrFqVZ-VrJcF4!rW(7DJH3:^_uB]:JcF'rrW)ZlJ,~> +cMrFqVZ-VrJcF4!rW(7DJH3:^_uB]:JcF'rrW)ZlJ,~> +cMrFqVZ-VrJcF4!!W[b$cMrFq])]bK!!%TMci4%HJ:R@nJ,~> +cMmkEKDtoOVZ-VrJcF4!rW(7D!!%TM^&J'4_uB]:JcF'rrW)ZlJ,~> +cMmkEKDtoOVZ-VrJcF4!rW(7D!!%TM^&J'4_uB]:JcF'rrW)ZlJ,~> +cMmkEKDtoOVZ-VrJcF4!!W[b$cMmkEJcEF`!!'q:!!%TMci4%HJ:R@nJ,~> +cMmkEKDtoOVZ-VrJcF4!rW(7D!!%TM^&J'4_uB]:JcF'rrW)ZlJ,~> +cMmkEKDtoOVZ-VrJcF4!rW(7D!!%TM^&J'4_uB]:JcF'rrW)ZlJ,~> +cMmkEKDtoOVZ-VrJcF4!!W[b$cMmkEJcEF`!!'q:!!%TMci4%HJ:R@nJ,~> +cMmkEKE(oNW;chtJcF4!rW(7D!!%TM^&S'3`W#o +cMmkEKE(oNW;chtJcF4!rW(7D!!%TM^&S'3`W#o +cMmkEKDtuQJ:OTu!!%TMe,KILJ:PrF!!%TM^&J-6J:PW=!!%TMci4%HJ:R@nJ,~> +cMmkEli6b\eGoIIdf9:HW;chtJcF4!rW(7D!!(.@q#KRWrW!0&!!*'!!!)for;a&$rW'q;!!%TM +ci +cMmkEli6b\eGoIIdf9:HW;chtJcF4!rW(7D!!(.@q#KRWrW!0&!!*'!!!)for;a&$rW'q;!!%TM +ci +cMmkEli6b\eGoIIdf0@KJ:OTu!!%TMe,KILJ:PrF!!(.@q#KRWrW!0&!!*'!!!)for;a&$!W[b$ +`W#o +cMmkEkl:Y_f)PaMs8W*!rr3*$s8N'!p](6nj8])YW;chtJcF4!rW(7D!!(%=rrD-[rrDoqrrDlp +rrE&u"9AK%!!)cnrrBk7rW'q;!!)Qh!!(:DquH]qrW)6`!!'P/rW)ZlJ,~> +cMmkEkl:Y_f)PaMs8W*!rr3*$s8N'!p](6nj8])YW;chtJcF4!rW(7D!!(%=rrD-[rrDoqrrDlp +rrE&u"9AK%!!)cnrrBk7rW'q;!!)Qh!!(:DquH]qrW)6`!!'P/rW)ZlJ,~> +cMmkEkl:Y_f)PaMs8W*!rr3*$s8N'!p](6nj8T/\J:OTu!!%TMe,KILJ:PrF!!(%=rrD-[rrDoq +rrDlprrE&u"9AK%!!)cnrrBk7!W[b$`W#o +cMmkEkl:Y_rVult!ri9#r;cfrr;cltrW)osr;cisrW)uur;Zp!!!)rsr;cltr;cltr;cisr;c0` +rW&ns!!%TMe,TCIcMmkE`rH&=rVult!ri9#r;cfrr;cltrW)uuqu?s$!!*'!!!*#urW)uurrDus +r;cltr;cltr;cisr;aq=rW'q;!!)Qh!!)Ti!!(mU!!)ip!!*#u!!)?b!!'P/rW)ZlJ,~> +cMmkEkl:Y_rVult!ri9#r;cfrr;cltrW)osr;cisrW)uur;Zp!!!)rsr;cltr;cltr;cisr;c0` +rW&ns!!%TMe,TCIcMmkE`rH&=rVult!ri9#r;cfrr;cltrW)uuqu?s$!!*'!!!*#urW)uurrDus +r;cltr;cltr;cisr;aq=rW'q;!!)Qh!!)Ti!!(mU!!)ip!!*#u!!)?b!!'P/rW)ZlJ,~> +cMmkEkl:Y_rVult!ri9#r;cfrr;cltrW)osr;cisrW)uur;Zp!!!)rsr;cltr;cltr;cisr;c0` +!W[b$W;chtJcF4!!W[b$cMmkE`rH&=rVult!ri9#r;cfrr;cltrW)uuqu?s$!!*'!!!*#urW)uu +rrDusr;cltr;cltr;cisr;aq=!W[b$`W#oUEprr2ruli-qb\GlU1J:R@n +J,~> +cMmkEkl:Y_rVuisr;Zcss8W*!rVult#6+Z's8N'!r;Zcs#lal)s8N'!s8W&ur;Zcsqu?Zrs8W*! +rr;uukl:V^W;chtJcF4!rW(7D!!(%=rrE#trW)lrrrE*!rrE#trr<9'!!*'!!!*#urrE*!rrE*! +rr<0$!!*&u!;uls!;lfr!<<*!!<3#u!6,!;!6+s +cMmkEkl:Y_rVuisr;Zcss8W*!rVult#6+Z's8N'!r;Zcs#lal)s8N'!s8W&ur;Zcsqu?Zrs8W*! +rr;uukl:V^W;chtJcF4!rW(7D!!(%=rrE#trW)lrrrE*!rrE#trr<9'!!*'!!!*#urrE*!rrE*! +rr<0$!!*&u!;uls!;lfr!<<*!!<3#u!6,!;!6+s +cMmkEkl:Y_rVuisr;Zcss8W*!rVult#6+Z's8N'!r;Zcs#lal)s8N'!s8W&ur;Zcsqu?Zrs8W*! +rr;uukl1\aJ:OTu!!%TMe,KILJ:PrF!!(%=rrE#trW)lrrrE*!rrE#trr<9'!!*'!!!*#urrE*! +rrE*!rr<0$!!*&u!;uls!;lfr!<<*!!<3#u!6+s>!.]U=rr<&orr<&rrr<&us8E#ss8Duus8E#n +s8E#urrE-"rW)rt!!*#u!s&B$!<2uu!;ZWp!;uj!!<<'!rr2rurr;rtrr3-%rr<'!!<2uu!5/=5 +!.]Uns*t~> +cMmkEkl:Y_rVultqu?Zrs8W*!rVult"TJH%s8W&urVult#6+Z's8N'!rr;osrr;uuqu?Zrs8W*! +rr;rtl2U__W;chtJcF4!rW(7D!!(%=rrE#trrDrrrrE*!rrE#trr<9'!!*'!!!*#urrE*!rrE*! +rr<3%!!*'!r;cisrrDrrrrE*!rrE&urW't +cMmkEkl:Y_rVultqu?Zrs8W*!rVult"TJH%s8W&urVult#6+Z's8N'!rr;osrr;uuqu?Zrs8W*! +rr;rtl2U__W;chtJcF4!rW(7D!!(%=rrE#trrDrrrrE*!rrE#trr<9'!!*'!!!*#urrE*!rrE*! +rr<3%!!*'!r;cisrrDrrrrE*!rrE&urW't +cMmkEkl:Y_rVultqu?Zrs8W*!rVult"TJH%s8W&urVult#6+Z's8N'!rr;osrr;uuqu?Zrs8W*! +rr;rtl2LebJ:OTu!!%TMe,KILJ:PrF!!(%=rrE#trrDrrrrE*!rrE#trr<9'!!*'!!!*#urrE*! +rrE*!rr<3%!!*'!r;cisrrDrrrrE*!rrE&urW't +cMmkEkl:Y_rVultrVufrs8W*!rVucqrr;rtrr;lrs8W*!r;Z`rs8W*!rVufrs8W*!rVuislMph` +W;chtJcF4!rW(7D!!(%=rrE#trrE#tr;cltrrE#tquHcsrrE&urrE*!rrE*!quH]qrW)uurrE#t +r;cltrrE#trW("=rW'q;!!)fo!!)or!!)or!s&B$!;uis!;QQo!;uis!<)p"!<<'!rr2ruqu6Wr +q>UEpr;Qj!s8N)urr<&rrrW9$rrE#t!W`6#])Va0pA]X~> +cMmkEkl:Y_rVultrVufrs8W*!rVucqrr;rtrr;lrs8W*!r;Z`rs8W*!rVufrs8W*!rVuislMph` +W;chtJcF4!rW(7D!!(%=rrE#trrE#tr;cltrrE#tquHcsrrE&urrE*!rrE*!quH]qrW)uurrE#t +r;cltrrE#trW("=rW'q;!!)fo!!)or!!)or!s&B$!;uis!;QQo!;uis!<)p"!<<'!rr2ruqu6Wr +q>UEpr;Qj!s8N)urr<&rrrW9$rrE#t!W`6#])Va0pA]X~> +cMmkEkl:Y_rVultrVufrs8W*!rVucqrr;rtrr;lrs8W*!r;Z`rs8W*!rVufrs8W*!rVuislMgnc +J:OTu!!%TMe,KILJ:PrF!!(%=rrE#trrE#tr;cltrrE#tquHcsrrE&urrE*!rrE*!quH]qrW)uu +rrE#tr;cltrrE#trW("=!W[b$`W#o +cMmkEkl:Y_rVultrr;uu#6+Z's8N'!rVultqZ$Nps8W*!r;Zcsqu?Zrs8W*!rr;uu#6+Z's8N'! +r;Z`rli6qaW;chtJcF4!rW(7D!!(%=rrE#trrE&urr<9'!!*'!!!)utrrDusrrE&urrE*!rrE*! +rrDlprrE*!rrE&urr<9'!!*'!!!)rsrW(%>rW'q;!!)orq>gNp!!*#ur;cisrrE&u!!)fo!!)rs +!!)ut!s&B$!<2uu!;lcr!;uj!!<<'!r;Qj!s8N)urr<&us8;rtrr<&ts8E#0s8E#ls*t~> +cMmkEkl:Y_rVultrr;uu#6+Z's8N'!rVultqZ$Nps8W*!r;Zcsqu?Zrs8W*!rr;uu#6+Z's8N'! +r;Z`rli6qaW;chtJcF4!rW(7D!!(%=rrE#trrE&urr<9'!!*'!!!)utrrDusrrE&urrE*!rrE*! +rrDlprrE*!rrE&urr<9'!!*'!!!)rsrW(%>rW'q;!!)orq>gNp!!*#ur;cisrrE&u!!)fo!!)rs +!!)ut!s&B$!<2uu!;lcr!;uj!!<<'!r;Qj!s8N)urr<&us8;rtrr<&ts8E#0s8E#ls*t~> +cMmkEkl:Y_rVultrr;uu#6+Z's8N'!rVultqZ$Nps8W*!r;Zcsqu?Zrs8W*!rr;uu#6+Z's8N'! +r;Z`rli."dJ:OTu!!%TMe,KILJ:PrF!!(%=rrE#trrE&urr<9'!!*'!!!)utrrDusrrE&urrE*! +rrE*!rrDlprrE*!rrE&urr<9'!!*'!!!)rsrW(%>!W[b$`W#o +cMmkEkl:Y_rVultrr;uu#6+Z's8N'!rVultq>^Hps8W*!r;Zcss8N'!rr;uus8W*!rr;uu#6+Z' +s8N'!qu?Zrli6qaW;chtJcF4!rW(7D!!(%=rrE#trrE&urr<9'!!*'!!!)utrrDusrrE&urrE*! +rrE*!rrE#t!!*#urrE*!rrE&urr<9'!!*'!!!)orrrC.?rW'q;!!)fo!!)or!s&B$!<2uu!;uj! +!<<'!q#: +cMmkEkl:Y_rVultrr;uu#6+Z's8N'!rVultq>^Hps8W*!r;Zcss8N'!rr;uus8W*!rr;uu#6+Z' +s8N'!qu?Zrli6qaW;chtJcF4!rW(7D!!(%=rrE#trrE&urr<9'!!*'!!!)utrrDusrrE&urrE*! +rrE*!rrE#t!!*#urrE*!rrE&urr<9'!!*'!!!)orrrC.?rW'q;!!)fo!!)or!s&B$!<2uu!;uj! +!<<'!q#: +cMmkEkl:Y_rVultrr;uu#6+Z's8N'!rVultq>^Hps8W*!r;Zcss8N'!rr;uus8W*!rr;uu#6+Z' +s8N'!qu?Zrli."dJ:OTu!!%TMe,KILJ:PrF!!(%=rrE#trrE&urr<9'!!*'!!!)utrrDusrrE&u +rrE*!rrE*!rrE#t!!*#urrE*!rrE&urr<9'!!*'!!!)orrrC.?!W[b$`W#o +cMmkEkl:Y_rVultrVucqs8W#ts8W#ts8W#trVufrrr;rt!<;utrVuiss8Vuss8W&u!<;utlMph` +W;chtJcF4!rW(7D!!(%=rrE#trrE#tquHcsr;cltr;cltrrE&urrE*!rrE&ur;cltr;cfrrW)uu +quHcsrVurur;an +cMmkEkl:Y_rVultrVucqs8W#ts8W#ts8W#trVufrrr;rt!<;utrVuiss8Vuss8W&u!<;utlMph` +W;chtJcF4!rW(7D!!(%=rrE#trrE#tquHcsr;cltr;cltrrE&urrE*!rrE&ur;cltr;cfrrW)uu +quHcsrVurur;an +cMmkEkl:Y_rVultrVucqs8W#ts8W#ts8W#trVufrrr;rt!<;utrVuiss8Vuss8W&u!<;utlMgnc +J:OTu!!%TMe,KILJ:PrF!!(%=rrE#trrE#tquHcsr;cltr;cltrrE&urrE*!rrE&ur;cltr;cfr +rW)uuquHcsrVurur;an +cMmkEKE(oNW;chtJcF4!rW(7D!!%TM^&S'3`W#o +cMmkEKE(oNW;chtJcF4!rW(7D!!%TM^&S'3`W#o +cMmkEKDtuQJ:OTu!!%TMe,KILJ:PrF!!%TM^&J-6J:PW=!!)fo!!)or!!*#uqu?`srW)os!!)fo +rW)uu! +cMmkEKE(oNW;chtJcF4!rW(7D!!%TM^&S'3`W#odERq>UEpiVrlXU&Y)lpA]X~> +cMmkEKE(oNW;chtJcF4!rW(7D!!%TM^&S'3`W#odERq>UEpiVrlXU&Y)lpA]X~> +cMmkEKDtuQJ:OTu!!%TMe,KILJ:PrF!!%TM^&J-6J:PW=!!(jTr;cZn!!)!X!!&_m!W[b$pA]X~> +cMmkEKE(oNW;chtJcF4!rW(7D!!%TM^&S'3`W#o +cMmkEKE(oNW;chtJcF4!rW(7D!!%TM^&S'3`W#o +cMmkEKDtuQJ:OTu!!%TMe,KILJ:PrF!!%TM^&J-6J:PW=!!(FH!!(sWrrAho!W[b$pA]X~> +cMmkEKE(oNW;hDKcN)8j!!%TM^&S'3`W#o +cMmkEKE(oNW;hDKcN)8j!!%TM^&S'3`W#o +cMmkEKDtuQJ:OTuJH4'ts+&Dp!!%TM^&J-6J:PW=!!%TMci4%HJ:R@nJ,~> +cMmkEKE(oNV>l)HdK%Sm!!%TM^&S'3`W#o +cMmkEKE(oNV>l)HdK%Sm!!%TM^&S'3`W#o +cMmkEKDtuQJ:OKrJUl1!cMmkEJcEF`!W[b$`W#o +cMmkEKE(oNV>l)HdK%Sm!!%TM^&S'3`W#o +cMmkEKE(oNV>l)HdK%Sm!!%TM^&S'3`W#o +cMmkEKDtuQJ:OKrJUl1!cMmkEJcEF`!W[b$`W#o +cMmkEKE(oNJcF4!!!%TMo)A[iJcEF`rW'q;!!%TMci +cMmkEKE(oNJcF4!!'l,8o)A[iJcEF`rW'q;!!%TMci +cMmkEKDtuQJ:N4Ne,KD5JcG<@!!%TM^&J-6J:PW=!!%TMci4%HJ:R@nJ,~> +cMmkEKE(oNJcF4!!!%TMo)A[iJcEF`rW'q;!!)Qh!!(:D!!)Wj!!(pV!!((>rW)ZlJ,~> +cMmkEKE(oNJcF4!!'l,8o)A[iJcEF`rW'q;!!)Qh!!(:D!!)Wj!!(pV!!((>rW)ZlJ,~> +cMmkEKDtuQJ:N4Ne,KD5JcG<@!!%TM^&J-6J:PW=!!)Qh!!(:D!!)Wj!!(pV!!((>!W[b$pA]X~> +cMmkEKE(oNJcF7"!W`6#JcG?A!!%TM^&S'3`W#o +cMmkEKE(oNJcF7"!^QcNJcG?A!!%TM^&S'3`W#o +cMmkEKDtuQJ:N4NeGfS8s$2/8oD\djJcEF`!W[b$`W#o +ci8OrrW%NLeGfRMrr@WMoDa@A\H'VK!!)fo!!)or!!*#urW)osrVururW)`nrW)uu! +ci8OrrW%NLeGfS8s$2/8oDa@A\H'VK!!)fo!!)or!!*#urW)osrVururW)`nrW)uu! +ci8Or!W[b$JcF7"!^QcNJcG?AJH3:^s+&)g!!)fo!!)or!!*#urW)osrVururW)`nrW)uu! +cMmkEKE(oNJcF7"!W`6#JcG?A!!%TM^&S'3`W#o +cMmkEKE(oNJcF7"!^QcNJcG?A!!%TM^&S'3`W#o +cMmkEKDtuQJ:N4NeGfS8s$2/8oD\djJcEF`!W[b$`W#o +cMmkEKE(oNJcF:#!!*#u!!%TMo`"mkJcEF`rW'q;!!)fo!!)or!!)or!s&B$!;uis!;QQo!;uis +!<)p"!<<'!rr2ruq>UNss8N)urrW9$rrE#t!s&B$!<2uu!<3!"!<3&qrr<&urrW9$rrE&u"p"]' +!<<'!rr3'#s8N)Hs8E#ls*t~> +cMmkEKE(oNJcF:#!'pP`!'l,8o`"mkJcEF`rW'q;!!)fo!!)or!!)or!s&B$!;uis!;QQo!;uis +!<)p"!<<'!rr2ruq>UNss8N)urrW9$rrE#t!s&B$!<2uu!<3!"!<3&qrr<&urrW9$rrE&u"p"]' +!<<'!rr3'#s8N)Hs8E#ls*t~> +cMmkEKDtuQJ:N4Nec,V7rr2s`JcGBB!!%TM^&J-6J:PW=!!)fo!!)or!!)or!s&B$!;uis!;QQo +!;uis!<)p"!<<'!rr2ruq>UNss8N)urrW9$rrE#t!s&B$!<2uu!<3!"!<3&qrr<&urrW9$rrE&u +"p"]'!<<'!rr3'#s8N)HrrN1NJG0"n~> +cMmkEKE(oNJcF:#!!*#u!!%TMo`"mkJcEF`rW'q;!!)orq>gNp!!*#ur;cisrrE&u!!)fo!!)rs +!!)ut!s&B$!<2uu!;ZWs!<<'!rr3'#s8N)trrN3#s82lqrr<&prr<&urrW9$rrE&u"p"]'!<<'! +rr3'#s8N)Hs8E#ls*t~> +cMmkEKE(oNJcF:#!'pP`!'l,8o`"mkJcEF`rW'q;!!)orq>gNp!!*#ur;cisrrE&u!!)fo!!)rs +!!)ut!s&B$!<2uu!;ZWs!<<'!rr3'#s8N)trrN3#s82lqrr<&prr<&urrW9$rrE&u"p"]'!<<'! +rr3'#s8N)Hs8E#ls*t~> +cMmkEKDtuQJ:N4Nec,V7rr2s`JcGBB!!%TM^&J-6J:PW=!!)orq>gNp!!*#ur;cisrrE&u!!)fo +!!)rs!!)ut!s&B$!<2uu!;ZWs!<<'!rr3'#s8N)trrN3#s82lqrr<&prr<&urrW9$rrE&u"p"]' +!<<'!rr3'#s8N)HrrN1NJG0"n~> +cMmkEKE(oNJcF:#!!*#u!!%TMo`"mkJcEF`rW'q;!!)fo!!)or!s&B$!<2uu!;uj!!<<'!q#: +cMmkEKE(oNJcF:#!'pP`!'l,8o`"mkJcEF`rW'q;!!)fo!!)or!s&B$!<2uu!;uj!!<<'!q#: +cMmkEKDtuQJ:N4Nec,V7rr2s`JcGBB!!%TM^&J-6J:PW=!!)fo!!)or!s&B$!<2uu!;uj!!<<'! +q#: +cMmkEKE(oNJcF=$!!)rs!!%TMp&>!lJcEF`rW'q;!!)fo!!)or!s&B$!<2uu!;uj!!<<'!q#: +cMmkEKE(oNJcF=$!'pJ^!'l,8p&>!lJcEF`rW'q;!!)fo!!)or!s&B$!<2uu!;uj!!<<'!q#: +cMmkEKDtuQJ:N4Nf)G_8r;Qa^JcGEC!!%TM^&J-6J:PW=!!)fo!!)or!s&B$!<2uu!;uj!!<<'! +q#: +cMmkEKE(oNJcF=$!!)rs!!%TMp&>!lJcEF`rW'q;!!)fo!!)or!!*#uqu?`srW)os!!)forW)uu +!UNss8N)urr<&us8E!!rrE&ur;clt!!*#u!s&B$!<)rt!!3*"rr;uu#QFf( +rrE*!!<2uu!<2uu!7LoH!;?GC~> +cMmkEKE(oNJcF=$!'pJ^!'l,8p&>!lJcEF`rW'q;!!)fo!!)or!!*#uqu?`srW)os!!)forW)uu +!UNss8N)urr<&us8E!!rrE&ur;clt!!*#u!s&B$!<)rt!!3*"rr;uu#QFf( +rrE*!!<2uu!<2uu!7LoH!;?GC~> +cMmkEKDtuQJ:N4Nf)G_8r;Qa^JcGEC!!%TM^&J-6J:PW=!!)fo!!)or!!*#uqu?`srW)os!!)fo +rW)uu!UNss8N)urr<&us8E!!rrE&ur;clt!!*#u!s&B$!<)rt!!3*"rr;uu +#QFf(rrE*!!<2uu!<2uu!7LlK!.]Uns*t~> +cMmkEKE(oNJcF=$!!)rs!!%TMp&>!lJcEF`rW'q;!!(jTr;cZn!!)Wjr;bIL!!'_4rW)ZlJ,~> +cMmkEKE(oNJcF=$!'pJ^!'l,8p&>!lJcEF`rW'q;!!(jTr;cZn!!)Wjr;bIL!!'_4rW)ZlJ,~> +cMmkEKDtuQJ:N4Nf)G_8r;Qa^JcGEC!!%TM^&J-6J:PW=!!(jTr;cZn!!)Wjr;bIL!!'_4!W[b$ +pA]X~> +cMmkEKE(oNJcF@%!!)lq!!%TMpAY*mJcEF`rW'q;!!(FH!!(.@r;aM1rW)ZlJ,~> +cMmkEKE(oNJcF@%!'pD\!'l,8pAY*mJcEF`rW'q;!!(FH!!(.@r;aM1rW)ZlJ,~> +cMmkEKDtuQJ:N4NfDbh9qYpO\JcGHD!!%TM^&J-6J:PW=!!(FH!!(.@r;aM1!W[b$pA]X~> +cMmkEKE(oNJcF@%!!)lq!!%TMpAY*mJcEF`rW'q;!!%TMci +cMmkEKE(oNJcF@%!'pD\!'l,8pAY*mJcEF`rW'q;!!%TMci +cMmkEKDtuQJ:N4NfDbh9qYpO\JcGHD!!%TM^&J-6J:PW=!!%TMci4%HJ:R@nJ,~> +cMmkEKE(oNJcF@%!!)lq!!%TMpAY*mJcEF`rW'q;!!%TMci +cMmkEKE(oNJcF@%!'pD\!'l,8pAY*mJcEF`rW'q;!!%TMci +cMmkEKDtuQJ:N4NfDbh9qYpO\JcGHD!!%TM^&J-6J:PW=!!%TMci4%HJ:R@nJ,~> +cMmkEKE(oNJcFC&!!)fo!!%TMp\t3nJcEF`rW'q;!!%TMci +cMmkEKE(oNJcFC&!'p>Z!'l,8p\t3nJcEF`rW'q;!!%TMci +cMmkEKDtuQJ:N4Nf`(q:q#:=ZJcGKE!!%TM^&J-6J:PW=!!%TMci4%HJ:R@nJ,~> +cMmkEdf0:Ili-qbnG`Rjs8N)^rr<&qs8E"Ls4I>O!;QQo!.k1Err<&Irr<&brr<&grrW9$rrD6^ +!!'D+rW'q;!!%TMci +cMmkEdf0:Ili-qbnG`Rjs8N)^rr<&qs8E"Ls4I>O5kt?Z5_8t0rr<&Irr<&brr<&grrW9$rrD6^ +!!'D+rW'q;!!%TMci +cMmkEdf0:Ili-qbnG`Rjs8N)^rr<&qrrN1NJ:[aQrr>=Zrr><8s7cNn!7LlI!:0Xb!:^!j!<<'! +kPkM^[/U1-J:PW=!!%TMci4%HJ:R@nJ,~> +cMmkEli-qbp\t3no`"mkr;Q`srr2rup&>!lrVlitrr2rupAY*moD\djq#C6lr;Z`rJcFC&!!)fo +!!%TMp\t3nli-qbp\t3no`"mkr;Q`srr2rup&>!lrVlitrr2rupAY*moD\djq#C6l[f?=,`W#o< +JcF'rrW)ZlJ,~> +cMmkEli-qbp\t3no`"mkr;Q`srr2rup&>!lrVlitrr2rupAY*moD\djq#C6lr;Z`rJcFC&!'p>Z +!'l,8p\t3nli-qbp\t3no`"mkr;Q`srr2rup&>!lrVlitrr2rupAY*moD\djq#C6l[f?=,`W#o< +JcF'rrW)ZlJ,~> +cMmkEli-qbp\t3no`"mkr;Q`srr2rup&>!lrVlitrr2rupAY*moD\djq#C6lr;QfuJ:N4Nf`(q: +q#:=ZJcGKE!!)?b!!)cn!!)Zk!!)rs!!*#u!!)]l!!)ut!!*#u!!)`m!!)Wj!!)foquF2*!W[b$ +`W#o +cMmkEq#: +cMmkEq#: +cMmkEq#: +cMmkEq#: +cMmkEq#: +cMmkEq#: +cMmkEq#: +cMmkEq#: +cMmkEq#: +cMmkEqu?KmrVultrr2rurVufrs8N'!rVultqYpNqr;Q`srr2rurVlp!rrE&u!!*#u!s&B$!<2uu +!<2uu!<)p"!<<'!rVls"s8N)urrW9$rrE&uquHHjrW%NLe,KCJJcG<@!!)orq>gKorrE&u!!)ut +r;clt!!)utrrDoq!!)rs!!*#u!!)ut!W`6#rr2rurr3'#s8N)urr<&urr<&trrW9$rrE#t!s&B$ +!<3!#!<<'!rr;lrYlF\&`W(Jhb5h89J,~> +cMmkEqu?KmrVultrr2rurVufrs8N'!rVultqYpNqr;Q`srr2rurVlp!rrE&u!!*#u!s&B$!<2uu +!<2uu!<)p"!<<'!rVls"s8N)urrW9$rrE&uquHHjrW%NLe,KD5JcG<@!!)orq>gKorrE&u!!)ut +r;clt!!)utrrDoq!!)rs!!*#u!!)ut!W`6#rr2rurr3'#s8N)urr<&urr<&trrW9$rrE#t!s&B$ +!<3!#!<<'!rr;lrYlF\&`W(Jhb5h89J,~> +cMmkEqu?KmrVultrr2rurVufrs8N'!rVultqYpNqr;Q`srr2rurVlp!rrE&u!!*#u!s&B$!<2uu +!<2uu!<)p"!<<'!rVls"s8N)urrW9$rrE&uquHHj!W[b$JcF4!!'l,8o)A[iqu?KmrVultrr2ru +rVufrs8N'!rVultqYpNqr;Q`srr2rurVlp!rrE&u!!*#u!s&B$!<2uu!<2uu!<)p"!<<'!rVls" +s8N)urrW9$rrE&uquEu$!W[b$`W(JhblIcopA]X~> +cMmkEq#: +cMmkEq#: +cMmkEq#:^Qr!<2uu!<2uu!<)p"!<<'!rVls"s8N)u +rrW9$rrE&u!!',#!W[b$`W#o +cMmkEq#: +cMmkEq#: +cMmkEq#:<8s760i!;QQo!;QQr!<<'! +rr2rurr3'#s8N)rrr<&rrr<&srr<&urr<&trriE&!<<'!r;QfurrE&u!!*#u!!)ut"9AK%!!*#u +$3:,+!<<'!!<<'!rr2ruXT&>%J:PW=!!%TMci4%HJ:R@nJ,~> +cMmkEq#: +cMmkEq#:<8s760i!;QQo!;lfq!<)ot!<3#r +!<<'#!<<)u!<2uu!<3#r!<<'!!<2uu!<)p"!<<'!qYpTsrrE&u!s&B$!<)p#!<<'!s8E#ss8N'" +rrE&u!!*#ur;a)%rW'q;!!%TMci +cMmkEq#: +cMmkEKE(oNJcF4!!!%TMo)A[iJcEF`rW'q;!!%TMci +cMmkEKE(oNJcF4!!'l,8o)A[iJcEF`rW'q;!!%TMci +cMmkEKDtuQJ:N4Ne,KD5JcG<@!!%TM^&J-6J:PW=!!%TMci4%HJ:R@nJ,~> +cMmkEKE(oNJcF4!!!%TMo)A[iJcEF`rW'q;!!%TMci +cMmkEKE(oNJcF4!!'l,8o)A[iJcEF`rW'q;!!%TMci +cMmkEKDtuQJ:N4Ne,KD5JcG<@!!%TM^&J-6J:PW=!!%TMci4%HJ:R@nJ,~> +cMmkEKE(oNJcF4!!!%TMo)A[iJcEF`rW'q;!!%TMci +cMmkEKE(oNJcF4!!'l,8o)A[iJcEF`rW'q;!!%TMci +cMmkEKDtuQJ:N4Ne,KD5JcG<@!!%TM^&J-6J:PW=!!%TMci4%HJ:R@nJ,~> +cMmkEKE(oNJcF4!!!%TMo)A[iJcEF`rW'q;!!%TMci +cMmkEKE(oNJcF4!!'l,8o)A[iJcEF`rW'q;!!%TMci +cMmkEKDtuQJ:N4Ne,KD5JcG<@!!%TM^&J-6J:PW=!!%TMci4%HJ:R@nJ,~> +cMmkEKE(oNJcF4!!!%TMo)A[iJcEF`rW'q;!!%TMci +cMmkEKE(oNJcF4!!'l,8o)A[iJcEF`rW'q;!!%TMci +cMmkEKDtuQJ:N4Ne,KD5JcG<@!!%TM^&J-6J:PW=!!%TMci4%HJ:R@nJ,~> +cMmkEKE(oNJcF4!!!%TMo)A[iJcEF`rW'q;!!%TMci +cMmkEKE(oNJcF4!!'l,8o)A[iJcEF`rW'q;!!%TMci +cMmkEKDtuQJ:N4Ne,KD5JcG<@!!%TM^&J-6J:PW=!!%TMci4%HJ:R@nJ,~> +cMmkEKE(oNPlH:7!!';(!!%TMr;Q`sli-qbnG`Rjs8N)fs8E#;rr<%Ms3L`E!;?GC~> +cMmkEKE(oNPlH:7!!';(!!%TMr;Q`sli-qbnG`Rjs8N)fs8E#;rr<%Ms3L`E!;?GC~> +cMmkEKDtuQJ:NmaJH,ZMZ2Xe(JcGZJ!!)?b!!)Ng!s&B$!:Tph!.]U=rr<%Ms3L]H!.]Uns*t~> +cMmkEKE(oNPlC^`K)YfNZ2Xe(bl7YCh>[HTo)A[ikPkM^r;Q`srr2rup&>!lrVlitrr2rupAY*m +oD\djrr;rt`W#o +cMmkEKE(oNPlC^`K)YfNZ2Xe(bl7YCh>[HTo)A[ikPkM^r;Q`srr2rup&>!lrVlitrr2rupAY*m +oD\djrr;rt`W#o +cMmkEKDtuQJ:Nma!!%WN!!';(!!(7C!!(jT!!)Ti!!)3^!!)rs!!*#u!!)]l!!)ut!!*#u!!)`m +!!)Wj!!*#u!W[b$`W#o +cMmkEKE(oNPlC^`K)YfNZ2Xe(q#: +cMmkEKE(oNPlC^`K)YfNZ2Xe(q#: +cMmkEKDtuQJ:Nma!!%WN!!';(!!)fo!!)lqrW)uu!!*#u%06G.!<3$!rrE'!!<<)u!<3!%!<3$! +s8W&uq#C +cMmkEKE(oNPlC^`K)bfMZi:"*q#: +cMmkEKE(oNPlC^`K)bfMZi:"*q#: +cMmkEKDtuQJ:Nma!!%WN!W[b$Zi:"*q#: +ci8OrrW&2_!!)$Yq#KUXr;an +ci8OrrW&2_!!)$Yq#KUXr;an +ci8Or!W[b$PlC^`irAfSkPtJ\a8Z2@J:P!+!!)fo!!)or!!)rs!!*#u!s&B$!<)ot!<)ot!<3!# +!<<'!rr3'#s8N)orr<&urrW9$rrE&u!W`6#rr3'#s8N)urrW9$rrE&u!!)`m!!*#u!!)ut!!*#u +!s&B$!<2uu!;lcr!;uis!<2uu!<2uu!<3!#!<<'!rr3'#s8N)urr<&urr<&trrW9$rrE#t!s&B$ +!<3!#!<<'!rr3$"J:PW=!!%TMci4%HJ:R@nJ,~> +cMmkEKE(oNPlC^`huE]Vjo>>\rr3*$s8N'!p](6nf`1pNZi:"*qu?Kmrr2rur;Q`srr3'#s8N)t +rr<&ts82lsrr<&urrW9$rrDioquH`r"9AH%s8Vuss8N'!rr3'#s8N)ts8N)orr<&urr<&ts82ls +s82lorr<&srr<&urr<&trrN3#!<2uu!<3!#!<<'!rr2rurr2rurVls"s8N)trrW9$rrE&u!s&B$ +!<3#t!6+s +cMmkEKE(oNPlC^`huE]Vjo>>\rr3*$s8N'!p](6nf`1pNZi:"*qu?Kmrr2rur;Q`srr3'#s8N)t +rr<&ts82lsrr<&urrW9$rrDioquH`r"9AH%s8Vuss8N'!rr3'#s8N)ts8N)orr<&urr<&ts82ls +s82lorr<&srr<&urr<&trrN3#!<2uu!<3!#!<<'!rr2rurr2rurVls"s8N)trrW9$rrE&u!s&B$ +!<3#t!6+s +cMmkEKDtuQJ:Nma!!(pVrrD0\rrE&u"9AK%!!)cnrrC^O!W[b$Zi:"*qu?Kmrr2rur;Q`srr3'# +s8N)trr<&ts82lsrr<&urrW9$rrDioquH`r"9AH%s8Vuss8N'!rr3'#s8N)ts8N)orr<&urr<&t +s82lss82lorr<&srr<&urr<&trrN3#!<2uu!<3!#!<<'!rr2rurr2rurVls"s8N)trrW9$rrE&u +!s&B$!<3!"!.]U=rr<%Ms3L]H!.]Uns*t~> +cMmkEKE(oNPlC^`huE]VrVult!ri9#r;cfrr;cltrW)uurrDusr;cltr;cltr;cisr;bdUrW';) +!!)fo!!)or!!)rs!!*#u!s&B$!<)ot!<)ot!;lcr!<3!#!<<'!q#:UEprr2rurVlitqu6Wrp\t3nr;Q`srr2rurVm!#rrE*!q>^Qr!<2uu!<2uu!<)p" +!<<'!rVls"s8N)urrW9$rrE&urW'q;!!%TMci +cMmkEKE(oNPlC^`huE]VrVult!ri9#r;cfrr;cltrW)uurrDusr;cltr;cltr;cisr;bdUrW';) +!!)fo!!)or!!)rs!!*#u!s&B$!<)ot!<)ot!;lcr!<3!#!<<'!q#:UEprr2rurVlitqu6Wrp\t3nr;Q`srr2rurVm!#rrE*!q>^Qr!<2uu!<2uu!<)p" +!<<'!rVls"s8N)urrW9$rrE&urW'q;!!%TMci +cMmkEKDtuQJ:Nma!!(pVrrE#trr<-#!<;utrVufrs8W&us8W*!r;Z]qs8W#ts8W#trr;osi;WiY +J:P!+!!)fo!!)or!!)rs!!*#u!s&B$!<)ot!<)ot!;lcr!<3!#!<<'!q#:UEprr2rurVlitqu6Wrp\t3nr;Q`srr2rurVm!#rrE*!q>^Qr!<2uu!<2uu +!<)p"!<<'!rVls"s8N)urrW9$rrE&u!W[b$`W#o +cMmkEKE(oNPlC^`huE]VrVuisr;Zcss8W*!rVult"9/?$s8E#rs8N)rs8N*!s8N)us8N)Ts8E#) +rr<&orr<&rrr<&srs&Q(rr<'!rrE#t!!)ut!!)or!!*#u!s&B$!;QQo!;c^!!<3'!rrDrr!!*#u +!s&B$!;lcr!;ZWp!<2uu!<)ot!;lcr!;HKn!;uis!<2uu!<)p$!<3'!rrDus!W`6#rr2rurr2ru +rVm!#s8N'!rr3<*s8N*!rr<'!rrE&urW'q;!!%TMci +cMmkEKE(oNPlC^`huE]VrVuisr;Zcss8W*!rVult"9/?$s8E#rs8N)rs8N*!s8N)us8N)Ts8E#) +rr<&orr<&rrr<&srs&Q(rr<'!rrE#t!!)ut!!)or!!*#u!s&B$!;QQo!;c^!!<3'!rrDrr!!*#u +!s&B$!;lcr!;ZWp!<2uu!<)ot!;lcr!;HKn!;uis!<2uu!<)p$!<3'!rrDus!W`6#rr2rurr2ru +rVm!#s8N'!rr3<*s8N*!rr<'!rrE&urW'q;!!%TMci +cMmkEKDtuQJ:Nma!!(pVrrE#trW)lrrrE*!rrE#trr<0$!!*&u!;uls!;lfr!<<*!!<3#u!8[YV +!.]U+rr<&orr<&rrr<&srs&Q(rr<'!rrE#t!!)ut!!)or!!*#u!s&B$!;QQo!;c^!!<3'!rrDrr +!!*#u!s&B$!;lcr!;ZWp!<2uu!<)ot!;lcr!;HKn!;uis!<2uu!<)p$!<3'!rrDus!W`6#rr2ru +rr2rurVm!#s8N'!rr3<*s8N*!rr<'!rrE&u!W[b$`W#o +cMmkEKE(oNPlC^`huE]VrVultqu?Zrs8W*!rVult"TJH%s8W#trr;uuqu?Zrs8W*!rr;rthZ*QT +Zi:"*q#: +cMmkEKE(oNPlC^`huE]VrVultqu?Zrs8W*!rVult"TJH%s8W#trr;uuqu?Zrs8W*!rr;rthZ*QT +Zi:"*q#: +cMmkEKDtuQJ:Nma!!(pVrrE#trrDrrrrE*!rrE#trr<3%!!*'!r;cisrrDrrrrE*!rrE&urW(gT +!W[b$Zi:"*q#: +cMmkEKE(oNPlC^`huE]VrVultrVufrs8W*!rVucqrVuiss8W*!rVufrs8W*!rVuishuEZUZi:"* +aT)2=gAh*OV>pMp`W#o +cMmkEKE(oNPlC^`huE]VrVultrVufrs8W*!rVucqrVuiss8W*!rVufrs8W*!rVuishuEZUZi:"* +aT)2=gAh*OV>pMp`W#o +cMmkEKDtuQJ:Nma!!(pVrrE#trrE#tr;cltrrE#tquH]qrW)uurrE#tr;cltrrE#trW(jU!W[b$ +Zi:"*aT)2=gAh*OV>gSsJ:PW=!!%TMci4%HJ:R@nJ,~> +cMmkEKE(oNPlC^`huE]VrVultrr;uu#6+Z's8N'!rVultq>^Hps8W*!rr;uu#6+Z's8N'!r;Z`r +i;`cVZi:"*JcEF`rW'q;!!%TMci +cMmkEKE(oNPlC^`huE]VrVultrr;uu#6+Z's8N'!rVultq>^Hps8W*!rr;uu#6+Z's8N'!r;Z`r +i;`cVZi:"*JcEF`rW'q;!!%TMci +cMmkEKDtuQJ:Nma!!(pVrrE#trrE&urr<9'!!*'!!!)utrrDlprrE*!rrE&urr<9'!!*'!!!)rs +rW(mV!W[b$Zi:"*JcEF`!W[b$`W#o +cMmkEKE(oNPlC^`huE]VrVultrr;uu#6+Z's8N'!rVultrVlitrr;uus8W*!rr;uu#6+Z's8N'! +qu?Zri;`cVZi:"*JcEF`rW'q;!!%TMci +cMmkEKE(oNPlC^`huE]VrVultrr;uu#6+Z's8N'!rVultrVlitrr;uus8W*!rr;uu#6+Z's8N'! +qu?Zri;`cVZi:"*JcEF`rW'q;!!%TMci +cMmkEKDtuQJ:Nma!!(pVrrE#trrE&urr<9'!!*'!!!)utrrE#t!!*#urrE*!rrE&urr<9'!!*'! +!!)orrrD!W!W[b$Zi:"*JcEF`!W[b$`W#o +cMmkEKE(oNPlC^`huE]VrVultrVucqs8W#ts8W#ts8W#trVuiss8Vuss8W&u!<;uthuEZUZi:"* +JcEF`rW'q;!!%TMci +cMmkEKE(oNPlC^`huE]VrVultrVucqs8W#ts8W#ts8W#trVuiss8Vuss8W&u!<;uthuEZUZi:"* +JcEF`rW'q;!!%TMci +cMmkEKDtuQJ:Nma!!(pVrrE#trrE#tquHcsr;cltr;cltr;cfrrW)uuquHcsrVurur;baT!W[b$ +Zi:"*JcEF`!W[b$`W#o +cMmkEKE(oNPlC^`K)bfMZi:"*JcEF`rW'q;!!%TMci +cMmkEKE(oNPlC^`K)bfMZi:"*JcEF`rW'q;!!%TMci +cMmkEKDtuQJ:Nma!!%WN!W[b$Zi:"*JcEF`!W[b$`W#o +cMmkEKE(oNPlC^`K)bfMZi:"*JcEF`rW'q;!!%TMci +cMmkEKE(oNPlC^`K)bfMZi:"*JcEF`rW'q;!!%TMci +cMmkEKDtuQJ:Nma!!%WN!W[b$Zi:"*JcEF`!W[b$`W#o +cMmkEKE(oNPlC^`K)bfMZi:"*JcFR+!!)?b!!)iprW'q;!!%TMci +cMmkEKE(oNPlC^`K)bfMZi:"*JcFR+!!)?b!!)iprW'q;!!%TMci +cMmkEKDtuQJ:Nma!!%WN!W[b$Zi:"*JcFR+!!)?b!!)ip!W[b$`W#o +cMmkEKE(oNPlC^`K)bfMZi:"*bl7YCh>[HTq#:[HTkPkM^r;Q`srr2rup&>!lrVlitrr;rt +`W#o +cMmkEKE(oNPlC^`K)bfMZi:"*bl7YCh>[HTq#:[HTkPkM^r;Q`srr2rup&>!lrVlitrr;rt +`W#o +cMmkEKDtuQJ:Nma!!%WN!W[b$Zi:"*bl7YCh>[HTq#:[HTkPkM^r;Q`srr2rup&>!lrVlit +rr3$"J:PW=!!%TMci4%HJ:R@nJ,~> +cMmkEKE(oNPlC^`K)bfMZi:"*q#: +cMmkEKE(oNPlC^`K)bfMZi:"*q#: +cMmkEKDtuQJ:Nma!!%WN!W[b$Zi:"*q#: +cMmkEKE(oNPlC^`K)bfMZi:"*q#: +cMmkEKE(oNPlC^`K)bfMZi:"*q#: +cMmkEKDtuQJ:Nma!!%WN!W[b$Zi:"*q#: +cMmkEKE(oNPlC^`K)bfMZi:"*q#: +cMmkEKE(oNPlC^`K)bfMZi:"*q#: +cMmkEKDtuQJ:Nma!!%WN!W[b$Zi:"*q#: +cMmkEKE(oNPlC^`K)bfMZi:"*qu?Kmrr2rur;Q`srr3'#s8N)trr<&ts82lsrr<&urrW9$rrDio +quH`r"9AH%s8Vuss8N'!rr3'#s8N)orr<&urr<&urrN3#!<)p!!<<)s!<3#u!;QQo!<2uu!<)rq +!<<)s!;lcr!;uis!<2uu!<)p!!<3&urr<&urrW9$rrE&urW'q;!!%TMci +cMmkEKE(oNPlC^`K)bfMZi:"*qu?Kmrr2rur;Q`srr3'#s8N)trr<&ts82lsrr<&urrW9$rrDio +quH`r"9AH%s8Vuss8N'!rr3'#s8N)orr<&urr<&urrN3#!<)p!!<<)s!<3#u!;QQo!<2uu!<)rq +!<<)s!;lcr!;uis!<2uu!<)p!!<3&urr<&urrW9$rrE&urW'q;!!%TMci +cMmkEKDtuQJ:Nma!!%WN!W[b$Zi:"*qu?Kmrr2rur;Q`srr3'#s8N)trr<&ts82lsrr<&urrW9$ +rrDioquH`r"9AH%s8Vuss8N'!rr3'#s8N)orr<&urr<&urrN3#!<)p!!<<)s!<3#u!;QQo!<2uu +!<)rq!<<)s!;lcr!;uis!<2uu!<)p!!<3&urr<&urrW9$rrE&u!W[b$`W#o +cMmkEKE(oNPlC^`K)bfMZi:"*q#: +cMmkEKE(oNPlC^`K)bfMZi:"*q#: +cMmkEKDtuQJ:Nma!!%WN!W[b$Zi:"*q#: +cMmkEKE(oNPlH:7rW';)!!)fo!!)or!!)rs#6=f(!!*'!!<)ot!<)ot!;lcr!<3!#!<<'!q#: +cMmkEKE(oNPlH:7rW';)!!)fo!!)or!!)rs#6=f(!!*'!!<)ot!<)ot!;lcr!<3!#!<<'!q#: +cMmkEKDtuQJ:NmaJH,`OJ:P!+!!)fo!!)or!!)rs#6=f(!!*'!!<)ot!<)ot!;lcr!<3!#!<<'! +q#: +cMmkEKE(oNPlC^`K)bfMZi:"*q#: +cMmkEKE(oNPlC^`K)bfMZi:"*q#: +cMmkEKDtuQJ:Nma!!%WN!W[b$Zi:"*q#: +cMmkEKE(oNPlC^`K)bfMZi:"*aT)2=i;``Uq>UEprr2rumJm+b`;ff:`W#o +cMmkEKE(oNPlC^`K)bfMZi:"*aT)2=i;``Uq>UEprr2rumJm+b`;ff:`W#o +cMmkEKDtuQJ:Nma!!%WN!W[b$Zi:"*aT)2=i;``Uq>UEprr2rumJm+b`;]l=J:PW=!!%TMci4%H +J:R@nJ,~> +cMmkEKE(oNPlC^`K)bfMZi:"*R/[-drVlitXT/8"`W#o +cMmkEKE(oNPlC^`K)bfMZi:"*R/[-drVlitXT/8"`W#o +cMmkEKDtuQJ:Nma!!%WN!W[b$Zi:"*R/[-drVlitXT&>%J:PW=!!%TMci4%HJ:R@nJ,~> +cMmkEKE(oNPlC^`K)bfMZi:"*JcEF`rW'q;!!%TMci +cMmkEKE(oNPlC^`K)bfMZi:"*JcEF`rW'q;!!%TMci +cMmkEKDtuQJ:Nma!!%WN!W[b$Zi:"*JcEF`!W[b$`W#o +cMmkEKE(oNPlC^`K)bfMZi:"*JcEF`rW'q;!!%TMci +cMmkEKE(oNPlC^`K)bfMZi:"*JcEF`rW'q;!!%TMci +cMmkEKDtuQJ:Nma!!%WN!W[b$Zi:"*JcEF`!W[b$`W#o +cMmkEKE(oNPlC^`K)bfMZi:"*JcEF`rW'q;!!%TMci +cMmkEKE(oNPlC^`K)bfMZi:"*JcEF`rW'q;!!%TMci +cMmkEKDtuQJ:Nma!!%WN!W[b$Zi:"*JcEF`!W[b$`W#o +cMmkEKE(oNPlC^`K)bfMZi:"*JcEF`rW'q;!!%TMci +cMmkEKE(oNPlC^`K)bfMZi:"*JcEF`rW'q;!!%TMci +cMmkEKDtuQJ:Nma!!%WN!W[b$Zi:"*JcEF`!W[b$`W#o +cMmkEKE(oNPlC^`K)bfMZi:"*JcEF`rW'q;!!%TMci +cMmkEKE(oNPlC^`K)bfMZi:"*JcEF`rW'q;!!%TMci +cMmkEKDtuQJ:Nma!!%WN!W[b$Zi:"*JcEF`!W[b$`W#o +cMmkEKE(oNPlC^`K)bfMZi:"*JcEF`rW'q;!!%TMci +cMmkEKE(oNPlC^`K)bfMZi:"*JcEF`rW'q;!!%TMci +cMmkEKDtuQJ:Nma!!%WN!W[b$Zi:"*JcEF`!W[b$`W#o +cMmkEKE(oNPlC^`K)bfMZi:"*JcEF`rW'q;!!%TMci +cMmkEKE(oNPlC^`K)bfMZi:"*JcEF`rW'q;!!%TMci +cMmkEKDtuQJ:Nma!!%WN!W[b$Zi:"*JcEF`!W[b$`W#o +cMmkEKE(oNPlC^`K)bfMZi:"*JcEF`rW'q;!!%TMci +cMmkEKE(oNPlC^`K)bfMZi:"*JcEF`rW'q;!!%TMci +cMmkEKDtuQJ:Nma!!%WN!W[b$Zi:"*JcEF`!W[b$`W#o +cMmkEKE(oNPlC^`df0:Ili-qbnG`Rjs8N)^rr<&ps8E#)s+(0\!6+s +cMmkEKE(oNPlC^`df0:Ili-qbnG`Rjs8N)^rr<&ps8E#)s+(0\!6+s +cMmkEKDtuQJ:Nma!!(II!!)?b!!)Ng!s&B$!9a@^!;ZWr!.]U+s+(0^!<7Sgrr<%Ms3L]H!.]Un +s*t~> +cMmkEKE(oNPlC^`li-qbp\t3no`"mkr;Q`srr2rup&>!lrVlitrr2rupAY*moD\djq#C6lqu?Wq +Zi:"*JcEF`rW'q;!!%TMci +cMmkEKE(oNPlC^`li-qbp\t3no`"mkr;Q`srr2rup&>!lrVlitrr2rupAY*moD\djq#C6lqu?Wq +Zi:"*JcEF`rW'q;!!%TMci +cMmkEKDtuQJ:Nma!!)?b!!)cn!!)Zk!!)rs!!*#u!!)]l!!)ut!!*#u!!)`m!!)Wj!!)foquHWo +!W[b$Zi:"*JcEF`!W[b$`W#o +cMmkEKE(oNPlC^`q#: +cMmkEKE(oNPlC^`q#: +cMmkEKDtuQJ:Nma!!)fo!!)lqrVururW)rtrW)uurW)rtrW)uu!!*#u!!)utrVururW!!!!;uj% +!<<'!rrE*!rVururW)uu#lsu*!<3'!rrE)u!<2uu!<3!!!<<#urr;rtrr;uuq>UKrJ:P!+!!%TM +^&J-6J:PW=!!%TMci4%HJ:R@nJ,~> +cMrFqrrA;`!!)fo!!)or!!)rs!!*#u!!*#u!s&B$!<2uu!;QQo!;uis!<2uu!<2uu!<2uu!<3!" +!<3&urr<&urr<&us8N)urr`?%rr<&urrW9$rrE&u!s&B$!<2uu!<3!%!<<'!rrDoqrW';)!!%TM +^&S'3`W#o +cMrFqrrA;`!!)fo!!)or!!)rs!!*#u!!*#u!s&B$!<2uu!;QQo!;uis!<2uu!<2uu!<2uu!<3!" +!<3&urr<&urr<&us8N)urr`?%rr<&urrW9$rrE&u!s&B$!<2uu!<3!%!<<'!rrDoqrW';)!!%TM +^&S'3`W#o +cMrFqs+$@6!!)fo!!)or!!)rs!!*#u!!*#u!s&B$!<2uu!;QQo!;uis!<2uu!<2uu!<2uu!<3!" +!<3&urr<&urr<&us8N)urr`?%rr<&urrW9$rrE&u!s&B$!<2uu!<3!%!<<'!rrDoq!W[b$Zi:"* +JcEF`!W[b$`W#o +bQ!.oPlC^`q#: +bQ!.oPlC^`q#: +bQ!0EPlC^`q#: +bQ!.oPlC^`qu?KmrVultrr2rurVufrs8N'!rVultqYpNqr;Q`srr2rurVlp!rrE&u!!*#u!s&B$ +!<2uu!<2uu!<)p"!<<'!rVls"s8N)urrW9$rrE&uquHEirW';)!!%TM^&S'3`W#o +bQ!.oPlC^`qu?KmrVultrr2rurVufrs8N'!rVultqYpNqr;Q`srr2rurVlp!rrE&u!!*#u!s&B$ +!<2uu!<2uu!<)p"!<<'!rVls"s8N)urrW9$rrE&uquHEirW';)!!%TM^&S'3`W#o +bQ!0EPlC^`qu?KmrVultrr2rurVufrs8N'!rVultqYpNqr;Q`srr2rurVlp!rrE&u!!*#u!s&B$ +!<2uu!<2uu!<)p"!<<'!rVls"s8N)urrW9$rrE&uquHEi!W[b$Zi:"*JcEF`!W[b$`W#o +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu;JBi]= +s*t~> +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$huA6-rW';)!!%TM^&S'3JcCE'J,~> +JcC<$huA6-rW';)!!%TM^&S'3JcCE'J,~> +JcC<$huA6-!W[b$Zi:"*JcEF`!W[b$JcCE'J,~> +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$huRV\H%3\K`?Q~> +JcC<$huRV\H%3\K`?Q~> +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$hu +JcC<$huA6-rW%NLJcC<$JcG0 +JcC<$huA6-rW%NLJcC<$JcG0 +JcC<$huA6-!W[b$JcC<$JcC<$mf.e~> +JcC<$h#Dp*JcC<$JcC<$mf.e~> +JcC<$h#Dp*JcC<$JcC<$mf.e~> +JcC<$h#DqUJcC<$JcC<$mf.e~> +JcC<$h#Dp*JcC<$JcC<$mf.e~> +JcC<$h#Dp*JcC<$JcC<$mf.e~> +JcC<$h#DqUJcC<$JcC<$mf.e~> +JcC<$JcC<$JcC<$JcEjlJ,~> +JcC<$JcC<$JcC<$JcEjlJ,~> +JcC<$JcC<$JcC<$JcEjlJ,~> +%%EndData +showpage +%%Trailer +end +%%EOF diff --git a/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/docbook/lttv-context.png b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/docbook/lttv-context.png new file mode 100644 index 00000000..a5b3d709 Binary files /dev/null and b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/docbook/lttv-context.png differ diff --git a/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/Makefile.am b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/Makefile.am new file mode 100644 index 00000000..a2b99fd3 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = c18.html c40.html c67.html index.html lttv-context.png x23.html x33.html x46.html x50.html x72.html x77.html x81.html x84.html diff --git a/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/c18.html b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/c18.html new file mode 100644 index 00000000..f8687fe2 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/c18.html @@ -0,0 +1,149 @@ + +Linux Trace Toolkit Viewer Text Module Tutorial

Linux Trace Toolkit Viewer Developer Guide
PrevNext

Chapter 1. Linux Trace Toolkit Viewer Text Module Tutorial

1.1. Introduction

This chapter explains all the steps that are necessary to create a text module +in LTTV. +


PrevHomeNext
Linux Trace Toolkit Viewer Developer Guide A typical module
\ No newline at end of file diff --git a/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/c40.html b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/c40.html new file mode 100644 index 00000000..a21da877 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/c40.html @@ -0,0 +1,155 @@ + +How to use the Linux Trace Toolkit Viewer's Reading Context
Linux Trace Toolkit Viewer Developer Guide
PrevNext

Chapter 2. How to use the Linux Trace Toolkit Viewer's Reading Context

2.1. Introduction

This chapter describes how to use the Linux Trace Toolkit reading context, a +data structure that is given as call data parameter of the modules'callbacks. +

Linux Trace Toolkit Viewer provides a backend that reads the traces. In combines +them in tracesets. A trace is an abstaction over many tracefiles, one per CPU. +LTTV reads the whole trace together, providing the events to modules by calling +their pre-registered hook lists in a chronological order. +


PrevHomeNext
The hooks Why an event driven trace reader ?
\ No newline at end of file diff --git a/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/c67.html b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/c67.html new file mode 100644 index 00000000..656c37ef --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/c67.html @@ -0,0 +1,152 @@ + +Linux Trace Toolkit Viewer Graphical Module Tutorial
Linux Trace Toolkit Viewer Developer Guide
PrevNext

Chapter 3. Linux Trace Toolkit Viewer Graphical Module Tutorial

3.1. Introduction

As a matter of fact, most of the things said for the text modules still hold for +the graphical modules. However, the fact that every module must instanciate +objects (called viewers) more than once changes a little bit the scenario. It is +then impossible to use static structures : everything must be instanciated at +run-time, except the structures related to the module itself. +


PrevHomeNext
Using the reading context The static part of a module
\ No newline at end of file diff --git a/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/index.html b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/index.html new file mode 100644 index 00000000..5dfa59c9 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/index.html @@ -0,0 +1,250 @@ + +Linux Trace Toolkit Viewer Developer Guide

Linux Trace Toolkit Viewer Developer Guide

Mathieu Desnoyers

This document describes the basic steps necessary to develop within the +Linux Trace Toolkit Viewer project. + +



  Next
  Linux Trace Toolkit Viewer Text Module Tutorial
\ No newline at end of file diff --git a/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/lttv-context.png b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/lttv-context.png new file mode 100644 index 00000000..a5b3d709 Binary files /dev/null and b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/lttv-context.png differ diff --git a/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x23.html b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x23.html new file mode 100644 index 00000000..fdbff006 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x23.html @@ -0,0 +1,200 @@ + +A typical module
Linux Trace Toolkit Viewer Developer Guide
PrevChapter 1. Linux Trace Toolkit Viewer Text Module TutorialNext

1.2. A typical module

A typical module must have a init() and destroy() function. Please refer to +lttv/modules/text/textDump.c for the detail of these functions. +

The init() function is called when the library is loaded and destroy() +inversely. It adds options to the command line by calling "lttv_option_add" from +option.h +

The module communicates with the main lttv program through the use of global +attributes. Use lttv/attribute.h, lttv/iattribute.h and lttv/lttv.h, and then +LTTV_IATTRIBUTE(lttv_global_attributes()) to get the pointer to these +global attributes. +

You can then add your hooks (functions that follows the prototype of a hook, as +defined in lttv/hook.h) in the different hook lists defined in lttv/lttv.h. Note +that hooks have an assigned priority. This is necessary to inform the trace +reader that a specific hook needs to be called, for example, before or after the +state update done for an event by the state module. For that specific example, a +hook could use the LTTV_PRIO_STATE-5 to get called before the state update and a +second hook could use the LTTV_PRIO_STATE+5 to get called after the state +update. This is especially important for graphical module, which is the subject +of a the chapter named "Linux Trace Toolkit Viewer Graphical Module Tutorial". +

You should also take a look at lttv/state.c, where by_id hooks are used. When +you only need some specific events, you should use this interface. It makes the +event filtering sooner in the dispatch chain : you hook doesn't have to be +called for each event, only the ones selected. That improves the performances a +lot! +

Note that you should use the lttv_trace_find_hook method from +lttv/tracecontext.h to connect the hook to the right facility/event type. See +state.c for an example. A problem that may arise is that the LttvTraceHook +structure must be passed as hook_data when registering the hook. In fact, it is +not necessary for it to be directly passed as the hook_data parameter. As long +as the hook function can access the LttvTraceHook fields necessary to parse the +LttEvent, there is no problem. In a complex viewer where you need a pointer to +your own data structure, just keep a pointer to the LttvTraceHook structure +inside your own data structure, and give to pointer to your data structure in +parameter as the hook_data. +

Then, you should use the macro LTTV_MODULE, defined in lttv/module.h. It allows +you to specify the module name, a short and a long description, the init and +destroy functions and the module dependencies. That permits to the module +backend to load the right dependencies when needed. +

A typical text module will depend on batchAnalysis for the batch computation of a +trace, and simply register before and after trace hooks, as weel as the most +important one : a event hook. +


PrevHomeNext
Linux Trace Toolkit Viewer Text Module TutorialUpThe hooks
\ No newline at end of file diff --git a/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x33.html b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x33.html new file mode 100644 index 00000000..754136b9 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x33.html @@ -0,0 +1,184 @@ + +The hooks
Linux Trace Toolkit Viewer Developer Guide
PrevChapter 1. Linux Trace Toolkit Viewer Text Module TutorialNext

1.3. The hooks

The before and after trace hooks only exists to be able to generate a report at +the end of a trace computation. The effective computation is done by the event +hooks. +

These hooks does particular computation on data arriving as argument, a +call_data. The type of the call_data, when a hook is called during the trace +read, is a traceset context. It contains all the necessary information about the +read in progress. This is the base class from which inherits trace set +state, and trace set/trace/tracefile state is the base classe of trace +set/trace/tracefile statistics. All these types can be casted to another without +problem (a TracesetState, for example, can be casted to a TracesetContext, but +it's not true for the casting between a TraceContext and a TracesetContext, see +the chapter "How to use the trace reading context" for details). They offer the +input data and they give a container (the attributes of the trace set/trace/tracefile +statistics) to write the output of this hook. +

The idea behind writing in the attributes container is to provide an extensible +way of storing any type of information. For example, a specific module that adds +statistics to a trace can store them there, and the statistic printout will +automatically include the results produced by the specific module. +

Output data does not necessarily need to be stored in such a global container +though. If we think of data of which we need to keed track during the execution, +an event counter for example, we should create our own data structure that +contains this counter, and pass the address of the allocated structure as the +hook_data parameter of the hook list creation function. That way, the hook will +be called with its hook_data as first parameter, which it can read and write. We +can think of this structure as the data related to the function that persists +between each call to the hook. You must make sure that you cast the hook_data to +the type of the structure before you use it in the hook function. +

The detail about how to access the different fields of the reading context (the +hook's call_data) will be discussed in the chapter "How to use the trace +reading context". +


PrevHomeNext
A typical moduleUpHow to use the Linux Trace Toolkit Viewer's Reading Context
\ No newline at end of file diff --git a/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x46.html b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x46.html new file mode 100644 index 00000000..3cd2e69e --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x46.html @@ -0,0 +1,162 @@ + +Why an event driven trace reader ?
Linux Trace Toolkit Viewer Developer Guide
PrevChapter 2. How to use the Linux Trace Toolkit Viewer's Reading ContextNext

2.2. Why an event driven trace reader ?

The complexity of synchronizing the tracesets is then hidden to the viewer. Some +future plans involve to be able to put many traces together in a trace set. +Before this becomes possible, the time of each trace must be synchronized in +some way. Some work is actually done to create a module that uses the network +traffic shared by different computers to synchronize the time of different +traces. +

In order to make each module integrate well with each other, we made the trace +reader a simple hook caller. For each event it reads, it just calls the hook +lists for this event. For each event, it calls the by_id specific hooks +registered for this event and also the "main" hooks, registered for all events. +Note that the two hook lists are merged when called so the priority of the +hooks of each list is respected. For example, a hook of higher priority (20) in +the main list will be called before a hook of lower priority (40) from the +by_id specific list. +


PrevHomeNext
How to use the Linux Trace Toolkit Viewer's Reading ContextUpUsing the reading context
\ No newline at end of file diff --git a/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x50.html b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x50.html new file mode 100644 index 00000000..f93edb1a --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x50.html @@ -0,0 +1,193 @@ + +Using the reading context
Linux Trace Toolkit Viewer Developer Guide
PrevChapter 2. How to use the Linux Trace Toolkit Viewer's Reading ContextNext

2.3. Using the reading context

If you have read the tutorials about writing a text and a graphic module, you +should be fairly ready to use the information provided to your hook by the +reading API. +

The data structures of the reading context are based on the gobject, a +object-oriented library from the glib. Some evolved types that are found in the +context also comes from the "glib" (GArray, GHashTable and so on). For detailed +information about "gobjects" and the "glib", see the www.gtk.org website. They provide a complete +API reference about the data types they provide. +

The reading context is object oriented. It is described by the lttv/tracecontext.h +header. Is can be illustrated with this UML class diagram : +

Linux Trace Toolkit Viewer Reading Context Class Diagram

+

Though, for performance's sake, navigating through it is not as encapsulated as +it could. Consider the class attributes to be all public (no get/set functions). +Sometimes, iteration upon a specific element can be uneasy. For example, you may +have to get the number of tracefiles in a trace from the "vt" field of the trace +context to be able to iterate over all the tracefiles contained by the trace. +

To facilitate the common operations on the reading context, LTTV now provides a +header that consists of simple macros : lttv/contextmacros.h. It gives an object +look-and-feel to the context classes. Simple "GET" macros can be used to easily +access the different fields are iterate over the elements (and get the total +number of elements too). +


PrevHomeNext
Why an event driven trace reader ?UpLinux Trace Toolkit Viewer Graphical Module Tutorial
\ No newline at end of file diff --git a/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x72.html b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x72.html new file mode 100644 index 00000000..bbb21cbc --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x72.html @@ -0,0 +1,163 @@ + +The static part of a module
Linux Trace Toolkit Viewer Developer Guide
PrevChapter 3. Linux Trace Toolkit Viewer Graphical Module TutorialNext

3.2. The static part of a module

A module must have a static part to be able to get loaded just like a text +module. Now, let's see the differences. The graphical module depends on the +"lttvwindow" module. See module.c from the control flow viewer for an example. +

The init() and destroy() functions must register functions that can be called by +user interaction to instanciate the viewers. That's the goal of +lttvwindow_register_constructor() and lttvwindow_unregister_constructor() : +they register a function with a menu entry and an icon. The main window will +shown them in its interface and call the function when the button or menu item +is selected. This hook function must receive a pointer to a "Tab" object in +parameter. +

Also note the presence of the destroy_walk() method. It is called when the +module is unloaded : it must destroy all the instances of the viewers from the +module. +


PrevHomeNext
Linux Trace Toolkit Viewer Graphical Module TutorialUpThe dynamic part of a module : the viewer
\ No newline at end of file diff --git a/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x77.html b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x77.html new file mode 100644 index 00000000..30827a5c --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x77.html @@ -0,0 +1,160 @@ + +The dynamic part of a module : the viewer
Linux Trace Toolkit Viewer Developer Guide
PrevChapter 3. Linux Trace Toolkit Viewer Graphical Module TutorialNext

3.3. The dynamic part of a module : the viewer

The dynamic part starts with the constructor of the viewer. It is called by the +main window when the corresponding button or menu item is selected. See +h_guicontrolflow() from control flow viewer eventhooks.c for an example. It does +basic connexion to the tab's events available : time window change notification, +current time notification, redraw notification, continue notification. All these +function should be implemented in your viewer if you want the data you shown to +be synchronised with the main window and the other viewers. It also calls the +background computation, which will be discussed in the next section. +

This is also at this point that the viewer does create it's own memory footprint +: its inner structure. This structure will have to be passed as hook_data to +each function registered by the viewer : this is what makes the functions +"belong" to this instance of the viewer. +


PrevHomeNext
The static part of a moduleUpHow to request background computation
\ No newline at end of file diff --git a/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x81.html b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x81.html new file mode 100644 index 00000000..c12a65b4 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x81.html @@ -0,0 +1,156 @@ + +How to request background computation
Linux Trace Toolkit Viewer Developer Guide
PrevChapter 3. Linux Trace Toolkit Viewer Graphical Module TutorialNext

3.4. How to request background computation

You will also notice the presence of a request_background_data() called in the +constructor. This function, in eventhooks.c, does verify for the presence of the +state information that could be precomputed by the main window background +computation. If it has not been precomputed, we ask for a computation and show +partial data. We also register a hook that will be called (notified) by the main +window when the requested data will become ready, so the viewer can update +itself with the new data. If no partial information would have made sense in a +particular viewer, one could choose to shown a "waiting for computation" message +while waiting for the notification. See lttvwindow/lttvwindowtraces.h for the API +of the background requests. +


PrevHomeNext
The dynamic part of a module : the viewerUpHow to handle events and use the graphical trace reading service
\ No newline at end of file diff --git a/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x84.html b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x84.html new file mode 100644 index 00000000..639350aa --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/developer_guide/html/x84.html @@ -0,0 +1,537 @@ + +How to handle events and use the graphical trace reading service
Linux Trace Toolkit Viewer Developer Guide
PrevChapter 3. Linux Trace Toolkit Viewer Graphical Module Tutorial 

3.5. How to handle events and use the graphical trace reading service

The events that are delivered by the main window are defined in +lttvwindow/lttvwindow.h. Let's describe them and their use in details. Remember +that you can refer to the control flow viewer module as an example. +

3.5.1. Module Related API

A viewer plugin is, before anything, a plugin. As a dynamically loadable +module, it thus has an init and a destroy function called whenever it is +loaded/initialized and unloaded/destroyed. A graphical module depends on +lttvwindow for construction of its viewer instances. In order to achieve +this, it must register its constructor function to the main window along +with button description or text menu entry description. A module keeps +a list of every viewer that currently sits in memory so it can destroy +them before the module gets unloaded/destroyed. +

The contructor registration to the main windows adds button and menu +entry to each main window, thus allowing instanciation of viewers. +

3.5.2. Main Window

The main window is a container that offers menus, buttons and a +notebook. Some of those menus and buttons are part of the core of the +main window, others are dynamically added and removed when modules are +loaded/unloaded. +

The notebook contains as much tabs as wanted. Each tab is linked with +a set of traces (traceset). Each trace contains many tracefiles (one +per cpu). A trace corresponds to a kernel being traced. A traceset +corresponds to many traces read together. The time span of a traceset +goes from the earliest start of all the traces to the latest end of all +the traces. +

Inside each tab are added the viewers. When they interact with the main +window through the lttvwindow API, they affect the other viewers located +in the same tab as they are. +

The insertion of many viewers in a tab permits a quick look at all the +information wanted in a glance. The main window does merge the read +requests from all the viewers in the same tab in a way that every viewer +will get exactly the events it asked for, while the event reading loop +and state update are shared. It improves performance of events delivery +to the viewers. +

3.5.3. Viewer Instance Related API

The lifetime of a viewer is as follows. The viewer constructor function +is called each time an instance view is created (one subwindow of this +viewer type is created by the user either by clicking on the menu item +or the button corresponding to the viewer). Thereafter, the viewer gets +hooks called for different purposes by the window containing it. These +hooks are detailed below. It also has to deal with GTK Events. Finally, +it can be destructed by having its top level widget unreferenced by the +main window or by any GTK Event causing a "destroy-event" signal on the +its top widget. Another possible way for it do be destroyed is if the +module gets unloaded. The module unload function will have to emit a +"destroy" signal on each top level widget of all instances of its viewers. +

3.5.4. Notices from Main Window

time_window

This is the time interval visible on the viewer's tab. Every + viewer that cares about being synchronised by respect to the + time with other viewers should register to this notification. + They should redraw all or part of their display when this + occurs.

traceset

This notification is called whenever a trace is added/removed + from the traceset. As it affects all the data displayed by the + viewer, it sould redraw itself totally.

filter

This feature has not been implemented yet.

current_time

Being able to zoom nearer a specific time or highlight a specific + time on every viewer in synchronicity implies that the viewer + has to shown a visual sign over the drawing or select an event + when it receives this notice. It should also inform the main + window with the appropriate report API function when a user + selects a specific time as being the current time.

dividor

This notice links the positions of the horizontal dividors + between the graphic display zone of every viewer and their Y axis, + typically showing processes, cpus, ...

3.5.5. Reporting Changes to the Main Window

In most cases, the enclosing window knows about updates such as described +in the Notification section higher. There are a few cases, however, where +updates are caused by actions known by a view instance. For example, +clicking in a view may update the current time; all viewers within +the same window must be told about the new current time to change the +currently highlighted time point. A viewer reports such events by calling +lttvwindow_report_current_time on its lttvwindow. The lttvwindow will +consequently call current_time_notify for each of its contained viewers. +

Available report methods are : +

  • lttvwindow_report_time_window : reports the new time window. +

  • lttvwindow_report_current_time : reports the new current time. +

  • lttvwindow_report_dividor : reports the new horizontal dividor's position. +

+

3.5.6. Requesting Events to Main Window

Events can be requested by passing a EventsRequest structure to the main +window. They will be delivered later when the next g_idle functions +will be called. Event delivery is done by calling the event hook for +this event ID, or the main event hooks. A pointer to the EventsRequest +structure is passed as hook_data to the event hooks of the viewers. +

EventsRequest consists in +

  • a pointer to the viewer specific data structure +

  • a start timestamp or position +

  • a stop_flag, ending the read process when set to TRUE +

  • a end timestamp and/or position and/or number of events to read +

  • hook lists to call for traceset/trace/tracefile begin and end, and for each + event (event hooks and event_by_id hooks). +

+

The main window will deliver events for every EventRequests it has +pending through an algorithm that guarantee that all events requested, +and only them, will be delivered to the viewer between the call of the +tracefile_begin hooks and the call of the tracefile_end hooks. +

If a viewer wants to stop the event request at a certain point inside the +event hooks, it has to set the stop_flag to TRUE and return TRUE from the +hook function. Then return value will stop the process traceset. Then, +the main window will look for the stop_flag and remove the EventRequests +from its lists, calling the process_traceset_end for this request (it +removes hooks from the context and calls the after hooks). +

It no stop_flag is risen, the end timestamp, end position or number +of events to read has to be reached to determine the end of the +request. Otherwise, the end of traceset does determine it. +

3.5.7. GTK Events

3.5.7.1. Events and Signals

GTK is quite different from the other graphical toolkits around +there. The main difference resides in that there are many X Windows +inside one GtkWindow, instead of just one. That means that X events are +delivered by the glib main loop directly to the widget corresponding to +the GdkWindow affected by the X event. +

Event delivery to a widget emits a signal on that widget. Then, if a +handler is connected to this widget's signal, it will be executed. There +are default handlers for signals, connected at class instantiation +time. There is also the possibility to connect other handlers to these +signals, which is what should be done in most cases when a viewer needs +to interact with X in any way. +

Signal emission and propagation is described there : + +

  • http://www.gtk.org/tutorial/sec-signalemissionandpropagation.html +

+

For further information on the GTK main loop (now a wrapper over glib main loop) +see : + +

  • http://developer.gnome.org/doc/API/2.0/gtk/gtk-General.html +

  • http://developer.gnome.org/doc/API/2.0/glib/glib-The-Main-Event-Loop.html +

+

For documentation on event handling in GTK/GDK, see : + +

  • http://developer.gnome.org/doc/API/2.0/gdk/gdk-Events.html +

  • http://developer.gnome.org/doc/API/2.0/gdk/gdk-Event-Structures.html +

+

Signals can be connected to handlers, emitted, propagated, blocked, +stopped. See : + +

  • http://developer.gnome.org/doc/API/2.0/gobject/gobject-Signals.html +

+

3.5.7.2. The "expose_event"

Provides the exposed region in the GdkEventExpose structure. +

There are two ways of dealing with exposures. The first one is to directly +draw on the screen and the second one is to draw in a pixmap buffer, +and then to update the screen when necessary. +

In the first case, the expose event will be responsible for registering +hooks to process_traceset and require time intervals to the main +window. So, in this scenario, if a part of the screen is damaged, the +trace has to be read to redraw the screen. +

In the second case, with a pixmap buffer, the expose handler is only +responsible of showing the pixmap buffer on the screen. If the pixmap +buffer has never been filled with a drawing, the expose handler may ask +for it to be filled. +

The interest of using events request to the main window instead of reading +the events directly from the trace comes from the fact that the main +window does merge requests from the different viewers in the same tab so +that the read loop and the state update is shared. As viewers will, in +the common scenario, request the same events, only one pass through the +trace that will call the right hooks for the right intervals will be done. +

When the traceset read is over for a events request, the traceset_end +hook is called. It has the responsibility of finishing the drawing if +some parts still need to be drawn and to show it on the screen (if the +viewer uses a pixmap buffer). +

It can add dotted lines and such visual effects to enhance the user's +experience. +


PrevHome 
How to request background computationUp 
\ No newline at end of file diff --git a/tags/lttv-0.11.3-23102008/doc/developer/developer_tutorial/LTTVTutorial.doc b/tags/lttv-0.11.3-23102008/doc/developer/developer_tutorial/LTTVTutorial.doc new file mode 100644 index 00000000..90029938 Binary files /dev/null and b/tags/lttv-0.11.3-23102008/doc/developer/developer_tutorial/LTTVTutorial.doc differ diff --git a/tags/lttv-0.11.3-23102008/doc/developer/developer_tutorial/LTTVTutorial.pdf b/tags/lttv-0.11.3-23102008/doc/developer/developer_tutorial/LTTVTutorial.pdf new file mode 100644 index 00000000..ba3bfd7a Binary files /dev/null and b/tags/lttv-0.11.3-23102008/doc/developer/developer_tutorial/LTTVTutorial.pdf differ diff --git a/tags/lttv-0.11.3-23102008/doc/developer/format.html b/tags/lttv-0.11.3-23102008/doc/developer/format.html new file mode 100644 index 00000000..811b6d1b --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/format.html @@ -0,0 +1,284 @@ + + + + + The LTTng trace format + + + + +

The LTTng trace format

+ +

+Last update: 2008/06/02 +

+ +

+This document describes the LTTng trace format. It should be useful mainly to +developers who code the LTTng tracer or the traceread LTTV library, as this +library offers all the necessary abstractions on top of the raw trace data. +

+ +

+A trace is contained in a directory tree. To send a trace remotely, the +directory tree may be tar-gzipped. The trace foo, placed in the home +directory of user john, /home/john, would have the following contents: +

+ +

+$ cd /home/john
+$ tree foo
+foo/
+|-- control
+|   |-- facilities_0
+|   |-- facilities_1
+|   |-- facilities_...
+|   |-- interrupts_0
+|   |-- interrupts_1
+|   |-- interrupts_...
+|   |-- modules_0
+|   |-- modules_1
+|   |-- modules_...
+|   |-- network_0
+|   |-- network_1
+|   |-- network_...
+|   |-- processes_0
+|   |-- processes_1
+|   `-- processes_...
+|-- cpu_0
+|-- cpu_1
+`-- cpu_...
+
+
+ +

+The root directory contains a tracefile for each cpu, numbered from 0, +in .trace format. A uniprocessor thus only contains the file cpu_0. +A multi-processor with some unused (possibly hotplug) CPU slots may have some +unused CPU numbers. For instance an 8 way SMP board with 6 CPUs randomly +installed may produce tracefiles named 0, 1, 2, 4, 6, 7. +

+ +

+The files in the control directory also follow the .trace format and are +also per cpu. The "facilities" files only contain "core" marker_id, +marker_format and time_heartbeat events. The first two are used to describe the +events that are in the trace. The other control files contain the initial +system state and various subsequent important events, for example process +creations and exit. The interest of placing such subsequent events in control +trace files instead of (or in addition to) in the per cpu trace files is that +they may be accessed more quickly/conveniently and that they may be kept even +when the per cpu files are overwritten in "flight recorder mode". +

+ +

Trace format

+ +

+Each tracefile is divided into equal size blocks with a header at the beginning +of the block. Events are packed sequentially in the block starting right after +the block header. +

+ +

+Each block consists of : +

+ +

+block start/end header
+trace header
+event 1 header
+event 1 variable length data
+event 2 header
+event 2 variable length data
+....
+padding
+
+ +

The block start/end header

+ +

+begin
+	* the beginning of buffer information
+	uint64 cycle_count
+		* TSC at the beginning of the buffer
+	uint64 freq
+		* frequency of the CPUs at the beginning of the buffer.
+end
+	* the end of buffer information
+	uint64 cycle_count
+		* TSC at the end of the buffer
+	uint64 freq
+		* frequency of the CPUs at the end of the buffer.
+uint32 lost_size
+	* number of bytes of padding at the end of the buffer.
+uint32 buf_size
+	* size of the sub-buffer.
+
+ + + +

The trace header

+ +

+uint32 magic_number
+	* 0x00D6B7ED, used to check the trace byte order vs host byte order.
+uint32 arch_type
+	* Architecture type of the traced machine.
+uint32 arch_variant
+	* Architecture variant of the traced machine. May be unused on some arch.
+uint32 float_word_order
+	* Byte order of floats and doubles, sometimes different from integer byte
+	  order. Useful only for user space traces.
+uint8 arch_size
+	* Size (in bytes) of the void * on the traced machine.
+uint8 major_version
+	* major version of the trace.
+uint8 minor_version
+	* minor version of the trace.
+uint8 flight_recorder
+	* Is flight recorder mode activated ? If yes, data might be missing
+	  (overwritten) in the trace.
+uint8	has_heartbeat
+	* Does this trace have heartbeat timer event activated ?
+		Yes (1) -> Event header has 32 bits TSC
+		No (0) -> Event header has 64 bits TSC
+uint8 alignment
+	* Are event headers in this trace aligned ?
+		Yes -> the value indicates the alignment
+		No (0) -> data is packed.
+uint8 tsc_lsb_truncate
+	* Used for compact channels
+uint8 tscbits
+	* Used for compact channels
+uint8 compact_data_shift
+	* Used for compact channels
+uint32 freq_scale
+		event time is always calculated from :
+			trace_start_time + ((event_tsc - trace_start_tsc) * (freq / freq_scale))
+uint64 start_freq
+	* CPUs clock frequency at the beginnig of the trace.
+uint64 start_tsc
+	* TSC at the beginning of the trace.
+uint64 start_monotonic
+	* monotonically increasing time at the beginning of the trace.
+		(currently not supported)
+start_time
+	* Real time at the beginning of the trace (as given by date, adjusted by NTP)
+		This is the only time reference with the real world : the rest of the trace
+		has monotonically increasing time from this point (with TSC difference and
+		clock frequency).
+	uint32 seconds
+	uint32 nanoseconds
+
+ + +

Event header

+ +

+Event headers differ according to the following conditions : does the +traced system have a heartbeat timer? Is tracing alignment activated? +

+ +

+Event header : +

+

+{ uint32 timestamp
+	or
+	uint64 timestamp }
+	* if has_heartbeat : 32 LSB of the cycle counter at the event record time.
+	* else : 64 bits complete cycle counter.
+uint8 facility_id
+	* Numerical ID of the facility corresponding to the event. See the facility
+	  tracefile to know which facility ID matches which facility name and
+		description.
+uint8 event_id
+	* Numerical ID of the event inside the facility.
+uint16 event_size
+	* Size of the variable length data that follows this header.
+
+ +

+Event header alignment +

+ +

+If trace alignment is activated (alignment), the event header is +aligned. In addition, padding is automatically added after the event header so +the variable length data is automatically aligned on the architecture size. +

+ + + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/library-header.txt b/tags/lttv-0.11.3-23102008/doc/developer/library-header.txt new file mode 100644 index 00000000..00e2ecf1 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/library-header.txt @@ -0,0 +1,17 @@ +/* This file is part of the Linux Trace Toolkit trace reading library + * Copyright (C) 2003-2004 Your Name + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License Version 2.1 as published by the Free Software Foundation. + * + * 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. + */ diff --git a/tags/lttv-0.11.3-23102008/doc/developer/lttng-atomic-up.txt b/tags/lttv-0.11.3-23102008/doc/developer/lttng-atomic-up.txt new file mode 100644 index 00000000..9ce3482c --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/lttng-atomic-up.txt @@ -0,0 +1,131 @@ + +Atomic UP test results. + + + + +using test-time-probe2.ko + +Clock speed : cpu MHz : 3000.077 + +Tracing inactive + +[ 125.787229] test init +[ 125.787303] test results : time per probe +[ 125.787306] number of loops : 20000 +[ 125.787309] total time : 204413 +[ 125.787312] test end +[ 175.660402] test init +[ 175.660475] test results : time per probe +[ 175.660479] number of loops : 20000 +[ 175.660482] total time : 203468 +[ 175.660484] test end +[ 179.337362] test init +[ 179.337436] test results : time per probe +[ 179.337440] number of loops : 20000 +[ 179.337443] total time : 204757 +[ 179.337446] test end + +Res : 10.21 cycles per loop + +Atomic UP, one trace, flight recorder. + +[ 357.983971] test init +[ 357.988837] test results : time per probe +[ 357.988843] number of loops : 20000 +[ 357.988846] total time : 12349013 +[ 357.988849] test end +[ 358.718896] test init +[ 358.723049] test results : time per probe +[ 358.723053] number of loops : 20000 +[ 358.723057] total time : 12332497 +[ 358.723059] test end +[ 359.422038] test init +[ 359.426173] test results : time per probe +[ 359.426179] number of loops : 20000 +[ 359.426182] total time : 12332535 +[ 359.426185] test end + +Res : 616.90 cycles per loop. +205.63 ns per loop + +Atomic SMP, one trace, flight. + + +[ 111.694180] test init +[ 111.700191] test results : time per probe +[ 111.700198] number of loops : 20000 +[ 111.700201] total time : 16925670 +[ 111.700204] test end +[ 112.285716] test init +[ 112.291321] test results : time per probe +[ 112.291326] number of loops : 20000 +[ 112.291329] total time : 16766633 +[ 112.291332] test end +[ 112.880602] test init +[ 112.884739] test results : time per probe +[ 112.884743] number of loops : 20000 +[ 112.884746] total time : 12358237 +[ 112.884748] test end + +Res : 767.51 cycles per loop +255.83 ns per loop + +(205.63-255.83)/255.83 * 100% = 19.62 % + + +Difference between +cmpxchg 2967855/20000 = 148.39 cycles or 49.46 ns +cmpxchg-up 540577/20000 = 27.02 cycles or 9.00 ns +irq save/restore 12636562/20000 = 631.82 cycles 210.60 ns + + + +* Memory ordering + +offset +written by local CPU +read by local CPU and other CPUs (reader) + +commit count +written by local CPU +read by local CPU and other CPUs (reader) + +consumed +written by any CPU +read by any CPU + +data +written by local CPU +read by any CPU + + +test done in the reader : +if ( consumed < offset ) + if ( subbuf.commit_count == multiple of SUBBUFSIZE) + read data + inc consumed + + +We must guarantee the following ordering : +* offset +Seen from the local CPU : +offset must always be incremented before the data is written (already +consistent) + +Seen from other cpus : +offset and data can be written out of order +(because offset is always incremented : in an out of order case, offset is lower +than the actual data ready, but the commit_count _has_ to be incremented to read +the data (and is preceded by a store fence) + +* commit_count +commit_count must always be seen by other CPUs after the data has been written. +Therefore, we must put a store fence before the commit_count write. (smp_wmb) + +* consumed +Rarely updated, use LOCK prefix. Acts as a full memory barrier. + + + +Mathieu Desnoyers, November 2006 diff --git a/tags/lttv-0.11.3-23102008/doc/developer/lttng-format-string.txt b/tags/lttv-0.11.3-23102008/doc/developer/lttng-format-string.txt new file mode 100644 index 00000000..d0580d58 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/lttng-format-string.txt @@ -0,0 +1,37 @@ + +LTTng format string parsing +July 26, 2007 + +"%lu" +"#4b%u" +"#lu%u" (no sign extension) +"#ld%c" (sign extension) +"#lld%p" (sign extension) + +Parsing: + +When # is encountered + Deal with ## case (escaped) + Read length modifier + gcc-like + trace specific : 1, 2, 4, 8, n, b + in both cases, save the output type size (1, 2, 4, 8). + Read conversion specifier + save the output type size if no length modifier is involved (1, 2, 4, 8). + save the signedness. + +(vsnprintf-like part) +When % is encountered + Deal with %% case (escaped) + (deal with field width, precision. Skip them.) + Read length modifier + if output type size is 0, set it to the gcc type size. + Read conversion specifier + if output type size is 0, set it to the gcc type size if no length modifier + is involved. + Perform trace write with conversion. + Reset output type size to 0 + + + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/lttng-lttv-compatibility.html b/tags/lttv-0.11.3-23102008/doc/developer/lttng-lttv-compatibility.html new file mode 100644 index 00000000..d925fbd8 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/lttng-lttv-compatibility.html @@ -0,0 +1,3092 @@ + + + +Quick list of compatible LTTV and LTTng versions :
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LTTV
+
LTTng
+
LTT Control
+
LTTng modules
+
LTTng userspace tracer / markers-userspace
+
Genevent
+
Trace Major.Minor
+
Kernels-architectures
+
Comments
+
Kernel Regression ok
+
Regression fails/not tested
+
LTTV
+
LTTng
+
LTT Control
+
LTTng modules
+
LTTng userspace tracer
+
Genevent
+
Trace Major.Minor
+
Kernels-architectures
+
Comments
+
Kernel Regression ok
+
Regression fails/not tested
+
0.6.9
+
0.4.4
+

+
0.3
+

+
0.2
+
0.4
+
+2.6.12-rc4-mm2-i386 (tarball)
+2.6.12-rc4-i386 (git)
+2.6.9-i386 (contribution)
+2.6.9-x86_64 (contribution)
+

+

+

+
0.7.0
+
0.4.5
+

+
0.3
+

+
0.2
+
0.5
+
2.6.12-rc4-i386 (git)
+
Traces can now be written/read between 32 and 64 bits arch.
+

+

+
0.8.0
+
0.5.0
+

+
0.3
+

+
0.3
+
0.6
+
+2.6.12-rc4-i386 (git)
+2.6.12-i386 (git)
+2.6.13-i386 (git)
+

+

+

+
0.8.0
+
0.5.0
+

+
0.4
+

+
0.3
+
0.6
+
+2.6.14-i386 (git)
+

+

+

+
0.8.0
+
0.5.0a
+

+
0.4
+

+
0.3
+
0.6
+
+2.6.14-i386 (tarball)
+

+

+

+
+0.8.1
+0.8.2
+0.8.3
+0.8.4
+
0.5.1
+

+
0.4
+

+
0.4
+
0.6
+
+2.6.14-i386 (git)
+2.6.14-i386 (tarball)
+2.6.15-i386 (git)
+2.6.15-i386 (tarball)
+
Per architecture syscall enumeration
+

+

+
+0.8.1
+0.8.2
+0.8.3
+0.8.4
+
+0.5.3
+0.5.4
+0.5.5
+

+
0.4
+

+
+0.4
+0.5
+
0.6
+
+2.6.15-i386 (git)
+2.6.15-i386 (tarball)
+
APIC NMI watchdog can now be logged
+

+

+
+0.8.5
+
+0.5.6
+

+
0.4
+

+
+0.6
+
0.6
+
+2.6.15-i386 (git)
+2.6.15-i386 (tarball)
+
+Custom write function in genevent (for process stack dump)
+Use atomic_cmpxchg()
+
+

+

+
+0.8.6
+0.8.7
+0.8.8
+0.8.9
+
+0.5.7
+0.5.8
+0.5.9a
+0.5.10
+0.5.11
+0.5.13
+0.5.16
+

+
+0.4
+

+
+0.6
+0.7
+0.8
+
+0.7
+
+2.6.15-i386 (git)
+2.6.15-i386 (tarball)
+
+Support for architectures without TSC.
+LTTV 0.8.7/genevent 0.7 fixes a bug in enum XML parsing.
+
+

+

+
+0.8.10
+0.8.11
+0.8.12
+
+0.5.17
+

+
+0.4
+

+
+0.8
+
+0.7
+
+2.6.15-i386 (git)
+2.6.15-i386 (tarball)
+
+Multithreaded lttd.
+Stack dump fix.
+LTTV 0.8.12 fixes timing problem in tracecontrol module.
+
+

+

+
+0.8.13
+0.8.14
+0.8.15
+0.8.16
+0.8.17
+
+0.5.18a
+0.5.19
+

+
+0.5
+0.6
+0.7
+

+
+0.8
+0.9
+
+0.7
+
+2.6.15-i386 (git)
+2.6.15-i386 (tarball)
+
+Kernel State Dump : get vmaps, process names, fd at the beginning of a trace.
+
+

+

+
+0.8.18
+0.8.19
+0.8.20
+0.8.21
+0.8.22
+0.8.23
+0.8.24
+0.8.25
+
+0.5.20
+0.5.20a
+0.5.22
+0.5.23
+0.5.24
+0.5.25
+0.5.27
+0.5.28
+

+
+0.6
+0.7
+0.8
+

+
+0.10
+0.11
+0.12
+
+0.7
+
+2.6.15-i386 (git)
+2.6.15-i386 (tarball)
+
+Add kernel stack dump instrumentation and facility.
+LTTV 0.8.21+genevent 0.11 : add support for network byte order data with the " +network " tag in the XML file.
+LTTng 0.5.27+LTTV 0.8.24 introduces the heartbeat timer.
+LTTng 0.5.28 introduces the synthetic 64 bits TSC for archs with 32 bits TSC.
+
+

+

+
+0.8.26
+
+0.5.29
+0.5.29a
+

+
+0.8
+
+0.1
+
+0.13
+
+0.7
+
+2.6.15-i386 (git)
+2.6.15-i386 (tarball)
+
+Add user space tracing through system call.
+
+

+

+
+0.8.27
+
+0.5.29
+0.5.29a
+0.5.30
+

+
+0.8
+0.9
+
+0.2
+0.3
+0.4
+
+0.14
+
+0.7
+
+2.6.15-i386 (git)
+2.6.15-i386 (tarball)
+
+Add function instrumentation tracing (gcc -finstrument-functions)
+
+

+

+
+0.8.28
+
+0.5.33
+0.5.34
+

+
+0.10
+
+0.5
+
+0.15
+0.16
+
+0.7
+
+2.6.15-i386 (git)
+2.6.15-i386 (tarball)
+
+Fast "Usertrace"
+Bugfixes.
+Network ip interfaces state.
+
+

+

+
+0.8.29
+0.8.30
+
+0.5.33
+0.5.34
+

+
+0.10
+
+0.6
+
+0.17
+
+0.7
+
+2.6.15-i386 (git)
+2.6.15-i386 (tarball)
+
+XML facilities description format changed. Now XML 1.0 : breaks compatibility +with old XML files.
+LTTV 0.8.30 changes the statistics tree structure. It adds user functions +statistics support.
+
+

+

+
+0.8.31
+0.8.32
+0.8.33
+0.8.34
+
+0.5.34
+0.5.36
+
+0.1
+
+0.10
+0.11
+
+0.6
+
+0.17
+
+0.7
+
+2.6.15-i386 (git)
+2.6.15-i386 (tarball)
+
+Separate the viewer from the tracing controller (userful for embedded tracing).
+
+

+

+
+0.8.34
+0.8.35
+
+0.5.37
+0.5.38
+0.5.39
+0.5.40
+0.5.41
+
+0.1
+0.2
+
+0.12
+
+0.7
+0.8
+
+0.17
+0.18
+
+0.7
+
+2.6.16-i386 (git)
+2.6.16-i386 (tarball)
+
+Kernel 2.6.16 support.
+LTTng 0.5.38 : Fix deadlock scenario involving deliver from an NMI handler.
+LTTng 0.5.40/ltt-usertrace 0.8 : Fix off-by-one which pushes the reader with small subbuffers (only in buffer full condition)
+genevent 0.18 fixes missing nested subtypes align/write functions.
+
+

+

+
+0.8.34
+0.8.35
+
+0.5.42
+0.5.43
+
+0.3
+0.4
+
+0.13
+0.14
+
+0.7
+0.8
+
+0.17
+0.18
+
+0.7
+
+2.6.16-i386 (git)
+2.6.16-i386 (tarball)
+
+Improvements in ltt-statedump process state.
+LTTng 0.5.43 : Add ARM IRQ statedump.
+
+

+

+
+0.8.34
+0.8.35
+0.8.36
+
+0.5.44
+0.5.45
+
+0.5
+
+0.13
+0.14
+0.15
+
+0.7
+0.8
+
+0.17
+0.18
+
+0.7
+
+2.6.16-i386 (git)
+2.6.16-i386 (tarball)
+
+Add IRQ enable/disable/save/restore instrumentation under locking.xml.
+Various architecture specific fixes.
+LTTV : Add process names to textDump.
+
+

+

+
+0.8.37
+0.8.38
+0.8.39
+0.8.40
+
+0.5.46
+
+0.6
+
+0.15
+
+0.8
+
+0.18
+
+0.7
+
+2.6.16-i386 (git)
+2.6.16-i386 (tarball)
+
+Add C2 Microsystems support in LTTV and ltt-control.
+Include fixes to instrumentation of MIPS and PPC from Yuri Frolov.
+Fix state dump states in LTTV.
+Fix interrupt and diskperformance views.
+
+

+

+
+0.8.37
+0.8.38
+0.8.39
+0.8.40
+
+0.5.47a
+
+0.6
+
+obsolete
+
+0.8
+
+0.18
+
+0.7
+
+2.6.16-i386 (git)
+2.6.16-i386 (tarball)
+
+Merge ltt-modules into the kernel tree.
+
+

+

+
+0.8.41
+0.8.42
+0.8.43
+0.8.44
+
+0.5.48
+0.5.49
+0.5.51
+0.5.52
+0.5.52a
+0.5.52b
+0.5.52c
+0.5.55
+0.5.56
+0.5.57
+
+0.6
+0.7
+
+obsolete
+
+0.8
+0.9
+0.10
+
+0.19
+0.20
+0.21
+
+0.7
+
+2.6.16-i386 (git)
+2.6.16-i386 (tarball)
+
+Fix LTTV for PowerPC. Integrate some fixes useful for other architectures.
+Basic PowerPC tracing (incomplete instrumentation).
+LTTng 0.5.51, 0.5.52, genevent 0.20 : code indentation fix.
+LTTV 0.4.42, LTTng 0.5.55, LTT control 0.7, genevent 0.21 : PowerPC 32 and 64 +bits support.
+LTTng 0.5.56 : MIPS time frequency fix.
+LTTng 0.5.57 : add 32 bits compat execve tracing (for 64 bits architectures).
+LTTV 0.8.43 : fix trace start time, 0.8.44 : interrupt view fixes.
+ltt-usertrace : add a java tracing example.
+
+

+

+
+0.8.45
+0.8.46
+0.8.47
+
+0.5.58
+0.5.59
+0.5.60
+0.5.61
+0.5.62
+0.5.63
+0.5.64
+0.5.65
+0.5.66
+0.5.68a
+0.5.69
+
+0.8
+0.9
+0.10
+
+obsolete
+
+0.8
+0.9
+0.10
+0.11
+0.12
+
+0.19
+0.20
+0.21
+0.22
+
+0.7
+
+2.6.16-i386 (git)
+2.6.16-i386 (tarball)
+
+Add support for fs_data facility : get the first 32 bytes of data in read and +write system calls.
+LTTng 0.5.59 : Fix usertrace facility registration : fork vs exec.
+LTTV 0.8.46 : fix end of process in state.c.
+LTTng 0.5.65 : Fix ltt-heartbeat.
+LTTng 0.5.69 : Fix ltt-usertrace blocking mode : causes OOPS on trace stop.
+
+

+

+
+0.8.45
+0.8.46
+0.8.47
+
+0.5.70
+0.5.71
+
+0.11
+
+obsolete
+
+0.13
+
+0.19
+0.20
+0.21
+0.22
+
+0.7
+
+2.6.17 (git)
+2.6.17 (tarball)
+
+LTTng 0.5.71 : Fix a bug with ltt-usertrace blocking mode.
+
+

+

+
+0.8.48
+
+0.5.72a
+0.5.72b
+
+0.12
+
+obsolete
+
+0.13
+
+0.19
+0.20
+0.21
+0.22
+
+0.7
+
+2.6.17 (git)
+2.6.17 (tarball)
+
+Add TGID.
+Merge Tim Bird fixes for compiling LTTV out of tree.
+
+

+

+
+0.8.49
+
+0.5.73
+0.5.74
+
+0.13
+
+obsolete
+
+0.14
+0.15
+
+0.23
+
+0.7
+
+2.6.17 (git)
+2.6.17 (tarball)
+
+Add x86_64 support (Martin Bisson for Autodesk).
+Make LTTV control flow lines thicker (Tim Bird).
+
+

+

+
+0.8.49
+0.8.50
+0.8.51
+0.8.52
+0.8.53
+
+0.5.75
+0.5.75a
+0.5.76
+0.5.78
+
+0.13
+
+obsolete
+
+0.14
+0.15
+
+0.24
+
+0.7
+
+2.6.17 (git)
+2.6.17 (tarball)
+
+Integrate transport abstraction from Richard Purdie.
+LTTV 0.8.52 : Add "stop" event handling to detailed event list (gui).
+
+

+

+
+0.8.49
+0.8.50
+0.8.51
+0.8.52
+0.8.53
+
+0.5.79
+
+0.14
+0.15
+0.16
+0.17
+
+obsolete
+
+0.14
+0.15
+
+0.24
+
+0.7
+
+2.6.17 (git)
+2.6.17 (tarball)
+
+Separate the transport mechanism from the tracing (Richard Purdie).
+
+

+

+
+0.8.49
+0.8.50
+0.8.51
+0.8.52
+0.8.53
+0.8.54
+0.8.55
+0.8.56
+0.8.57
+0.8.59
+
+0.5.80
+0.5.81
+0.5.82
+0.5.83
+0.5.86
+0.5.87
+0.5.88
+0.5.89
+0.5.90
+0.5.91
+0.5.92
+0.5.94
+0.5.95
+0.5.96
+0.5.98
+0.5.99
+0.5.100
+0.5.103
+
+0.14
+0.15
+0.16
+0.17
+0.18
+
+obsolete
+
+0.15
+0.16
+0.17
+0.18
+
+0.24
+0.25
+0.26
+0.27
+
+0.7
+
+2.6.17 (git)
+2.6.17 (tarball)
+
+Fix syscall enum on x86_64.
+Fix types in ltt-core.h.
+ltt-usertrace 0.16 makes headers completely kernel independant, which is sane.
+genevent 0.25, ltt-usertrace 0.18 and lttng 0.5.83 add support for g++.
+LTTV 0.8.55 fixes stats accounting for PID 0 at beginning of trace.
+LTTng 0.5.87 fixes x86 kernel and process stack dump and adds nice menu options +for it.
+LTTV 0.8.57 fixes a bug recently introduced in event list scroll/page/move +up.
+LTTng 0.5.88 fixes an important bug, present in all LTTng versions, which +makes it discard silently every event nested on an already executing probe.
+genevent 0.27 and ltt-usertrace 0.18 fix a problem with alignment of the +userspace printf event.
+LTTng 0.5.101 Adds sysenter/sysexit instrumentation. It also fixes i386 stack +dump.
+
+

+

+
+0.8.49
+0.8.50
+0.8.51
+0.8.52
+0.8.53
+0.8.54
+0.8.55
+0.8.56
+0.8.57
+0.8.59
+
+0.5.104
+
+0.19
+
+obsolete
+
+0.15
+0.16
+0.17
+0.18
+
+0.24
+0.25
+0.26
+0.27
+
+0.7
+
+2.6.17 (git)
+2.6.17 (tarball)
+
+High, medium and low event rate channel size and number of subbuffers. Default : +high 1MB, medium 256KB, low 64KB.
+
+

+

+
+0.8.49
+0.8.50
+0.8.51
+0.8.52
+0.8.53
+0.8.54
+0.8.55
+0.8.56
+0.8.57
+0.8.59
+
+0.5.105
+
+0.20
+
+obsolete
+
+0.15
+0.16
+0.17
+0.18
+
+0.24
+0.25
+0.26
+0.27
+
+0.7
+
+2.6.17 (git)
+2.6.17 (tarball)
+
+Add hybrid mode : High event rate channels are in flight recorder mode, +low/medium rate channels in normal tracing mode.
+
+

+

+
+0.8.60
+
+0.5.106
+0.5.107
+
+0.21
+
+obsolete
+
+0.19
+
+0.28
+
+0.7
+
+2.6.17 (git)
+2.6.17 (tarball)
+
+Send statedump events to medium rate channels, thread branding is now +"high_priority" : saved to the processes channel.
+LTTng 0.5.107 : fixes a potential oops when freeing the trace structure (use +kref now).
+
+

+

+
+0.8.61
+0.8.62
+
+0.5.111
+0.5.112
+0.5.113
+
+0.22
+0.23
+0.24
+
+obsolete
+
+0.19
+
+0.28
+
+0.7
+
+2.6.17 (git)
+2.6.17 (tarball)
+
+Now use Relay+DebugFS.
+Add printk instrumentation.
+
+

+

+
+0.8.61
+0.8.62
+
+0.5.112
+
+0.23
+
+obsolete
+
+0.19
+
+0.28
+
+0.7
+
+2.6.17 (git)
+2.6.17 (tarball)
+
+Add printk instrumentation.
+
+

+

+
+0.8.61
+0.8.62
+
+0.5.113
+
+0.24
+
+obsolete
+
+0.19
+
+0.28
+
+0.7
+
+2.6.17 (git)
+2.6.17 (tarball)
+
+Add printk_locate instrumentation.
+
+

+

+
+0.8.61
+0.8.62
+
+0.6.0preX
+
+0.24
+
+obsolete
+
+0.19
+
+SVN head
+
+0.7
+
+2.6.17 (git)
+2.6.17 (tarball)
+
+Marker+Probe mechanism.
+
+

+

+
+0.8.61
+0.8.62
+
+0.6.0
+
+0.25
+
+obsolete
+
+0.19
+
+0.29
+
+0.7
+
+2.6.17 (git)
+2.6.17 (tarball)
+
+LTTng 0.6.0 with markers and probes.
+
+

+

+
+0.8.61
+0.8.62
+
+0.6.2
+
+0.26
+
+obsolete
+
+0.20
+
+0.29
+
+0.7
+
+2.6.18 (git)
+2.6.18 (tarball)
+
+2.6.18 kernel.
+
+

+

+
+0.8.61
+0.8.62
+0.8.63
+0.8.64
+0.8.65
+0.8.66
+0.8.67
+0.8.68
+0.8.69
+0.8.70
+
+0.6.3
+0.6.4
+0.6.5
+0.6.6
+0.6.7
+0.6.8
+0.6.9
+0.6.10
+0.6.11
+0.6.13
+0.6.14
+0.6.15
+0.6.16
+0.6.17
+0.6.18
+0.6.19
+0.6.20
+0.6.21
+0.6.22
+0.6.23
+0.6.24
+0.6.25
+0.6.26
+0.6.27
+
+0.27
+0.28
+0.29
+
+obsolete
+
+0.20
+0.21
+
+0.29
+0.30
+
+0.7
+
+2.6.18 (git)
+2.6.18 (tarball)
+
+Locking, hardirq and softirq instrumentation.
+Coding style fixes.
+Round to count order for subbuffer size and number of subbuffers.
+Fix ltt-statedump with unnamed irq chips.
+LTTng 0.6.18 fixes an important bug in LTT statedump (semaphore on the +stack).
+LTTng 0.6.23 implements optimisez per-cpu atomic operations for non shared +variables. It provides cheap NMI protection.
+
+

+

+
+0.8.61
+0.8.62
+0.8.63
+0.8.64
+0.8.65
+0.8.66
+0.8.67
+0.8.68
+0.8.69
+0.8.70
+0.8.71
+0.8.72
+
+0.6.28
+0.6.29
+0.6.30
+0.6.31
+0.6.32
+
+0.30
+
+obsolete
+
+0.20
+0.21
+
+0.29
+0.30
+
+0.7
+
+2.6.18 (git)
+2.6.18 (tarball)
+
+Syscall facilities update for 2.6.18.
+0.8.29 fixes a typo in asm-generic/atomic-up.h.
+
+

+

+
+0.8.61
+0.8.62
+0.8.63
+0.8.64
+0.8.65
+0.8.66
+0.8.67
+0.8.68
+0.8.69
+0.8.70
+0.8.71
+0.8.72
+
+0.6.35
+0.6.36
+0.6.36a
+0.6.37
+0.6.38
+0.6.39
+0.6.40
+0.6.41
+
+0.30
+
+obsolete
+
+0.20
+0.21
+
+0.31
+
+0.7
+
+2.6.18 (git)
+2.6.18 (tarball)
+
+Add CPU hotplug support (hotplug events in Relay and inotify support in debugfs +and lttd).
+
+

+

+
+0.8.61
+0.8.62
+0.8.63
+0.8.64
+0.8.65
+0.8.66
+0.8.67
+0.8.68
+0.8.69
+0.8.70
+0.8.71
+0.8.72
+0.8.73
+0.8.74
+0.8.75
+0.8.76
+0.8.77
+0.8.78
+0.8.79
+
+0.6.42
+0.6.42a
+0.6.43
+0.6.44
+0.6.45
+0.6.46
+0.6.47
+0.6.48
+
+0.32
+
+obsolete
+
+0.22
+
+0.31
+
+0.7
+
+2.6.19 (git)
+2.6.19 (tarball)
+
+2.6.19 support.
+LTTV 0.8.73 has important statistics fixes.
+LTTV 0.8.76 adds TASK_DEAD support, new task state from 2.6.19.
+
+

+

+
+0.8.61
+0.8.62
+0.8.63
+0.8.64
+0.8.65
+0.8.66
+0.8.67
+0.8.68
+0.8.69
+0.8.70
+0.8.71
+0.8.72
+0.8.73
+0.8.74
+0.8.75
+0.8.76
+0.8.77
+0.8.78
+0.8.79
+
+0.6.51
+0.6.52
+
+0.33
+
+obsolete
+
+0.22
+
+0.31
+
+0.7
+
+2.6.20-rc1-git7 (git)
+2.6.20-rc1-git7 (tarball)
+
+2.6.20-rc1-git7 support.
+LTTng 0.6.52 fixes the ltt-heartbeat synthetic TSC with cpu hotplug.
+LTTng 0.6.52 marks ltt-heartbeat pediodic events as EXPERIMENTAL, as it is +problematic with cpu hotplug and trace stop/restart sequence.
+
+

+

+
+0.8.61
+0.8.62
+0.8.63
+0.8.64
+0.8.65
+0.8.66
+0.8.67
+0.8.68
+0.8.69
+0.8.70
+0.8.71
+0.8.72
+0.8.73
+0.8.74
+0.8.75
+0.8.76
+0.8.77
+0.8.78
+0.8.79
+
+0.6.53
+0.6.54
+0.6.55
+0.6.55a
+0.6.56
+0.6.58
+
+0.34
+
+obsolete
+
+0.23
+
+0.31
+0.32
+
+0.7
+
+2.6.20-rc4-git3 (git)
+2.6.20-rc4-git3 (tarball)
+
+2.6.20-rc4-git3 support.
+Change marker format to %p[type], %u[extended type], i.e. : %p[struct +task_struct], %u[__be].
+LTTng 0.6.56 and genevent 0.32 changes the preempt_enable_no_resched() calls for +a preempt_enable() call.
+LTTng 0.6.58 fixes markers for i386 : optimized version correctly does XMC +following Pentium III erratum 49.
+
+

+

+
+0.8.61
+0.8.62
+0.8.63
+0.8.64
+0.8.65
+0.8.66
+0.8.67
+0.8.68
+0.8.69
+0.8.70
+0.8.71
+0.8.72
+0.8.73
+0.8.74
+0.8.75
+0.8.76
+0.8.77
+0.8.78
+0.8.79
+
+0.6.59
+0.6.60
+0.6.61
+
+0.34
+
+obsolete
+
+0.23
+
+0.31
+0.32
+
+0.7
+
+2.6.20-rc5-git4 (git)
+2.6.20-rc5-git4 (tarball)
+
+2.6.20-rc5-git4 support.
+Use cpuid instruction in pIII XMC.
+
+

+

+
+0.8.61
+0.8.62
+0.8.63
+0.8.64
+0.8.65
+0.8.66
+0.8.67
+0.8.68
+0.8.69
+0.8.70
+0.8.71
+0.8.72
+0.8.73
+0.8.74
+0.8.75
+0.8.76
+0.8.77
+0.8.78
+0.8.79
+
+0.6.62
+0.6.63
+0.6.64
+0.6.68
+0.6.69
+0.6.70
+0.6.71
+0.6.72
+0.6.73
+0.6.74
+0.6.75
+0.6.76
+0.6.77
+
+0.34
+0.35
+
+obsolete
+
+0.23
+0.24
+0.25
+0.26
+0.27
+
+0.31
+0.32
+
+0.7
+
+2.6.20-rc6
+ARM, MIPS32/64, powerpc32, powerpc64, ppc, i386, x86_64
+2.6.20
+ARM, MIPS32/64, powerpc32, powerpc64, ppc, i386, x86_64
+
+2.6.20-rc6 support.
+Use my own infrastructure to support intel pIII xmc instead of using +kprobes.
+ltt-usertrace 0.25 fixes signal reentrancy in ltt-usertrace-fast.
+LTTng 0.6.64 adds 2.6.20 support.
+LTTng 0.6.68 fixes regression for MIPS, ARM, powerpc64.
+LTTng 0.6.69 fixes menus, regression for i686 ok, fixes an upstream bug for +sparc64.
+LTTng 0.6.70 fixes regression for m68k.
+LTTng 0.6.71 fixes regression for ppc 405 and sparc (fixing alignment of +.markers.c section).
+LTTng 0.6.71 fixes upstream error in ppc/powerpc.
+LTTng 0.6.72 fixes corrects .markers.c section declaration : fixes +regression on sparc.
+LTTng 0.6.72 fixes regression on alpha.
+ltt-usertrace 0.27 fixes a signal race.
+LTTng 0.6.77 puts all process events in medium rate process channel.
+ltt-control 0.35 adds the Xen facility.
+Matches LTTng for xen-unstable up to changeset: 14390.
+
+ARM, mips, mipsel, powerpc64, i386, sparc64, m68k, ia64, s390, sparc, alpha.
+
+arm26, avr32, cris, frv, h8300, m32r, m68knommu, parisc, sh, sh64, um, v850, +xtensa.
+
+0.8.80
+0.8.81
+
+0.6.78
+0.6.80
+
+0.36
+
+obsolete
+
+0.28
+
+0.33
+
+0.8
+
+2.6.20
+ARM, MIPS32/64, powerpc32, powerpc64, ppc, i386, x86_64
+
+Add compact channel.
+Fix start of trace get full timestamp. Caused problems with time gap between +trace create/start and 32 bits (or less) TSC in events.
+Ok for xen-unstable changeset starting at 14391.
+Adds TSC testing in kernel.
+Adds monotonic logical clock based on highest TSC count for x86 and x86_64 with +async TSCs.
+Fix missing compat_fs_exec event : rename to fs_exec.
+
+ +ARM, mips, mipsel, powerpc64, i386, sparc64, m68k, ia64, s390, sparc, alpha.
+arm26, avr32, cris, frv, h8300, m32r, m68knommu, parisc, sh, sh64, um, v850, +xtensa.
+
+0.8.82
+
+0.9.0
+
+0.37
+
+obsolete
+
+0.29
+
+0.34
+
+0.8
+
+2.6.20
+ARM, MIPS32/64, powerpc32, powerpc64, ppc, i386, x86_64
+
+Introduces the LTT serialization library. Cuts the kernel patch size from +2M to 500k. Deprecates genevent for kernel instrumentation, but keeps backward +compatibility with genevent generated code (still used for user space tracing). +Slightly longer execution time at instrumentation site (270ns vs 200ns for 4 +bytes write on P4 3GHz), but the global advantage of using less memory, and +therefore less cache, should overweight this cost in cycles. Note : In this +version, the ltt-probe-* kernel modules and the XML definitions must be kept in +sync by hand, otherwise, LTTV will detect a size mismatch between the kernel +trace and the expected event size..
+
+arm, i686, ia64, m68k, mips, mipsel, x86_64, powerpc 405, powerpc64, s390, +sparc, sparc64.
+
+alpha, arm26, avr32, cris, frv, h8300, m32r, m68knommu, parisc, sh, sh64, um, +v850, xtensa.
+
+0.8.82
+
+0.9.1
+0.9.2
+0.9.3
+0.9.4
+0.9.5
+0.9.6
+
+0.38
+0.39
+0.40
+0.43
+
+obsolete
+
+0.30
+0.31
+0.32
+
+0.34
+
+0.8
+
+2.6.21-rc6-mm1
+2.6.21
+2.6.21-mm2
+ARM, MIPS32/64, powerpc32, powerpc64, ppc, i386, x86_64
+2.6.22-rc2-mm1
+
+Ported to Andrew Morton's tree.
+LTTng 0.9.3 and ltt-usertrace 0.32 fixes a syscall parameter size mismatch +between 64 bits kernel and 32 bits user-space processes for user-space +tracing.
+LTTng 0.9.5 adds support for kernel 2.6.21.
+LTTng 0.9.6 supports kernel 2.6.21-mm2.
+
+ +alpha, arm26, avr32, cris, frv, h8300, m32r, m68knommu, parisc, sh, sh64, um, +v850, xtensa.
+arm, i686, ia64, m68k, mips, mipsel, x86_64, powerpc 405, powerpc64, s390, +sparc, sparc64.
+
+0.8.83
+0.8.84
+
+0.9.7
+0.9.8
+0.9.9
+0.9.10
+
+0.41
+0.42
+0.43
+
+obsolete
+
+0.33
+
+0.35
+
+0.8
+
+2.6.22-rc2-mm1
+2.6.22-rc4-mm2
+
+LTTng 0.9.7 supports kernel 2.6.22-rc2-mm1.
+automake updates in LTTV and ltt-control.
+format="" format strings support for XML descriptions.
+LTTng 0.9.9 adds support for kernel 2.6.22-rc4-mm2.
+LTTng 0.9.10 fixes a bug in i386 optimized immediate values.
+ltt-control 0.43 fixes a bug with LTTng lseek event.
+
+ +alpha, arm26, avr32, cris, frv, h8300, m32r, m68knommu, parisc, sh, sh64, um, +v850, xtensa.
+arm, i686, ia64, m68k, mips, mipsel, x86_64, powerpc 405, powerpc64, s390, +sparc, sparc64.
+
+0.8.83
+
+0.9.10
+
+0.41
+0.42
+0.43
+0.44
+
+obsolete
+
+Not available (syscall IDs not updated)
+
+obsolete
+
+0.8
+
+2.6.22.1-rt4
+
+LTTng 0.9.10 port to 2.6.22.1-rt4.
+LTT control 0.44 fixes facilities makefile.
+
+Tested on x86_32. + +alpha, arm26, avr32, cris, frv, h8300, m32r, m68knommu, parisc, sh, sh64, um, +v850, xtensa.
+arm, i686, ia64, m68k, mips, mipsel, x86_64, powerpc 405, powerpc64, s390, +sparc, sparc64.
+
+0.10.0-pre1
+
+0.10.0-pre5
+0.10.0-pre6
+0.10.0-pre7
+0.10.0-pre8
+0.10.0-pre9
+0.10.0-pre10
+0.10.0-pre11
+0.10.0-pre12
+0.10.0-pre13
+0.10.0-pre14
+0.10.0-pre15
+0.10.0-pre16
+
+0.45
+
+obsolete
+
+Not available (syscall IDs not updated)
+
+obsolete
+
+1.0
+
+2.6.23-mm1
+2.6.23.1
+2.6.24-rc1-git11
+2.6.24-rc1-git13
+
+Simplified trace_mark().
+Remove XML.
+Per marker activation through /proc/ltt.
+Crash dump trace extraction.
+LTTV --edebug for raw binary even debugging, hexedit style.
+LTTng is now mostly arch-agnostic, using a non precise logical clock +fallback.
+LTTng 0.10.0-pre6 is a compile fix.
+LTTng 0.10.0-pre7 adds the upstream 2.6.23.1 sata_mv fix.
+LTTng 0.10.0-pre7-port to 2.6.23.1.
+LTTng 0.10.0-pre8 fixes x86_64 segfault due to thread flags and fixes +ltt-serialize x86_64 va_list argument passing array nonsense.
+LTTng 0.10.0-pre9 fixes SH and polishes the menus.
+LTTng 0.10.0-pre9 fixes s390 instrumentation mistake.
+LTTng 0.10.0-pre12 checkpatch coding style fixes.
+LTTng 0.10.0-pre16 Change LTT menu location : now in general setup.
+LTTng 0.10.0-pre17
+LTTng 0.10.0-pre18 : supports 2.6.24-rc1-git13, add markers support for multiple +probes. Immediate values updates.
+
+Tested on x86_32. + +alpha, arm26, avr32, cris, frv, h8300, m32r, m68knommu, parisc, sh, sh64, um, +v850, xtensa.
+arm, i686, ia64, m68k, mips, mipsel, x86_64, powerpc 405, powerpc64, s390, +sparc, sparc64.
+
+0.10.0-pre2
+
+0.10.0-pre20
+0.10.0-pre21
+0.10.0-pre22
+0.10.0-pre23
+0.10.0-pre24
+0.10.0-pre25
+0.10.0-pre26
+0.10.0-pre27
+0.10.0-pre28
+
+0.46
+
+obsolete
+
+Not available (syscall IDs not updated)
+
+obsolete
+
+1.0
+
+2.6.24-rc2
+2.6.24-rc2-git3
+2.6.24-rc2-git5
+2.6.24-rc3-git1
+
+Fix multi-probes markers.
+LTTng 0.10.0-pre22 fixes multi-probes markers, fixes markers mutex usage at +module load.
+LTTng 0.10.0-pre22 fixes a multi-probes pointer bug.
+LTTng 0.10.0-pre24, announcement to LKML.
+
+Tested on x86_32. + +alpha, arm26, avr32, cris, frv, h8300, m32r, m68knommu, parisc, sh, sh64, um, +v850, xtensa.
+arm, i686, ia64, m68k, mips, mipsel, x86_64, powerpc 405, powerpc64, s390, +sparc, sparc64.
+
+0.10.0-pre3
+0.10.0-pre4
+
+0.10.0-pre30
+0.10.0-pre31
+0.10.0-pre32
+0.10.0-pre33
+0.10.0-pre34
+0.10.0-pre35
+0.10.0-pre36
+
+0.46
+
+obsolete
+
+Not available (syscall IDs not updated)
+
+obsolete
+
+1.0
+
+2.6.24-rc3-git1
+2.6.24-rc3-git3
+
+Fix process release in LTTV.
+Add missing APIC interrupts on x86 and x86_64.
+lttng pre31, pre32, pre33 fixes x86_64 instrumentation.
+lttng pre36 adds listing of syscalls, interrupts and softirqs. KALLSYMS is +useful to get the symbol names.
+lttng pre36 fixes the missing ltt-test-tsc. in Kconfig options.
+
+Tested on x86_32. + +alpha, arm26, avr32, cris, frv, h8300, m32r, m68knommu, parisc, sh, sh64, um, +v850, xtensa.
+arm, i686, ia64, m68k, mips, mipsel, x86_64, powerpc 405, powerpc64, s390, +sparc, sparc64.
+
+0.10.0-pre5
+0.10.0-pre6
+0.10.0-pre7
+0.10.0-pre8
+0.10.0-pre9
+0.10.0-pre10
+0.10.0-pre11
+
+0.10.0-pre37
+0.10.0-pre38
+0.10.0-pre39
+0.10.0-pre40
+0.10.0-pre41
+0.10.0-pre42
+
+0.46
+
+obsolete
+
+Not available (syscall IDs not updated)
+
+obsolete
+
+1.0
+
+2.6.24-rc4
+2.6.24-rc4-git3
+2.6.24-rc5-git7
+2.6.24-rc8-git3
+
+Fix process end of life (LTTng/LTTV).
+Fix endianness (LTTV).
+LTTV 0.10.0-pre6 shows system call and softirq names. (depends on KALLSYMS)
+LTTV 0.10.0-pre7 fixes large irq tables.
+LTTng 0.10 pre10 fixes a segfault in state.c
+
+Tested on x86_32. +Tested on x86_64. + +alpha, arm26, avr32, cris, frv, h8300, m32r, m68knommu, parisc, sh, sh64, um, +v850, xtensa.
+arm, i686, ia64, m68k, mips, mipsel, x86_64, powerpc 405, powerpc64, s390, +sparc, sparc64.
+
+0.10.0-pre5
+0.10.0-pre6
+0.10.0-pre7
+0.10.0-pre8
+0.10.0-pre9
+0.10.0-pre10
+0.10.0-pre11
+
+0.10.0-pre43
+
+0.47
+
+obsolete
+
+Not available (syscall IDs not updated)
+
+obsolete
+
+1.0
+
+2.6.24
+
+LTTng 0.10.0-pre43 supports kernel 2.6.24
+
+ +x86, alpha, arm26, avr32, cris, frv, h8300, m32r, m68knommu, parisc, sh, sh64, +um, v850, xtensa.
+arm, i686, ia64, m68k, mips, mipsel, x86_64, powerpc 405, +powerpc64, s390, sparc, sparc64.
+
+0.10.0-pre5
+0.10.0-pre6
+0.10.0-pre7
+0.10.0-pre8
+0.10.0-pre9
+0.10.0-pre10
+0.10.0-pre11
+0.10.0-pre12
+0.10.0-pre13
+
+0.10.0-pre44
+0.10.0-pre45
+0.10.0-pre47
+0.10.0-pre49
+0.10.0-pre50
+0.10.0-pre51
+0.10.0-pre52
+0.10.0-pre53
+0.10.0-pre54
+0.10.0-pre56
+
+0.48
+
+obsolete
+
+Not available (syscall IDs not updated)
+
+obsolete
+
+1.0
+
+2.6.25-rc3
+2.6.25-rc6-git8
+2.6.25-rc7-git6
+2.6.25-rc9-git1
+2.6.25
+2.6.25.4
+2.6.26-rc8
+2.6.26-rc9
+
+LTTng 0.10.0-pre44 supports kernel 2.6.25-rc3 and includes various fixes.
+LTTng 0.10.0-pre50 fixes x86 NMIs instrumentation, includes workaround for RCU +preempt for markers (in mainline rc9).
+LTTng 0.10.0-pre51 fixes the buffer switch in active mode.
+LTTng 0.10.0-pre52 adds nop/jump optimization to immediate values and fix NMI in +the Linux kernel.
+LTTng 0.10.0-pre52 supports 2.6.25.
+LTTng 0.10.0-pre55 supports 2.6.25.4, includes bugfixes.
+LTTV 0.10-pre12 fixes control flow view interaction with newer GTK versions.
+LTTV 0.10-pre13 fixes ressource view interaction with newer GTK versions.
+LTTng 0.10-pre56 supports kernel 2.6.26-rc8.
+LTTng 0.10-pre57 starts using Tracepoints and re-introduces userspace tracing +through a userspace marker infrastructure, only for x86 for now.
+LTTng 0.10-pre58 fixes tracepoint probe build in some configurations and x86 +arch-specific irq instrumentation.
+LTTng 0.11 has an immediate values bugfix which could case a kernel OOPS when +enabling tracepoints or markers.
+
+ +x86, alpha, arm26, avr32, cris, frv, h8300, m32r, m68knommu, parisc, sh, sh64, +um, v850, xtensa.
+arm, i686, ia64, m68k, mips, mipsel, x86_64, powerpc 405, +powerpc64, s390, sparc, sparc64.
+
+0.10.0-pre5
+0.10.0-pre6
+0.10.0-pre7
+0.10.0-pre8
+0.10.0-pre9
+0.10.0-pre10
+0.10.0-pre11
+0.10.0-pre12
+0.10.0-pre13
+0.10.0-pre14
+
+0.10
+0.11
+0.12
+0.13
+0.14
+0.15
+
+0.49
+0.50
+
+obsolete
+
+0.5
+
+obsolete
+
+1.0
+
+2.6.26
+2.6.26.1
+
+LTTng 0.10 support kernel 2.6.26 and adds markers support for userspace on x86 +32 and 64 bits. ltt-control 0.49 contains sample scripts to enable all userspace +markers visible in /proc at script execution.
+ltt-control 0.50 fixes lttd mutex usage in when multithreaded (-N x).
+lttv 0.10.0-pre14 fixes support of traces where CPU are hotplugged.
+LTTng 0.14 includes a fix to the LTTng buffering scheme, found by running a Spin +model through Promela.
+LTTng 0.15 has whitespace cleanups.
+
+ +x86, alpha, arm26, avr32, cris, frv, h8300, m32r, m68knommu, parisc, sh, sh64, +um, v850, xtensa.
+arm, i686, ia64, m68k, mips, mipsel, x86_64, powerpc 405, +powerpc64, s390, sparc, sparc64.
+
+0.10.0-pre15
+
+0.16
+0.17
+0.18
+0.19
+0.20
+0.21
+0.22
+0.23
+0.24
+0.25
+0.26
+
+0.51
+0.52
+
+obsolete
+
+0.6
+
+obsolete
+
+1.0
+
+2.6.27-rc2
+2.6.27-rc3
+2.6.27-rc6
+2.6.27-rc7
+
+LTTng 0.16 supports 2.6.27-rc2, including ftrace and "taps".
+LTTV 0.10.0-pre15 includes some fixes in state.c (uninitialized variables).
+lttctl 0.51 adds support for taps.
+markers-userspace 0.6 supports kernel 2.6.27-rc2.
+LTTng 0.17 uses per-cpu variables to keep ltt_nesting.
+LTTng 0.18 reverts the formal-verif fix patch, which was broken since LTTng +0.14.
+LTTng 0.19 really just fixes the "author" in the lttng git tree.
+LTTng 0.23 contains a build fix for psrwlock on x86_32.
+LTTng 0.24 fixes the git tree.
+LTTng 0.25 fixes x86_64 compilation.
+LTTng 0.26 supports kernel 2.6.27-rc7.
+lttctl 0.52 fixes the ltt-armall script.
+
+ +x86, alpha, arm26, avr32, cris, frv, h8300, m32r, m68knommu, parisc, sh, sh64, +um, v850, xtensa.
+arm, i686, ia64, m68k, mips, mipsel, x86_64, powerpc 405, +powerpc64, s390, sparc, sparc64.
+
+0.10.0-pre15
+
+0.27
+0.28
+0.29
+0.30
+0.31
+0.32
+0.33
+0.34
+0.35
+0.36
+
+0.53
+
+obsolete
+
+0.6
+
+obsolete
+
+1.0
+
+2.6.27-rc7
+2.6.27-rc8
+2.6.27-rc9
+
+LTTng 0.27 and lttd (ltt-control) 0.53 implement a vmap-less buffering scheme +using splice().
+LTTng 0.28 includes a small build fix.
+LTTng 0.29 supports 2.6.27-rc8.
+LTTng 0.32 supports 2.6.27-rc9 and adds a spinlock/irqoff buffering scheme +(can be used with lttctl -T relay-locked).
+LTTng 0.33, 0.34 : checkpatch.pl coding style fixes. 0.35, 0.36 : size_t cast.
+
+ +x86, alpha, arm26, avr32, cris, frv, h8300, m32r, m68knommu, parisc, sh, sh64, +um, v850, xtensa.
+arm, i686, ia64, m68k, mips, mipsel, x86_64, powerpc 405, +powerpc64, s390, sparc, sparc64.
+
+0.11.0
+
+0.38
+
+0.53
+0.54
+
+obsolete
+
+0.6
+
+obsolete
+
+2.0
+
+2.6.27
+
+LTTng 0.38 and LTTV 0.11.0
+
+I just cleaned up the LTTng trace format heavily, got rid of the +heartbeat timer (by checking for overflow at the tracing site), got rid +of the "special" compact channel; it removed about 5 FIXMEs in LTTng +(cpu hotplug and compact channel related). Everything is compact now :
+
+This is much more compact than the previous format, and permits a +"tracer debug mode" in menuconfig which enables the "event size" field, +which helps cross-checking the size expected by the userspace tool and +the size written by the kernel.
+
+Note that this "event size" field can eventually be enabled on a +per-event or per-channel basis. This will probably be useful to encode +"binary blobs"...
+
+I also cleanup up the subbuffer header to make is much smaller than the +previous one by removing unneeded information.
+
+I renamed the "facilities" channel to a better name : "metadata". It +contains marker descriptions.
+
+The trace major number is bumped to "2" and is completely incompatible +with old LTTV.
+
+ +x86, alpha, arm26, avr32, cris, frv, h8300, m32r, m68knommu, parisc, sh, sh64, +um, v850, xtensa.
+arm, i686, ia64, m68k, mips, mipsel, x86_64, powerpc 405, +powerpc64, s390, sparc, sparc64.
+
+0.11.1
+0.11.2
+0.11.3
+
+0.39
+0.40
+0.41
+0.42
+0.43
+0.44
+
+0.53
+0.54
+0.55
+
+obsolete
+
+0.6
+
+obsolete
+
+2.1
+
+2.6.27
+
+LTTng 0.39 and LTTV 0.11.1 fixes 32-64 bits subbuffer header portability.
+LTTng 0.41 reintroduces the formal verification fix, which solves the +case where the reader thinks the uncommitted subbuffer is fully committed. +Unlikely to happen, but could, with small buffers especially.
+
+ +x86, alpha, arm26, avr32, cris, frv, h8300, m32r, m68knommu, parisc, sh, sh64, +um, v850, xtensa.
+arm, i686, ia64, m68k, mips, mipsel, x86_64, powerpc 405, +powerpc64, s390, sparc, sparc64.
+
+ +
+ + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/lttng-lttv-new-features.html b/tags/lttv-0.11.3-23102008/doc/developer/lttng-lttv-new-features.html new file mode 100644 index 00000000..4ada4245 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/lttng-lttv-new-features.html @@ -0,0 +1,34 @@ + + +
LTTV & LTTng new features
+
+
+

+One might ask what are the new features present in LTTng and LTTV that are not +in the LTT toolkit. Here are the major enhancements.
+
+Here is a list of the new features in LTTV :
+Modular architecture based on plugins.
+Reads from several traces. It supports SMP machines.
+Synchronized graphical views. Plugins can cooperate to show the trace +together.
+Supports event types with metainformation.
+Supports huge traces : 10-15 GB.
+
+
+Here are the new features in LTTng/genevent :
+Real ~100ns time accuracy, with monotonic increment.
+Implements the "facility" event type metainformation.
+Supports variable size event records.
+Supports nested compound types : structures, array, sequences, unions, strings.
+Supports compiler and architecture independant elementary types.
+Supports ISO C compatible dynamic alignment of data.
+Solid atomic (lockless) logging (NMI, traps, faults handler safe).
+Supports writing to multiple traces at once (eventually with different +filters).
+Uses separate RelayFS channels for interrupts, modules, process and facility +events.
+ + + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/lttng-lttv-roadmap.html b/tags/lttv-0.11.3-23102008/doc/developer/lttng-lttv-roadmap.html new file mode 100644 index 00000000..02d2fa0e --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/lttng-lttv-roadmap.html @@ -0,0 +1,125 @@ + + +

LTTV & LTTng roadmap
+
+
+Here are the roadmaps for the LTTV and LTTng development. I use a priority indice +for the TODO items :
+(1) : very high priority
+(10): lowest priority
+
+
+Dependencies are written between brackets [ ].
+The # symbol marks who is currently working on the item.
+The % symbol marks who is interested in the realisation of the item.
+
+LTTV Roadmap
+
+* TODO
+(3) Add cluster support.
+ # Eric Clement
+(3) Make LTTV aware of type formats (visual separators) defined in the XML +file.
+ # Gabriel Matni
+(4) Statistics per time window.
+(4) Add Xen per physical CPU view.
+(4) Add Xen per vcpu view.
+(4) Disable plugins when threshold reached (i.e. too much process in control +flow view). Draw, and, when the threshold is reached, stop drawing. The global +statistics view can inhibit showing the per process stats.
+(4) Add a visual artifact : PID 0 could be named swapper instead of UNNAMED for +cpus > 0.
+(4) Add event specific fields support to filter.
+(4) Add a periodic event interval view. (useful to verify event periodicity)
+(4) create a graphical per cpu activity view.
+(4) Filter by target process.
+(4) Compensate for time spent in probes in LTTV analysis.
+(4) Add CPU, network, disk, memory usage histogram. [Per interval statistics]
+(4) Add sort by process priority in the control flow view (must also instrument +priority information of the processes).
+% Airbus
+(5) Add Python scripting hooks.
+(5) Add GUI interface to take an hybrid trace.
+(5) Flight recorder : start lttd automatically upon GUI trace control stop.
+(5) Automatically detect traces with too much processes and disable faulty operations.
+(5) Event sequence detector (inspired from regular expressions).
+(7) Create a hardware counter viewer (low cost rate counters : L1 cache miss, +page faults, interrupts...). This will be a generalisation of the event rate +view into a view of the evolution of a user definable event field.
+
+* TO FIX
+(10) Add cancel button to LTTV filter GUI window.
+(10) Sometimes, in the control flow view, a process with 0 creation time is +created in addition to the real process itself. Seems to be caused by end of +process life.
+(10) Statistics do not take in account the time spent in the mode present at +the beginning of the trace. Example : real time spent in system call on behalf +of process 0.
+
+
+LTT Next Generation Roadmap
+
+* TODO
+(1) Dump mounts.
+(1) Bring userspace tracing in sync with new lttng.
+(1) Bring stack dump in sync with new lttng.
+(1) efficient dynamic event filtering while recording trace.
+ % Google
+ % Sensis Corp. Tim Bish
+ # Mathieu Desnoyers
+ - Partly implemented: per marker selection through /proc interface.
+ - Must still implement per active trace filtering.
+(1) Add Xen support. (Trace buffer desallocation needs to be fixed)
+ # Mathieu Desnoyers
+(1) Integrate SystemTAP logging with LTTng.
+(4) instrument kernel bottom half irqsave, seqlocks, semaphores, mutexes, brlock.
+(4) integrate NPTL instrumentation (see +PTT).
+(4) Probe calibration kernel module.
+(4) Make page faults detect nested fault without nesting 4 times in the page +fault handler.
+(5) Support CPUs with scalable frequency.
+(5) Add boot time tracing support.
+(5) Integrate LTTng and lttd with LKCD.
+(7) Integrate periodical dump of perfctr hardware counters.
+(8) Integrate periodical dump of SystemTAP computed information.
+(9) Add support for setjmp/longjmp and jump tables instrumentation to +ltt-instrument-functions.
+(9) Make ltt-usertrace-fast support internal heartbeat timer.
+(3) port LTTng to :
+alpha
+user-mode Linux
+Xen
+# IBM
+S/390
+RTLinux
+% Wind River for 2.6.14
+sparc64
+# Wind River
+sh4
+
+
+
+* TODO (low priority)
+enhance RPM packages for lttng kernel
+Integrate header generation (genevent) in kernel build system.
+Export channels via network sockets instead of writing them to disk.
+Export buffers with time constraint for "live" visualisation. Use +ltt_force_switch periodically from a timer to insure slow channels do not +interfere with viewing.
+Have an optional round-robin mode to write information into multiple channels +from the same source.
+
+* Need to be discussed
+Use 2.6.14 RelayFS control files.
+Drop ltt-module-register and ltt-module-unregister, use exported variables.
+drop ltt_filter_control, use functions pointers instead.
+Merge facilities headers into one big header.
+Change the name of XML files from XML to something else.
+Remove ltt-base.c.
+
+Mathieu Desnoyers
+ + + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/lttng-new-marker-event-desc.txt b/tags/lttv-0.11.3-23102008/doc/developer/lttng-new-marker-event-desc.txt new file mode 100644 index 00000000..a22b40f2 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/lttng-new-marker-event-desc.txt @@ -0,0 +1,119 @@ + +Mathieu Desnoyers, June 2007 + +Marker design + +Events are described in data structures in DECLARE_MARKER(), in the same file +the marker is in, or exported and in a globally included header. + +When a new marker is encountered (module load, marker armed), an event_load +event is traced in the facilities channel. If the marker refers to an already +existing marker declaration, nothing changes, except that it must check that the +types match. + +At trace start, we take the superset of the filtered in events and arm their +markers. We trace an event load event for each armed marker encountered. + +Format string verification by GCC. + +Format string extensions (sequence, structs, arrays..) + +Field names. + + +User space tracing + +Declare their own DECLARE_MARKER, use MARK() macros that refers to it. At module +load, (to decide) either the kernel or the library walks on the markers sections +and issues a marker_load system call. Facility namespacing prefix "user_" is +used for userspace facilities. + +We have to deal with multiple registrations of the same event with different +caracteristics but same name. + +Markers must be enabled by the kernel, if activated and tracing starts. + +Modify linker script to get section start and end in programs. + + +------------------------- + +Maybe DECLARE_MARKER is not necessary: + +Use LTTV dictionnary to add information about events. +event and field description. + + + assdfas + asdfasdf + .... asdfasdf + + +Field names + +trace_mark(facility_event, "field1 %u field2 %p", asdf, asdf); + +Extract the field names from the format string. +Check for format string equivalence before enabling a marker. Allow enabling +markers with the same format string as the first one found, but warn about the +others which do not match. +Trace an event_load event for each enabled marker with non-NULL format string at +trace start and when they are activated. Since we want to keep the same format +string if module loads/unload/reload, and we cannot point into this module's +memory, we reallocate the entry and copy the format string whenever the newly +loaded module contains active markers and these markers have a NULL format +string. +When we enable a marker, if the marker has a non null format string, we trace a +marker_load. + +Default : cardinal field1, field2.... +Calling: ltt_trace +callback[0]: ltt_serialize_data + +Types + +-- Add network byte ordering to types. +-- Change format strings to use backward compatible types. (format check) + +Reserve 8 IDs for core events. Never allow wrapping over 65535 IDs during a +trace : need compaction when no trace is active. + +Marker hash table points to a structure containing the marker's event ID (16 +bits). Assign event ID when a probe is registered. + +Compact channel. Use separate 8 bits event IDs for events going in the +compact channel. Identified by registering probe with id type MARKER_ID_COMPACT. + +update probes (temp) + +-- Enabling a marker: +-- give: state marker name, channel. +/proc/markers: enable/disable name [channel] + +probe_data should provide, for ltt: +callbacks +id <-- will stay in marker because otherwise we would need another hash table. +channel index : put inside the marker structure (parameter to register). +A marker control module + +Q: field names in dictionnary or in marker ? + + +What users will do with marker interface: + +from userspace: +enable/disable marker_name (act on any marker, reg by module or other) +channel marker_name channel_name (act on any marker) + +Default: use serializer +But.. module can override _default_ probe with a "register" operation. + +Use case +Set channel +Register by specific module (unregister default, register specific) +activate. + +Serve as a proxy for probe registration. + + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/lttng-synthetic-tsc-msb.txt b/tags/lttv-0.11.3-23102008/doc/developer/lttng-synthetic-tsc-msb.txt new file mode 100644 index 00000000..ffac4289 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/lttng-synthetic-tsc-msb.txt @@ -0,0 +1,52 @@ + +LTTng synthetic TSC MSB + +Mathieu Desnoyers, Mars 1, 2006 + +A problem found on some architectures is that the TSC is limited to 32 bits, +which induces a wrap-around every 8 seconds or so. + +The wraps arounds are detectable by the use of a heartbeat timer, which +generates an event in each trace at periodic interval. It makes reading the +trace sequentially possible. + +What causes problem is fast time seek in the trace : it uses the buffer +boundary timestamps (64 bits) to seek to the right block in O(log(n)). It +cannot, however, read the trace sequentially. + +So the problem posed is the following : we want to generate a per cpu 64 bits +TSC from the available 32 bits with the 32 MSB generated synthetically. I should +be readable by the buffer switch event. + +The idea is the following : we keep a 32 bits previous_tsc value per cpu. It +helps detect the wrap around. Each time a heartbeat fires or a buffer switch +happens, the previous_tsc is read, and then written to the new value. If a wrap +around is detected, the msb_tsc for the cpu is atomically incremented. + +We are sure that there is only one heartbeat at a given time because they are +fired at fixed interval : typically 10 times per 32bit TSC wrap around. Even +better, as they are launched by a worker thread, it can only be queued once in +the worker queue. + +Now with buffer switch vs heartbeat concurrency. Worse case : a heartbeat is +happenning : one CPU is in process context (worker thread), the other ones are +in interrupt context (IPI). On one CPU in IPI, we have an NMI triggered that +generates a buffer switch. + +What is sure is that the heartbeat needs to read and write the previous_tsc. It +also needs to increment atomically the msb_tsc. However, the buffer switch only +needs to read the previous_tsc, compare it to the current tsc and read the +msb_tsc. + +Another race case is that the buffer switch can be interrupted by the heartbeat. + +So what we need is to have an atomic write. As the architecture does not support +64 bits cmpxchg, we will need this little data structure to overcome this +problem : + +An array of two 64 bits elements. Elements are updated in two memory writes, but +the element switch (current element) is made atomically. As there is only one +writer, this has no locking problem. + +We make sure the synthetic tcs reader does not sleep by disabling preemption. We +do the same for the writer. diff --git a/tags/lttv-0.11.3-23102008/doc/developer/lttng-userspace-tracing-lite.txt b/tags/lttv-0.11.3-23102008/doc/developer/lttng-userspace-tracing-lite.txt new file mode 100644 index 00000000..fe63f0d8 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/lttng-userspace-tracing-lite.txt @@ -0,0 +1,69 @@ + + +Mathieu Desnoyers + +March 2006 + +Here is a simpler, but slower, version of user space tracing design. + +It will be useful on architectures where doing a system call is mandatory, for +example when CPUs are lacking a 64 bits TSC : it must be worked around by doing +a system call to the the time or the synthetic MSB of the TSC (yes, disabling +preemption is at least required). + + +So the design is simple : + +Two system calls : + +One system call for event logging + + +ltt_trace_generic +args : +fac_id +event_id +data pointer +data size + +ltt_register_generic +args: +struct pointer (in) +fac_id pointer (out) + +#define NAME_MAX 4096 + +struct : +char name[NAME_MAX] +u32 num_events +u32 checksum +u32 alignment +u32 int_size +u32 long_size +u32 pointer_size +u32 size_t_size + + +If a facility is registered twice, the same handle is used. + For a facility to be the exact same, it must share _every_ element of the + struct (even type sizes). +Potential problem : If a facility is registered, it is never unregistered. + +Now.. not being able to free facilities when they are not used is not fun. So +how can we know every process that has registered a facility have finished using +it ? If we know that, we might do a cleanup when _all_ the traces are destroyed: +this is way better than a reboot. + +A solution might be to keep a reference count to the fac_id : it would be +incremented upon registration and decremented when a process exits. To do that, +a process must keep an array of fac ids it uses. 0 is unset. + +CONFIG option : + +CONFIG_LTT_USERSPACE_GENERIC + + + + + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/lttng-userspace-tracing.txt b/tags/lttv-0.11.3-23102008/doc/developer/lttng-userspace-tracing.txt new file mode 100644 index 00000000..d61953f5 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/lttng-userspace-tracing.txt @@ -0,0 +1,314 @@ + +Some thoughts about userspace tracing + +Mathieu Desnoyers January 2006 + + + +* Goals + +Fast and secure user space tracing. + +Fast : + +- 5000ns for a system call is too long. Writing an event directly to memory + takes 220ns. +- Still, we can afford a system call for buffer switch, which occurs less often. +- No locking, no signal disabling. Disabling signals require 2 system calls. + Mutexes are implemented with a short spin lock, followed by a yield. Yet + another system call. In addition, we have no way to know on which CPU we are + running when in user mode. We can be preempted anywhere. +- No contention. +- No interrupt disabling : it doesn't exist in user mode. + +Secure : + +- A process shouldn't be able to corrupt the system's trace or another + process'trace. It should be limited to its own memory space. + + + +* Solution + +- Signal handler concurrency + +Using atomic space reservation in the buffer(s) will remove the requirement for +locking. This is the fast and safe way to deal with concurrency coming from +signal handlers. + +- Start/stop tracing + +Two possible solutions : + +Either we export a read-only memory page from kernel to user space. That would +be somehow seen as a hack, as I have never even seen such interface anywhere +else. It may lead to problems related to exported types. The proper, but slow, +way to do it would be to have a system call that would return the tracing +status. + +My suggestion is to go for a system call, but only call it : + +- when the thread starts +- when receiving a SIGRTMIN+3 (multithread ?) + +Note : save the thread ID (process ID) in the logging function and the update +handler. Use it as a comparison to check if we are a forked child thread. +Start a brand new buffer list in that case. + + +Two possibilities : + +- one system call per information to get/one system call to get all information. +- one signal per information to get/one signal for "update" tracing info. + +I would tend to adopt : + +- One signal for "general tracing update" + One signal handler would clearly be enough, more would be unnecessary + overhead/pollution. +- One system call for all updates. + We will need to have multiple parameters though. We have up to 6 parameters. + +syscall get_tracing_info + +parameter 1 : trace buffer map address. (id) + +parameter 2 : active ? (int) + + +Concurrency + +We must have per thread buffers. Then, no memory can be written by two threads +at once. It removes the need for locks (ok, atomic reservation was already doing +that) and removes false sharing. + + +Multiple traces + +By having the number of active traces, we can allocate as much buffers as we +need. Allocation is done in the kernel with relay_open. User space mapping is +done when receiving the signal/starting the process and getting the number of +traces actives. + +It means that we must make sure to only update the data structures used by +tracing functions once the buffers are created. + +We could have a syscall "get_next_buffer" that would basically mmap the next +unmmapped buffer, or return NULL is all buffers are mapped. + +If we remove a trace, the kernel should stop the tracing, and then get the last +buffer for this trace. What is important is to make sure no writers are still +trying to write in a memory region that get desallocated. + +For that, we will keep an atomic variable "tracing_level", which tells how many +times we are nested in tracing code (program code/signal handlers) for a +specific trace. + +We could do that trace removal in two operations : + +- Send an update tracing signal to the process + - the sig handler get the new tracing status, which tells that tracing is + disabled for the specific trace. It writes this status in the tracing + control structure of the process. + - If tracing_level is 0, well, it's fine : there are no potential writers in + the removed trace. It's up to us to buffer switch the removed trace, and, + after the control returns to us, set_tracing_info this page to NULL and + delete this memory area. + - Else (tracing_level > 0), flag the removed trace for later switch/delete. + + It then returns control to the process. + +- If the tracing_level was > 0, there was one or more writers potentially + accessing this memory area. When the control comes back to the writer, at the + end of the write in a trace, if the trace is marked for switch/delete and the + tracing_level is 0 (after the decrement of the writer itself), then the + writer must buffer switch, and then delete the memory area. + + +Filter + +The update tracing info signal will make the thread get the new filter +information. Getting this information will also happen upon process creation. + +parameter 3 for the get tracing info : a integer containing the 32 bits mask. + + +Buffer switch + +There could be a tracing_buffer_switch system call, that would give the page +start address as parameter. The job of the kernel is to steal this page, +possibly replacing it with a zeroed page (we don't care about the content of the +page after the syscall). + +Process dying + +The kernel should be aware of the current pages used for tracing in each thread. +If a thread dies unexpectedly, we want the kernel to get the last bits of +information before the thread crashes. + +Memory protection + +If a process corrupt its own mmaped buffers, the rest of the trace will be +readable, and each process have its own memory space. + +Two possibilities : + +Either we create one channel per process, or we have per cpu tracefiles for all +the processes, with the specification that data is written in a monotically +increasing time order and that no process share a 4k page with another process. + +The problem with having only one tracefile per cpu is that we cannot safely +steal a process'buffer upon a schedule change because it may be currently +writing to it. + +It leaves the one tracefile per thread as the only solution. + +Another argument in favor of this solution is the possibility to have mixed +32-64 bits processes on the same machine. Dealing with types will be easier. + + +Corrupted trace + +A corrupted tracefile will only affect one thread. The rest of the trace will +still be readable. + + +Facilities + +Upon process creation or when receiving the signal of trace info update, when a +new trace appears, the thread should write the facility information into it. It +must then have a list of registered facilities, all done at the thread level. + +We must decide if we allow a facility channel for each thread. The advantage is +that we have a readable channel in flight recorder mode, while the disadvantage +is to duplicate the number of channels, which may become quite high. To follow +the general design of a high throughput channel and a low throughput channel for +vital information, I suggest to have a separate channel for facilities, per +trace, per process. + + + +API : + +syscall 1 : + +in : +buffer : NULL means get new traces + non NULL means to get the information for the specified buffer +out : +buffer : returns the address of the trace buffer +active : is the trace active ? +filter : 32 bits filter mask + +return : 0 on success, 1 on error. + +int ltt_update(void **buffer, int *active, int *filter); + +syscall 2 : + +in : +buffer : Switch the specified buffer. +return : 0 on success, 1 on error. + +int ltt_switch(void *buffer); + + +Signal : + +SIGRTMIN+3 +(like hardware fault and expiring timer : to the thread, see p. 413 of Advances +prog. in the UNIX env.) + +Signal is sent on tracing create/destroy, start/stop and filter change. + +Will update for itself only : it will remove unnecessary concurrency. + + + +Notes : + +It doesn't matter "when" the process receives the update signal after a trace +start : it will receive it in priority, before executing anything else when it +will be scheduled in. + + + +Major enhancement : + +* Buffer pool * + +The problem with the design, up to now, is if an heavily threaded application +launches many threads that has a short lifetime : it will allocate memory for +each traced thread, consuming time and it will create an incredibly high +number of files in the trace (or per thread). + +(thanks to Matthew Khouzam) +The solution to this sits in the use of a buffer poll : We typically create a +buffer pool of a specified size (say, 10 buffers by default, alterable by the +user), each 8k in size (4k for normal trace, 4k for facility channel), for a +total of 80kB of memory. It has to be tweaked to the maximum number of +expected threads running at once, or it will have to grow dynamically (thus +impacting on the trace). + +A typical approach to dynamic growth is to double the number of allocated +buffers each time a threashold near the limit is reached. + +Each channel would be found as : + +trace_name/user/facilities_0 +trace_name/user/cpu_0 +trace_name/user/facilities_1 +trace_name/user/cpu_1 +... + +When a thread asks for being traced, it gets a buffer from free buffers pool. If +the number of available buffers falls under a threshold, the pool is marked for +expansion and the thread gets its buffer quickly. The expansion will be executed +a little bit later by a worker thread. If however, the number of available +buffer is 0, then an "emergency" reservation will be done, allocating only one +buffer. The goal of this is to modify the thread fork time as less as possible. + +When a thread releases a buffer (the thread terminates), a buffer switch is +performed, so the data can be flushed to disk and no other thread will mess +with it or render the buffer unreadable. + +Upon trace creation, the pre-allocated pool is allocated. Upon trace +destruction, the threads are first informed of the trace destruction, any +pending worker thread (for pool allocation) is cancelled and then the pool is +released. Buffers used by threads at this moment but not mapped for reading +will be simply destroyed (as their refcount will fall to 0). It means that +between the "trace stop" and "trace destroy", there should be enough time to let +the lttd daemon open the newly created channels or they will be lost. + +Upon buffer switch, the reader can read directly from the buffer. Note that when +the reader finish reading a buffer, if the associated thread writer has +exited, it must fill the buffer with zeroes and put it back into the free pool. +In the case where the trace is destroyed, it must just derement its refcount (as +it would do otherwise) and the buffer will be destroyed. + +This pool will reduce the number of trace files created to the order of the +number of threads present in the system at a given time. + +A worse cast scenario is 32768 processes traced at the same time, for a total +amount of 256MB of buffers. If a machine has so many threads, it probably have +enough memory to handle this. + +In flight recorder mode, it would be interesting to use a LRU algorithm to +choose which buffer from the pool we must take for a newly forked thread. A +simple queue would do it. + +SMP : per cpu pools ? -> no, L1 and L2 caches are typically too small to be +impacted by the fact that a reused buffer is on a different or the same CPU. + + + + + + + + + + + + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/lttng-xen.txt b/tags/lttv-0.11.3-23102008/doc/developer/lttng-xen.txt new file mode 100644 index 00000000..38a3e543 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/lttng-xen.txt @@ -0,0 +1,86 @@ + + +LTTng for Xen + +Design document + +Mathieu Desnoyers, November 2006 + +Last modification : + +Only allow a single channel. Directory consumed by a lttd-xen client through +hypercalls. This design choice is made because there are no RCU lists and there +is no link between the number of CPUs seen by Xen and number of CPUs in the +dom0. + +Does better than Xentrace : +Variable size records +Easily extensible event description +// not planned : missing RCU list. Multiple active traces +Reader uses poll()-like mechanism instead of polling the buffers each 100ms. + + +* Dom0 lttd-xen process + +Does an hypercall to map the buffers (increment refcount) +Does an hypercall to poll for data, put to sleep by the OS. +Uses write from the mapped buffers to a file. +unmap buffer, decrement refcount. + + +* Xen + +tb_control (hypercalls seen from dom0 OS) +(hypercalls done by lttctl-xen) +- trace_create +Map a page for control information, return it for mapping by user space. (rw) +Map multiple pages for buffers. return them for mapping by user space. +(shared, read-only for consumer) +increment refcount +- trace_start +Activate tracing in control information. +- trace_stop +Stop tracing in control information. +- trace_destroy +wait for pending writers (is the hypervisor preemptible ? not full.) + Preemption : not full -> spinlock does not disable preemption : would deadlock + for the non irq_disable case. + volountary : poll call -> volountary. explicit __schedule call., do_yield(). +Quiescent state detection. + for_each_domain( d ) + for_each_vcpu( d, v ) + vcpu_sleep_sync(v); +decrement refcount : destroy the channels + +Events coming from xen : +- Buffer switch + through virq sent to dom0 +- wakes up poll on the buffers FD. + + +* Dom0 Linux OS + +Receive virq upon buffer switch. +xen_wakeup_readers handler : wakes up the readers for the channel. + +Hypercalls are autonomous from LTTng linux kernel tracing. + +on lttngxentrace create, hypercall channel_create +on lttngxentrace start, hypercall channel_start +on lttngxentrace stop, hypercall channel_stop +on lttngxentrace destroy, hypercall channel_destroy + + +* Facilities + +Export a facilities per cpu channel too. + + +Q1 : In Xen preemptible ? Full ? Volountary ? Volountary X +Q2 : How to synchronize to wait for writers to finish writing ? X + + +Number of CPUs in dom0 != number of CPUs in Xen. +CPU hotplug in kernel tracing ? Done. + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/lttng_java_instrumentation.txt b/tags/lttv-0.11.3-23102008/doc/developer/lttng_java_instrumentation.txt new file mode 100644 index 00000000..9078a3f1 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/lttng_java_instrumentation.txt @@ -0,0 +1,12 @@ + +Random notes on Java instrumentation + +Mathieu Desnoyers, May 2006 + +http://www.nag.com/IndustryArticles/CallingCLibraryRoutinesfromJava.pdf +Use : Java Native Interface (JNI) + +String : +http://java.sun.com/developer/onlineTraining/Programming/JDCBook/jnistring.html +http://java.sun.com/docs/books/jni/html/objtypes.html + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/lttv.html b/tags/lttv-0.11.3-23102008/doc/developer/lttv.html new file mode 100644 index 00000000..0f2f6dd3 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/lttv.html @@ -0,0 +1,417 @@ + + + + Linux Trace Toolkit trace analysis tools + + + +

Linux Trace Toolkit trace analysis tools

+ +

The Linux Trace Toolkit Visualizer, lttv, is a modular and extensible +tool to read, analyze, annotate and display traces. It accesses traces through +the libltt API and produces either textual output or graphical output using +the GTK library. This document describes the architecture of lttv for +developers. + +

Lttv is a small executable which links to the trace reading API, libltt, +and to the glib and gobject base libraries. +By itself it contains just enough code to +convert a trace to a textual format and to load modules. +The public +functions defined in the main program are available to all modules. +A number of +text modules may be dynamically loaded to extend the capabilities of +lttv, for instance to compute and print various statistics. + +

A more elaborate module, traceView, dynamically links to the GTK library +and to a support library, libgtklttv. When loaded, it displays graphical +windows in which one or more viewers in subwindows may be used to browse +details of events in traces. A number of other graphical modules may be +dynamically loaded to offer a choice of different viewers (e.g., process, +CPU or block devices state versus time). + +

Main program: main.c

+ +

The main program parses the command line options, loads the requested +modules and executes the hooks registered in the global attributes +(/hooks/main/before, /hooks/main/core, /hooks/main/after). + +

Hooks for callbacks: hook.h (hook.c)

+ +

In a modular extensible application, each module registers callbacks to +insure that it gets called at appropriate times (e.g., after command line +options processing, at each event to compute statistics...). Hooks and lists +of hooks are defined for this purpose and are normally stored in the global +attributes under /hooks/*. + +

Browsable data structures: iattribute.h (iattribute.c)

+ +

In several places, functions should operate on data structures for which the +list of members is extensible. For example, the statistics printing +module should not be +modified each time new statistics are added by other modules. +For this purpose, a gobject interface is defined in iattribute.h to +enumerate and access members in a data structure. Even if new modules +define custom data structures for efficiently storing statistics while they +are being computed, they will be generically accessible for the printing +routine as long as they implement the iattribute interface. + +

Extensible data structures: attribute.h (attribute.c)

+ +

To allow each module to add its needed members to important data structures, +for instance new statistics for processes, the LttvAttributes type is +a container for named typed values. Each attribute has a textual key (name) +and an associated typed value. +It is similar to a C data structure except that the +number and type of the members can change dynamically. It may be accessed +either directly or through the iattribute interface. + +

Some members may be LttvAttributes objects, thus forming a tree of +attributes, not unlike hierarchical file systems or registries. This is used +for the global attributes, used to exchange information between modules. +Attributes are also attached to trace sets, traces and contexts to allow +storing arbitrary attributes. + +

Modules: module.h (module.c)

+ +

The benefit of modules is to avoid recompiling the whole application when +adding new functionality. It also helps insuring that only the needed code +is loaded in memory. + +

Modules are loaded explicitly, being on the list of default modules or +requested by a command line option, with g_module_open. The functions in +the module are not directly accessible. +Indeed, direct, compiled in, references to their functions would be dangerous +since they would exist even before (if ever) the module is loaded. +Each module contains a function named init. Its handle is obtained by +the main program using g_module_symbol and is called. +The init function of the module +then calls everything it needs from the main program or from libraries, +typically registering callbacks in hooks lists stored in the global attributes. +No module function other than init is +directly called. Modules cannot see the functions from other modules since +they may or not be loaded at the same time. + +

The modules must see the declarations for the functions +used, from the main program and from libraries, by including the associated +.h files. The list of libraries used must be provided as argument when +a module is linked. This will insure that these libraries get loaded +automatically when that module is loaded. + +

Libraries contain a number of functions available to modules and to the main +program. They are loaded automatically at start time if linked by the main +program or at module load time if linked by that module. Libraries are +useful to contain functions needed by several modules. Indeed, functions +used by a single module could be simply part of that module. + +

A list of loaded modules is maintained. When a module is requested, it +is verified if the module is already loaded. A module may request other modules +at the beginning of its init function. This will insure that these modules +get loaded and initialized before the init function of the current module +proceeds. Circular dependencies are obviously to be avoided as the +initialization order among mutually dependent modules will be arbitrary. + +

Command line options: option.h (option.c)

+ +

Command line options are added as needed by the main program and by modules +as they are loaded. Thus, while options are scanned and acted upon (i.e., +options to load modules), the +list of options to recognize continues to grow. The options module registers +to get called by /hooks/main/before. It offers hooks /hooks/option/before +and /hooks/option/after which are called just before and just after +processing the options. Many modules register in their init function to +be called in /hooks/options/after to verify the options specified and +register further hooks accordingly. + +

Trace Analysis

+ +

The main purpose of the lttv application is to process trace sets, +calling registered hooks for each event in the traces and maintaining +a context (system state, accumulated statistics). + +

Trace Sets: traceSet.h (traceSet.c)

+ +

Trace sets are defined such that several traces can be analyzed together. +Traces may be added and removed as needed to a trace set. +The main program stores a trace set in /trace_set/default. +The content of the trace_set is defined by command line options and it is +used by analysis modules (batch or interactive). + +

Trace Set Analysis: processTrace.h (processTrace.c)

+ +

The function lttv_process_trace_set loops over all the events +in the specified trace set for the specified time interval. Before +Hooks are first +called for the trace set and for each trace and tracefile +(one per cpu plus control tracefiles) in the trace set. +Then hooks are called for +each event in sorted time order. Finally, after hooks are called +for the trace set and for each trace and tracefile in it. + +

To call all the event hooks in sorted time order, a priority queue +(or sorted tree) is used. The first event from each tracefile is read and its +time used as key in the sorted tree. The event with the lowest key is removed +from the tree, the next event from that tracefile is read and reinserted in +the tree. + +

Each hook is called with a LttvContext gobject as call data. The LttvContext +object for the trace set before/after hooks is provided in the call to +lttv_process_trace_set. Shallow copies of this context are made for each +trace in the trace set for the trace before/after hooks. Again, shallow +copies of each trace context are made for each tracefile in a trace. +The context for each tracefile is used both for the tracefile before/after +hooks and when calling the hooks for the contained events. + +

The lttv_process_trace_set function sets appropriately the fields in the +context before calling a hook. For example, when calling a hook event, +the context contains: + +

+
trace_set_context
context for the trace set. +
trace_context
context for the trace. +
ts
trace set. +
t
trace. +
tf
tracefile. +
e
event. +
+ +

The cost of providing all this information in the context is relatively +low. When calling a hook from one event to the next, in the same tracefile, +only the event field needs to be changed. +The contexts used when processing traces are key to extensibility and +performance. New modules may need additional data members in the context to +store intermediate results. For this purpose, it is possible to derive +subtypes of LttvContext in order to add new data members. + + +

Reconstructing the system state from the trace: state.h (state.c)

+ +

The events in a trace often represent state transitions in the traced +system. When the trace is processed, and events accessed in time sorted +order, it is thus possible to reconstruct in part the state of the +traced system: state of each CPU, process, disk queue. The state of each +process may contain detailed information such as opened file descriptors +and memory map if needed by the analysis and if sufficient information is +available in the trace. This incrementally updated state information may be +used to display state graphs, or simply to compute state dependent +statistics (time spent in user or system mode, waiting for a file...). + +

+When tracing starts, at T0, no state is available. The OS state may be +obtained through "initial state" events which enumerate the important OS data +structures. Unless the state is obtained atomically, other events +describing state changes may be interleaved in the trace and must be +processed in the correct order. Once all the special initial state +events are obtained, at Ts, the complete state is available. From there the +system state can be deduced incrementally from the events in the trace. + +

+Analysis tools must be prepared for missing state information. In some cases +only a subset of events is traced, in others the trace may be truncated +in flight recorder mode. + +

+In interactive processing, the interval for which processing is required +varies. After scrolling a viewer, the events in the new interval to display +need to be processed in order to redraw the view. To avoid restarting +the processing at the trace start to reconstruct incrementally the system +state, the computed state may be memorized at regular interval, for example at +each 100 000 events, in a time indexed database associated with a trace. +To conserve space, it may be possible in some cases to only store state +differences. + +

To process a specific time interval, the state at the beginning of the +interval would be obtained by copying the last preceeding saved state +and processing the events since then to update the state. + +

A new subtype of LttvContext, LttvStateContext, is defined to add storage +for the state information. It defines a trace set state as a set of trace +state. The trace state is composed of processes, CPUs and block devices. +Each CPU has a currently executing process and each process state keeps +track the interrupt stack frames (faults, interrupts, +system calls), executable file name and other information such as opened +file descriptors. Each frame stores the process status, entry time +and last status change time. + +

File state.c provides state updating hooks to be called when the trace is +processed. When a scheduling change event is delivered to the hook, for +instance, the current process for the CPU is changed and the state of the +incoming and outgoing processes is changed. +The state updating hooks are stored in the global attributes under +/hooks/state/core/trace_set/before, after, +/hooks/state/core/trace/before, after... +to be used by processing functions requiring state updating (batch and +interactive alalysis, computing the state at time T by updating a preceeding +saved state...). + +

Computing Statistics: stats.h (stats.c)

+ +

This file defines a subtype of LttvStateContext, LttvStatsContext, +to store statistics on various aspects of a trace set. The LttvTraceSetStats +structure contains a set of LttvTraceStats structures. Each such structure +contains structures for CPUs, processes, interrupt types (IRQ, system call, +fault), subtypes (individual system calls, IRQs or faults) and +block devices. The CPUs also contain structures for processes, interrupt types, +subtypes and block devices. Process structures similarly contain +structures for interrupt types, subtypes and block devices. At each level +(trace set, trace, cpu, process, interrupt stack frames) +attributes are used to store statistics. + +

File stats.c provides statistics computing hooks to be called when the +trace is processed. For example, when a write event is processed, +the attribute BytesWritten in the corresponding system, cpu, process, +interrupt type (e.g. system call) and subtype (e.g. write) is incremented +by the number of bytes stored in the event. When the processing is finished, +perhaps in the after hooks, the number of bytes written and other statistics +may be summed over all CPUs for a given process, over all processes for a +given CPU or over all traces. + +

The basic set of statistics computed by stats.c include for the whole + trace set: + +

    +
  • Trace start time, end time and duration. +
  • Total number of events. +
  • Number of each event type (Interrupts, faults, system calls...) +
  • For each interrupt type and each subtype, the number of each event type. +
  • For each system: +
      +
    • Total number of events. +
    • Number of each event type (Interrupts, faults, system calls...) +
    • For each interrupt type and each subtype, the number of each event type. +
    • For each CPU: +
        +
      • CPU id +
      • User/System time +
      • Number of each event type +
      • For each interrupt type and each subtype, + the number of each event type. +
      +
    • For each block device: +
        +
      • block device name +
      • time busy/idle, average queue length +
      • Number of each relevant event type (requests added, merged, served) +
      +
    • For each process: +
        +
      • Exec'ed file names. +
      • Start and end time, User/System time +
      • Number of each event type +
      • For each interrupt type and each subtype, + the number of each event type. +
      +
    +
+ +

The structure to store statistics differs from the state storage structure +in several ways. Statistics are maintained in different ways (per CPU all +processes, per process all CPUs, per process on a given CPU...). Furthermore, +statistics are maintained for all processes which existed during the trace +while the state at time T only stores information about current processes. + +

The hooks defined by stats.c are stored in the global attributes under +/hooks/stats/core/trace_set/before, after, +/hooks/stats/core/trace/before, after to be used by processing functions +interested in statistics. + +

Filtering events: filter.h (filter.c)

+ +

+Filters are used to select which events in a trace are shown in a viewer or are +used in a computation. The filtering rules are based on the values of +events fields. The filter module receives a filter expression and computes +a compiled filter. The compiled filter then serves as hook data for +check event +filter hooks which, given a context containing an event, +return TRUE or FALSE to +indicate if the event satisfies the filter. Trace and tracefile check +filter hooks +may be used to determine if a system and CPU satisfy the filter. Finally, +the filter module has a function to return the time bounds, if any, imposed +by a filter. + +

For some applications, the hooks provided by the filter module may not +be sufficient, since they are based on simple boolean combinations +of comparisons between fields and constants. In that case, custom code may be +used for check hooks during the processing. An example of complex +filtering could be to only show events belonging to processes which consumed +more than 10% of the CPU in the last 10 seconds. + +

In module filter.c, filters are specified using textual expressions +with AND, OR, NOT operations on +nested subexpressions. Primitive expressions compare an event field to +a constant. In the graphical user interface, a filter editor is provided. + +


+tokens: ( ! && || == <= >= > < != name [ ] int float string )
+
+expression = ( expression ) OR ! expression OR
+     expression && expression OR expression || expression OR 
+     simple_expression
+
+simple_expression = field_selector OP value
+
+value = int OR float OR string OR enum
+
+field_selector = component OR component . field_selector
+
+component = name OR name [ int ]
+
+ + +

Batch Analysis: batchAnalysis.h (batchAnalysis.c)

+ +

This module registers to be called by the main program (/hooks/main/core). +When called, it gets the current trace set (/trace_set/default), +state updating hooks (/hooks/state/*) the statistics hooks +(/hooks/stats/*) and other analysis hooks (/hooks/batch/*) +and runs lttv_process_trace_set for the entire +trace set time interval. This simple processing of the complete trace set +is normally sufficient for batch operations such as converting a trace to +text and computing various statistics. + + +

Text output for events and statistics: textDump.h (textDump.c)

+ +

+This module registers hooks (/hooks/batch) +to print a textual representation of each event +(event hooks) and to print the content of the statistics accumulated in the +context (after trace set hook). + +

Trace Set Viewers

+ +

+A library, libgtklttv, is defined to provide utility functions for +the second set of modules, wich compose the interactive graphical user +interface. It offers functions to create and interact with top level trace +viewing windows, and to insert specialized embedded viewer modules. +The libgtklttv library requires the gtk library. +The viewer modules include a detailed event list, eventsTableView, +a process state graph, processStateView, and a CPU state graph, cpuStateView. + +

+The top level gtkTraceSet, defined in libgtklttv, +window has the usual FILE EDIT... menu and a toolbar. +It has an associated trace set (and filter) and contains several tabs, each +containing several vertically stacked time synchronized trace set viewers. +It manages the space allocated to each contained viewer, the menu items and +tools registered by each contained viewer and the current time and current +time interval. + +

+When viewers change the current time or time interval, the gtkTraceSet +window notifies all contained viewers. When one or more viewers need +redrawing, the gtkTraceSet window calls the lttv_process_trace_set +function for the needed time interval, after computing the system state +for the interval start time. While events are processed, drawing hooks +from the viewers are called. + +

+TO COMPLETE; description and motivation for the gtkTraceSet widget structure +and interaction with viewers. Description and motivation for the detailed +event view and process state view. + + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/lttv_filter_specification.docbook b/tags/lttv-0.11.3-23102008/doc/developer/lttv_filter_specification.docbook new file mode 100644 index 00000000..9de15a07 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/lttv_filter_specification.docbook @@ -0,0 +1,502 @@ + + + + + + + +Spécifications du filtre de Linux Trace Toolkit Viewer + + +Mathieu +Desnoyers + + + +26/01/2005 +1.00.00 + + + +Ce document décrit les spécifications requises pour la fonctionnalité de +filtrage pour l'application +Linux Trace Toolkit Viewer. + + + + + +Linux Trace Toolkit Viewer +spécification +filtre +specification +filter + + + + +Introduction + + +Le filtre de Linux Trace Toolkit Viewer est une fonctionnalité nécessaire pour +rendre l'outil pleinement utilisable. Des cas typiques de filtrage ont déjà été +identifiés : filtrer par CPU ou par ID d'événement. + + +Cependant, un utilisateur plus avancé peut vouloir filtrer selon n'importe quel +champs d'information disponible lors de la lecture d'un événement. L'approche +qui sera suivie, afin d'assurer l'applicabilité du filtrage aux différents types +de traces, sera de ne pas limiter les champs selon lesquels le filtrage pourra +être fait. + + +Comme le filtrage se fait à la lecture de chaque événement, il peut rapidement +devenir un goulot d'étranglement important. Les performances doivent donc être +un souci de taille lors de la réalisation de cette fonctionnalité. C'est +pourquoi l'architecture proposée se base sur une compilation des règles de +filtrage lors de leur définition afin de générer une structure de données +optimale pour le parcours de la trace. + + +Ce document explique les différents défis à surmonter dans les différents +sous-modules du filtre, soit : la partie "core" du filtre, qui sera intégrée, +comme son nom l'indique, au coeur de l'application ainsi que les parties +modulaires textes et graphiques. À la fin du document, des optimisations +éventuelles sont énoncées, sans plus. Elles pourront être utiles dans le cas où +les performances s'avéreraient problématiques. + + +Ce document est le fruit d'un échange entre Michel Dagenais et moi-même, Mathieu +Desnoyers. Certains passages sont laissés sous leur forme originale. + + + + + + +Design + + +Core + + + Application des règles de filtrage aux événements. Les règles de + filtrage pourraient être représentées par un arbre. Cette section du + filtrage est assez intégrée au reste de l'application pour mériter d'être + au coeur même de lttv (pas dans un module séparé). Les feuilles de l'arbre + sont des 3-tuples (champs, relation, valeur), alors que les noeuds + intermédiaires sont des relations logiques (and, or, xor, not). Le and, le + or et le xor sont limités à deux enfants, alors que le not est limité à un + seul enfant. + + + Les champs du 3-tuple devraient respecter une arborescence qui représente + l'organisation des données dans les événements. Celles-ci sont, lors de la + lecture de la trace, accessibles via les structures : + + +LttEvent (plus les champs spécifiques à l'événement) +LttvTracefile +LttvTrace +LttvTraceset +LttvState + + + On pourrait donc, au niveau de la description du champs, représenter + celui-ci par une chaîne de caractères imbriquable dont les niveaux sont + séparés par le ".". Voici la représentation des niveaux d'imbrication : + + + + * + |->event (pour accéder aux champs sous LttEvent) + | |->name (string) + | |->category (string) + | |->time (LttTime) + | |->tsc (LttCycleCount) + | |->fields + | |->"event name" + | |->"field name" + | |->"sub-field name" + | |->... + | |->"leaf-field name" (field type) + | + |->tracefile + | |->name (string) + |->trace + | |->name (string) + |->state + |->pid (guint) + |->ppid (guint) + |->creation_time (LttTime) + |->insertion_time (LttTime) + |->process_name (string) + |->execution_mode (user_mode, syscall, trap, irq, unknown) + |->execution_submode (none, unknown) + |->process_status (wait_fork,wait_cpu,exit,zombie,wait,run,unnamed) + |->cpu (guint) + + + + +L'objet contenant l'arbre des règles de filtrage ainsi que la cache du filtre, + qu'on pourrait appeler "LttvFilter", serait associé à une trace, non pas à un + trace set, pour des raisons de performance. En effet, le même nom d'événement + peut très bien être associé à un ID différent d'une trace à l'autre. Comme on + ne souhaite pas faire la translation nom->id (qui est coûteuse) à chaque + utilisation du filtre, on le ferait lors de sa construction. Ceci implique de + garder un objet LttvFilter par trace. Rien n'empêche cependant d'avoir une + façon de créer, au niveau usager, des filtres pour toutes les traces d'un + traceset, mais ceux-ci seront associés à chaque trace du trace set. + + + +Michel Dagenais : + + +Je m`inquiète beaucoup pour la performance. Il faut pouvoir précompiler +ces expressions en offset (ltt_field) et avoir un espèce d`index pour ne +pas passer séquentiellement à travers toutes les règles et pour chaque +règle interpréter les noms de champs à chaque événement traité!! + + + +Mathieu Desnoyers : + + +C'est ce que j'avais en tête : fixer les positions des champs au moment de la +création de la règle de filtrage, quitte à la recalculer si jamais la trace +change en cours de route (mais ça ne devrait pas arriver, puisque les paires +(facility, event id) sont uniques au cours d'une trace). + + + +Cependant, de la manière dont je vois ça, on aura pas le choix de se garder un +arbre représentant l'expression logique et de le parcourir séquentiellement pour +chaque événement. On peut évidemment éviter de balayer certaines branches en se +basant sur les relations and, or, xor lors du parcours. + + + +Donc, je vois ça, dans le pire cas, comme un parcours infixe de l'arbre +représentant les règles. Chaque feuille serait une règle représentée par un +3-tuple (position, (type, relation), valeur), où chaque paire de (type,relation) +devrait être défini pour chaque type (possiblement incorrect, comme la relation +< sur un type string). Lors de la compilation, on passerait du 3-tuple (champs, +relation, valeur) à un 3-tuple (position, (type, relation), valeur). + + + +À noter : un simple offset n'est en réalité pas assez pour représenter la +position, puisque toutes les données ne résident pas dans une seule structure. +Certaines sont dans le contexte (TracesetContext), d'autres dans la structure de +l'événement. Il faut donc décrire la position, en réalité, comme une paire +(structure, offset), où nous limitons structure aux noms de structure connus +(qui peuvent être encodés sur la forme de GQuarks) : {LTTV_TRACE, +LTTV_TRACEFILE, LTTV_TRACE_STATE, LTT_EVENT}. + + + +Lors de la compilation, on a, en entrée, le tuple : + + + +(champs, relation, valeur) + + + +où champs est un tuple : (structure, offset, type) + + + +On produit, en sortie, (toujours dans la même structure en arbre pour les +expressions logiques), les 3-tuples suivants (aux feuilles) : + + + +(position, fonction, valeur) + + + +où : + +position = (structure, offset) +fonction = (type, relation) + + + + +Il me reste une question : que fait-on lors qu'un facility est rechargé en cours +de traçage ? Les événements vont-ils changer d'id ? + + + +Michel Dagenais : + + +Non, c`est un nouveau facility avec le même nom mais un fingerprint +différent qui s`ajoute puisqu`il est possible que des modules utilisent +l`ancienne version alors que d`autres utilisent la nouvelle +simultanément. Il est possible que les règles ne spécifient que le nom +de la facilité auquel cas elles pourraient s`appliquer à toutes les +facilités du même nom et demanderaient d`avoir une précompilation +différente pour chaque facilité. + + + +Mathieu Desnoyers : + + +J'en conclue que le 2-tuple (facility, event id) est unique pour la trace, c'est +ça ? + + +Michel Dagenais : + + +> Oui. + + + + + +Module texte + + +Lecture d'une chaîne de caractères formée d'expressions +booléennes qui décrivent le filtre à partir des opérations de base : +and, or, xor, not +et de parenthèses : ( et ). +Les entrées logiques de ce filtre sont composées 3-tuples +(champs, relation, valeur), +où le champs est le type d'information (i.e. pid) +la relation est la limite sur la valeur (i.e. <) +la valeur est la valeur qui doit être respectée par le champs selon la +relation. Doit être du type associé au champs. À priori, on utilise +le type de champs pour savoir sous quel type encoder l'information +lue, tout en vérifiant le contenu de la chaîne lue pour des +débordements (overflow) avant encodage vers le type associé. +La lecture de cette expression booléenne formerait un arbre de règles de +filtrage, qui serait ensuite utilisé par la partie "core" de filtrage de +lttv pour être appliqué aux événements lors de la lecture de trace. + + + + + +Module graphique + + +Une classe filtre graphique serait associée à un arbre +de règles de filtrage. On pourrait le modifier les objets de la classe +filtre graphique en invoquant une fenêtre de modification de filtre qui +permettrait d'entrer les spécifications du filtre à l'aide de champs +graphiques de sélection (drop down list) pour les types d'éléments selon +lesquels filtrer, et d'un champs d'entrée de texte libre pour spécifier la +valeur que ce champs doit prendre. La relation qui doit être appliquée +(<, >, <=, >=, =) doit être sélectionnée dans un autre drop-down list. +En plus de la sélection graphique, l'entrée d'une chaîne de caractère serait +possible pour spécifier le filtre selon l'entrée décrite ci-haut pour le +module texte. + + + +Michel Dagenais : + + +Oui, à la rigueur la partie graphique n`est probablement pas tellement +plus difficile que celle textuelle. On pourrait commencer avec seulement +la partie graphique qui produit directement la représentation en arbre. + + + +Mathieu Desnoyers : + + +Comme je prévois réutiliser l'entrée texte à l'intérieur de la fenêtre +graphique, je crois qu'il est préférable de commencer par l'entrée texte. +D'ailleurs, l'avantage pour quelqu'un qui commence à contribuer au projet est de +ne pas avoir à apprendre l'API de GTK en même temps qu'il fait le développement +de son module. Il est un peu trop facile de ne pas assez découpler la logique +interne de la présentation. + + + +Michel Dagenais : + + +Le cas classique est de choisir un CPU ou un type d`événement, auquel +cas un menu simple de CPU et type serait beaucoup plus pratique que +d`avoir à taper une expression. + + +Mathieu Desnoyers : + + +On pourrait penser à faire un module graphique de création de filtre de base, +pour les fonctionnalités les plus utilisées. Celui-ci pourra être étendu par +la suite, si besoin est. L'intérêt est d'avoir quand-même la possibilité, pour +un utilisateur plus avancé, de spécifier toutes les caractéristiques via +l'interface. Dans ce cas, quelqu'un serait tout-à-fait prêt à faire une +expression pour décrire en détail son filtre. C'est quand-même quelque chose de +plus en plus commun avec la venue des moteurs de recherche. + + + + + +Choix de syntaxe, documentation et messages d'erreur + +Michel Dagenais : + + +Oui, une partie non négligeable est un choix de syntaxe convivial, la +documentation et les messages d`erreur... + + +Mathieu Desnoyers : + + +C'est bel et bien ce qui sera perçu par l'utilisateur, de là l'importance.. + + +Je me demande s'il est mieux d'adopter une syntaxe un peu à la Google ou bien à +la C : + + + +(, ), and, or, xor, not, field op value +où op peut prendre : <, >, <=, >=, = + + + +ou bien à la C +(, ), &, |, ^, !, field op value + + + + +Ou bien on peut faire un alphabet mixte entre les deux, où les mots and et & +seraient équivalents. Ce serait probablement plus facile de reconnaître les +symboles comme & par contre, et moins limitant sur le traitement du mot +identifiant le field. + + + +Mais cette question est de moindre importance : tant qu'on se fixe un standard +et qu'on le documente, je crois qu'il n'y a pas vraiment de mauvais choix. + + + +Pour la documentation, l'ajout d'une section au guide de l'utilisateur (déjà en +docbook) me semble très adéquat. + + + +Pour les messages d'erreur, il faudra entres autres penser à valider les +opération par rapport à celles permises pour chaque type. Par exemple, un > n'a +pas vraiment de sens pour un string. + + + + +Michel Dagenais : + + +Je tendrais à prendre la syntaxe C. + + + +Mathieu Desnoyers : + + +Si on utilise de manière stricte la syntaxe C, il faudrait utiliser ceci : + + + +&&, ||, ==, (rien pour le xor logique ?) + + + +Je me dis que, puisque nous n'avons pas besoin des opérations "bitwise", nous +pourrions utiliser celles-ci. + + + +Donc, ce que je propose, est d'utiliser l'alphabet utilisé pour les opérateurs +bitwise du C en tant qu'opérateurs logiques pour notre parser d'expressions de +filtre : + + + +&, |, =, ^ + + + +Ceci est un détail sémantique, mais je veux juste m'assurer qu'on est d'accord. + + + + + + +Optimisation éventuelles + + + + "Cache" de filtre, afin d'optimiser le cas courant. + Au niveau de la partie "core" du filtrage, on pourrait faire une hash + table indexée par une clé formée d'un xor des champs à filtrer. Alors, si un + événement possède les même caractéristiques de filtrage, on pourrait accéder à + la solution (V/F) en O(1). Le coût est de calculer la clé de hashage pour + chaque événement. L'avantage apparaît lorsqu'il y a plusieurs critères de + filtrage à comparer. La remise à zéro de cette cache devrait être faite + lorsqu'il y a des modifications au filtre. + + + +Michel Dagenais : + + +Les travaux sur l`optimisation de requêtes dans les bases de données est +probablement pertinent pour ce genre de choses. + + + +Mathieu Desnoyers : + + +Il faudra que je m'y arrête. Cependant, ceci constitue une optimisation et n'est +donc pas crucial pour le fonctionnement. + + + + +Michel Dagenais : + + +Sauf si c`est inutilisable sans une telle optimisation :-). + + + +Mathieu Desnoyers : + + +N'est-ce pas toi qui m'a déjà dit, citant Donald Knuth : "Premature +optimisation is the root of all evil" ? :) + + + +Effectivement, si on s'aperçoit que c'est trop lent, il faudra passer à cette +étape. + + + + + + + + + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/lttv_gui_layout.txt b/tags/lttv-0.11.3-23102008/doc/developer/lttv_gui_layout.txt new file mode 100644 index 00000000..c3a82d21 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/lttv_gui_layout.txt @@ -0,0 +1,142 @@ +GUI Layout + + + + +In order to implement the GUI, choices has to be made based on habitual interfaces that we know users are familiar with. The inspiration for these choices came mainly from the Mozilla browser project and also from Openoffice, which are currently used as two userfriendly applications in various Linux distribution at the time of this writing. + +This document describes the layout of the GUI in three sections : containers, menus and toolbars. + +A status bar is also placed at the bottom of the window. + +- Containers + +elements hierarchy + +Window Mainwindow +|->vbox + |->menus + |->toolbar of the main window + |->toolbar of the currently selected viewer + |->notebook + | |->vpaned + | |->viewer's widget + | |->vpaned + | |->viewer's widget + | |->vpaned + | |->... + |->Status bar + +- Menus + +Here is a short description of each menu entry +* by itself means a separator + + +- File +*New -> *Empty trace set : open a new window with an empty trace set. + *Clone trace set : copy the content of the current window in a new + window. + * + *Tab : Opens a new tab. +*Open : open a trace set. Calls a file selection dialog. +*Close : close the current window. +*Close Tab : close the current tab. +* +*Add trace : Add a trace to the window's traceset. Calls file selection dialog. +*Remove trace : Removes a trace from the traceset. +*Save : save the trace set. Calls a file save dialog of no current filename. +*Save as : save a trace set. Calls a file save dialog. +* +*Quit : quit the program. + +- Edit ? (not needed for now) + +- View +*Zoom In : Multiply the zoom factor by a certain quantity. +*Zoom Out : Divide the zoom factor by a certain quantity. +*Zoom Extended : Show the entire traceset's largest time interval. +*Go to time : Keep same zoom, ask user for time to center view on and make + it the current time. +*Show time frame : ask user for time interval to show. + (modify zoom and current event in consequence) + +- Tools (this is an example of how viewer's menu entries should look like) +*Move viewer up -> Moves the current viewer up one position. +*Move viewer down -> Moves the current viewer down one position. +*Remove : remove the current viewer +* +*DumpToFile -> *Dump Text (This is a text module which adds graphical menu + entries) + *Dump binary +* (separator between text tools and graphical tools implies different function + to register each type of menu entries) +*Insert Events View : insert this type of viewer +*Insert ControlFlow View +- Plugins +*Load module : ask the user a module to load (list modules in search path). +*Unload module : list all modules, click to choose, then unload button. +*Add module search path : ask user for a new path (file selection dialog). + +- Options +//FIXME *Color : change the color of the currently selected element ? +*Filter : Show traceset's filter option window. +*Save configuration : Save the currently loaded modules/traceset/filters + to gconf. + + +(aligned to the right) +- Help +*Content +*About + + +- Toolbar + +The toolbar is separated in two parts : like the two lines used in Openoffice. The first one is applying to the top level window (or current tab) while the one below contains the current viewer's toolbar. + +So we have something like this : + +-------------------------------------------------------------------------------- +| Menus | +-------------------------------------------------------------------------------- +| Toolbar of the top level window | +-------------------------------------------------------------------------------- +| Toolbar of the current viewer | +-------------------------------------------------------------------------------- +||Current Tab| | +|-----------------------------------------------------------------------------|| +||viewers in vpaned || +|| || +||----------------------------------------------------------------------------|| +-------------------------------------------------------------------------------- +| Status bar | +-------------------------------------------------------------------------------- + + +The toolbar of the top level window is the only one described in this document, as the second one is defined by the viewers and specific to each of them. + +This toolbar is mainly a selection of the menu entries. + +New : New window with empty trace set. +Open : open a trace set. +Add Trace +Remove Trace +Save : save the current trace set. +Save as +* +Zoom in +(Show the current zoom factor, modifiable) +Zoom out +Zoom Extended +Go to time (shows time directly) +Show time frame (Could be a special field showing the time frame) +* +Move up current viewer +Move down current viewer +Delete current viewer +* +Add viewer's specific insertion buttons are added here. + + +Mathieu Desnoyers, June 2003 diff --git a/tags/lttv-0.11.3-23102008/doc/developer/lttv_guidetailed-event-list-redesign.txt b/tags/lttv-0.11.3-23102008/doc/developer/lttv_guidetailed-event-list-redesign.txt new file mode 100644 index 00000000..8159f469 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/lttv_guidetailed-event-list-redesign.txt @@ -0,0 +1,139 @@ + +Redesign of the GUI detailed event list + +Mathieu Desnoyers 08/2005 + +The basic problem about this list is that it uses the number of events, not the +time, as a vertical axis (for the Y axis scrollbar). + +Seeking in the traces is done by time. We have no clue of the number of events +between two times without doing preparsing. + +If we want to fully reuse textDump, it's bettwer if we depend upon state +computation. It would be good to make the viewer work with this information +missing though. + +textDump's print_field should be put in a lttv/lttv core file, so we can use it +as is in the detailed event list module without depending upon batchAnalysis. + + +* With preparsing only : + +The problem then becomes simpler : + +We can precompute the event number while doing state computation and save it +periodically with saved states. We can then use the event number in the trace +as scrollbar value, which, when scrolled, would result into a search in the +saved states by event number. + +How much time would it take to seek back to the wanted position from the last +saved state ? + +compudj@dijkstra:~/local/bin$ ./lttv -m batchtest -1 -2 -t +/home/compudj/traces/200MB +** Message: Processing trace while counting events (12447572 events in 14.0173 +seconds) +** Message: Processing trace while updating state (9.46535 seconds) + +9.46535 s / 12447572 events * 50000 events = 0.038 s + +38 ms latency shouldn't be too noticeable by a user when scrolling. + +(note : counting events batchtest does also verify time flow integrity and get +the position for each event (not optimal), that's why it takes 14s) + +As an optimisation, we could use a backing text buffer (an array of strings), +where we would save the 50000 computed events between two consecutive saved +states. + +Memory required : 50000 * 20 bytes/event = 1MB + +Which seems ok, but costy. In would be better, in fact, not to depend on the +saved states interval for such a value : we could keep a 1000 events array, for +instance (for 20KB cost, which is really better). + +The backing text buffer would, by itself, make sure it has a sufficient +number of events so a scroll up/down of one page would be responded directly. +That imply that a scroll up/down would first update the shown fields, and only +afterward make the backing buffer resync its events in the background. In the +case where the events were not directly available, it would have to update the +buffer in the foreground and only then show the requested events. + + +Important note : this design doesn't support filtering of events, which is + an important downside. + + + +* If we want the viewer to be able to show information without preparsing : + +This is the hardest the problem could get. We have to seek by time (even the +scrollbar must seek by time), but increment/decrement event by event when using +the scrollbar up/down, page up/page down. Let's call them "far scroll" and "near +scroll", respectively. + +A far scroll must resync the trace to the time requested by the scrollbar value. + +A near scroll must sync the trace to a time that is prior to the requested +event, show the events requested, and then sync the scrollbar value (without +event updating) to the shown event time. + +* seek n events backward + +We have no information about how far back we must request events in the trace : + +The algorithm would look like : + +seek_n_events_backward(current time, current position, time_offset, filter) +Returns : a TracesetPosition + - If the current time < beginning of trace, is means we cannot get any more + events, inform the requester that a list of less than n events is ready. + - Else, request a read to a the time_offset backward, calling the + per event hook, and calling the after_traceset hook when finished. The end + position would be the position of the current first event. + +per_event_hook + - if filter returns true + - Append the traceset position to a list of maximum size n. Remove the first + entries. + +after_traceset_hook + - if the list has a size less than n, invoke a seek_n_events_backward + subsequent iteration, for completing the list. The new time_offset is the + last time_offset used multiplied by 2. (can be done by tail recursion (if we + want to split this operation in multiple segments) or by an iterative + algorithm (seek_n_events_backward would be a while() calling its own + process_traceset_middle()). + - if the list a a size of n, it's complete : call the viewer get_print_events + hook. + + +* seek n events forward + +seek_n_events_forward(current position, filter) + - Simple : seek to the current position, request read of trace calling an + event counting hook (starts at 0). + +event_counting_hook + - if filter returns true + - increment event count. + - if event count > requested count, inform that the current position if the + wanted position. Return TRUE, so the read will stop. + + +* Printing events + +get_print_events + - seek to the position at the beginning of the list. End position is the + current one (not in the list! the one currently shown). Call a events + request between this positions, printing the fields to strings shown in the + viewer. + + + +seek_n_events backward and forward seems to be interesting algorithms that +should be implemented in the tracecontext library. With those helpers, it would +become simpler to implement a detailed event list not depending on state +computation. + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/lttv_hook_prio.txt b/tags/lttv-0.11.3-23102008/doc/developer/lttv_hook_prio.txt new file mode 100644 index 00000000..910d86b6 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/lttv_hook_prio.txt @@ -0,0 +1,24 @@ +Linux Trace Toolkit + +Mathieu Desnoyers 18-05-2004 + + +Seeing that a very precise hook call ordering is needed when processing events +(especially the order for calling state update hooks and event delivery hooks), +this document defines a new type and interface that permits to merge all kind of +hooks, eventually sorted by the priority associated to them. + +- Type LttvHooks with priorities + +This is a modification to the actual LttvHooks that associates a priority with +each hook. The container for this type would be a garray, just like hook.c, but +hooks would be added at the right position in the list, by priority. Hooks in a +hook list are ordered by priority : from highest priority (0) to +lowest (99). The default priority is 50 (defined as LTTV_PRIO_DEFAULT). + +A new lttv_hooks_call_merge that will get the hooks from two hook lists in the +right order will deal with the multiple lists priority problem. + + + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/lttv_process_traceset_strict_boundaries.txt b/tags/lttv-0.11.3-23102008/doc/developer/lttv_process_traceset_strict_boundaries.txt new file mode 100644 index 00000000..858ee576 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/lttv_process_traceset_strict_boundaries.txt @@ -0,0 +1,184 @@ +Linux Trace Toolkit + +Mathieu Desnoyers 17-05-2004 + + +1. Read Requests Cases Study + +The goal of this document is to describe the typical behavior of viewers when +they request data to a process traceset. After the implementation of three +viewers, with different needs, the idea of their need for a trace processing API +is getting clearer. We then describe a new API for process traceset that would +better suits the needs of those viewers. + +They are splitted in two different categories : the first one is the one where +the viewers select the events they need by specifying a time interval in the +traceset and the second one is where the viewers specify a start event by its +position in the traceset and a certain amount of events it needs. + +This is a simplified case study : we look at the direct interaction between +graphical viewers and process traceset, without the main window as a negociator. + +Control Flow Viewer + +This viewer, consisting in a two dimensions graph, shows the different processes +as its y axis and the time as x axis. It's clear that it needs to get the events +by specifying a start time and an end time, constituing a time interval. + + +Detailed Events List + +This list has nothing to do with time : it shows the events one by one. It cares +about the quantity of events, not their time. + +It would be simple to get the events one by one if we were reading only one +tracefile (one cpu), but the way events are read through each trace +(monothically increasing time) makes it a little bit more difficult to specify +how to increment event position. We will determine how it could be done simply. + +Let's define an event position. It's a pointer to a position into each +tracefile. It's only meaningful when associated with a context. Comparisons +between positions are done by looking comparing saved positions for each +tracefile, until a difference is found. + +A viewer could use a start time as a start event. It would specify a number of +events it needs. As a first call, it could ask for the start time of the +traceset. Afterward, it can save the position of the context after the last +event has been delivered in its after traceset function. + +Now, let's see how process traceset could handle it. It would seek in the +traceset, searching the position number. +(need a new lttv_process_traceset_seek_position) + +Then, the viewer could simply call a process traceset middle function +specifying a number of events to get. + +The whole concept of numbering events would be hidden in the order in which the +process traceset gets the events in a monothically increasing time. + + + +2. Architecture + +API to seek/read traceset will be extended to fully support both start time, +start position, end time, end position and number of events as possible +boundaries for reading. + +lttv_process_traceset_seek_time +lttv_process_traceset_seek_position + +lttv_process_traceset_middle + +It must be modified to end when it encounters the first criterion : number of +events to read reached, end time reached, end position reached. + +lttv_traceset_context_position_save + +The position_save saves a position that can be used later to seek back to this +exact same position, with event granularity. This implies that the +process_traceset must deliver events with the same timestamp in a deterministic +manner. This is actually done by using tracefile and trace numbers in the +context in the comparison function. + + + +Description of new context API useage + +1. seek +2. begin -> add middle hooks + -> call begin hooks by id +3. middle -> call middle hooks by id +4. end -> call end hooks by id + -> remove middle hooks + +3. Impact on State + +From now on, the state computation will be done in the middle hook call, with a +priority higher than default. We will define this priority as PRIO_STATE, +defined to 25. + +If state has to be computed, lttv_process_traceset_begin has to be called in +a first time. It adds the state hooks to the context. Then, the state +seek_closest will have to be used to restore the nearest state, plus a +process_traceset with no hooks present other than the state hooks will have to +be called to go from the closest state to the real time seeked. + +The lttv_process_traceset_end will only need to be called if no further state +computation is needed. + + +4. Implementation in tracecontext.c + + +- Type LttvTracesetContextPosition + +struct _LttvTraceContextPosition { + LttEventPosition *tf_pos; /* Position in each trace */ + guint nb_tracefile; /* Number of tracefiles (check) */ +} + +struct _LttvTracesetContextPosition { + LttTraceContextPosition *t_pos; /* Position in each trace */ + guint nb_trace; /* Number of traces (check) */ +} + +with interfaces : + +lttv_traceset_context_position_save +(const LttvTracesetContext *context, + LttvTracesetContextPosition *pos); + + +Dependencies : + +- lttv_process_traceset_seek_position(LttvTracesetContext *self, + const LttvTracesetContextPosition *position); + - ltt_tracefile_seek_position : already implemented + +lttv_process_traceset_seek_position will seek each tracefile to the right +position. We keep information about number of tracefiles for extra integrity +checking when reloading the position in the context. It also loads the pqueue. + + + +- lttv_process_traceset_middle +We modify lttv_process_traceset_middle so that it takes as arguments : +(LttvTracesetContext *self, +LttTime end, +unsigned nb_events, +const LttvTracesetContextPosition *end_position) + +This new version of process traceset middle will call the event hooks for +events until the first criterion is fulfilled : either the end time is reached, +the number of events requested is passed, the end position is reached or the +last event hook list called returned TRUE. When this function ends, the end +position can be extracted from the context, the end event is set as described +below and the number of events read is returned. + +The end event is a pointer to the last event the hooks has been called for. + +- lttv_process_traceset_seek_time : already implemented + - now loads the pqueue. + +- lttv_process_traceset_begin(LttvTracesetContext *self, + LttvHooks *before_traceset, + LttvHooks *before_trace, + LttvHooks *before_tracefile, + LttvHooks *event, + LttvHooksById *event_by_id) + + +- lttv_process_traceset_end(LttvTracesetContext *self, + LttvHooks *after_traceset, + LttvHooks *after_trace, + LttvHooks *after_tracefile, + LttvHooks *event, + LttvHooksById *event_by_id) + +- lttv_traceset_context_add_hooks and lttv_traceset_context_remove_hooks + +These functions now become internal to tracecontext.c + + + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/lttv_requests_servicing_schedulers.txt b/tags/lttv-0.11.3-23102008/doc/developer/lttv_requests_servicing_schedulers.txt new file mode 100644 index 00000000..beaca699 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/lttv_requests_servicing_schedulers.txt @@ -0,0 +1,324 @@ +Linux Trace Toolkit + +Requests Servicing Schedulers + + +Mathieu Desnoyers, 07/06/2004 + + +In the LTT graphical interface, two main types of events requests may occur : + +- events requests made by a viewer concerning a traceset for a ad hoc + computation. +- events requests made by a viewer concerning a trace for a precomputation. + + +Ad Hoc Computation + +The ad hoc computation must be serviced immediately : they are directly +responding to events requests that must be serviced to complete the graphical +widgets'data. This kind of computation may lead to incomplete result as long as +precomputation are not finished. Once precomputation is over, the widgets will +be redrawn if they needed such information. A ad hoc computation is done on a +traceset : the workspace of a tab. + +Precomputation + +Traces are global objects. Only one instance of a trace is opened for all the +program. Precomputation will append data to the traces attributes (states, +statistics). It must inform the widgets which asked for such states or +statistics of their availability. Only one precomputation must be launched for +each trace and no duplication of precomputation must be done. + + +Schedulers + +There is one tracesetcontext per traceset. Each reference to a trace by a +traceset also has its own tracecontext. Each trace, by itself, has its own +tracecontext. + +Let's define a scheduler as a g_idle events request servicing function. + +There is one scheduler per traceset context (registered when there are requests +to answer). There is also one scheduler per autonomous trace context (not +related to any traceset context). + +A scheduler processes requests for a specific traceset or trace by combining +time intervals of the requests. It is interruptible by any GTK event. A +precomputation scheduler has a lower priority than a ad hoc computation +scheduler. That means that no precomputation will be performed until there is +no more ad hoc compuation pending. When a scheduler is interrupted, it makes no +assumption about the presence or absence of the current requests in its pool +when it starts back. + + +Foreground Scheduler + +There can be one foreground scheduler per traceset (one traceset per tab). It +simply calls the hooks given by the events requests of the viewers for the +specified time intervals. + + +Background Scheduler + +Right now, to simplify the problem of the background scheduler, we assume that +the module that loads the extended statistics hooks has been loaded before the +data is requested and that it is not unloaded until the program stops. We will +eventually have to deal with the requests removal based on module load/unload, +but it complicates the problem quite a bit. + +A background scheduler adds hooks located under a global attributes path +(specified by the viewer who makes the request) to the trace's traceset +context (the trace is specified by the viewer). Then, it processes the whole +trace with this context (and hooks). + +Typically, a module that extends statistics will register hooks in the global +attributes tree under /computation/modulename/hook_name . A viewer +that needs these statistics for a set of traces does a background computation +request through a call to the main window API function. It must specify all +types of hooks that must be called for the specified trace. + +The background computation requests for a trace are queued. When the idle +function kicks in to answer these requests, it add the hooks of all the requests +toghether in the context and starts the read. It also keeps a list of the +background requests currently serviced. + +The read is done from start to end of the trace, calling all the hooks present +in the context. Only when the read is over, the after_request hooks of the +currently serviced requests are called and the requests are destroyed. + +If there are requests in the waiting queue, they are all added to the current +pool and processed. It is important to understand that, while a processing is in +being done, no requests are added to the pool : they wait for their turn in the +queue. + +Every hook that are added to the context by the scheduler comes from global +attributes, i.e. +/traces/# + in LttvTrace attributes : modulename/hook_name + +They come with a flag telling either in_progress or ready. If the flag +ready is set, a viewer knows that the data it needs is already ready and he +doesn't have to make a request. + +If the flag in_progress is set, that means that the data it needs is currently +being serviced, and it must wait for the current servicing to be finished. It +tells the lttvwindow API to call a hook when the actual servicing is over (there +is a special function for this, as it requires to modify the pool of requests +actually being serviced : we must make sure that no new reading hooks are +added!). + + + + + +New Global Attributes + +/traces/# + in LttvTrace attributes : + +When a processing is fired, a variable + computation/modulename/in_progress is set. + +When a processing finished, a variable + computation/modulename/in_progress is unset + computation/modulename/ready is set + + + + + +Typical Use For a Viewer + +When a viewer wants extended information, it must first check if it is ready. +if not : +Before a viewer makes a request, it must check the in_progress status of the +hooks. + +If the in_progress is unset, it makes the request. + +If the in_progress is set, it makes a special request for being informed of the +end of request. + + + + +Hooks Lists + +In order to answer the problems of background processing, we need to add a +reference counter for each hook of a hook list. If the same hook is added twice, +it will be called only once, but it will need two "remove" to be really removed +from the list. Two hooks are identical if they have the same function pointer +and hook_data. + + + + + + +Implementation + +Ad Hoc Computation + +see lttvwindow_events_delivery.txt + + +Hooks Lists + +need new ref_count field with each hook +lttv_hook_add and lttv_hook_add_list must compare addition with present and +increment ref counter if already present. + +lttv_hook_remove and remove_with_data must decrement ref_count is >1, or remove +the element otherwise (==1). + + + +Background Scheduler + +Global traces + +Two global attributes per trace : +traces/# + It is a pointer to the LttvTrace structure. + In the LttvTrace attributes : + state/ + saved_states/ + statistics/ + modes/ + cpu/ + processes/ + modulename1/ + modulename2/ + ... + computation/ /* Trace specific background computation hooks status */ + state/ + in_progress + ready + stats/ + in_progress + ready + modulename1/ + in_progress + ready + requests_queue/ /* Background computation requests */ + requests_current/ /* Type : BackgroundRequest */ + notify_queue/ + notify_current/ + computation_traceset/ + computation_traceset_context/ + + +computation/ /* Global background computation hooks */ + state/ + before_chunk_traceset + before_chunk_trace + before_chunk_tracefile + after_... + before_request + after_request + event_hook + event_hook_by_id + hook_adder + hook_remover + stats/ + ... + modulename1/ + ... + +Hook Adder and Hook remover + +Hook functions that takes a trace context as call data. They simply +add / remove the computation related hooks from the trace context. + + + +Modify Traceset +Points to the global traces. Main window must open a new one only when no +instance of the pathname exists. + +Modify trace opening / close to make them create and destroy +LttvBackgroundComputation (and call end requests hooks for servicing requests) +and global trace info when references to the trace is zero. + + + +EventsRequest Structure + +This structure is the element of the events requests pools. The owner field is +used as an ownership identifier. The viewer field is a pointer to the data +structure upon which the action applies. Typically, both will be pointers to +the viewer's data structure. + +In a ad hoc events request, a pointer to the EventsRequest structure is used as +hook_data in the hook lists : it must have been added by the viewers. + + +Modify module load/unload + +A module that registers global computation hooks in the global attributes upon +load should unregister them when unloaded. Also, it must remove every background +computation request for each trace that has its own module_name as GQuark. + + +Give an API for calculation modules + +Must have an API for module which register calculation hooks. Unregistration +must also remove all requests made for these hooks. + + +Background Requests Servicing Algorithm (v1) + + +list_in : currently serviced requests +list_out : queue of requests waiting for processing + +notification lists : +notify_in : currently checked notifications +notify_out : queue of notifications that comes along with next processing. + + +0.1 Lock traces +0.2 Sync tracefiles + +1. Before processing + - if list_in is empty + - Add all requests in list_out to list_in, empty list_out + - for each request in list_in + - set hooks'in_progress flag to TRUE + - call before request hook + - seek trace to start + - Move all notifications from notify_out to notify_in. + - for each request in list_in + - Call before chunk hooks for list_in + - add hooks to context *note only one hook of each type added. + +2. call process traceset middle for a chunk + (assert list_in is not empty! : should not even be called in that case) + +3. After the chunk + 3.1 call after_chunk hooks for list_in + - for each request in list_in + - Call after chunk hooks for list_in + - remove hooks from context *note : only one hook of each type + 3.2 for each notify_in + - if current time >= notify time, call notify and remove from notify_in + - if current position >= notify position, call notify and remove from + notify_in + 3.3 if end of trace reached + - for each request in list_in + - set hooks'in_progress flag to FALSE + - set hooks'ready flag to TRUE + - call after request hook + - remove request + - for each notifications in notify_in + - call notify and remove from notify_in + - reset the context + - if list_out is empty + return FALSE (scheduler stopped) + - else + return TRUE (scheduler still registered) + 3.4 else + - return TRUE (scheduler still registered) + +4. Unlock traces diff --git a/tags/lttv-0.11.3-23102008/doc/developer/lttvwindow_events_delivery.txt b/tags/lttv-0.11.3-23102008/doc/developer/lttvwindow_events_delivery.txt new file mode 100644 index 00000000..c52b6a3a --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/lttvwindow_events_delivery.txt @@ -0,0 +1,427 @@ +Linux Trace Toolkit + +Mathieu Desnoyers 17-05-2004 + + +This document explains how the lttvwindow API could process the event requests +of the viewers, merging event requests and hook lists to benefit from the fact +that process_traceset can call multiple hooks for the same event. + +First, we will explain the detailed process of event delivery in the current +framework. We will then study its strengths and weaknesses. + +In a second time, a framework where the events requests are dealt by the main +window with fine granularity will be described. We will then discussed the +advantages and inconvenients over the first framework. + + +1. (Actual) Boundaryless event reading + +Actually, viewers request events in a time interval from the main window. They +also specify a (not so) maximum number of events to be delivered. In fact, the +number of events to read only gives a stop point, from where only events with +the same timestamp will be delivered. + +Viewers register hooks themselves in the traceset context. When merging read +requests in the main window, all hooks registered by viewers will be called for +the union of all the read requests, because the main window has no control on +hook registration. + +The main window calls process_traceset on its own for all the intervals +requested by all the viewers. It must not duplicate a read of the same time +interval : it could be very hard to filter by viewers. So, in order to achieve +this, time requests are sorted by start time, and process_traceset is called for +each time request. We keep the last event time between each read : if the start +time of the next read is lower than the time reached, we continue the reading +from the actual position. + +We deal with specific number of events requests (infinite end time) by +garantying that, starting from the time start of the request, at least that +number of events will be read. As we can't do it efficiently without interacting +very closely with process_traceset, we always read the specified number of +events requested starting from the current position when we answer to a request +based on the number of events. + +The viewers have to filter events delivered by traceset reading, because they +can be asked by another viewer for a totally (or partially) different time +interval. + + +Weaknesses + +- process_middle does not guarantee the number of events read + +First of all, a viewer that requests events to process_traceset has no garantee +that it will get exactly what it asked for. For example, a direct call to +traceset_middle for a specific number of events will delived _at least_ that +quantity of events, plus the ones that have the same timestamp that the last one +has. + +- Border effects + +Viewer's writers will have to deal with a lot of border effects caused by the +particularities of the reading. They will be required to select the information +they need from their input by filtering. + +- Lack of encapsulation and difficulty of testing + +The viewer's writer will have to take into account all the border effects caused +by the interaction with other modules. This means that event if a viewer works +well alone or with another viewer, it's possible that new bugs arises when a new +viewer comes around. So, even if a perfect testbench works well for a viewer, it +does not confirm that no new bug will arise when another viewer is loaded at the +same moment asking for different time intervals. + + +- Duplication of the work + +Time based filters and counters of events will have to be implemented at the +viewer's side, which is a duplication of the functionnalities that would +normally be expected from the tracecontext API. + +- Lack of control over the data input + +As we expect module's writers to prefer to be as close as possible from the raw +datas, making them interact with a lower level library that gives them a data +input that they only control by further filtering of the input is not +appropriated. We should expect some reluctancy from them about using this API +because of this lack of control on the input. + +- Speed cost + +All hooks of all viewers will be called for all the time intervals. So, if we +have a detailed events list and a control flow view, asking both for different +time intervals, the detailed events list will have to filter all the events +delivered originally to the control flow view. This can be a case occuring quite +often. + + + +Strengths + +- Simple concatenation of time intervals at the main window level. + +Having the opportunity of delivering more events than necessary to the viewers +means that we can concatenate time intervals and number of events requested +fairly easily, while being hard to determine if some specific cases will be +wrong, in depth testing being impossible. + +- No duplication of the tracecontext API + +Viewers deal directly with the tracecontext API for registering hooks, removing +a layer of encapsulation. + + + + + +2. (Proposed) Strict boundaries events reading + +The idea behind this method is to provide exactly the events requested by the +viewers to them, no more, no less. + +It uses the new API for process traceset suggested in the document +process_traceset_strict_boundaries.txt. + +It also means that the lttvwindow API will have to deal with viewer's hooks. +Those will not be allowed to add them directly in the context. They will give +them to the lttvwindow API, along with the time interval or the position and +number of events. The lttvwindow API will have to take care of adding and +removing hooks for the different time intervals requested. That means that hooks +insertion and removal will be done between each traceset processing based on +the time intervals and event positions related to each hook. We must therefore +provide a simple interface for hooks passing between the viewers and the main +window, making them easier to manage from the main window. A modification to the +LttvHooks type solves this problem. + + +Architecture + +Added to the lttvwindow API : + + +void lttvwindow_events_request +( Tab *tab, + const EventsRequest *events_request); + +void lttvwindow_events_request_remove_all +( Tab *tab, + gconstpointer viewer); + + +Internal functions : + +- lttvwindow_process_pending_requests + + +Events Requests Removal + +A new API function will be necessary to let viewers remove all event requests +they have made previously. By allowing this, no more out of bound requests will +be serviced : a viewer that sees its time interval changed before the first +servicing is completed can clear its previous events requests and make a new +one for the new interval needed, considering the finished chunks as completed +area. + +It is also very useful for dealing with the viewer destruction case : the viewer +just has to remove its events requests from the main window before it gets +destroyed. + + +Permitted GTK Events Between Chunks + +All GTK Events will be enabled between chunks. A viewer could ask for a +long computation that has no impact on the display : in that case, it is +necessary to keep the graphical interface active. While a processing is in +progress, the whole graphical interface must be enabled. + +We needed to deal with the coherence of background processing and diverse GTK +events anyway. This algorithm provides a generalized way to deal with any type +of request and any GTK events. + + +Background Computation Request + +A background computation has a trace scope, and is therefore not linked to a +main window. It is not detailed in this document. +see requests_servicing_schedulers.txt + +A New "Redraw" Button + +It will be used to redraw the viewers entirely. It is useful to restart the +servicing after a "stop" action. + +A New "Continue" Button + +It will tell the viewers to send requests for damaged areas. It is useful to +complete the servicing after a "stop" action. + + + +Tab change + +If a tab change occurs, we still want to do background processing. +Events requests must be stocked in a list located in the same scope than the +traceset context. Right now, this is tab scope. All functions called from the +request servicing function must _not_ use the current_tab concept, as it may +change. The idle function must the take a tab, and not the main window, as +parameter. + +If a tab is removed, its associated idle events requests servicing function must +also be removed. + +It now looks a lot more useful to give a Tab* to the viewer instead of a +MainWindow*, as all the information needed by the viewer is located at the tab +level. It will diminish the dependance upon the current tab concept. + + + +Idle function (lttvwindow_process_pending_requests) + +The idle function must return FALSE to be removed from the idle functions when +no more events requests are pending. Otherwise, it returns TRUE. It will service +requests until there is no more request left. + + + +Implementation + + +- Type LttvHooks + +see hook_prio.txt + +The viewers will just have to pass hooks to the main window through this type, +using the hook.h interface to manipulate it. Then, the main window will add +them and remove them from the context to deliver exactly the events requested by +each viewer through process traceset. + + +- lttvwindow_events_request + +It adds the an EventsRequest struct to the list of events requests +pending and registers a pending request for the next g_idle if none is +registered. The viewer can access this structure during the read as its +hook_data. Only the stop_flag can be changed by the viewer through the +event hooks. + +typedef struct _EventsRequest { + gpointer owner; /* Owner of the request */ + gpointer viewer_data; /* Unset : NULL */ + gboolean servicing; /* service in progress: TRUE */ + LttTime start_time;/* Unset : { G_MAXUINT, G_MAXUINT }*/ + LttvTracesetContextPosition *start_position; /* Unset : NULL */ + gboolean stop_flag; /* Continue:TRUE Stop:FALSE */ + LttTime end_time;/* Unset : { G_MAXUINT, G_MAXUINT } */ + guint num_events; /* Unset : G_MAXUINT */ + LttvTracesetContextPosition *end_position; /* Unset : NULL */ + LttvHooks *before_chunk_traceset; /* Unset : NULL */ + LttvHooks *before_chunk_trace; /* Unset : NULL */ + LttvHooks *before_chunk_tracefile;/* Unset : NULL */ + LttvHooks *event; /* Unset : NULL */ + LttvHooksById *event_by_id; /* Unset : NULL */ + LttvHooks *after_chunk_tracefile; /* Unset : NULL */ + LttvHooks *after_chunk_trace; /* Unset : NULL */ + LttvHooks *after_chunk_traceset; /* Unset : NULL */ + LttvHooks *before_request; /* Unset : NULL */ + LttvHooks *after_request; /* Unset : NULL */ +} EventsRequest; + + +- lttvwindow_events_request_remove_all + +It removes all the events requests from the pool that has their "owner" field +maching the owner pointer given as argument. + +It calls the traceset/trace/tracefile end hooks for each request removed if +they are currently serviced. + + +- lttvwindow_process_pending_requests + +This internal function gets called by g_idle, taking care of the pending +requests. It is responsible for concatenation of time intervals and position +requests. It does it with the following algorithm organizing process traceset +calls. Here is the detailed description of the way it works : + + + +- Revised Events Requests Servicing Algorithm (v2) + +The reads are splitted in chunks. After a chunk is over, we want to check if +there is a GTK Event pending and execute it. It can add or remove events +requests from the event requests list. If it happens, we want to start over +the algorithm from the beginning. The after traceset/trace/tracefile hooks are +called after each chunk, and before traceset/trace/tracefile are +called when the request processing resumes. Before and after request hooks are +called respectively before and after the request processing. + + +Data structures necessary : + +List of requests added to context : list_in +List of requests not added to context : list_out + +Initial state : + +list_in : empty +list_out : many events requests + + +0.1 Lock the traces +0.2 Seek traces positions to current context position. + +A. While (list_in !empty or list_out !empty) + 1. If list_in is empty (need a seek) + 1.1 Add requests to list_in + 1.1.1 Find all time requests with lowest start time in list_out (ltime) + 1.1.2 Find all position requests with lowest position in list_out (lpos) + 1.1.3 If lpos.start time < ltime + - Add lpos to list_in, remove them from list_out + 1.1.4 Else, (lpos.start time >= ltime) + - Add ltime to list_in, remove them from list_out + 1.2 Seek + 1.2.1 If first request in list_in is a time request + - If first req in list_in start time != current time + - Seek to that time + 1.2.2 Else, the first request in list_in is a position request + - If first req in list_in pos != current pos + - seek to that position + 1.3 Add hooks and call before request for all list_in members + 1.3.1 If !servicing + - begin request hooks called + - servicing = TRUE + 1.3.2 call before chunk + 1.3.3 events hooks added + 2. Else, list_in is not empty, we continue a read + 2.0 For each req of list_in + - Call before chunk + - events hooks added + 2.1 For each req of list_out + - if req.start time == current context time + or req.start position == current position + - Add to list_in, remove from list_out + - If !servicing + - Call before request + - servicing = TRUE + - Call before chunk + - events hooks added + + 3. Find end criterions + 3.1 End time + 3.1.1 Find lowest end time in list_in + 3.1.2 Find lowest start time in list_out (>= than current time*) + * To eliminate lower prio requests (not used) + 3.1.3 Use lowest of both as end time + 3.2 Number of events + 3.2.1 Find lowest number of events in list_in + 3.2.2 Use min(CHUNK_NUM_EVENTS, min num events in list_in) as num_events + 3.3 End position + 3.3.1 Find lowest end position in list_in + 3.3.2 Find lowest start position in list_out (>= than current + position *not used) + 3.3.3 Use lowest of both as end position + + 4. Call process traceset middle + 4.1 Call process traceset middle (Use end criterion found in 3) + * note : end criterion can also be viewer's hook returning TRUE + 5. After process traceset middle + - if current context time > traceset.end time + - For each req in list_in + - Remove events hooks for req + - Call end chunk for req + - Call end request for req + - remove req from list_in + 5.1 For each req in list_in + - Call end chunk for req + - Remove events hooks for req + - req.num -= count + - if req.num == 0 + or + current context time >= req.end time + or + req.end pos == current pos + or + req.stop_flag == TRUE + - Call end request for req + - remove req from list_in + If GTK Event pending : break A loop + +B. When interrupted between chunks + 1. for each request in list_in + 1.1. Use current postition as start position + 1.2. Remove start time + 1.3. Move from list_in to list_out + +C. Unlock the traces + + + +Notes : +End criterions for process traceset middle : +If the criterion is reached, event is out of boundaries and we return. +Current time >= End time +Event count > Number of events +Current position >= End position +Last hook list called returned TRUE + +The >= for position is necessary to make ensure consistency between start time +requests and positions requests that happens to be at the exact same start time +and position. + + + + +Weaknesses + +- ? + +Strengths + +- Removes the need for filtering of information supplied to the viewers. + +- Viewers have a better control on their data input. + +- Solves all the weaknesses idenfied in the actual boundaryless traceset +reading. + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/marker-types.txt b/tags/lttv-0.11.3-23102008/doc/developer/marker-types.txt new file mode 100644 index 00000000..f68204c5 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/marker-types.txt @@ -0,0 +1,86 @@ + +#define MARK_TRAP 1 +#define _MARK_TRAP (1 << MARK_TRAP) + Can generate a trap + +#define MARK_PREEMPT 2 +#define _MARK_PREEMPT (1 << MARK_PREEMPT) + Permits blocking calls within probe. + How to deal with probe removal : + Each site has its per cpu probe_exec counters. The sum of the signed values + gives the number of executors. Operations inc/dec on those values are done + within preempt disable so they can be done non atomically without risking + to be corrupted by another CPU. + 1 - disable site and remove call + 2 - while sum of probe_exec counters != 0, sleep 50ms + fail after 10 loops + - if someone sleeps in here for a long time or waits for + a busy ressource, removal may fail with -EBUSY. + +site : + +if (enable) { + preempt_disable(); + probe_exec[smp_processor_id()]++; + preempt_enable(); + handler(); + preempt_disable(); + probe_exec[smp_processor_id()]--; + preempt_enable(); +} + +#define MARK_RESCHED 3 +#define _MARK_RESCHED (1 << MARK_RESCHED) + preempt_schedule() will be called by the marker. + +#define MARK_PRINTK 4 +#define _MARK_PRINTK (1 << MARK_PRINTK) + vprintk can be called in the probe/printk can be called as probe. + +#define MARK_LOCK_SAFE +#define _MARK_LOCK_SAFE (1 << MARK_LOCK_SAFE) + It is completely safe to take a lock, disable irqs, softirqs, ... from this marker. + If unset, checking the context must be done to insure no deadlock or recursive + call will occur. + +#define _MARK_DEFAULT (_MARK_TRAP | _MARK_RESCHED | _MARK_PRINTK) + +#define MARK (format, args...) _MARK(MARK_DEFAULT, format, ## args) + +ex. i386 +#define _MARK(opt, format, args...) \ +do { \ + if (opt & _MARK_TRAP) \ + MARK(opt, format, ## args); \ + else \ + GEN_MARK(opt, format, ## args); \ +} while (0) + +ex. powerpc +#define _MARK(opt, format, args...) MARK(opt, format, ## args); + + +MARK(opt, format, ...) \ +static declare opt in struct; \ +if (enable) { + preempt_disable(); + if (opt & _MARK_PREEMPT) { + probe_exec[smp_processor_id()]++; + if (opt & _MARK_RESCHED) + preempt_enable(); + else + preempt_enable_no_resched(); + } + handler(); + if (opt & _MARK_PREEMPT) { + preempt_disable(); + probe_exec[smp_processor_id()]--; + } + if (opt & _MARK_RESCHED) + preempt_enable(); + else + preempt_enable_no_resched(); +} + + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/markers-update.txt b/tags/lttv-0.11.3-23102008/doc/developer/markers-update.txt new file mode 100644 index 00000000..d94b5fc3 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/markers-update.txt @@ -0,0 +1,116 @@ + +Tracepoint proposal + +- Tracepoint infrastructure + - In-kernel users + - Complete typing, verified by the compiler + - Dynamically linked and activated + +- Marker infrastructure + - Exported API to userland + - Basic types only + +- Dynamic vs static + - In-kernel probes are dynamically linked, dynamically activated, connected to + tracepoints. Type verification is done at compile-time. Those in-kernel + probes can be a probe extracting the information to put in a marker or a + specific in-kernel tracer such as ftrace. + - Information sinks (LTTng, SystemTAP) are dynamically connected to the + markers inserted in the probes and are dynamically activated. + +- Near instrumentation site vs in a separate tracer module + +A probe module, only if provided with the kernel tree, could connect to internal +tracing sites. This argues for keeping the tracepoing probes near the +instrumentation site code. However, if a tracer is general purpose and exports +typing information to userspace through some mechanism, it should only export +the "basic type" information and could be therefore shipped outside of the +kernel tree. + +In-kernel probes should be integrated to the kernel tree. They would be close to +the instrumented kernel code and would translate between the in-kernel +instrumentation and the "basic type" exports. Other in-kernel probes could +provide a different output (statistics available through debugfs for instance). +ftrace falls into this category. + +Generic or specialized information "sinks" (LTTng, systemtap) could be connected +to the markers put in tracepoint probes to extract the information to userspace. +They would extract both typing information and the per-tracepoint execution +information to userspace. + +Therefore, the code would look like : + +kernel/sched.c: + +#include "sched-trace.h" + +schedule() +{ + ... + trace_sched_switch(prev, next); + ... +} + + +kernel/sched-trace.h: + +DEFINE_TRACE(sched_switch, struct task_struct *prev, struct task_struct *next); + + +kernel/sched-trace.c: + +#include "sched-trace.h" + +static probe_sched_switch(struct task_struct *prev, struct task_struct + *next) +{ + trace_mark(kernel_sched_switch, "prev_pid %d next_pid %d prev_state %ld", + prev->pid, next->pid, prev->state); +} + +int __init init(void) +{ + return register_sched_switch(probe_sched_switch); +} + +void __exit exit(void) +{ + unregister_sched_switch(probe_sched_switch); +} + + +Where DEFINE_TRACE internals declare a structure, a trace_* inline function, +a register_trace_* and unregister_trace_* inline functions : + +static instrumentation site structure, containing function pointers to +deactivated functions and activation boolean. It also contains the +"sched_switch" string. This structure is placed in a special section to create +an array of these structures. + +static inline void trace_sched_switch(struct task_struct *prev, + struct task_struct *next) +{ + if (sched_switch tracing is activated) + marshall_probes(&instrumentation_site_structure, prev, next); +} + +static inline int register_trace_sched_switch( + void (*probe)(struct task_struct *prev, struct task_struct *next) +{ + return do_register_probe("sched_switch", (void *)probe); +} + +static inline void unregister_trace_sched_switch( + void (*probe)(struct task_struct *prev, struct task_struct *next) +{ + do_unregister_probe("sched_switch", (void *)probe); +} + + +We need a a new kernel probe API : + +do_register_probe / do_unregister_probe + - Connects the in-kernel probe to the site + - Activates the site tracing (probe reference counting) + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/obsolete/index.html b/tags/lttv-0.11.3-23102008/doc/developer/obsolete/index.html new file mode 100644 index 00000000..1b793883 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/obsolete/index.html @@ -0,0 +1,19 @@ + + + + Linux Trace Toolkit trace analysis tools development + + +

Linux Trace Toolkit trace analysis tools development

+ + + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/obsolete/ltt-to-do.html b/tags/lttv-0.11.3-23102008/doc/developer/obsolete/ltt-to-do.html new file mode 100644 index 00000000..0fda1170 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/obsolete/ltt-to-do.html @@ -0,0 +1,204 @@ + + + + Linux Trace Toolkit Status + + + +

Linux Trace Toolkit Status

+ +

Last updated July 1, 2003.

+ +

During the 2002 Ottawa Linux Symposium tracing BOF, a list of desirable + features for LTT was collected by Richard Moore. Since then, a lot of infrastructure + work on LTT has been taking place. This status report aims to track current + development efforts and the current status of the various features. This +status page is most certainly incomplete, please send +any additions and corrections to Michel Dagenais (michel.dagenais at polymtl.ca)

+ +

As of this writing, the most active LTT contributors include Karim Yaghmour, +author and maintainer from opersys.com, Tom Zanussi, Robert Wisniewski, +Richard J Moore and others from IBM, mainly at the Linux Technology Center, +XiangXiu Yang, Mathieu Desnoyers, Benoit des Ligneris and Michel Dagenais, +from the department of Computer Engineering at Ecole Polytechnique de +Montreal, and Frank Rowand, from Monte Vista.

+ +

Work recently performed

+ +

Lockless per cpu buffers: Tom Zanussi of IBM has implemented per CPU lockless buffering, with low +overhead very fine grained timestamping, and has updated accordingly the +kernel patch and the trace visualizer except for viewing multiple per CPU +traces simultaneously.

+ +

RelayFS: Tom Zanussi has implemented RelayFS, a separate, simple +and efficient component for moving data between the kernel and user space +applications. This component is reusable by other projects (printk, evlog, +lustre...) and removes a sizeable chunk from the current LTT, making each +piece (relayfs and relayfs-based LTT) simpler, more modular and possibly +more palatable for inclusion in the standard Linux kernel. Besides LTT on +RelayFS, He has implemented printk over RelayFS with an automatically +resizeable printk buffer.

+ +

New trace format: Karim Yaghmour and Michel Dagenais, with input +from several LTT contributors, have designed a new trace format to accomodate +per buffer tracefiles and dynamically defined event types. The new format +includes both the binary trace format and the event type description format. +XiangXiu Yang has developed a simple parser for the event type description +format. This parser is used to generate the tracing macros in the kernel +(genevent) and to support reading tracefiles in the trace reading library +(libltt). + +

Ongoing work

+ +

Libltt: XiangXiu Yang is finishing up an event reading library +and API which parses event descriptions and accordingly reads traces and +decodes events.

+ +

lttv: XiangXiu Yang, Mathieu Desnoyers and Michel Dagenais are +remodeling the trace visualizer to use the new trace format and libltt API, +and to allow compiled and scripted plugins, which can dynamically +add new custom trace analysis functions.

+ +

Planned work

+ +

LTT already interfaces with Dynamic Probes. This feature will need to +be updated for the new LTT version.

+ +

The Kernel Crash Dump utilities is another very interesting complementary + project. Interfacing it with RelayFS will help implement useful +flight-recorder like tracing for post-mortem analysis.

+ +

User level tracing is available in the current LTT version but requires +one system call per event. With the new RelayFS based infrastructure, it +would be interesting to use a shared memory buffer directly accessible from +user space. Having one RelayFS channel per user would allow an extremely +efficient, yet secure, user level tracing mechanism.

+ +

Sending important events (process creation, event types/facilities +definitions) to a separate channel could be used to browse traces +interactively more efficiently. Only this concise trace of important +events would need to be processed in its entirety, other larger +gigabyte size traces could be used in random access without requiring +a first preprocessing pass. A separate channel would also be required +in case of incomplete traces such as when tracing to a circular buffer +in "flight recorder" mode; the important events would all be kept +while only the last buffers of ordinary events would be kept.

+ +

Once the visualizer is able to read and display several traces, it + will be interesting to produce side by side synchronized views + (events from two interacting machines A and B one above the other) + or even merged views (combined events from several CPUs in a single + merged graph). Time differences between interacting systems will + need to be estimated and somewhat compensated for.

+ +

LTT currently writes a proc file at trace start time. This + file only contains minimal information about processes and + interrupts names. More information would be desirable for several + applications (process maps, opened descriptors, content of buffer + cache). Furthermore, this information may be more conveniently + gathered from within the kernel and simply written to the trace as + events at start time.

+ +

New features already implemented since LTT 0.9.5

+ +
    +
  1. Per-CPU Buffering scheme.
  2. +
  3. Logging without locking.
  4. +
  5. Minimal latency - minimal or no serialisation. (Lockless tracing +using read_cycle_counter instead of gettimeofday.)
  6. +
  7. Fine granularity time stamping - min=o(CPU cycle time), +max=.05 Gb Ethernet interrupt rate. (Cycle counter being used).
  8. +
  9. Random access to trace event stream. (Random access reading +of events in the trace is already available in LibLTT. However, one first +pass is required through the trace to find all the process creation events; +the cost of this first pass may be reduced in the future if process creation + events are sent to a separate much smaller trace.)
  10. + +
+ +

Features being worked on

+ +
    +
  1. Simple wrapper macros for trace instrumentation. (GenEvent) +
  2. +
  3. Easily expandable with new trace types. (GenEvent)
  4. +
  5. Multiple buffering schemes - switchable globally or selectable +by trace client. (Will be simpler to obtain with RelayFS.)
  6. +
  7. Global buffer scheme. (Will be simpler to obtain with RelayFS.) +
  8. +
  9. Per-process buffer scheme. (Will be simpler to obtain with RelayFS.) +
  10. +
  11. Per-NGPT thread buffer scheme. (Will be simpler to obtain with + RelayFS.)
  12. +
  13. Per-component buffer scheme. (Will be simpler to obtain with +RelayFS.)
  14. +
  15. A set of extensible and modular performance analysis post-processing +programs. (Lttv)
  16. +
  17. Filtering and selection mechanisms within formatting utility. (Lttv) +
  18. +
  19. Variable size event records. (GenEvent, LibEvent, Lttv) +
  20. +
  21. Data reduction facilities able to logically combine traces from + more than one system. (LibEvent, Lttv)
  22. +
  23. Data presentation utilities to be able to present data from multiple + trace instances in a logically combined form (LibEvent, Lttv) +
  24. +
  25. Major/minor code means of identification/registration/assignment. + (GenEvent)
  26. +
  27. A flexible formatting mechanism that will cater for structures +and arrays of structures with recursion. (GenEvent)
  28. + +
+ +

Features already planned for

+ +
    +
  1. Init-time tracing. (To be part of RelayFS.)
  2. +
  3. Updated interface for Dynamic Probes. (As soon as things stabilize.) +
  4. +
  5. Support "flight recorder" always on tracing with minimal resource +consumption. (To be part of RelayFS and interfaced to the Kernel crash +dump facilities.)
  6. +
  7. Fine grained dynamic trace instrumentation for kernel space and +user subsystems. (Dynamic Probes, more efficient user level tracing.)
  8. +
  9. System information logged at trace start. (New special events +to add.)
  10. +
  11. Collection of process memory map information at trace start/restart + and updates of that information at fork/exec/exit. This allows address-to-name + resolution for user space.
  12. +
  13. Include the facility to write system snapshots (total memory layout + for kernel, drivers, and all processes) to a file. This is required for + trace post-processing on a system other than the one producing the trace. + Perhaps some of this is already implemented in the Kernel Crash Dump.
  14. +
  15. Even more efficient tracing from user space.
  16. +
  17. Better integration with tools to define static trace hooks.
  18. +
  19. Better integration with tools to dynamically activate tracing statements.
  20. + +
+ +

Features not currently planned

+ +
    +
  1. POSIX Tracing API compliance.
  2. +
  3. Ability to do function entry/exit tracing facility. (Probably + a totally orthogonal mechanism using either Dynamic Probes hooks or static + code instrumentation using the suitable GCC options for basic blocks instrumentation.)
  4. +
  5. Processor performance counter (which most modern CPUs have) sampling +and recording. (These counters can be read and their value sent in traced +events. Some support to collect these automatically at specific state change +times and to visualize the results would be nice.)
  6. +
  7. Suspend & Resume capability. (Why not simply stop the + trace and start a new one later, otherwise important information like process +creations while suspended must be obtained in some other way.)
  8. +
  9. Per-packet send/receive event. (New event types will be easily +added as needed.)
  10. + +
+
+
+ + + + + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/obsolete/status.html b/tags/lttv-0.11.3-23102008/doc/developer/obsolete/status.html new file mode 100644 index 00000000..9fc1e4d7 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/obsolete/status.html @@ -0,0 +1,26 @@ + + + + Current status + + + +

Current status

+ +

+As of january 28 2004, the Linux Trace Toolkit viewer is nearing feature +completeness for the first release. A few features need some additional work. +Thereafter, polishing, stabilizing, and documentation will take place before +adding new features. It can be considered pre alpha but usable for displaying +detailed event lists. Furthermore, it may be used to plan the development of +new modules for the viewer. + +The underlying libltt library is fairly stable and mature. It may be considered +alpha. +

+ + + + + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/obsolete/todo.html b/tags/lttv-0.11.3-23102008/doc/developer/obsolete/todo.html new file mode 100644 index 00000000..dca65c4c --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/obsolete/todo.html @@ -0,0 +1,40 @@ + + + + Roadmap + + + +

Roadmap

+ +

+As of january 28 2004, the short term development plans include the following +items. + +

    +
  • Add the copyright notices in each program file. +
  • Insure that the coding practices are being implemented. +
  • Polish the visual appearance: icons for the tabs and buttons, + background, lines and labels in the control flow viewer... +
  • When a trace is opened, start a background thread to precompute + the system state and memorize it after each ~100 000 events. + Have the option to save the precomputed state when a trace is closed. + Use the precomputed state if available when opening a trace or + seeking in a trace. Use the same thread for computing statistics. +
  • Update module.c to ease changing a module into a builtin feature. +
  • Split processTrace into tracecontext and processtrace. +
  • Insure that g_info logging is generally available but off by default. +
  • Document each header file such that developer documentation can + be extracted automatically using + Doxygen. +
  • Complete the user and developer documentation. +
  • Test the viewer on large SMP traces. Insure that 2GB files do not cause + crashes. Note and fix unduly long delays. +
  • Add C99 test (declaration of variable inside a function) to configure.in + + + + + + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/program-header.txt b/tags/lttv-0.11.3-23102008/doc/developer/program-header.txt new file mode 100644 index 00000000..ccf99692 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/program-header.txt @@ -0,0 +1,17 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Your Name + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ diff --git a/tags/lttv-0.11.3-23102008/doc/developer/time-monotonic-accurate.txt b/tags/lttv-0.11.3-23102008/doc/developer/time-monotonic-accurate.txt new file mode 100644 index 00000000..52de9ae8 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/time-monotonic-accurate.txt @@ -0,0 +1,125 @@ + +Monotonic accurate time + +The goal of this design is to provide a monotonic time : + +Readable from userspace without a system call +Readable from NMI handler +Readable without disabling interrupts +Readable without disabling preemption +Only one clock source (most precise available : tsc) +Support architectures with variable TSC frequency. + +Main difference with wall time currently implemented in the Linux kernel : the +time update is done atomically instead of using a write seqlock. It permits +reading time from NMI handler and from userspace. + +struct time_info { + u64 tsc; + u64 freq; + u64 walltime; +} + +static struct time_struct { + struct time_info time_sel[2]; + long update_count; +} + +DECLARE_PERCPU(struct time_struct, cpu_time); + +/* Number of times the scheduler is called on each CPU */ +DECLARE_PERCPU(unsigned long, sched_nr); + +/* On frequency change event */ +/* In irq context */ +void freq_change_cb(unsigned int new_freq) +{ + struct time_struct this_cpu_time = + per_cpu(cpu_time, smp_processor_id()); + struct time_info *write_time, *current_time; + write_time = + this_cpu_time->time_sel[(this_cpu_time->update_count+1)&1]; + current_time = + this_cpu_time->time_sel[(this_cpu_time->update_count)&1]; + write_time->tsc = get_cycles(); + write_time->freq = new_freq; + /* We cumulate the division imprecision. This is the downside of using + * the TSC with variable frequency as a time base. */ + write_time->walltime = + current_time->walltime + + (write_time->tsc - current_time->tsc) / + current_time->freq; + wmb(); + this_cpu_time->update_count++; +} + + +/* Init cpu freq */ +init_cpu_freq() +{ + struct time_struct this_cpu_time = + per_cpu(cpu_time, smp_processor_id()); + struct time_info *current_time; + memset(this_cpu_time, 0, sizeof(this_cpu_time)); + current_time = this_cpu_time->time_sel[this_cpu_time->update_count&1]; + /* Init current time */ + /* Get frequency */ + /* Reset cpus to 0 ns, 0 tsc, start their tsc. */ +} + + +/* After a CPU comes back from hlt */ +/* The trick is to sync all the other CPUs on the first CPU up when they come + * up. If all CPUs are down, then there is no need to increment the walltime : + * let's simply define the useful walltime on a machine as the time elapsed + * while there is a CPU running. If we want, when no cpu is active, we can use + * a lower resolution clock to somehow keep track of walltime. */ + +wake_from_hlt() +{ + /* TODO */ +} + + + +/* Read time from anywhere in the kernel. Return time in walltime. (ns) */ +/* If the update_count changes while we read the context, it may be invalid. + * This would happen if we are scheduled out for a period of time long enough to + * permit 2 frequency changes. We simply start the loop again if it happens. + * We detect it by comparing the update_count running counter. + * We detect preemption by incrementing a counter sched_nr within schedule(). + * This counter is readable by user space through the vsyscall page. */ + */ +u64 read_time(void) +{ + u64 walltime; + long update_count; + struct time_struct this_cpu_time; + struct time_info *current_time; + unsigned int cpu; + long prev_sched_nr; + do { + cpu = _smp_processor_id(); + prev_sched_nr = per_cpu(sched_nr, cpu); + if(cpu != _smp_processor_id()) + continue; /* changed CPU between CPUID and getting + sched_nr */ + this_cpu_time = per_cpu(cpu_time, cpu); + update_count = this_cpu_time->update_count; + current_time = this_cpu_time->time_sel[update_count&1]; + walltime = current_time->walltime + + (get_cycles() - current_time->tsc) / + current_time->freq; + if(per_cpu(sched_nr, cpu) != prev_sched_nr) + continue; /* been preempted */ + } while(this_cpu_time->update_count != update_count); + return walltime; +} + +/* Userspace */ +/* Export all this data to user space through the vsyscall page. Use a function + * like read_time to read the walltime. This function can be implemented as-is + * because it doesn't need to disable preemption. */ + + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/tracing_tools_overview.html b/tags/lttv-0.11.3-23102008/doc/developer/tracing_tools_overview.html new file mode 100644 index 00000000..b6159254 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/tracing_tools_overview.html @@ -0,0 +1,189 @@ + + + + Tracing Tools + + + +

    Tracing Tools

    + +

    Tracing is routinely used to help understanding the behavior and performance +of various aspects of the Linux kernel and associated drivers. +Many of the 80K+ printk statements in the Linux kernel +serve this purpose, although printk is relatively low +performance and unreliable. The small default printk buffer size coupled with +the low performance brings lost messages as soon as the volume becomes +significant. + +

    For this reason, a number of drivers include their own tracing macros +and infrastructure. A quick search looking for TRACE and related keywords +in the Linux kernel source reveals some form of tracing in at least +the following files: + +

      +
    • ./fs/hpfs/hpfs_fn.h +
    • ./fs/smbfs/smb_debug.h +
    • ./fs/autofs/autofs_i.h +
    • ./fs/jffs2/nodelist.h +
    • ./include/linux/wait.h +
    • ./include/linux/parport_pc.h +
    • ./include/linux/amigaffs.h +
    • ./include/linux/parport_pc.h +
    • ./include/linux/ncp_fs.h +
    • drivers/net/wireless airport and orinoco +
    • drivers/char/ftape +
    • drivers/char/dtlk.c +
    • drivers/char/mwave +
    • drivers/char/n_r3964.c +
    • drivers/scsi/qlogicfc.c +
    • drivers/usb/pwc-if.c +
    • drivers/usb/hpusbscsi.c +
    • drivers/acpi/include/acmacros.h +
    • arch/sparc/kernel/signal.c +
    • arch/mips/math-emu/cp1emu.c +
    • drivers/net/wavelan.c +
    • drivers/net/hp100.c +
    • drivers/net/wan/lmc/lmc_debug.c +
    • drivers/net/skfp/h/targetos.h +
    • drivers/char/ip2main.c +
    • drivers/scsi/gdth.c +
    • drivers/scsi/megaraid.c +
    • drivers/scsi/qlogicisp.c +
    • drivers/scsi/ips.c +
    • drivers/scsi/qla1280.c +
    • drivers/scsi/cpqfcTSstructs.h +
    • drivers/cdrom/sjcd.c +
    • drivers/isdn/eicon/sys.h +
    • drivers/sbus/char/bbc_envctrl.c +
    • drivers/ide/ide-tape.c +
    • drivers/video/radeonfb.c +
    • fs/intermezzo/sysctl.c +
    • fs/ext3/balloc.c +
    • net/ipv6/ip6_fib.c +
    • net/irda/irnet/irnet.h +
        + +

        A number of tracing tools have been developed for the Linux kernel. +The best known, particularly in the embedded systems area, is the Linux Trace +Toolkit, LTT at +http://www.opersys.com/LTT. It +comes with a nice graphical user interface and is currently under active +development to add dynamically defined event types and graphical trace +analysis modules. + +

        +The Linux Kernel State Tracer at +http://lkst.sf.netwas developed by Hitachi and offers basic, +low overhead, tracing functionality. There is no grahical user interface +available. + +

        +MAGNET was recently released. It was initially developed to trace the network +stack and drivers. Its performance has not been optimized for SMP systems. +It is available from + +http://public.lanl.gov/radiant/software/magnet.html +. + +

        +The IKD patch from Andrea Arcangeli + +ftp://ftp.kernel.org/pub/linux/kernel/people/andrea/ikd/ + +includes ktrace which adds the -pg gcc compilation option +to specified source files. This adds a call to function mcount +upon entry in any function compiled with that option. A function mcount +is provided which records in a trace the address of the function entered. +Using the system map, this is later translated into a trace of names of +functions entered. + +

        Reliability, Availability and Serviceability

        + +

        +Tracing may be placed in the larger context of Reliability, Availability and +Serviceability (RAS). The Linux RAS project is probably the most active and +well organized, + +http://systemras.sourceforge.net/ + + +http://www-124.ibm.com/linux/projects/linuxras/ +. +It links to several underlying projects, including the Linux Trace Toolkit +LTT. + +

        +Several other projects within Linux RAS directly relate to tracing. + +

        Enterprise Event Logging

        + +

        The Enterprise Event Logging project, +EVLOG project +at http://evlog.sourceforge.net/, produces traces and thus shares a number +of underlying implementation needs +(events recording, kernel to user mode transfer, +trace analysis and viewing tools, event types format). The intended purpose +and thus implementation constraints differ significantly, however. +EVLOG records important system events for two purposes, +to trigger service and security alarms (e.g. weak signals in a magnetic disk, +unauthorized access attempt) and to provide permament records. The volume +is typically low and full context is required for each event. While logging +(EVLOG) is therefore implemented separately from tracing (LTT), some +underlying technology may be reused as appropriate (e.g. kernel hooks, +kernel to user mode data relay...). + +

        Kernel Crash Dump

        + +

        A common symptom of a serious kernel problem is a crash. Traces may +be extremely useful to understand the problem except that, because of the +crash, the important last events in the current trace buffer cannot be +stored on disk. The Linux Kernel Crash Dump facility (LKCD) at + +http://oss.software.ibm.com/developer/opensource/linux/projects/flexdump/ + is used to recover such information, when warm rebooting from a +crash while this information is still available in memory. + +

        LKCD needs to be told how to find the tracing buffers in the memory +(address in a map or signature to look for) and in which file to save +their content. + +

        Kernel Hooks

        + +

        +Kernel hooks, at + +http://www-124.ibm.com/developerworks/oss/linux/projects/kernelhooks/ + are a mechanism to insert hooks at desired locations in the kernel. +Handlers may later be registered to be called at these hooks locations. +When no handler is registered, the cost associated with a hook is almost +negligeable, a few NOPs. Skipping NOPs is even faster than testing a +global boolean variable. Kernel hooks would be ideally suited for the +dynamic activation of trace points. Furthermore, kernel hooks allow registering +multiple handlers. A same location could have a tracing handler and a +performance tool handler, reducing the number of points needed to be +inserted in the kernel source code. + +

        Interactive tools may be used to rapidly select groups of hooks to be +activated based on facilities (networking, block devices...), level +of details (core events, detailed events) or severity level (warning, info, +debug). + +

        As part of Kernel Hooks and Dynamic Probes, were defined handlers +which produce tracing information. The tracing data models for Dynamic Probes +and LTT are fairly similar and may eventually be consolidated. + +

        Dynamic Probes

        + +

        The Dynamic Probes, + +http://www-124.ibm.com/linux/projects/kprobes/ +, +allow inserting kernel hooks dynamically in a running kernel, just like +breakpoints in debuggers. The instruction +at the desired location is saved and replaced by an interrupt instruction. +When the interrupt instruction is executed, the handlers are called, the +original instruction restored and executed in single step mode, and the +interrupt instruction is reinserted. + + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/tsc-smallv1.txt b/tags/lttv-0.11.3-23102008/doc/developer/tsc-smallv1.txt new file mode 100644 index 00000000..58f19520 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/tsc-smallv1.txt @@ -0,0 +1,134 @@ +Adding support for "compact" 32 bits events. + +Mathieu Desnoyers +March 9, 2007 + + +event header + + +32 bits header +Aligned on 32 bits + 1 bit to select event type + 4 bits event ID + 16 events (too few) + 27 bits TSC (cut MSB) + wraps 32 times per second at 4GHz + each wraps spaced from 0.03125s + 100HZ clock : tick each 0.01s + detect wrap at least each 3 jiffies (dangerous, may miss) + granularity : 2^0 = 1 cycle : 0.25ns @4GHz +payload size known by facility + +32 bits header +Aligned on 32 bits + 1 bit to select event type + 4 bits event ID + 16 events (too few) + 27 bits TSC (cut LSB) + wraps each second at 4GHz + 100HZ clock : tick each 0.01s + granularity : 2^5 = 32 cycles : 8ns @4GHz +payload size known by facility + +32 bits header +Aligned on 32 bits + 1 bit to select event type + 5 bits event ID + 32 events + 26 bits TSC (cut LSB) + wraps each 0.5 second at 4GHz + 100HZ clock : tick each 0.01s + granularity : 2^6 = 64 cycles : 16ns @4GHz +payload size known by facility + +32 bits header +Aligned on 32 bits + 1 bit to select event type + 6 bits event ID + 64 events + 25 bits TSC (cut LSB) + wraps each 0.5 second at 4GHz + 100HZ clock : tick each 0.01s + granularity : 2^7 = 128 cycles : 32ns @4GHz +payload size known by facility + +64 bits header +Aligned on 32 bits + 1 bit to select event type + 15 bits event id, (major 8 minor 8) + 32768 events + 16 bits event size (extra) + 32 bits TSC + wraps each second at 4GHz + 100HZ clock : tick each 0.01s + +96 or 128 bits header (full 64 bits TSC, useful when no heartbeat available + size depends on internal alignment) +Aligned on 32 bits + 1 bit to select event type + 15 bits event id, (major 8 minor 8) + 32768 events + 16 bits event size (extra) +Align on 64 bits + 64 bits TSC + wraps each 146.14 years at 4GHz + + + + + +Must put the event ID fields first in the large (64, 96-128 bits) event headers +Create a "compact" facility which reserves the facility IDs with the MSB at 1. + - or better : select mapping for events +What is the minimum granularity required ? (so we know how much LSB to cut) + - How much can synchonized CPU TSCs drift apart one from another ? + PLL + http://en.wikipedia.org/wiki/Phase-locked_loop + static phase offset -> tracking jitter + 25 MHz oscillator on motherboard for CPU + jitter : expressed in ±picoseconds (should therefore be lower than 0.25ns) + http://www.eetasia.com/ART_8800082274_480600_683c4e6b200103.HTM + NEED MORE INFO. + - What is the cacheline synchronization latency between the CPUs ? + Worse case : Intel Core 2, Intel Xeon 5100, Intel core solo, intel core duo + Unified L2 cache. http://www.intel.com/design/processor/manuals/253668.pdf + Intel Core 2, Intel Xeon 5100 + http://www.intel.com/design/processor/manuals/253665.pdf + Up to 10.7 GB/s FSB + http://www.xbitlabs.com/articles/mobile/display/core2duo_2.html + Intel Core Duo Intel Core 2 Duo + L2 cache latency 14 cycles 14 cycles + (round-trip : 28 cycles) 7ns @4GHz + sparc64 : between threads : shares L1 cache. + suspected to be ~2 cycles total (1+1) (to check) + - How close (cycle-wise) can be two consecutive recorded events in the same + buffer ? (~200ns, time for logging an event) (~800 cycles @4GHz) + - Tracing code itself : if it's at a subbuffer boundary, more check to do. + Must see the maximum duration of a non interrupted probe. + Worse case (had NMIs enabled) : 6997 cycles. 1749 ns @4GHz. + TODO : test with NMIs disabled and HT disabled. + Ordering can be changed if an interrupt comes between the memory operation + and the tracer call. Therefore, we cannot rely on more precision than the + expected interrupt handler duration. (guess : ~10000cycles, 2500ns@4GHz) + - If there is a faster interconnect between the CPUs, it can be a problem, but + seems to only be proprietary interconnects, not used in general. + - IPI are expected to take much more than 28 cycles. +What is the minimum wrap-around interval ? (must be safe for timer interrupt +miss and multiple timer HZ (configurable) and CPU MHZ frequencies) +Must align _all_ headers on 32 bits, not 64. + +Granularity : 800ns (200 cycles@4GHz) : 2^9 = 512 (remove 9 LSB) + Number of LSB skipped : first_bit(probe_duration_in_cycles)-1 + +Min wrap : 100HZ system, each 3 timer ticks : 0.03s (32-4 MSB for 4 GHZ : 0.26s) + (heartbeat each 100HZ, to be safe) + Number of MSB to skip : + 32 - first_bit(( (expected_longest_cli()[ms] + max_timer_interval[ms]) * 2 / + cpu_khz )) + + +9LSB + 4MSB = 13 bits total. 12 bits for event IDs : 4096 events. + + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/tsc-smallv2.txt b/tags/lttv-0.11.3-23102008/doc/developer/tsc-smallv2.txt new file mode 100644 index 00000000..d65cfed3 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/tsc-smallv2.txt @@ -0,0 +1,143 @@ +Adding support for "compact" 32 bits events. + +Mathieu Desnoyers +March 12, 2007 + +Use a separate channel for compact events + +Mux those events into this channel and magically they are "compact". Isn't it +beautiful. + +event header + +### COMPACT EVENTS + +32 bits header +Aligned on 32 bits + 5 bits event ID + 32 events + 27 bits TSC (cut MSB) + wraps 32 times per second at 4GHz + each wraps spaced from 0.03125s + 100HZ clock : tick each 0.01s + detect wrap at least each 3 jiffies (dangerous, may miss) + granularity : 2^0 = 1 cycle : 0.25ns @4GHz +payload size known by facility + +32 bits header +Aligned on 32 bits + 5 bits event ID + 32 events + 27 bits TSC (cut LSB) + wraps each second at 4GHz + 100HZ clock : tick each 0.01s + granularity : 2^5 = 32 cycles : 8ns @4GHz +payload size known by facility + +32 bits header +Aligned on 32 bits + 6 bits event ID + 64 events + 26 bits TSC (cut LSB) + wraps each 0.5 second at 4GHz + 100HZ clock : tick each 0.01s + granularity : 2^6 = 64 cycles : 16ns @4GHz +payload size known by facility + +32 bits header +Aligned on 32 bits + 7 bits event ID + 128 events + 25 bits TSC (cut LSB) + wraps each 0.5 second at 4GHz + 100HZ clock : tick each 0.01s + granularity : 2^7 = 128 cycles : 32ns @4GHz +payload size known by facility + + + +### NORMAL EVENTS + +64 bits header +Aligned on 64 bits + 32 bits TSC + wraps each second at 4GHz + 100HZ clock : tick each 0.01s + 16 bits event id, (major 8 minor 8) + 65536 events + 16 bits event size (extra) + +96 bits header (full 64 bits TSC, useful when no heartbeat available) +Aligned on 64 bits + 64 bits TSC + wraps each 146.14 years at 4GHz + 16 bits event id, (major 8 minor 8) + 65536 events + 16 bits event size (extra) + + +## Discussion of compact events + +Must put the event ID fields first in the large (64, 96-128 bits) event headers +What is the minimum granularity required ? (so we know how much LSB to cut) + - How much can synchonized CPU TSCs drift apart one from another ? + PLL + http://en.wikipedia.org/wiki/Phase-locked_loop + static phase offset -> tracking jitter + 25 MHz oscillator on motherboard for CPU + jitter : expressed in ±picoseconds (should therefore be lower than 0.25ns) + http://www.eetasia.com/ART_8800082274_480600_683c4e6b200103.HTM + NEED MORE INFO. + - What is the cacheline synchronization latency between the CPUs ? + Worse case : Intel Core 2, Intel Xeon 5100, Intel core solo, intel core duo + Unified L2 cache. http://www.intel.com/design/processor/manuals/253668.pdf + Intel Core 2, Intel Xeon 5100 + http://www.intel.com/design/processor/manuals/253665.pdf + Up to 10.7 GB/s FSB + http://www.xbitlabs.com/articles/mobile/display/core2duo_2.html + Intel Core Duo Intel Core 2 Duo + L2 cache latency 14 cycles 14 cycles + (round-trip : 28 cycles) 7ns @4GHz + sparc64 : between threads : shares L1 cache. + suspected to be ~2 cycles total (1+1) (to check) + - How close (cycle-wise) can be two consecutive recorded events in the same + buffer ? (~200ns, time for logging an event) (~800 cycles @4GHz) + - Tracing code itself : if it's at a subbuffer boundary, more check to do. + Must see the maximum duration of a non interrupted probe. + Worse case (had NMIs enabled) : 6997 cycles. 1749 ns @4GHz. + TODO : test with NMIs disabled and HT disabled. + Ordering can be changed if an interrupt comes between the memory operation + and the tracer call. Therefore, we cannot rely on more precision than the + expected interrupt handler duration. (guess : ~10000cycles, 2500ns@4GHz) + - If there is a faster interconnect between the CPUs, it can be a problem, but + seems to only be proprietary interconnects, not used in general. + - IPI are expected to take much more than 28 cycles. +What is the minimum wrap-around interval ? (must be safe for timer interrupt +miss and multiple timer HZ (configurable) and CPU MHZ frequencies) + +Granularity : 800ns (200 cycles@4GHz) : 2^9 = 512 (remove 9 LSB) + Probe never takes 1 cycle. + Number of LSB skipped : max(0, (long)find_first_bit(probe_duration_in_cycles)-1) + +Min wrap : 100HZ system, each 3 timer ticks : 0.03s (32-4 MSB for 4 GHZ : 0.26s) + (heartbeat each 100HZ, to be safe) + Number of MSB to skip : + 32 - find_first_bit(( (expected_longest_interrupt_latency()[ms] + + max_timer_interval[ms]) * cpu_khz[kcycles/s] )) - 1 + (the last -1 is to make sure we remove less or exact amount of bits, round + near to 0, not round up). + +Heartbeat timer : + Each timer interrupt + Event : 32 bytes in size + each timer tick : 100HZ + 3.2kB/s + +9LSB + 4MSB = 13 bits total. 13 bits for event IDs : 8192 events. + + + + + + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/tsc.txt b/tags/lttv-0.11.3-23102008/doc/developer/tsc.txt new file mode 100644 index 00000000..6733d46c --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/tsc.txt @@ -0,0 +1,189 @@ + +************************************************************* +AMD +http://lkml.org/lkml/2005/11/4/173 +http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/26094.PDF +http://developer.amd.com/article_print.jsp?id=92 (RH) + +[7] AMD's 7th generation processors return a CPUID base + family value of '7'. These include AMD Athlon, AthlonXP, + AthlonMP, and Duron. + +[8] AMD's 8th generation processors return an effective + CPUID family of '0x0F'. These include AMD Opteron, + Athlon64, and Turion. + + +before 7th gen : ok +7th gen: + P-state (performance state) change + UP : warn about time inaccuracy + SMP + sol : disable powernow + Use monotonic pseudo-TSC + STPCLK-Throttling (temperature) : only done on UP, ok + UP : warn about time inaccuracy +8th gen : + P-state change + UP : inaccuracy + dual-core : locked-step ; inaccuracy + SMP : may drift + sol : disable powernow + Use monotonic pseudo-TSC + SMP, dual core : C1-clock ramping (halt) (power state : C-state) + sol : idle=poll or disable C1-ramping + Use monotonic pseudo-TSC + STPCLK-Throttling (temperature) : + single processor dual-core ok ; inaccuracy + SMP : NOT ok (rare) + Use monotonic pseudo-TSC + + +Until TSC becomes invariant, AMD recommends that operating +system developers avoid TSC as a fast timer source on +affected systems. (AMD recommends that the operating system +should favor these time sources in a prioritized manner: +HPET first, then ACPI PM Timer, then PIT.) The following +pseudo-code shows one way of determining when to use TSC: + + use_AMD_TSC() { // returns TRUE if ok to use TSC + if (CPUID.base_family < 0xf) { + // TSC drift doesn't exist on 7th Gen or less + // However, OS still needs to consider effects + // of P-state changes on TSC + return TRUE; + } else if (CPUID.AdvPowerMgmtInfo.TscInvariant) { + // Invariant TSC on 8th Gen or newer, use it + // (assume all cores have invariant TSC) + return TRUE; + } else if ((number_processors == 1)&&(number_cores == 1)){ + // OK to use TSC on uni-processor-uni-core + // However, OS still needs to consider effects + // of P-state changes on TSC + return TRUE; + } else if ( (number_processors == 1) && + (CPUID.effective_family == 0x0f) && + !C1_ramp_8gen ){ + // Use TSC on 8th Gen uni-proc with C1_ramp off + // However, OS still needs to consider effects + // of P-state changes on TSC + return TRUE; + } else { + return FALSE; + } + } + C1_ramp_8gen() { + // Check if C1-Clock ramping enabled in PMM7.CpuLowPwrEnh + // On 8th-Generation cores only. Assume BIOS has setup + // all Northbridges equivalently. + return (1 & read_pci_byte(bus=0,dev=0x18,fcn=3,offset=0x87)); + } + + +When an operating system can not avoid using TSC in the +short-term, the operating system will need to either +re-synchronize the TSC of the halted core when exiting halt +or disable C1-clock ramping. The pseudo-code for disabling +C1-clock ramping follows: + + if ( !use_AMD_TSC() && + (CPUID.effective_family == 0x0f) && + C1_ramp_8gen() ){ + for (i=0; i < number_processors; ++i){ + // Do for all NorthBridges in platform + tmp = read_pci_byte(bus=0,dev=0x18+i,fcn=3,offset=0x87); + tmp &= 0xFC; // clears pmm7[1:0] + write_pci_byte(bus=0,dev=0x18+i,fcn=3,offset=0x87,tmp) + } + } + + +Future TSC Directions and Solutions +=================================== +Future AMD processors will provide a TSC that is P-state and +C-State invariant and unaffected by STPCLK-throttling. This +will make the TSC immune to drift. Because using the TSC +for fast timer APIs is a desirable feature that helps +performance, AMD has defined a CPUID feature bit that +software can test to determine if the TSC is +invariant. Issuing a CPUID instruction with an %eax register +value of 0x8000_0007, on a processor whose base family is +0xF, returns "Advanced Power Management Information" in the +%eax, %ebx, %ecx, and %edx registers. Bit 8 of the return +%edx is the "TscInvariant" feature flag which is set when +TSC is P-state, C-state, and STPCLK-throttling invariant; it +is clear otherwise. + +The rate of the invariant TSC is implementation-dependent +and will likely *not* be the frequency of the processor +core; however, its period should be short enough such that +it is not possible for two back-to-back rdtsc instructions +to return the same value. Software which is trying to +measure actual processor frequency or cycle-performance +should use Performance Event 76h, CPU Clocks not Halted, +rather than the TSC to count CPU cycles. + + + +************************************************************* +Intel + +Pentium M + family [06H], models [09H, 0DH] + UP + warn about time inaccuracy + SMP + SOL : disable speedstep +Pentium 4 processors, Intel Xeon + family [0FH], models [00H, 01H, or 02H] + UP + warn about time inaccuracy + SMP + SOL : disable speedstep + +Other : ok + +http://download.intel.com/design/Xeon/specupdt/30675712.pdf +http://forum.rightmark.org/post.cgi?id=print:6:269&user=%20Dmitri%20Besedin&color=1 +http://bochs.sourceforge.net/techspec/24161821.pdf cpuid + +cpuz +AFAIK this problem only concerns Prescott CPUs, but I bet future production will +also use the same rule. + +Well, from what Intel states in one of their docs (Intel(R) Pentium(R) M +Processor on 90 nm Process with 2-MB L2 Cache, Specification Update, Document +No. 302209-010 (http://www.intel.com/design/mobile/specupdt/302209.htm) ), your +supposition is true: + + +Article V. For Pentium M processors (family [06H], models [09H, 0DH]); for +Pentium 4 processors, Intel Xeon processors (family [0FH], models [00H, 01H, or +02H]); and for P6 family processors: the timestamp counter increments with every +internal processor clock cycle. The internal processor clock cycle is determined +by the current core-clock to bus-clock ratio. Intel(R) SpeedStep(R) technology +transitions may also impact the processor clock. + +Article VI. For Pentium 4 processors, Intel Xeon processors (family [0FH], +models [03H and higher]): the time-stamp counter increments at a constant rate. +That rate may be set by the maximum core-clock to bus-clock ratio of the +processor or may be set by the frequency at which the processor is booted. The +specific processor configuration determines the behavior. Constant TSC behavior +ensures that the duration of each clock tick is uniform and supports the use of +the TSC as a wall clock timer even if the processor core changes frequency. This +is the architectural behavior moving forward. + + +It's a pity they call this sucking TSC behavior an "architectural behavior +moving forward" + + + +************************************************************* +HPET + +http://www.intel.com/hardwaredesign/hpetspec_1.pdf + + + + diff --git a/tags/lttv-0.11.3-23102008/doc/developer/xen-port.txt b/tags/lttv-0.11.3-23102008/doc/developer/xen-port.txt new file mode 100644 index 00000000..a7218e3d --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/developer/xen-port.txt @@ -0,0 +1,146 @@ + + +Xen port notes + +Mathieu Desnoyers, April 2006 + +Useful files : + +common/trace.c +include/xen/trace.h and the dom0 op in include/public/dom0_ops.h + +alloc buffer (alloc_trace_bufs) : +alloc_xenheap_pages + +Shared buffer between dom0 and hypervisor : +share_xen_page_with_privileged_guests + + + +trace_create / trace_destroy : + +call xen hooks from dom0 to allocate a new buffer, get the newly allocated +struct trace pointer. +Share struct traces with dom0 to have offset and consumed counts. +Share the buffers with dom0 too. + +Create a new "xen" channel. + + +Create 4 new xen facilities (see include/public/trace.h) : +xen_sched (grep TRC_SCHED) + sched_dom_add + sched_dom_rem + sched_sleep + sched_wake + sched_yield + sched_block + sched_shutdown + sched_ctl + sched_switch + sched_s_timer_fn + sched_t_timer_fn + sched_dom_timer_fn + sched_dom_timer_fn + sched_switch_infprev + sched_switch_infnext +xen_dom0op (grep TRC_DOM0OP) + dom0op_enter_base + dom0op_leave_base +xen_vmx (grep TRC_VMX) + vmx_vmexit + vmx_vmentry + vmx_timer_intr + vmx_int + vmx_io +xen_mem (grep TRC_MEM) + mem_page_grant_map + mem_page_grant_unmap + mem_page_grant_transfer + + +How to share xen pages efficiently ? +Pages allocated in xen heap, the shared with dom0. + +How buffers are accessed in dom0 ? +directly from pointers to buffers contained in get_info control information. + +do_dom0_op + +tbufcontrol -> control info + int tb_control(dom0_tbufcontrol_t *tbc) + +DOM0_TBUF_GET_INFO buffer_mfn __pa(t_bufs[0]) >> PAGE_SHIFT; + + +create trace : + +in : +trace name +buffer size +return : 0 success, 1 failure +do : +alloc buffer xen +share with dom0 + +destroy trace : +in : +trace name +do : unshare buffer / free. + + +start/stop : +tb_control that will update the control structure. +in : trace name +start : put active to 1 + inc. nb_active traces +stop : put active to 0 + dec nb_active traces. +return : 0 success, 1 failure. + +consume : +in : trace name +tb_control that will update the consumed count. +in : old count. +return : 0 success, 1 failure (pushed). + +get_info : +tb_control that returns a fresh copy of trace control info. + + + +NOTE 8-11-2006 + +Xen 3.0.3 +Only one init of buffers (cannot change buffer size) +tbc->buffer_mfn = opt_tbuf_size ? virt_to_mfn(per_cpu(t_bufs, 0)) : 0; +/* Convert between Xen-heap virtual addresses and machine frame numbers. */ +#define virt_to_mfn(va) (virt_to_maddr(va) >> PAGE_SHIFT) + +struct t_buf *tbufs_mapped; + + tbufs_mapped = xc_map_foreign_range(xc_handle, DOMID_XEN, + size * num, PROT_READ | PROT_WRITE, + tbufs_mfn); + +int fd = open("/proc/xen/privcmd", O_RDWR); + +/* sleep for this long (milliseconds) between checking the trace buffers */ +#define POLL_SLEEP_MILLIS 100 + + +netfront.c +xenbus_alloc_evtchn +bind_evtchn_to_irqhandler + + +virq : + +include/public/xen.h VIRQ_TBUF + /* G. (DOM0) Trace buffer has records available. */ + +trace_notify_guest : send_guest_global_virq(dom0, VIRQ_TBUF); + +see arch/i386/oprofile/xenoprof.c +bind_virq + + + diff --git a/tags/lttv-0.11.3-23102008/doc/user/Makefile.am b/tags/lttv-0.11.3-23102008/doc/user/Makefile.am new file mode 100644 index 00000000..b4d5aabc --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/user/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = user_guide + +EXTRA_DIST = guiEvents.html diff --git a/tags/lttv-0.11.3-23102008/doc/user/guiEvents.html b/tags/lttv-0.11.3-23102008/doc/user/guiEvents.html new file mode 100644 index 00000000..4e4af883 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/user/guiEvents.html @@ -0,0 +1,59 @@ + + + + Graphical Event Viewer Plugin User Manual + + +

        Graphical Event Viewer Plugin User Manual

        + +

        +This viewers shows a list of the events that are included in the currently +viewed time interval. This is a detailed manner of seeing each event's +information. + +

        +Showing this viewer is as simple as adding it to the current tab by selecting +the Insert Event Viewer menu item from the Tools menu or by clicking on +this viewer's Insertion Button. You can then use up/down arrows buttons and +delete button when the viewer is selected to change it's position in the +tab or remove it. + +

        +It them appears in the current tab. If there are events to display in the +current time frame selected by the tab, you should already see them in the +list. You can move the scrollbar on the right side up and down to display +the different events in the list. As you can see, this list is limited to +the currently viewed time interval. In order to see more events in this +list, you can use a Zoom Extended of the main window (this will show all +the trace's events). You can also specify the laps of time you want to see +using the main window's Show Time Frame. + +

        +One important feature of this plugin is the ability to inform the tab in +which it resides of the currently selected event. By clicking on an event, +it will appear highlighted in the list and this event will then become the +current event. + +

        +The events shown are selected by the filters applied on them. Hence, you can +configure the viewer's associated filter using various criterias in order +to select the events you want to show. + +

        +By clicking on the viewer with the right mouse button, a popup menu appears, +giving the opportunity to configure the viewer's filter or to configure +the viewer itself. Configuration of the filter will be explained in another +document, but event viewer's configuration follows. + +

        +The viewer's configuration window permits a selection of the fields that +must be displayed in the event viewer. There are fields which can always be +selected, as, for example, event type or time of the event, while there are +fields that are specific to the event types present in the trace. A browse +list, following the nested architecture of the description of events, will +show all the available fields on the left. A list at the right side will +list all the currently selectied fields. Two arrows in the middle of the +two lists can be used to add or remove a field from the shown fields list. + + + diff --git a/tags/lttv-0.11.3-23102008/doc/user/user_guide/Makefile.am b/tags/lttv-0.11.3-23102008/doc/user/user_guide/Makefile.am new file mode 100644 index 00000000..2f3c6237 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/user/user_guide/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = docbook html + +EXTRA_DIST = user_guide.dvi diff --git a/tags/lttv-0.11.3-23102008/doc/user/user_guide/docbook/Makefile.am b/tags/lttv-0.11.3-23102008/doc/user/user_guide/docbook/Makefile.am new file mode 100644 index 00000000..b9df7911 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/user/user_guide/docbook/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = lttv-color-list.eps lttv-color-list.png lttv-numbered-5.eps lttv-numbered-5.png user_guide.docbook diff --git a/tags/lttv-0.11.3-23102008/doc/user/user_guide/docbook/lttv-color-list.eps b/tags/lttv-0.11.3-23102008/doc/user/user_guide/docbook/lttv-color-list.eps new file mode 100644 index 00000000..4f201860 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/user/user_guide/docbook/lttv-color-list.eps @@ -0,0 +1,6750 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: inkscape 0.43 +%%Pages: 1 +%%Orientation: Portrait +%%BoundingBox: 3 440 528 697 +%%HiResBoundingBox: 3.0770441 440.39428 527.71256 696.19813 +%%EndComments +%%Page: 1 1 +0 842 translate +0.8 -0.8 scale +gsave [1 0 0 1 0 0] concat +gsave +<< +/ShadingType 2 +/ColorSpace /DeviceRGB +/Coords [13.768714 1253.7404 69.307785 1253.7404] +/Extend [true true] +/Domain [0 1] +/Function << +/FunctionType 3 +/Functions +[ +<< +/FunctionType 2 +/Domain [0 1] +/C0 [1 1 1] +/C1 [1 1 1] +/N 1 +>> +] +/Domain [0 1] +/Bounds [ ] +/Encode [ 0 1 ] +>> +>> +newpath +6.5744788 227.30679 moveto +456.83872 227.30679 456.83872 227.30679 456.83872 227.30679 curveto +eoclip +gsave [8.209048 0 0 0.09472844 -109.2829 108.5418] concat +shfill +grestore +grestore +0 1 0 setrgbcolor +[] 0 setdash +5.4563475 setlinewidth +0 setlinejoin +0 setlinecap +newpath +6.5744788 227.30679 moveto +456.83872 227.30679 456.83872 227.30679 456.83872 227.30679 curveto +stroke +gsave +<< +/ShadingType 3 +/ColorSpace /DeviceRGB +/Coords [112.05622 705.04535 0 111.55042 699.38416 124.54283] +/Extend [true true] +/Domain [0 1] +/Function << +/FunctionType 3 +/Functions +[ +<< +/FunctionType 2 +/Domain [0 1] +/C0 [0 0 0] +/C1 [0 0 0] +/N 1 +>> +] +/Domain [0 1] +/Bounds [ ] +/Encode [ 0 1 ] +>> +>> +newpath +3.873708 182.11452 moveto +459.53948 182.11452 lineto +459.53948 213.37127 lineto +3.873708 213.37127 lineto +3.873708 182.11452 lineto +closepath +eoclip +gsave [3.134277 0 0 0.248105 -112.3747 22.81742] concat +shfill +grestore +grestore +1 1 1 setrgbcolor +[] 0 setdash +0 setlinewidth +0 setlinejoin +0 setlinecap +newpath +3.873708 182.11452 moveto +459.53948 182.11452 lineto +459.53948 213.37127 lineto +3.873708 213.37127 lineto +3.873708 182.11452 lineto +closepath +stroke +gsave +1 1 1 setrgbcolor +newpath +6.5744788 197.17861 moveto +456.83872 197.17861 456.83872 197.17861 456.83872 197.17861 curveto +eofill +grestore +1 1 1 setrgbcolor +[] 0 setdash +5.4563475 setlinewidth +0 setlinejoin +0 setlinecap +newpath +6.5744788 197.17861 moveto +456.83872 197.17861 456.83872 197.17861 456.83872 197.17861 curveto +stroke +gsave [0.914554 0 0 0.850284 393.4446 45.59173] concat +gsave +0 0 0 setrgbcolor +newpath +76.69548 174.83551 moveto +77.890793 174.83551 lineto +79.730637 182.23004 lineto +81.564621 174.83551 lineto +82.894699 174.83551 lineto +84.734543 182.23004 lineto +86.568527 174.83551 lineto +87.769699 174.83551 lineto +85.572433 183.58356 lineto +84.084152 183.58356 lineto +82.238449 175.98981 lineto +80.375168 183.58356 lineto +78.886887 183.58356 lineto +76.69548 174.83551 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +94.742355 179.62262 moveto +94.742355 183.58356 lineto +93.66423 183.58356 lineto +93.66423 179.65778 lineto +93.664225 179.03669 93.543131 178.57184 93.300949 178.26324 curveto +93.058757 177.95466 92.695476 177.80036 92.211105 177.80035 curveto +91.629071 177.80036 91.170087 177.98591 90.834152 178.35699 curveto +90.498212 178.72809 90.330244 179.23395 90.330246 179.87457 curveto +90.330246 183.58356 lineto +89.246262 183.58356 lineto +89.246262 174.46637 lineto +90.330246 174.46637 lineto +90.330246 178.04059 lineto +90.588056 177.64606 90.89079 177.35114 91.238449 177.15582 curveto +91.590008 176.96052 91.994305 176.86286 92.45134 176.86285 curveto +93.205241 176.86286 93.775553 177.09724 94.162277 177.56598 curveto +94.54899 178.03083 94.742349 178.71637 94.742355 179.62262 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +96.904465 177.02106 moveto +97.98259 177.02106 lineto +97.98259 183.58356 lineto +96.904465 183.58356 lineto +96.904465 177.02106 lineto +96.904465 174.46637 moveto +97.98259 174.46637 lineto +97.98259 175.8316 lineto +96.904465 175.8316 lineto +96.904465 174.46637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +101.299 175.15778 moveto +101.299 177.02106 lineto +103.5197 177.02106 lineto +103.5197 177.85895 lineto +101.299 177.85895 lineto +101.299 181.42145 lineto +101.29899 181.95661 101.37126 182.30036 101.51579 182.4527 curveto +101.66423 182.60504 101.96306 182.68121 102.41228 182.68121 curveto +103.5197 182.68121 lineto +103.5197 183.58356 lineto +102.41228 183.58356 lineto +101.58024 183.58356 101.00603 183.42926 100.68962 183.12067 curveto +100.37321 182.80817 100.21501 182.24176 100.21501 181.42145 curveto +100.21501 177.85895 lineto +99.423996 177.85895 lineto +99.423996 177.02106 lineto +100.21501 177.02106 lineto +100.21501 175.15778 lineto +101.299 175.15778 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +110.55681 180.03278 moveto +110.55681 180.56012 lineto +105.59978 180.56012 lineto +105.64665 181.30231 105.86931 181.86872 106.26775 182.25934 curveto +106.67009 182.64606 107.22868 182.83942 107.94353 182.83942 curveto +108.35759 182.83942 108.75798 182.78864 109.1447 182.68707 curveto +109.53532 182.58551 109.92204 182.43317 110.30486 182.23004 curveto +110.30486 183.24957 lineto +109.91813 183.41364 109.52165 183.53864 109.1154 183.62457 curveto +108.70915 183.71051 108.29704 183.75348 107.87907 183.75348 curveto +106.8322 183.75348 106.00212 183.44879 105.38884 182.83942 curveto +104.77946 182.23004 104.47478 181.40582 104.47478 180.36676 curveto +104.47478 179.29255 104.76384 178.44098 105.34196 177.81207 curveto +105.92399 177.17927 106.7072 176.86286 107.69157 176.86285 curveto +108.57438 176.86286 109.27165 177.14802 109.78337 177.71832 curveto +110.29899 178.28473 110.5568 179.05622 110.55681 180.03278 curveto +109.47868 179.71637 moveto +109.47087 179.12653 109.30485 178.65583 108.98064 178.30426 curveto +108.66032 177.9527 108.23454 177.77692 107.70329 177.77692 curveto +107.10173 177.77692 106.61931 177.94684 106.25603 178.28668 curveto +105.89665 178.62653 105.68962 179.10505 105.63493 179.72223 curveto +109.47868 179.71637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +116.42204 182.09528 moveto +117.65837 182.09528 lineto +117.65837 183.58356 lineto +116.42204 183.58356 lineto +116.42204 182.09528 lineto +116.42204 177.37848 moveto +117.65837 177.37848 lineto +117.65837 178.86676 lineto +116.42204 178.86676 lineto +116.42204 177.37848 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +129.13103 178.28082 moveto +129.40055 177.79645 129.72282 177.43903 130.09782 177.20856 curveto +130.47282 176.97809 130.91422 176.86286 131.42204 176.86285 curveto +132.10563 176.86286 132.63297 177.10309 133.00407 177.58356 curveto +133.37516 178.06013 133.5607 178.73981 133.56071 179.62262 curveto +133.56071 183.58356 lineto +132.47673 183.58356 lineto +132.47673 179.65778 lineto +132.47672 179.02887 132.36539 178.56208 132.14275 178.25739 curveto +131.92008 177.9527 131.58024 177.80036 131.12321 177.80035 curveto +130.56461 177.80036 130.12321 177.98591 129.799 178.35699 curveto +129.47477 178.72809 129.31266 179.23395 129.31267 179.87457 curveto +129.31267 183.58356 lineto +128.22868 183.58356 lineto +128.22868 179.65778 lineto +128.22868 179.02497 128.11735 178.55817 127.8947 178.25739 curveto +127.67204 177.9527 127.32829 177.80036 126.86345 177.80035 curveto +126.31266 177.80036 125.87516 177.98786 125.55095 178.36285 curveto +125.22673 178.73395 125.06462 179.23786 125.06462 179.87457 curveto +125.06462 183.58356 lineto +123.98064 183.58356 lineto +123.98064 177.02106 lineto +125.06462 177.02106 lineto +125.06462 178.04059 lineto +125.31071 177.63825 125.60563 177.34138 125.94939 177.14996 curveto +126.29313 176.95856 126.70134 176.86286 127.174 176.86285 curveto +127.65055 176.86286 128.05485 176.98395 128.38689 177.22614 curveto +128.72282 177.46833 128.97086 177.81989 129.13103 178.28082 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +138.25993 177.77692 moveto +137.68181 177.77692 137.22477 178.00348 136.88884 178.4566 curveto +136.5529 178.90583 136.38493 179.52301 136.38493 180.30817 curveto +136.38493 181.09333 136.55095 181.71247 136.88298 182.16559 curveto +137.21892 182.61481 137.6779 182.83942 138.25993 182.83942 curveto +138.83415 182.83942 139.28923 182.61285 139.62517 182.15973 curveto +139.9611 181.70661 140.12907 181.08942 140.12907 180.30817 curveto +140.12907 179.53083 139.9611 178.91559 139.62517 178.46246 curveto +139.28923 178.00544 138.83415 177.77692 138.25993 177.77692 curveto +138.25993 176.86285 moveto +139.19743 176.86286 139.93376 177.16755 140.46892 177.77692 curveto +141.00407 178.3863 141.27165 179.23005 141.27165 180.30817 curveto +141.27165 181.38239 141.00407 182.22614 140.46892 182.83942 curveto +139.93376 183.44879 139.19743 183.75348 138.25993 183.75348 curveto +137.31852 183.75348 136.58024 183.44879 136.04509 182.83942 curveto +135.51384 182.22614 135.24821 181.38239 135.24821 180.30817 curveto +135.24821 179.23005 135.51384 178.3863 136.04509 177.77692 curveto +136.58024 177.16755 137.31852 176.86286 138.25993 176.86285 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +147.37126 178.01715 moveto +147.37126 174.46637 lineto +148.44939 174.46637 lineto +148.44939 183.58356 lineto +147.37126 183.58356 lineto +147.37126 182.59918 lineto +147.14469 182.98981 146.85758 183.28082 146.50993 183.47223 curveto +146.16618 183.65973 145.75212 183.75348 145.26775 183.75348 curveto +144.47477 183.75348 143.82829 183.43707 143.32829 182.80426 curveto +142.8322 182.17145 142.58415 181.33942 142.58415 180.30817 curveto +142.58415 179.27692 142.8322 178.44489 143.32829 177.81207 curveto +143.82829 177.17927 144.47477 176.86286 145.26775 176.86285 curveto +145.75212 176.86286 146.16618 176.95856 146.50993 177.14996 curveto +146.85758 177.33747 147.14469 177.62653 147.37126 178.01715 curveto +143.69743 180.30817 moveto +143.69743 181.10114 143.85954 181.72418 144.18376 182.17731 curveto +144.51188 182.62653 144.9611 182.85114 145.53142 182.85114 curveto +146.10173 182.85114 146.55094 182.62653 146.87907 182.17731 curveto +147.20719 181.72418 147.37126 181.10114 147.37126 180.30817 curveto +147.37126 179.5152 147.20719 178.89411 146.87907 178.44489 curveto +146.55094 177.99177 146.10173 177.7652 145.53142 177.7652 curveto +144.9611 177.7652 144.51188 177.99177 144.18376 178.44489 curveto +143.85954 178.89411 143.69743 179.5152 143.69743 180.30817 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +156.28337 180.03278 moveto +156.28337 180.56012 lineto +151.32634 180.56012 lineto +151.37321 181.30231 151.59587 181.86872 151.99431 182.25934 curveto +152.39665 182.64606 152.95524 182.83942 153.67009 182.83942 curveto +154.08415 182.83942 154.48454 182.78864 154.87126 182.68707 curveto +155.26188 182.58551 155.6486 182.43317 156.03142 182.23004 curveto +156.03142 183.24957 lineto +155.64469 183.41364 155.24821 183.53864 154.84196 183.62457 curveto +154.43571 183.71051 154.0236 183.75348 153.60564 183.75348 curveto +152.55876 183.75348 151.72868 183.44879 151.1154 182.83942 curveto +150.50603 182.23004 150.20134 181.40582 150.20134 180.36676 curveto +150.20134 179.29255 150.4904 178.44098 151.06853 177.81207 curveto +151.65056 177.17927 152.43376 176.86286 153.41814 176.86285 curveto +154.30094 176.86286 154.99821 177.14802 155.50993 177.71832 curveto +156.02555 178.28473 156.28336 179.05622 156.28337 180.03278 curveto +155.20525 179.71637 moveto +155.19743 179.12653 155.03141 178.65583 154.7072 178.30426 curveto +154.38688 177.9527 153.9611 177.77692 153.42986 177.77692 curveto +152.82829 177.77692 152.34587 177.94684 151.98259 178.28668 curveto +151.62321 178.62653 151.41618 179.10505 151.3615 179.72223 curveto +155.20525 179.71637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +161.76189 180.99371 moveto +161.76189 177.02106 lineto +162.84001 177.02106 lineto +162.84001 180.9527 lineto +162.84001 181.57379 162.9611 182.04059 163.20329 182.35309 curveto +163.44548 182.66168 163.80876 182.81598 164.29314 182.81598 curveto +164.87516 182.81598 165.33415 182.63043 165.67009 182.25934 curveto +166.00993 181.88825 166.17985 181.38239 166.17986 180.74176 curveto +166.17986 177.02106 lineto +167.25798 177.02106 lineto +167.25798 183.58356 lineto +166.17986 183.58356 lineto +166.17986 182.57574 lineto +165.91813 182.97418 165.61344 183.27106 165.26579 183.46637 curveto +164.92204 183.65778 164.52165 183.75348 164.06462 183.75348 curveto +163.31071 183.75348 162.73845 183.5191 162.34782 183.05035 curveto +161.9572 182.58161 161.76189 181.89606 161.76189 180.99371 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +174.94548 179.62262 moveto +174.94548 183.58356 lineto +173.86736 183.58356 lineto +173.86736 179.65778 lineto +173.86735 179.03669 173.74626 178.57184 173.50407 178.26324 curveto +173.26188 177.95466 172.8986 177.80036 172.41423 177.80035 curveto +171.8322 177.80036 171.37321 177.98591 171.03728 178.35699 curveto +170.70134 178.72809 170.53337 179.23395 170.53337 179.87457 curveto +170.53337 183.58356 lineto +169.44939 183.58356 lineto +169.44939 177.02106 lineto +170.53337 177.02106 lineto +170.53337 178.04059 lineto +170.79118 177.64606 171.09392 177.35114 171.44157 177.15582 curveto +171.79313 176.96052 172.19743 176.86286 172.65446 176.86285 curveto +173.40837 176.86286 173.97868 177.09724 174.3654 177.56598 curveto +174.75211 178.03083 174.94547 178.71637 174.94548 179.62262 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +177.06657 174.46637 moveto +178.15056 174.46637 lineto +178.15056 179.85114 lineto +181.36736 177.02106 lineto +182.74431 177.02106 lineto +179.26384 180.09137 lineto +182.89079 183.58356 lineto +181.48454 183.58356 lineto +178.15056 180.37848 lineto +178.15056 183.58356 lineto +177.06657 183.58356 lineto +177.06657 174.46637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +189.52361 179.62262 moveto +189.52361 183.58356 lineto +188.44548 183.58356 lineto +188.44548 179.65778 lineto +188.44547 179.03669 188.32438 178.57184 188.0822 178.26324 curveto +187.84001 177.95466 187.47673 177.80036 186.99236 177.80035 curveto +186.41032 177.80036 185.95134 177.98591 185.6154 178.35699 curveto +185.27946 178.72809 185.11149 179.23395 185.1115 179.87457 curveto +185.1115 183.58356 lineto +184.02751 183.58356 lineto +184.02751 177.02106 lineto +185.1115 177.02106 lineto +185.1115 178.04059 lineto +185.36931 177.64606 185.67204 177.35114 186.0197 177.15582 curveto +186.37126 176.96052 186.77555 176.86286 187.23259 176.86285 curveto +187.98649 176.86286 188.5568 177.09724 188.94353 177.56598 curveto +189.33024 178.03083 189.5236 178.71637 189.52361 179.62262 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +194.22868 177.77692 moveto +193.65056 177.77692 193.19352 178.00348 192.85759 178.4566 curveto +192.52165 178.90583 192.35368 179.52301 192.35368 180.30817 curveto +192.35368 181.09333 192.5197 181.71247 192.85173 182.16559 curveto +193.18767 182.61481 193.64665 182.83942 194.22868 182.83942 curveto +194.8029 182.83942 195.25798 182.61285 195.59392 182.15973 curveto +195.92985 181.70661 196.09782 181.08942 196.09782 180.30817 curveto +196.09782 179.53083 195.92985 178.91559 195.59392 178.46246 curveto +195.25798 178.00544 194.8029 177.77692 194.22868 177.77692 curveto +194.22868 176.86285 moveto +195.16618 176.86286 195.90251 177.16755 196.43767 177.77692 curveto +196.97282 178.3863 197.2404 179.23005 197.2404 180.30817 curveto +197.2404 181.38239 196.97282 182.22614 196.43767 182.83942 curveto +195.90251 183.44879 195.16618 183.75348 194.22868 183.75348 curveto +193.28727 183.75348 192.54899 183.44879 192.01384 182.83942 curveto +191.48259 182.22614 191.21696 181.38239 191.21696 180.30817 curveto +191.21696 179.23005 191.48259 178.3863 192.01384 177.77692 curveto +192.54899 177.16755 193.28727 176.86286 194.22868 176.86285 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +198.3947 177.02106 moveto +199.47282 177.02106 lineto +200.82048 182.14215 lineto +202.16228 177.02106 lineto +203.43376 177.02106 lineto +204.78142 182.14215 lineto +206.12321 177.02106 lineto +207.20134 177.02106 lineto +205.48454 183.58356 lineto +204.21306 183.58356 lineto +202.80095 178.20465 lineto +201.38298 183.58356 lineto +200.1115 183.58356 lineto +198.3947 177.02106 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +214.29704 179.62262 moveto +214.29704 183.58356 lineto +213.21892 183.58356 lineto +213.21892 179.65778 lineto +213.21891 179.03669 213.09782 178.57184 212.85564 178.26324 curveto +212.61344 177.95466 212.25016 177.80036 211.76579 177.80035 curveto +211.18376 177.80036 210.72477 177.98591 210.38884 178.35699 curveto +210.0529 178.72809 209.88493 179.23395 209.88493 179.87457 curveto +209.88493 183.58356 lineto +208.80095 183.58356 lineto +208.80095 177.02106 lineto +209.88493 177.02106 lineto +209.88493 178.04059 lineto +210.14274 177.64606 210.44548 177.35114 210.79314 177.15582 curveto +211.1447 176.96052 211.54899 176.86286 212.00603 176.86285 curveto +212.75993 176.86286 213.33024 177.09724 213.71696 177.56598 curveto +214.10368 178.03083 214.29704 178.71637 214.29704 179.62262 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +222.82243 177.77692 moveto +222.24431 177.77692 221.78727 178.00348 221.45134 178.4566 curveto +221.1154 178.90583 220.94743 179.52301 220.94743 180.30817 curveto +220.94743 181.09333 221.11345 181.71247 221.44548 182.16559 curveto +221.78142 182.61481 222.2404 182.83942 222.82243 182.83942 curveto +223.39665 182.83942 223.85173 182.61285 224.18767 182.15973 curveto +224.5236 181.70661 224.69157 181.08942 224.69157 180.30817 curveto +224.69157 179.53083 224.5236 178.91559 224.18767 178.46246 curveto +223.85173 178.00544 223.39665 177.77692 222.82243 177.77692 curveto +222.82243 176.86285 moveto +223.75993 176.86286 224.49626 177.16755 225.03142 177.77692 curveto +225.56657 178.3863 225.83415 179.23005 225.83415 180.30817 curveto +225.83415 181.38239 225.56657 182.22614 225.03142 182.83942 curveto +224.49626 183.44879 223.75993 183.75348 222.82243 183.75348 curveto +221.88102 183.75348 221.14274 183.44879 220.60759 182.83942 curveto +220.07634 182.22614 219.81071 181.38239 219.81071 180.30817 curveto +219.81071 179.23005 220.07634 178.3863 220.60759 177.77692 curveto +221.14274 177.16755 221.88102 176.86286 222.82243 176.86285 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +231.41814 178.02887 moveto +231.29704 177.95856 231.16423 177.90778 231.0197 177.87653 curveto +230.87907 177.84138 230.72282 177.8238 230.55095 177.82379 curveto +229.94157 177.8238 229.47282 178.02302 229.1447 178.42145 curveto +228.82048 178.81598 228.65837 179.38434 228.65837 180.12653 curveto +228.65837 183.58356 lineto +227.57439 183.58356 lineto +227.57439 177.02106 lineto +228.65837 177.02106 lineto +228.65837 178.04059 lineto +228.88493 177.64216 229.17985 177.34724 229.54314 177.15582 curveto +229.90641 176.96052 230.34782 176.86286 230.86736 176.86285 curveto +230.94157 176.86286 231.0236 176.86872 231.11345 176.88043 curveto +231.20329 176.88825 231.3029 176.90192 231.41228 176.92145 curveto +231.41814 178.02887 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +236.2697 180.99371 moveto +236.2697 177.02106 lineto +237.34782 177.02106 lineto +237.34782 180.9527 lineto +237.34782 181.57379 237.46892 182.04059 237.71111 182.35309 curveto +237.95329 182.66168 238.31657 182.81598 238.80095 182.81598 curveto +239.38298 182.81598 239.84196 182.63043 240.1779 182.25934 curveto +240.51774 181.88825 240.68766 181.38239 240.68767 180.74176 curveto +240.68767 177.02106 lineto +241.76579 177.02106 lineto +241.76579 183.58356 lineto +240.68767 183.58356 lineto +240.68767 182.57574 lineto +240.42594 182.97418 240.12126 183.27106 239.77361 183.46637 curveto +239.42985 183.65778 239.02946 183.75348 238.57243 183.75348 curveto +237.81852 183.75348 237.24626 183.5191 236.85564 183.05035 curveto +236.46501 182.58161 236.2697 181.89606 236.2697 180.99371 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +249.45329 179.62262 moveto +249.45329 183.58356 lineto +248.37517 183.58356 lineto +248.37517 179.65778 lineto +248.37516 179.03669 248.25407 178.57184 248.01189 178.26324 curveto +247.76969 177.95466 247.40641 177.80036 246.92204 177.80035 curveto +246.34001 177.80036 245.88102 177.98591 245.54509 178.35699 curveto +245.20915 178.72809 245.04118 179.23395 245.04118 179.87457 curveto +245.04118 183.58356 lineto +243.9572 183.58356 lineto +243.9572 177.02106 lineto +245.04118 177.02106 lineto +245.04118 178.04059 lineto +245.29899 177.64606 245.60173 177.35114 245.94939 177.15582 curveto +246.30095 176.96052 246.70524 176.86286 247.16228 176.86285 curveto +247.91618 176.86286 248.48649 177.09724 248.87321 177.56598 curveto +249.25993 178.03083 249.45329 178.71637 249.45329 179.62262 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +257.07048 179.62262 moveto +257.07048 183.58356 lineto +255.99236 183.58356 lineto +255.99236 179.65778 lineto +255.99235 179.03669 255.87126 178.57184 255.62907 178.26324 curveto +255.38688 177.95466 255.0236 177.80036 254.53923 177.80035 curveto +253.9572 177.80036 253.49821 177.98591 253.16228 178.35699 curveto +252.82634 178.72809 252.65837 179.23395 252.65837 179.87457 curveto +252.65837 183.58356 lineto +251.57439 183.58356 lineto +251.57439 177.02106 lineto +252.65837 177.02106 lineto +252.65837 178.04059 lineto +252.91618 177.64606 253.21892 177.35114 253.56657 177.15582 curveto +253.91813 176.96052 254.32243 176.86286 254.77946 176.86285 curveto +255.53337 176.86286 256.10368 177.09724 256.4904 177.56598 curveto +256.87711 178.03083 257.07047 178.71637 257.07048 179.62262 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +262.21501 180.28473 moveto +261.34391 180.28473 260.7404 180.38434 260.40446 180.58356 curveto +260.06853 180.78278 259.90056 181.12262 259.90056 181.60309 curveto +259.90056 181.9859 260.02556 182.29059 260.27556 182.51715 curveto +260.52946 182.73981 260.87321 182.85114 261.30681 182.85114 curveto +261.90446 182.85114 262.38298 182.6402 262.74236 182.21832 curveto +263.10563 181.79254 263.28727 181.22809 263.28728 180.52496 curveto +263.28728 180.28473 lineto +262.21501 180.28473 lineto +264.3654 179.83942 moveto +264.3654 183.58356 lineto +263.28728 183.58356 lineto +263.28728 182.58746 lineto +263.04118 182.9859 262.73454 183.28082 262.36736 183.47223 curveto +262.00016 183.65973 261.55095 183.75348 261.0197 183.75348 curveto +260.34782 183.75348 259.81267 183.56598 259.41423 183.19098 curveto +259.0197 182.81207 258.82243 182.30621 258.82243 181.6734 curveto +258.82243 180.93512 259.06853 180.37848 259.56071 180.00348 curveto +260.05681 179.62848 260.79509 179.44098 261.77556 179.44098 curveto +263.28728 179.44098 lineto +263.28728 179.33551 lineto +263.28727 178.83942 263.12321 178.45661 262.79509 178.18707 curveto +262.47087 177.91364 262.01384 177.77692 261.424 177.77692 curveto +261.04899 177.77692 260.68376 177.82184 260.32829 177.91168 curveto +259.97282 178.00153 259.63103 178.1363 259.3029 178.31598 curveto +259.3029 177.31989 lineto +259.69743 177.16755 260.08024 177.05427 260.45134 176.98004 curveto +260.82243 176.90192 261.18376 176.86286 261.53532 176.86285 curveto +262.48454 176.86286 263.19352 177.10895 263.66228 177.60114 curveto +264.13102 178.09333 264.3654 178.83942 264.3654 179.83942 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +271.70134 178.28082 moveto +271.97086 177.79645 272.29313 177.43903 272.66814 177.20856 curveto +273.04313 176.97809 273.48453 176.86286 273.99236 176.86285 curveto +274.67594 176.86286 275.20328 177.10309 275.57439 177.58356 curveto +275.94547 178.06013 276.13102 178.73981 276.13103 179.62262 curveto +276.13103 183.58356 lineto +275.04704 183.58356 lineto +275.04704 179.65778 lineto +275.04703 179.02887 274.93571 178.56208 274.71306 178.25739 curveto +274.49039 177.9527 274.15055 177.80036 273.69353 177.80035 curveto +273.13493 177.80036 272.69352 177.98591 272.36931 178.35699 curveto +272.04508 178.72809 271.88297 179.23395 271.88298 179.87457 curveto +271.88298 183.58356 lineto +270.799 183.58356 lineto +270.799 179.65778 lineto +270.79899 179.02497 270.68766 178.55817 270.46501 178.25739 curveto +270.24235 177.9527 269.8986 177.80036 269.43376 177.80035 curveto +268.88298 177.80036 268.44548 177.98786 268.12126 178.36285 curveto +267.79704 178.73395 267.63493 179.23786 267.63493 179.87457 curveto +267.63493 183.58356 lineto +266.55095 183.58356 lineto +266.55095 177.02106 lineto +267.63493 177.02106 lineto +267.63493 178.04059 lineto +267.88102 177.63825 268.17595 177.34138 268.5197 177.14996 curveto +268.86345 176.95856 269.27165 176.86286 269.74431 176.86285 curveto +270.22087 176.86286 270.62516 176.98395 270.9572 177.22614 curveto +271.29313 177.46833 271.54118 177.81989 271.70134 178.28082 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +283.90056 180.03278 moveto +283.90056 180.56012 lineto +278.94353 180.56012 lineto +278.9904 181.30231 279.21306 181.86872 279.6115 182.25934 curveto +280.01384 182.64606 280.57243 182.83942 281.28728 182.83942 curveto +281.70134 182.83942 282.10173 182.78864 282.48845 182.68707 curveto +282.87907 182.58551 283.26579 182.43317 283.64861 182.23004 curveto +283.64861 183.24957 lineto +283.26188 183.41364 282.8654 183.53864 282.45915 183.62457 curveto +282.0529 183.71051 281.64079 183.75348 281.22282 183.75348 curveto +280.17595 183.75348 279.34587 183.44879 278.73259 182.83942 curveto +278.12321 182.23004 277.81853 181.40582 277.81853 180.36676 curveto +277.81853 179.29255 278.10759 178.44098 278.68571 177.81207 curveto +279.26774 177.17927 280.05095 176.86286 281.03532 176.86285 curveto +281.91813 176.86286 282.6154 177.14802 283.12712 177.71832 curveto +283.64274 178.28473 283.90055 179.05622 283.90056 180.03278 curveto +282.82243 179.71637 moveto +282.81462 179.12653 282.6486 178.65583 282.32439 178.30426 curveto +282.00407 177.9527 281.57829 177.77692 281.04704 177.77692 curveto +280.44548 177.77692 279.96306 177.94684 279.59978 178.28668 curveto +279.2404 178.62653 279.03337 179.10505 278.97868 179.72223 curveto +282.82243 179.71637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +289.98845 178.01715 moveto +289.98845 174.46637 lineto +291.06657 174.46637 lineto +291.06657 183.58356 lineto +289.98845 183.58356 lineto +289.98845 182.59918 lineto +289.76188 182.98981 289.47477 183.28082 289.12712 183.47223 curveto +288.78337 183.65973 288.3693 183.75348 287.88493 183.75348 curveto +287.09196 183.75348 286.44548 183.43707 285.94548 182.80426 curveto +285.44939 182.17145 285.20134 181.33942 285.20134 180.30817 curveto +285.20134 179.27692 285.44939 178.44489 285.94548 177.81207 curveto +286.44548 177.17927 287.09196 176.86286 287.88493 176.86285 curveto +288.3693 176.86286 288.78337 176.95856 289.12712 177.14996 curveto +289.47477 177.33747 289.76188 177.62653 289.98845 178.01715 curveto +286.31462 180.30817 moveto +286.31462 181.10114 286.47673 181.72418 286.80095 182.17731 curveto +287.12907 182.62653 287.57829 182.85114 288.14861 182.85114 curveto +288.71891 182.85114 289.16813 182.62653 289.49626 182.17731 curveto +289.82438 181.72418 289.98844 181.10114 289.98845 180.30817 curveto +289.98844 179.5152 289.82438 178.89411 289.49626 178.44489 curveto +289.16813 177.99177 288.71891 177.7652 288.14861 177.7652 curveto +287.57829 177.7652 287.12907 177.99177 286.80095 178.44489 curveto +286.47673 178.89411 286.31462 179.5152 286.31462 180.30817 curveto +fill +grestore +grestore +gsave +<< +/ShadingType 2 +/ColorSpace /DeviceRGB +/Coords [13.768714 1253.7404 69.307785 1253.7404] +/Extend [true true] +/Domain [0 1] +/Function << +/FunctionType 3 +/Functions +[ +<< +/FunctionType 2 +/Domain [0 1] +/C0 [1 1 1] +/C1 [1 1 1] +/N 1 +>> +] +/Domain [0 1] +/Bounds [ ] +/Encode [ 0 1 ] +>> +>> +newpath +7.1723278 465.10727 moveto +457.43657 465.10727 457.43657 465.10727 457.43657 465.10727 curveto +eoclip +gsave [8.209048 0 0 0.09472844 -108.6852 346.3422] concat +shfill +grestore +grestore +0.027450981 0.39215687 0 setrgbcolor +[] 0 setdash +5.4563475 setlinewidth +0 setlinejoin +0 setlinecap +newpath +7.1723278 465.10727 moveto +457.43657 465.10727 457.43657 465.10727 457.43657 465.10727 curveto +stroke +gsave +<< +/ShadingType 2 +/ColorSpace /DeviceRGB +/Coords [13.768714 1253.7404 69.307785 1253.7404] +/Extend [true true] +/Domain [0 1] +/Function << +/FunctionType 3 +/Functions +[ +<< +/FunctionType 2 +/Domain [0 1] +/C0 [1 1 1] +/C1 [1 1 1] +/N 1 +>> +] +/Domain [0 1] +/Bounds [ ] +/Encode [ 0 1 ] +>> +>> +newpath +7.1723278 434.97909 moveto +457.43657 434.97909 457.43657 434.97909 457.43657 434.97909 curveto +eoclip +gsave [8.209048 0 0 0.09472844 -108.6852 316.2141] concat +shfill +grestore +grestore +0.39215687 0 0.36470589 setrgbcolor +[] 0 setdash +5.4563475 setlinewidth +0 setlinejoin +0 setlinecap +newpath +7.1723278 434.97909 moveto +457.43657 434.97909 457.43657 434.97909 457.43657 434.97909 curveto +stroke +gsave +<< +/ShadingType 2 +/ColorSpace /DeviceRGB +/Coords [13.768714 1253.7404 69.307785 1253.7404] +/Extend [true true] +/Domain [0 1] +/Function << +/FunctionType 3 +/Functions +[ +<< +/FunctionType 2 +/Domain [0 1] +/C0 [1 1 1] +/C1 [1 1 1] +/N 1 +>> +] +/Domain [0 1] +/Bounds [ ] +/Encode [ 0 1 ] +>> +>> +newpath +7.1723278 404.85091 moveto +457.43657 404.85091 457.43657 404.85091 457.43657 404.85091 curveto +eoclip +gsave [8.209048 0 0 0.09472844 -108.6851 286.0859] concat +shfill +grestore +grestore +0.46666667 0.46666667 0 setrgbcolor +[] 0 setdash +5.4563475 setlinewidth +0 setlinejoin +0 setlinecap +newpath +7.1723278 404.85091 moveto +457.43657 404.85091 457.43657 404.85091 457.43657 404.85091 curveto +stroke +gsave +<< +/ShadingType 2 +/ColorSpace /DeviceRGB +/Coords [13.768714 1253.7404 69.307785 1253.7404] +/Extend [true true] +/Domain [0 1] +/Function << +/FunctionType 3 +/Functions +[ +<< +/FunctionType 2 +/Domain [0 1] +/C0 [1 1 1] +/C1 [1 1 1] +/N 1 +>> +] +/Domain [0 1] +/Bounds [ ] +/Encode [ 0 1 ] +>> +>> +newpath +7.1723278 374.72273 moveto +457.43657 374.72273 457.43657 374.72273 457.43657 374.72273 curveto +eoclip +gsave [8.209048 0 0 0.09472844 -108.6851 255.9577] concat +shfill +grestore +grestore +0.40000001 0 0 setrgbcolor +[] 0 setdash +5.4563475 setlinewidth +0 setlinejoin +0 setlinecap +newpath +7.1723278 374.72273 moveto +457.43657 374.72273 457.43657 374.72273 457.43657 374.72273 curveto +stroke +gsave +<< +/ShadingType 2 +/ColorSpace /DeviceRGB +/Coords [13.768714 1253.7404 69.307785 1253.7404] +/Extend [true true] +/Domain [0 1] +/Function << +/FunctionType 3 +/Functions +[ +<< +/FunctionType 2 +/Domain [0 1] +/C0 [1 1 1] +/C1 [1 1 1] +/N 1 +>> +] +/Domain [0 1] +/Bounds [ ] +/Encode [ 0 1 ] +>> +>> +newpath +6.5744788 317.69134 moveto +456.83872 317.69134 456.83872 317.69134 456.83872 317.69134 curveto +eoclip +gsave [8.209048 0 0 0.09472844 -109.2829 198.9264] concat +shfill +grestore +grestore +1 0.36862746 0 setrgbcolor +[] 0 setdash +5.4563475 setlinewidth +0 setlinejoin +0 setlinecap +newpath +6.5744788 317.69134 moveto +456.83872 317.69134 456.83872 317.69134 456.83872 317.69134 curveto +stroke +gsave +<< +/ShadingType 2 +/ColorSpace /DeviceRGB +/Coords [13.768714 1253.7404 69.307785 1253.7404] +/Extend [true true] +/Domain [0 1] +/Function << +/FunctionType 3 +/Functions +[ +<< +/FunctionType 2 +/Domain [0 1] +/C0 [1 1 1] +/C1 [1 1 1] +/N 1 +>> +] +/Domain [0 1] +/Bounds [ ] +/Encode [ 0 1 ] +>> +>> +newpath +6.5744788 287.56315 moveto +456.83872 287.56315 456.83872 287.56315 456.83872 287.56315 curveto +eoclip +gsave [8.209048 0 0 0.09472844 -109.2829 168.7982] concat +shfill +grestore +grestore +1 1 0.0039215689 setrgbcolor +[] 0 setdash +5.4563475 setlinewidth +0 setlinejoin +0 setlinecap +newpath +6.5744788 287.56315 moveto +456.83872 287.56315 456.83872 287.56315 456.83872 287.56315 curveto +stroke +gsave +<< +/ShadingType 2 +/ColorSpace /DeviceRGB +/Coords [13.768714 1253.7404 69.307785 1253.7404] +/Extend [true true] +/Domain [0 1] +/Function << +/FunctionType 3 +/Functions +[ +<< +/FunctionType 2 +/Domain [0 1] +/C0 [1 1 1] +/C1 [1 1 1] +/N 1 +>> +] +/Domain [0 1] +/Bounds [ ] +/Encode [ 0 1 ] +>> +>> +newpath +6.5744788 257.43497 moveto +456.83872 257.43497 456.83872 257.43497 456.83872 257.43497 curveto +eoclip +gsave [8.209048 0 0 0.09472844 -109.2829 138.67] concat +shfill +grestore +grestore +0.0039215689 0.61960787 1 setrgbcolor +[] 0 setdash +5.4563475 setlinewidth +0 setlinejoin +0 setlinecap +newpath +6.5744788 257.43497 moveto +456.83872 257.43497 456.83872 257.43497 456.83872 257.43497 curveto +stroke +gsave +<< +/ShadingType 2 +/ColorSpace /DeviceRGB +/Coords [13.768714 1253.7404 69.307785 1253.7404] +/Extend [true true] +/Domain [0 1] +/Function << +/FunctionType 3 +/Functions +[ +<< +/FunctionType 2 +/Domain [0 1] +/C0 [1 1 1] +/C1 [1 1 1] +/N 1 +>> +] +/Domain [0 1] +/Bounds [ ] +/Encode [ 0 1 ] +>> +>> +newpath +7.1723278 495.23545 moveto +457.43657 495.23545 457.43657 495.23545 457.43657 495.23545 curveto +eoclip +gsave [8.209048 0 0 0.09472844 -108.6852 376.4704] concat +shfill +grestore +grestore +0.53725493 0 0.51764709 setrgbcolor +[] 0 setdash +5.4563475 setlinewidth +0 setlinejoin +0 setlinecap +newpath +7.1723278 495.23545 moveto +457.43657 495.23545 457.43657 495.23545 457.43657 495.23545 curveto +stroke +gsave [0.914554 0 0 0.850284 393.8303 135.9762] concat +gsave +0 0 0 setrgbcolor +newpath +76.273605 174.83551 moveto +77.54509 174.83551 lineto +79.970871 178.43317 lineto +82.379074 174.83551 lineto +83.650558 174.83551 lineto +80.556808 179.41754 lineto +80.556808 183.58356 lineto +79.367355 183.58356 lineto +79.367355 179.41754 lineto +76.273605 174.83551 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +88.783371 180.03278 moveto +88.783371 180.56012 lineto +83.82634 180.56012 lineto +83.873213 181.30231 84.095869 181.86872 84.494308 182.25934 curveto +84.896649 182.64606 85.455243 182.83942 86.17009 182.83942 curveto +86.584148 182.83942 86.984538 182.78864 87.371262 182.68707 curveto +87.761881 182.58551 88.148599 182.43317 88.531418 182.23004 curveto +88.531418 183.24957 lineto +88.144693 183.41364 87.748209 183.53864 87.341965 183.62457 curveto +86.93571 183.71051 86.523601 183.75348 86.105637 183.75348 curveto +85.058759 183.75348 84.228681 183.44879 83.615402 182.83942 curveto +83.006026 182.23004 82.701339 181.40582 82.70134 180.36676 curveto +82.701339 179.29255 82.990401 178.44098 83.568527 177.81207 curveto +84.150556 177.17927 84.933759 176.86286 85.918137 176.86285 curveto +86.800944 176.86286 87.498209 177.14802 88.009933 177.71832 curveto +88.525552 178.28473 88.783364 179.05622 88.783371 180.03278 curveto +87.705246 179.71637 moveto +87.697428 179.12653 87.531412 178.65583 87.207199 178.30426 curveto +86.886882 177.9527 86.461101 177.77692 85.929855 177.77692 curveto +85.32829 177.77692 84.845868 177.94684 84.48259 178.28668 curveto +84.123213 178.62653 83.916182 179.10505 83.861496 179.72223 curveto +87.705246 179.71637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +90.552902 174.46637 moveto +91.631027 174.46637 lineto +91.631027 183.58356 lineto +90.552902 183.58356 lineto +90.552902 174.46637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +93.881027 174.46637 moveto +94.959152 174.46637 lineto +94.959152 183.58356 lineto +93.881027 183.58356 lineto +93.881027 174.46637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +99.752121 177.77692 moveto +99.173993 177.77692 98.716962 178.00348 98.381027 178.4566 curveto +98.045088 178.90583 97.877119 179.52301 97.877121 180.30817 curveto +97.877119 181.09333 98.043135 181.71247 98.375168 182.16559 curveto +98.711103 182.61481 99.170087 182.83942 99.752121 182.83942 curveto +100.32634 182.83942 100.78141 182.61285 101.11736 182.15973 curveto +101.45329 181.70661 101.62126 181.08942 101.62126 180.30817 curveto +101.62126 179.53083 101.45329 178.91559 101.11736 178.46246 curveto +100.78141 178.00544 100.32634 177.77692 99.752121 177.77692 curveto +99.752121 176.86285 moveto +100.68962 176.86286 101.42594 177.16755 101.96111 177.77692 curveto +102.49626 178.3863 102.76383 179.23005 102.76384 180.30817 curveto +102.76383 181.38239 102.49626 182.22614 101.96111 182.83942 curveto +101.42594 183.44879 100.68962 183.75348 99.752121 183.75348 curveto +98.810712 183.75348 98.072431 183.44879 97.537277 182.83942 curveto +97.006026 182.22614 96.740402 181.38239 96.740402 180.30817 curveto +96.740402 179.23005 97.006026 178.3863 97.537277 177.77692 curveto +98.072431 177.16755 98.810712 176.86286 99.752121 176.86285 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +103.91814 177.02106 moveto +104.99626 177.02106 lineto +106.34392 182.14215 lineto +107.68571 177.02106 lineto +108.9572 177.02106 lineto +110.30486 182.14215 lineto +111.64665 177.02106 lineto +112.72478 177.02106 lineto +111.00798 183.58356 lineto +109.7365 183.58356 lineto +108.32439 178.20465 lineto +106.90642 183.58356 lineto +105.63493 183.58356 lineto +103.91814 177.02106 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +118.46111 182.09528 moveto +119.69743 182.09528 lineto +119.69743 183.58356 lineto +118.46111 183.58356 lineto +118.46111 182.09528 lineto +118.46111 177.37848 moveto +119.69743 177.37848 lineto +119.69743 178.86676 lineto +118.46111 178.86676 lineto +118.46111 177.37848 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +129.86345 178.02887 moveto +129.74235 177.95856 129.60954 177.90778 129.46501 177.87653 curveto +129.32438 177.84138 129.16813 177.8238 128.99626 177.82379 curveto +128.38688 177.8238 127.91813 178.02302 127.59001 178.42145 curveto +127.26579 178.81598 127.10368 179.38434 127.10368 180.12653 curveto +127.10368 183.58356 lineto +126.0197 183.58356 lineto +126.0197 177.02106 lineto +127.10368 177.02106 lineto +127.10368 178.04059 lineto +127.33024 177.64216 127.62517 177.34724 127.98845 177.15582 curveto +128.35173 176.96052 128.79313 176.86286 129.31267 176.86285 curveto +129.38688 176.86286 129.46891 176.86872 129.55876 176.88043 curveto +129.6486 176.88825 129.74821 176.90192 129.85759 176.92145 curveto +129.86345 178.02887 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +130.8947 180.99371 moveto +130.8947 177.02106 lineto +131.97282 177.02106 lineto +131.97282 180.9527 lineto +131.97282 181.57379 132.09392 182.04059 132.33611 182.35309 curveto +132.57829 182.66168 132.94157 182.81598 133.42595 182.81598 curveto +134.00798 182.81598 134.46696 182.63043 134.8029 182.25934 curveto +135.14274 181.88825 135.31266 181.38239 135.31267 180.74176 curveto +135.31267 177.02106 lineto +136.39079 177.02106 lineto +136.39079 183.58356 lineto +135.31267 183.58356 lineto +135.31267 182.57574 lineto +135.05094 182.97418 134.74626 183.27106 134.39861 183.46637 curveto +134.05485 183.65778 133.65446 183.75348 133.19743 183.75348 curveto +132.44352 183.75348 131.87126 183.5191 131.48064 183.05035 curveto +131.09001 182.58161 130.8947 181.89606 130.8947 180.99371 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +144.07829 179.62262 moveto +144.07829 183.58356 lineto +143.00017 183.58356 lineto +143.00017 179.65778 lineto +143.00016 179.03669 142.87907 178.57184 142.63689 178.26324 curveto +142.39469 177.95466 142.03141 177.80036 141.54704 177.80035 curveto +140.96501 177.80036 140.50602 177.98591 140.17009 178.35699 curveto +139.83415 178.72809 139.66618 179.23395 139.66618 179.87457 curveto +139.66618 183.58356 lineto +138.5822 183.58356 lineto +138.5822 177.02106 lineto +139.66618 177.02106 lineto +139.66618 178.04059 lineto +139.92399 177.64606 140.22673 177.35114 140.57439 177.15582 curveto +140.92595 176.96052 141.33024 176.86286 141.78728 176.86285 curveto +142.54118 176.86286 143.11149 177.09724 143.49821 177.56598 curveto +143.88493 178.03083 144.07829 178.71637 144.07829 179.62262 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +151.69548 179.62262 moveto +151.69548 183.58356 lineto +150.61736 183.58356 lineto +150.61736 179.65778 lineto +150.61735 179.03669 150.49626 178.57184 150.25407 178.26324 curveto +150.01188 177.95466 149.6486 177.80036 149.16423 177.80035 curveto +148.5822 177.80036 148.12321 177.98591 147.78728 178.35699 curveto +147.45134 178.72809 147.28337 179.23395 147.28337 179.87457 curveto +147.28337 183.58356 lineto +146.19939 183.58356 lineto +146.19939 177.02106 lineto +147.28337 177.02106 lineto +147.28337 178.04059 lineto +147.54118 177.64606 147.84392 177.35114 148.19157 177.15582 curveto +148.54313 176.96052 148.94743 176.86286 149.40446 176.86285 curveto +150.15837 176.86286 150.72868 177.09724 151.1154 177.56598 curveto +151.50211 178.03083 151.69547 178.71637 151.69548 179.62262 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +153.85759 177.02106 moveto +154.93571 177.02106 lineto +154.93571 183.58356 lineto +153.85759 183.58356 lineto +153.85759 177.02106 lineto +153.85759 174.46637 moveto +154.93571 174.46637 lineto +154.93571 175.8316 lineto +153.85759 175.8316 lineto +153.85759 174.46637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +162.64079 179.62262 moveto +162.64079 183.58356 lineto +161.56267 183.58356 lineto +161.56267 179.65778 lineto +161.56266 179.03669 161.44157 178.57184 161.19939 178.26324 curveto +160.95719 177.95466 160.59391 177.80036 160.10954 177.80035 curveto +159.52751 177.80036 159.06852 177.98591 158.73259 178.35699 curveto +158.39665 178.72809 158.22868 179.23395 158.22868 179.87457 curveto +158.22868 183.58356 lineto +157.1447 183.58356 lineto +157.1447 177.02106 lineto +158.22868 177.02106 lineto +158.22868 178.04059 lineto +158.48649 177.64606 158.78923 177.35114 159.13689 177.15582 curveto +159.48845 176.96052 159.89274 176.86286 160.34978 176.86285 curveto +161.10368 176.86286 161.67399 177.09724 162.06071 177.56598 curveto +162.44743 178.03083 162.64079 178.71637 162.64079 179.62262 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +169.12126 180.22614 moveto +169.12126 179.44489 168.95915 178.83942 168.63493 178.40973 curveto +168.31462 177.98005 167.86344 177.7652 167.28142 177.7652 curveto +166.70329 177.7652 166.25212 177.98005 165.9279 178.40973 curveto +165.60759 178.83942 165.44743 179.44489 165.44743 180.22614 curveto +165.44743 181.00348 165.60759 181.607 165.9279 182.03668 curveto +166.25212 182.46637 166.70329 182.68121 167.28142 182.68121 curveto +167.86344 182.68121 168.31462 182.46637 168.63493 182.03668 curveto +168.95915 181.607 169.12126 181.00348 169.12126 180.22614 curveto +170.19939 182.7691 moveto +170.19938 183.88629 169.95133 184.71637 169.45525 185.25934 curveto +168.95915 185.80621 168.19938 186.07965 167.17595 186.07965 curveto +166.79704 186.07965 166.43962 186.05035 166.10368 185.99176 curveto +165.76774 185.93707 165.44157 185.85113 165.12517 185.73395 curveto +165.12517 184.68512 lineto +165.44157 184.85699 165.75407 184.98395 166.06267 185.06598 curveto +166.37126 185.14801 166.68571 185.18902 167.00603 185.18903 curveto +167.71305 185.18902 168.24235 185.00348 168.59392 184.63239 curveto +168.94548 184.2652 169.12126 183.70856 169.12126 182.96246 curveto +169.12126 182.42926 lineto +168.8986 182.81598 168.61344 183.10504 168.26579 183.29645 curveto +167.91813 183.48785 167.50212 183.58356 167.01775 183.58356 curveto +166.21306 183.58356 165.56462 183.27692 165.07243 182.66364 curveto +164.58025 182.05036 164.33415 181.23786 164.33415 180.22614 curveto +164.33415 179.21051 164.58025 178.39606 165.07243 177.78278 curveto +165.56462 177.1695 166.21306 176.86286 167.01775 176.86285 curveto +167.50212 176.86286 167.91813 176.95856 168.26579 177.14996 curveto +168.61344 177.34138 168.8986 177.63044 169.12126 178.01715 curveto +169.12126 177.02106 lineto +170.19939 177.02106 lineto +170.19939 182.7691 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +176.2404 177.02106 moveto +177.31853 177.02106 lineto +177.31853 183.58356 lineto +176.2404 183.58356 lineto +176.2404 177.02106 lineto +176.2404 174.46637 moveto +177.31853 174.46637 lineto +177.31853 175.8316 lineto +176.2404 175.8316 lineto +176.2404 174.46637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +185.02361 179.62262 moveto +185.02361 183.58356 lineto +183.94548 183.58356 lineto +183.94548 179.65778 lineto +183.94547 179.03669 183.82438 178.57184 183.5822 178.26324 curveto +183.34001 177.95466 182.97673 177.80036 182.49236 177.80035 curveto +181.91032 177.80036 181.45134 177.98591 181.1154 178.35699 curveto +180.77946 178.72809 180.61149 179.23395 180.6115 179.87457 curveto +180.6115 183.58356 lineto +179.52751 183.58356 lineto +179.52751 177.02106 lineto +180.6115 177.02106 lineto +180.6115 178.04059 lineto +180.86931 177.64606 181.17204 177.35114 181.5197 177.15582 curveto +181.87126 176.96052 182.27555 176.86286 182.73259 176.86285 curveto +183.48649 176.86286 184.0568 177.09724 184.44353 177.56598 curveto +184.83024 178.03083 185.0236 178.71637 185.02361 179.62262 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +193.98845 180.28473 moveto +193.11735 180.28473 192.51384 180.38434 192.1779 180.58356 curveto +191.84196 180.78278 191.67399 181.12262 191.674 181.60309 curveto +191.67399 181.9859 191.79899 182.29059 192.049 182.51715 curveto +192.3029 182.73981 192.64665 182.85114 193.08025 182.85114 curveto +193.6779 182.85114 194.15641 182.6402 194.51579 182.21832 curveto +194.87907 181.79254 195.06071 181.22809 195.06071 180.52496 curveto +195.06071 180.28473 lineto +193.98845 180.28473 lineto +196.13884 179.83942 moveto +196.13884 183.58356 lineto +195.06071 183.58356 lineto +195.06071 182.58746 lineto +194.81462 182.9859 194.50798 183.28082 194.14079 183.47223 curveto +193.7736 183.65973 193.32438 183.75348 192.79314 183.75348 curveto +192.12126 183.75348 191.5861 183.56598 191.18767 183.19098 curveto +190.79314 182.81207 190.59587 182.30621 190.59587 181.6734 curveto +190.59587 180.93512 190.84196 180.37848 191.33415 180.00348 curveto +191.83024 179.62848 192.56852 179.44098 193.549 179.44098 curveto +195.06071 179.44098 lineto +195.06071 179.33551 lineto +195.06071 178.83942 194.89665 178.45661 194.56853 178.18707 curveto +194.2443 177.91364 193.78727 177.77692 193.19743 177.77692 curveto +192.82243 177.77692 192.4572 177.82184 192.10173 177.91168 curveto +191.74626 178.00153 191.40446 178.1363 191.07634 178.31598 curveto +191.07634 177.31989 lineto +191.47087 177.16755 191.85368 177.05427 192.22478 176.98004 curveto +192.59587 176.90192 192.9572 176.86286 193.30876 176.86285 curveto +194.25798 176.86286 194.96696 177.10895 195.43571 177.60114 curveto +195.90446 178.09333 196.13883 178.83942 196.13884 179.83942 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +203.25212 175.15778 moveto +203.25212 177.02106 lineto +205.47282 177.02106 lineto +205.47282 177.85895 lineto +203.25212 177.85895 lineto +203.25212 181.42145 lineto +203.25212 181.95661 203.32438 182.30036 203.46892 182.4527 curveto +203.61735 182.60504 203.91618 182.68121 204.3654 182.68121 curveto +205.47282 182.68121 lineto +205.47282 183.58356 lineto +204.3654 183.58356 lineto +203.53337 183.58356 202.95915 183.42926 202.64275 183.12067 curveto +202.32634 182.80817 202.16814 182.24176 202.16814 181.42145 curveto +202.16814 177.85895 lineto +201.37712 177.85895 lineto +201.37712 177.02106 lineto +202.16814 177.02106 lineto +202.16814 175.15778 lineto +203.25212 175.15778 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +210.69939 178.02887 moveto +210.57829 177.95856 210.44548 177.90778 210.30095 177.87653 curveto +210.16032 177.84138 210.00407 177.8238 209.8322 177.82379 curveto +209.22282 177.8238 208.75407 178.02302 208.42595 178.42145 curveto +208.10173 178.81598 207.93962 179.38434 207.93962 180.12653 curveto +207.93962 183.58356 lineto +206.85564 183.58356 lineto +206.85564 177.02106 lineto +207.93962 177.02106 lineto +207.93962 178.04059 lineto +208.16618 177.64216 208.4611 177.34724 208.82439 177.15582 curveto +209.18766 176.96052 209.62907 176.86286 210.14861 176.86285 curveto +210.22282 176.86286 210.30485 176.86872 210.3947 176.88043 curveto +210.48454 176.88825 210.58415 176.90192 210.69353 176.92145 curveto +210.69939 178.02887 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +214.82439 180.28473 moveto +213.95329 180.28473 213.34977 180.38434 213.01384 180.58356 curveto +212.6779 180.78278 212.50993 181.12262 212.50993 181.60309 curveto +212.50993 181.9859 212.63493 182.29059 212.88493 182.51715 curveto +213.13884 182.73981 213.48259 182.85114 213.91618 182.85114 curveto +214.51384 182.85114 214.99235 182.6402 215.35173 182.21832 curveto +215.71501 181.79254 215.89665 181.22809 215.89665 180.52496 curveto +215.89665 180.28473 lineto +214.82439 180.28473 lineto +216.97478 179.83942 moveto +216.97478 183.58356 lineto +215.89665 183.58356 lineto +215.89665 182.58746 lineto +215.65055 182.9859 215.34391 183.28082 214.97673 183.47223 curveto +214.60954 183.65973 214.16032 183.75348 213.62907 183.75348 curveto +212.9572 183.75348 212.42204 183.56598 212.02361 183.19098 curveto +211.62907 182.81207 211.43181 182.30621 211.43181 181.6734 curveto +211.43181 180.93512 211.6779 180.37848 212.17009 180.00348 curveto +212.66618 179.62848 213.40446 179.44098 214.38493 179.44098 curveto +215.89665 179.44098 lineto +215.89665 179.33551 lineto +215.89665 178.83942 215.73258 178.45661 215.40446 178.18707 curveto +215.08024 177.91364 214.62321 177.77692 214.03337 177.77692 curveto +213.65837 177.77692 213.29313 177.82184 212.93767 177.91168 curveto +212.5822 178.00153 212.2404 178.1363 211.91228 178.31598 curveto +211.91228 177.31989 lineto +212.30681 177.16755 212.68962 177.05427 213.06071 176.98004 curveto +213.43181 176.90192 213.79313 176.86286 214.1447 176.86285 curveto +215.09391 176.86286 215.8029 177.10895 216.27165 177.60114 curveto +216.7404 178.09333 216.97477 178.83942 216.97478 179.83942 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +220.24431 182.59918 moveto +220.24431 186.07965 lineto +219.16032 186.07965 lineto +219.16032 177.02106 lineto +220.24431 177.02106 lineto +220.24431 178.01715 lineto +220.47087 177.62653 220.75602 177.33747 221.09978 177.14996 curveto +221.44743 176.95856 221.86149 176.86286 222.34196 176.86285 curveto +223.13883 176.86286 223.78532 177.17927 224.28142 177.81207 curveto +224.78141 178.44489 225.03141 179.27692 225.03142 180.30817 curveto +225.03141 181.33942 224.78141 182.17145 224.28142 182.80426 curveto +223.78532 183.43707 223.13883 183.75348 222.34196 183.75348 curveto +221.86149 183.75348 221.44743 183.65973 221.09978 183.47223 curveto +220.75602 183.28082 220.47087 182.98981 220.24431 182.59918 curveto +223.91228 180.30817 moveto +223.91227 179.5152 223.74821 178.89411 223.42009 178.44489 curveto +223.09587 177.99177 222.6486 177.7652 222.07829 177.7652 curveto +221.50798 177.7652 221.05876 177.99177 220.73064 178.44489 curveto +220.40642 178.89411 220.24431 179.5152 220.24431 180.30817 curveto +220.24431 181.10114 220.40642 181.72418 220.73064 182.17731 curveto +221.05876 182.62653 221.50798 182.85114 222.07829 182.85114 curveto +222.6486 182.85114 223.09587 182.62653 223.42009 182.17731 curveto +223.74821 181.72418 223.91227 181.10114 223.91228 180.30817 curveto +fill +grestore +grestore +gsave [0.914554 0 0 0.850284 393.1927 166.1044] concat +gsave +0 0 0 setrgbcolor +newpath +81.025558 175.63824 moveto +80.16618 175.63825 79.482587 175.95856 78.974777 176.59918 curveto +78.470869 177.23981 78.218916 178.11286 78.218918 179.21832 curveto +78.218916 180.31989 78.470869 181.19098 78.974777 181.8316 curveto +79.482587 182.47223 80.16618 182.79254 81.025558 182.79254 curveto +81.884928 182.79254 82.564615 182.47223 83.064621 181.8316 curveto +83.56852 181.19098 83.820473 180.31989 83.82048 179.21832 curveto +83.820473 178.11286 83.56852 177.23981 83.064621 176.59918 curveto +82.564615 175.95856 81.884928 175.63825 81.025558 175.63824 curveto +81.025558 174.67731 moveto +82.252115 174.67732 83.232583 175.08942 83.966965 175.91364 curveto +84.701331 176.73395 85.068518 177.83552 85.068527 179.21832 curveto +85.068518 180.59723 84.701331 181.69879 83.966965 182.52301 curveto +83.232583 183.34332 82.252115 183.75348 81.025558 183.75348 curveto +79.795086 183.75348 78.810712 183.34332 78.072433 182.52301 curveto +77.338057 181.7027 76.97087 180.60114 76.970871 179.21832 curveto +76.97087 177.83552 77.338057 176.73395 78.072433 175.91364 curveto +78.810712 175.08942 79.795086 174.67732 81.025558 174.67731 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +90.675949 178.02887 moveto +90.554851 177.95856 90.422038 177.90778 90.277512 177.87653 curveto +90.136882 177.84138 89.980632 177.8238 89.808762 177.82379 curveto +89.199383 177.8238 88.730634 178.02302 88.402512 178.42145 curveto +88.078291 178.81598 87.916181 179.38434 87.916183 180.12653 curveto +87.916183 183.58356 lineto +86.832199 183.58356 lineto +86.832199 177.02106 lineto +87.916183 177.02106 lineto +87.916183 178.04059 lineto +88.142744 177.64216 88.437665 177.34724 88.800949 177.15582 curveto +89.164227 176.96052 89.605633 176.86286 90.125168 176.86285 curveto +90.199382 176.86286 90.281413 176.86872 90.371262 176.88043 curveto +90.461101 176.88825 90.56071 176.90192 90.67009 176.92145 curveto +90.675949 178.02887 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +94.800949 180.28473 moveto +93.929852 180.28473 93.326337 180.38434 92.990402 180.58356 curveto +92.654463 180.78278 92.486494 181.12262 92.486496 181.60309 curveto +92.486494 181.9859 92.611494 182.29059 92.861496 182.51715 curveto +93.1154 182.73981 93.459149 182.85114 93.892746 182.85114 curveto +94.490398 182.85114 94.968914 182.6402 95.328293 182.21832 curveto +95.691569 181.79254 95.87321 181.22809 95.873215 180.52496 curveto +95.873215 180.28473 lineto +94.800949 180.28473 lineto +96.95134 179.83942 moveto +96.95134 183.58356 lineto +95.873215 183.58356 lineto +95.873215 182.58746 lineto +95.627116 182.9859 95.320476 183.28082 94.953293 183.47223 curveto +94.586101 183.65973 94.136883 183.75348 93.605637 183.75348 curveto +92.933759 183.75348 92.398604 183.56598 92.000168 183.19098 curveto +91.605636 182.81207 91.40837 182.30621 91.408371 181.6734 curveto +91.40837 180.93512 91.654464 180.37848 92.146652 180.00348 curveto +92.642744 179.62848 93.381025 179.44098 94.361496 179.44098 curveto +95.873215 179.44098 lineto +95.873215 179.33551 lineto +95.87321 178.83942 95.709147 178.45661 95.381027 178.18707 curveto +95.056804 177.91364 94.599773 177.77692 94.009933 177.77692 curveto +93.634931 177.77692 93.269697 177.82184 92.91423 177.91168 curveto +92.55876 178.00153 92.216963 178.1363 91.88884 178.31598 curveto +91.88884 177.31989 lineto +92.283369 177.16755 92.666181 177.05427 93.037277 176.98004 curveto +93.408368 176.90192 93.769696 176.86286 94.121262 176.86285 curveto +95.070476 176.86286 95.77946 177.10895 96.248215 177.60114 curveto +96.716959 178.09333 96.951333 178.83942 96.95134 179.83942 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +104.63298 179.62262 moveto +104.63298 183.58356 lineto +103.55486 183.58356 lineto +103.55486 179.65778 lineto +103.55485 179.03669 103.43376 178.57184 103.19157 178.26324 curveto +102.94938 177.95466 102.5861 177.80036 102.10173 177.80035 curveto +101.5197 177.80036 101.06071 177.98591 100.72478 178.35699 curveto +100.38884 178.72809 100.22087 179.23395 100.22087 179.87457 curveto +100.22087 183.58356 lineto +99.136887 183.58356 lineto +99.136887 177.02106 lineto +100.22087 177.02106 lineto +100.22087 178.04059 lineto +100.47868 177.64606 100.78142 177.35114 101.12907 177.15582 curveto +101.48063 176.96052 101.88493 176.86286 102.34196 176.86285 curveto +103.09587 176.86286 103.66618 177.09724 104.0529 177.56598 curveto +104.43961 178.03083 104.63297 178.71637 104.63298 179.62262 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +111.11345 180.22614 moveto +111.11344 179.44489 110.95133 178.83942 110.62712 178.40973 curveto +110.3068 177.98005 109.85563 177.7652 109.27361 177.7652 curveto +108.69548 177.7652 108.24431 177.98005 107.92009 178.40973 curveto +107.59978 178.83942 107.43962 179.44489 107.43962 180.22614 curveto +107.43962 181.00348 107.59978 181.607 107.92009 182.03668 curveto +108.24431 182.46637 108.69548 182.68121 109.27361 182.68121 curveto +109.85563 182.68121 110.3068 182.46637 110.62712 182.03668 curveto +110.95133 181.607 111.11344 181.00348 111.11345 180.22614 curveto +112.19157 182.7691 moveto +112.19157 183.88629 111.94352 184.71637 111.44743 185.25934 curveto +110.95133 185.80621 110.19157 186.07965 109.16814 186.07965 curveto +108.78923 186.07965 108.43181 186.05035 108.09587 185.99176 curveto +107.75993 185.93707 107.43376 185.85113 107.11736 185.73395 curveto +107.11736 184.68512 lineto +107.43376 184.85699 107.74626 184.98395 108.05486 185.06598 curveto +108.36345 185.14801 108.6779 185.18902 108.99821 185.18903 curveto +109.70524 185.18902 110.23454 185.00348 110.58611 184.63239 curveto +110.93766 184.2652 111.11344 183.70856 111.11345 182.96246 curveto +111.11345 182.42926 lineto +110.89079 182.81598 110.60563 183.10504 110.25798 183.29645 curveto +109.91032 183.48785 109.4943 183.58356 109.00993 183.58356 curveto +108.20524 183.58356 107.55681 183.27692 107.06462 182.66364 curveto +106.57243 182.05036 106.32634 181.23786 106.32634 180.22614 curveto +106.32634 179.21051 106.57243 178.39606 107.06462 177.78278 curveto +107.55681 177.1695 108.20524 176.86286 109.00993 176.86285 curveto +109.4943 176.86286 109.91032 176.95856 110.25798 177.14996 curveto +110.60563 177.34138 110.89079 177.63044 111.11345 178.01715 curveto +111.11345 177.02106 lineto +112.19157 177.02106 lineto +112.19157 182.7691 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +120.02556 180.03278 moveto +120.02556 180.56012 lineto +115.06853 180.56012 lineto +115.1154 181.30231 115.33806 181.86872 115.7365 182.25934 curveto +116.13884 182.64606 116.69743 182.83942 117.41228 182.83942 curveto +117.82634 182.83942 118.22673 182.78864 118.61345 182.68707 curveto +119.00407 182.58551 119.39079 182.43317 119.77361 182.23004 curveto +119.77361 183.24957 lineto +119.38688 183.41364 118.9904 183.53864 118.58415 183.62457 curveto +118.1779 183.71051 117.76579 183.75348 117.34782 183.75348 curveto +116.30095 183.75348 115.47087 183.44879 114.85759 182.83942 curveto +114.24821 182.23004 113.94353 181.40582 113.94353 180.36676 curveto +113.94353 179.29255 114.23259 178.44098 114.81071 177.81207 curveto +115.39274 177.17927 116.17595 176.86286 117.16032 176.86285 curveto +118.04313 176.86286 118.7404 177.14802 119.25212 177.71832 curveto +119.76774 178.28473 120.02555 179.05622 120.02556 180.03278 curveto +118.94743 179.71637 moveto +118.93962 179.12653 118.7736 178.65583 118.44939 178.30426 curveto +118.12907 177.9527 117.70329 177.77692 117.17204 177.77692 curveto +116.57048 177.77692 116.08806 177.94684 115.72478 178.28668 curveto +115.3654 178.62653 115.15837 179.10505 115.10368 179.72223 curveto +118.94743 179.71637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +125.89079 182.09528 moveto +127.12712 182.09528 lineto +127.12712 183.58356 lineto +125.89079 183.58356 lineto +125.89079 182.09528 lineto +125.89079 177.37848 moveto +127.12712 177.37848 lineto +127.12712 178.86676 lineto +125.89079 178.86676 lineto +125.89079 177.37848 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +137.674 177.21442 moveto +137.674 178.23395 lineto +137.3693 178.0777 137.0529 177.96052 136.72478 177.88239 curveto +136.39665 177.80427 136.0568 177.7652 135.70525 177.7652 curveto +135.17009 177.7652 134.76774 177.84723 134.49821 178.01129 curveto +134.23259 178.17536 134.09978 178.42145 134.09978 178.74957 curveto +134.09978 178.99958 134.19548 179.19684 134.38689 179.34137 curveto +134.57829 179.482 134.96306 179.61676 135.54118 179.74567 curveto +135.91032 179.8277 lineto +136.67594 179.99176 137.21891 180.22419 137.53923 180.52496 curveto +137.86344 180.82184 138.02555 181.23786 138.02556 181.77301 curveto +138.02555 182.38239 137.78337 182.86481 137.299 183.22028 curveto +136.81852 183.57574 136.15641 183.75348 135.31267 183.75348 curveto +134.9611 183.75348 134.59392 183.71832 134.21111 183.64801 curveto +133.8322 183.5816 133.43181 183.48004 133.00993 183.34332 curveto +133.00993 182.23004 lineto +133.40837 182.43707 133.80095 182.59332 134.18767 182.69879 curveto +134.57438 182.80035 134.9572 182.85114 135.33611 182.85114 curveto +135.84391 182.85114 136.23454 182.7652 136.50798 182.59332 curveto +136.78141 182.41754 136.91813 182.17145 136.91814 181.85504 curveto +136.91813 181.56207 136.81852 181.33747 136.61931 181.18121 curveto +136.42399 181.02497 135.99235 180.87458 135.32439 180.73004 curveto +134.94939 180.64215 lineto +134.28142 180.50153 133.79899 180.28669 133.50212 179.99762 curveto +133.20525 179.70465 133.05681 179.30426 133.05681 178.79645 curveto +133.05681 178.17927 133.27556 177.7027 133.71306 177.36676 curveto +134.15056 177.03083 134.77165 176.86286 135.57634 176.86285 curveto +135.97477 176.86286 136.34977 176.89216 136.70134 176.95074 curveto +137.0529 177.00934 137.37712 177.09724 137.674 177.21442 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +145.3615 180.03278 moveto +145.3615 180.56012 lineto +140.40446 180.56012 lineto +140.45134 181.30231 140.67399 181.86872 141.07243 182.25934 curveto +141.47477 182.64606 142.03337 182.83942 142.74821 182.83942 curveto +143.16227 182.83942 143.56266 182.78864 143.94939 182.68707 curveto +144.34001 182.58551 144.72672 182.43317 145.10954 182.23004 curveto +145.10954 183.24957 lineto +144.72282 183.41364 144.32633 183.53864 143.92009 183.62457 curveto +143.51383 183.71051 143.10173 183.75348 142.68376 183.75348 curveto +141.63688 183.75348 140.80681 183.44879 140.19353 182.83942 curveto +139.58415 182.23004 139.27946 181.40582 139.27946 180.36676 curveto +139.27946 179.29255 139.56853 178.44098 140.14665 177.81207 curveto +140.72868 177.17927 141.51188 176.86286 142.49626 176.86285 curveto +143.37907 176.86286 144.07633 177.14802 144.58806 177.71832 curveto +145.10368 178.28473 145.36149 179.05622 145.3615 180.03278 curveto +144.28337 179.71637 moveto +144.27555 179.12653 144.10954 178.65583 143.78532 178.30426 curveto +143.46501 177.9527 143.03923 177.77692 142.50798 177.77692 curveto +141.90641 177.77692 141.42399 177.94684 141.06071 178.28668 curveto +140.70134 178.62653 140.49431 179.10505 140.43962 179.72223 curveto +144.28337 179.71637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +150.93376 178.02887 moveto +150.81266 177.95856 150.67985 177.90778 150.53532 177.87653 curveto +150.39469 177.84138 150.23844 177.8238 150.06657 177.82379 curveto +149.4572 177.8238 148.98845 178.02302 148.66032 178.42145 curveto +148.3361 178.81598 148.17399 179.38434 148.174 180.12653 curveto +148.174 183.58356 lineto +147.09001 183.58356 lineto +147.09001 177.02106 lineto +148.174 177.02106 lineto +148.174 178.04059 lineto +148.40056 177.64216 148.69548 177.34724 149.05876 177.15582 curveto +149.42204 176.96052 149.86345 176.86286 150.38298 176.86285 curveto +150.45719 176.86286 150.53923 176.86872 150.62907 176.88043 curveto +150.71891 176.88825 150.81852 176.90192 150.9279 176.92145 curveto +150.93376 178.02887 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +151.3029 177.02106 moveto +152.44548 177.02106 lineto +154.49626 182.52887 lineto +156.54704 177.02106 lineto +157.68962 177.02106 lineto +155.22868 183.58356 lineto +153.76384 183.58356 lineto +151.3029 177.02106 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +159.1779 177.02106 moveto +160.25603 177.02106 lineto +160.25603 183.58356 lineto +159.1779 183.58356 lineto +159.1779 177.02106 lineto +159.1779 174.46637 moveto +160.25603 174.46637 lineto +160.25603 175.8316 lineto +159.1779 175.8316 lineto +159.1779 174.46637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +167.22868 177.27301 moveto +167.22868 178.28082 lineto +166.92399 178.11286 166.61735 177.98786 166.30876 177.90582 curveto +166.00407 177.81989 165.69548 177.77692 165.38298 177.77692 curveto +164.68376 177.77692 164.14079 177.99958 163.75407 178.44489 curveto +163.36735 178.8863 163.17399 179.50739 163.174 180.30817 curveto +163.17399 181.10895 163.36735 181.732 163.75407 182.17731 curveto +164.14079 182.61871 164.68376 182.83942 165.38298 182.83942 curveto +165.69548 182.83942 166.00407 182.7984 166.30876 182.71637 curveto +166.61735 182.63043 166.92399 182.50348 167.22868 182.33551 curveto +167.22868 183.3316 lineto +166.9279 183.47223 166.6154 183.5777 166.29118 183.64801 curveto +165.97087 183.71832 165.62907 183.75348 165.26579 183.75348 curveto +164.27751 183.75348 163.49235 183.44293 162.91032 182.82184 curveto +162.32829 182.20075 162.03728 181.36286 162.03728 180.30817 curveto +162.03728 179.23786 162.33025 178.39606 162.91618 177.78278 curveto +163.50603 177.1695 164.31266 176.86286 165.33611 176.86285 curveto +165.66813 176.86286 165.99235 176.89802 166.30876 176.96832 curveto +166.62516 177.03474 166.9318 177.1363 167.22868 177.27301 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +169.1154 177.02106 moveto +170.19353 177.02106 lineto +170.19353 183.58356 lineto +169.1154 183.58356 lineto +169.1154 177.02106 lineto +169.1154 174.46637 moveto +170.19353 174.46637 lineto +170.19353 175.8316 lineto +169.1154 175.8316 lineto +169.1154 174.46637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +177.89861 179.62262 moveto +177.89861 183.58356 lineto +176.82048 183.58356 lineto +176.82048 179.65778 lineto +176.82047 179.03669 176.69938 178.57184 176.4572 178.26324 curveto +176.21501 177.95466 175.85173 177.80036 175.36736 177.80035 curveto +174.78532 177.80036 174.32634 177.98591 173.9904 178.35699 curveto +173.65446 178.72809 173.48649 179.23395 173.4865 179.87457 curveto +173.4865 183.58356 lineto +172.40251 183.58356 lineto +172.40251 177.02106 lineto +173.4865 177.02106 lineto +173.4865 178.04059 lineto +173.74431 177.64606 174.04704 177.35114 174.3947 177.15582 curveto +174.74626 176.96052 175.15055 176.86286 175.60759 176.86285 curveto +176.36149 176.86286 176.9318 177.09724 177.31853 177.56598 curveto +177.70524 178.03083 177.8986 178.71637 177.89861 179.62262 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +184.37907 180.22614 moveto +184.37907 179.44489 184.21696 178.83942 183.89275 178.40973 curveto +183.57243 177.98005 183.12126 177.7652 182.53923 177.7652 curveto +181.9611 177.7652 181.50993 177.98005 181.18571 178.40973 curveto +180.8654 178.83942 180.70524 179.44489 180.70525 180.22614 curveto +180.70524 181.00348 180.8654 181.607 181.18571 182.03668 curveto +181.50993 182.46637 181.9611 182.68121 182.53923 182.68121 curveto +183.12126 182.68121 183.57243 182.46637 183.89275 182.03668 curveto +184.21696 181.607 184.37907 181.00348 184.37907 180.22614 curveto +185.4572 182.7691 moveto +185.45719 183.88629 185.20915 184.71637 184.71306 185.25934 curveto +184.21696 185.80621 183.45719 186.07965 182.43376 186.07965 curveto +182.05485 186.07965 181.69743 186.05035 181.3615 185.99176 curveto +181.02556 185.93707 180.69938 185.85113 180.38298 185.73395 curveto +180.38298 184.68512 lineto +180.69938 184.85699 181.01188 184.98395 181.32048 185.06598 curveto +181.62907 185.14801 181.94352 185.18902 182.26384 185.18903 curveto +182.97087 185.18902 183.50016 185.00348 183.85173 184.63239 curveto +184.20329 184.2652 184.37907 183.70856 184.37907 182.96246 curveto +184.37907 182.42926 lineto +184.15641 182.81598 183.87126 183.10504 183.52361 183.29645 curveto +183.17594 183.48785 182.75993 183.58356 182.27556 183.58356 curveto +181.47087 183.58356 180.82243 183.27692 180.33025 182.66364 curveto +179.83806 182.05036 179.59196 181.23786 179.59196 180.22614 curveto +179.59196 179.21051 179.83806 178.39606 180.33025 177.78278 curveto +180.82243 177.1695 181.47087 176.86286 182.27556 176.86285 curveto +182.75993 176.86286 183.17594 176.95856 183.52361 177.14996 curveto +183.87126 177.34138 184.15641 177.63044 184.37907 178.01715 curveto +184.37907 177.02106 lineto +185.4572 177.02106 lineto +185.4572 182.7691 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +194.48064 180.28473 moveto +193.60954 180.28473 193.00602 180.38434 192.67009 180.58356 curveto +192.33415 180.78278 192.16618 181.12262 192.16618 181.60309 curveto +192.16618 181.9859 192.29118 182.29059 192.54118 182.51715 curveto +192.79509 182.73981 193.13884 182.85114 193.57243 182.85114 curveto +194.17009 182.85114 194.6486 182.6402 195.00798 182.21832 curveto +195.37126 181.79254 195.5529 181.22809 195.5529 180.52496 curveto +195.5529 180.28473 lineto +194.48064 180.28473 lineto +196.63103 179.83942 moveto +196.63103 183.58356 lineto +195.5529 183.58356 lineto +195.5529 182.58746 lineto +195.3068 182.9859 195.00016 183.28082 194.63298 183.47223 curveto +194.26579 183.65973 193.81657 183.75348 193.28532 183.75348 curveto +192.61345 183.75348 192.07829 183.56598 191.67986 183.19098 curveto +191.28532 182.81207 191.08806 182.30621 191.08806 181.6734 curveto +191.08806 180.93512 191.33415 180.37848 191.82634 180.00348 curveto +192.32243 179.62848 193.06071 179.44098 194.04118 179.44098 curveto +195.5529 179.44098 lineto +195.5529 179.33551 lineto +195.5529 178.83942 195.38883 178.45661 195.06071 178.18707 curveto +194.73649 177.91364 194.27946 177.77692 193.68962 177.77692 curveto +193.31462 177.77692 192.94938 177.82184 192.59392 177.91168 curveto +192.23845 178.00153 191.89665 178.1363 191.56853 178.31598 curveto +191.56853 177.31989 lineto +191.96306 177.16755 192.34587 177.05427 192.71696 176.98004 curveto +193.08806 176.90192 193.44938 176.86286 193.80095 176.86285 curveto +194.75016 176.86286 195.45915 177.10895 195.9279 177.60114 curveto +196.39665 178.09333 196.63102 178.83942 196.63103 179.83942 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +204.31267 179.62262 moveto +204.31267 183.58356 lineto +203.23454 183.58356 lineto +203.23454 179.65778 lineto +203.23454 179.03669 203.11344 178.57184 202.87126 178.26324 curveto +202.62907 177.95466 202.26579 177.80036 201.78142 177.80035 curveto +201.19938 177.80036 200.7404 177.98591 200.40446 178.35699 curveto +200.06852 178.72809 199.90056 179.23395 199.90056 179.87457 curveto +199.90056 183.58356 lineto +198.81657 183.58356 lineto +198.81657 177.02106 lineto +199.90056 177.02106 lineto +199.90056 178.04059 lineto +200.15837 177.64606 200.4611 177.35114 200.80876 177.15582 curveto +201.16032 176.96052 201.56462 176.86286 202.02165 176.86285 curveto +202.77555 176.86286 203.34587 177.09724 203.73259 177.56598 curveto +204.1193 178.03083 204.31266 178.71637 204.31267 179.62262 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +210.34196 174.83551 moveto +211.52556 174.83551 lineto +211.52556 183.58356 lineto +210.34196 183.58356 lineto +210.34196 174.83551 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +218.02946 179.48199 moveto +218.28337 179.56794 218.52946 179.75153 218.76775 180.03278 curveto +219.00993 180.31403 219.25211 180.70075 219.49431 181.19293 curveto +220.69548 183.58356 lineto +219.424 183.58356 lineto +218.30486 181.33942 lineto +218.01579 180.75348 217.73454 180.36481 217.46111 180.1734 curveto +217.19157 179.982 216.82243 179.8863 216.35368 179.88629 curveto +215.06462 179.88629 lineto +215.06462 183.58356 lineto +213.88103 183.58356 lineto +213.88103 174.83551 lineto +216.5529 174.83551 lineto +217.5529 174.83552 218.29899 175.0445 218.79118 175.46246 curveto +219.28336 175.88044 219.52946 176.5113 219.52946 177.35504 curveto +219.52946 177.90583 219.40055 178.36286 219.14275 178.72614 curveto +218.88883 179.08942 218.51774 179.34137 218.02946 179.48199 curveto +215.06462 175.80817 moveto +215.06462 178.91364 lineto +216.5529 178.91364 lineto +217.12321 178.91364 217.5529 178.78278 217.84196 178.52106 curveto +218.13493 178.25544 218.28141 177.86677 218.28142 177.35504 curveto +218.28141 176.84333 218.13493 176.45856 217.84196 176.20074 curveto +217.5529 175.93903 217.12321 175.80817 216.5529 175.80817 curveto +215.06462 175.80817 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +225.77556 175.63824 moveto +224.91618 175.63825 224.23259 175.95856 223.72478 176.59918 curveto +223.22087 177.23981 222.96892 178.11286 222.96892 179.21832 curveto +222.96892 180.31989 223.22087 181.19098 223.72478 181.8316 curveto +224.23259 182.47223 224.91618 182.79254 225.77556 182.79254 curveto +226.63493 182.79254 227.31461 182.47223 227.81462 181.8316 curveto +228.31852 181.19098 228.57047 180.31989 228.57048 179.21832 curveto +228.57047 178.11286 228.31852 177.23981 227.81462 176.59918 curveto +227.31461 175.95856 226.63493 175.63825 225.77556 175.63824 curveto +227.43376 183.42535 moveto +228.99236 185.13043 lineto +227.56267 185.13043 lineto +226.26775 183.73004 lineto +226.13883 183.73785 226.03923 183.74371 225.96892 183.74762 curveto +225.90251 183.75153 225.83805 183.75348 225.77556 183.75348 curveto +224.54509 183.75348 223.56071 183.34332 222.82243 182.52301 curveto +222.08806 181.69879 221.72087 180.59723 221.72087 179.21832 curveto +221.72087 177.83552 222.08806 176.73395 222.82243 175.91364 curveto +223.56071 175.08942 224.54509 174.67732 225.77556 174.67731 curveto +227.00212 174.67732 227.98258 175.08942 228.71696 175.91364 curveto +229.45133 176.73395 229.81852 177.83552 229.81853 179.21832 curveto +229.81852 180.23395 229.61344 181.10309 229.20329 181.82574 curveto +228.79704 182.5484 228.20719 183.0816 227.43376 183.42535 curveto +fill +grestore +grestore +gsave [1.037105 0 0 0.964223 0 0] concat +gsave +0 0 0 setrgbcolor +newpath +448.62109 417.69488 moveto +448.62109 423.69376 lineto +449.88183 423.69376 lineto +450.94623 423.69376 451.72472 423.45264 452.21732 422.97038 curveto +452.71334 422.48813 452.96136 421.72686 452.96136 420.68657 curveto +452.96136 419.65318 452.71334 418.89707 452.21732 418.41826 curveto +451.72472 417.93601 450.94623 417.69489 449.88183 417.69488 curveto +448.62109 417.69488 lineto +447.57735 416.83716 moveto +449.72166 416.83716 lineto +451.21664 416.83717 452.31376 417.14891 453.01303 417.77238 curveto +453.71229 418.39243 454.06193 419.36382 454.06193 420.68657 curveto +454.06193 422.01621 453.71057 422.99277 453.00787 423.61626 curveto +452.30515 424.23974 451.20975 424.55148 449.72166 424.55148 curveto +447.57735 424.55148 lineto +447.57735 416.83716 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +458.3092 421.64246 moveto +457.54104 421.64247 457.00884 421.73031 456.7126 421.90598 curveto +456.41636 422.08166 456.26824 422.38135 456.26824 422.80504 curveto +456.26824 423.14262 456.37847 423.4113 456.59893 423.61109 curveto +456.82283 423.80744 457.12596 423.90561 457.50832 423.90561 curveto +458.03535 423.90561 458.45732 423.7196 458.77423 423.34757 curveto +459.09458 422.97211 459.25476 422.47435 459.25476 421.85431 curveto +459.25476 421.64246 lineto +458.3092 421.64246 lineto +460.20549 421.24977 moveto +460.20549 424.55148 lineto +459.25476 424.55148 lineto +459.25476 423.67309 lineto +459.03775 424.02445 458.76734 424.28452 458.44355 424.45331 curveto +458.11974 424.61865 457.72361 424.70133 457.25514 424.70133 curveto +456.66265 424.70133 456.19073 424.53598 455.83938 424.20529 curveto +455.49147 423.87116 455.31751 423.42508 455.31751 422.86704 curveto +455.31751 422.216 455.53453 421.72514 455.96856 421.39445 curveto +456.40603 421.06376 457.05707 420.89842 457.92168 420.89842 curveto +459.25476 420.89842 lineto +459.25476 420.80541 lineto +459.25476 420.36794 459.11008 420.03037 458.82074 419.79268 curveto +458.53483 419.55156 458.1318 419.431 457.61166 419.43099 curveto +457.28097 419.431 456.95889 419.47061 456.64543 419.54983 curveto +456.33197 419.62906 456.03056 419.7479 455.74121 419.90635 curveto +455.74121 419.02796 lineto +456.08912 418.89363 456.42669 418.79373 456.75394 418.72828 curveto +457.08118 418.65939 457.39981 418.62494 457.70983 418.62494 curveto +458.54688 418.62494 459.17209 418.84196 459.58545 419.27598 curveto +459.99881 419.71001 460.20549 420.36794 460.20549 421.24977 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +465.52233 419.65317 moveto +465.41554 419.59117 465.29842 419.54639 465.17098 419.51883 curveto +465.04696 419.48783 464.90918 419.47233 464.75762 419.47233 curveto +464.22024 419.47233 463.80689 419.64801 463.51754 419.99936 curveto +463.23163 420.34728 463.08867 420.84847 463.08868 421.50296 curveto +463.08868 424.55148 lineto +462.13278 424.55148 lineto +462.13278 418.76445 lineto +463.08868 418.76445 lineto +463.08868 419.66351 lineto +463.28846 419.31215 463.54854 419.05208 463.86889 418.88329 curveto +464.18924 418.71106 464.57849 418.62494 465.03663 418.62494 curveto +465.10208 418.62494 465.17442 418.63011 465.25365 418.64044 curveto +465.33287 418.64734 465.42071 418.65939 465.51716 418.67661 curveto +465.52233 419.65317 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +466.49373 416.51164 moveto +467.44962 416.51164 lineto +467.44962 421.26011 lineto +470.2863 418.76445 lineto +471.50055 418.76445 lineto +468.43135 421.47195 lineto +471.62972 424.55148 lineto +470.38964 424.55148 lineto +467.44962 421.72514 lineto +467.44962 424.55148 lineto +466.49373 424.55148 lineto +466.49373 416.51164 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +478.44499 425.08885 moveto +478.1763 425.77778 477.91451 426.22731 477.6596 426.43744 curveto +477.4047 426.64756 477.06367 426.75262 476.63654 426.75262 curveto +475.87699 426.75262 lineto +475.87699 425.95691 lineto +476.43502 425.95691 lineto +476.69682 425.9569 476.90005 425.8949 477.04473 425.77089 curveto +477.1894 425.64689 477.34958 425.35409 477.52526 424.8925 curveto +477.69577 424.45848 lineto +475.35512 418.76445 lineto +476.36269 418.76445 lineto +478.17114 423.29074 lineto +479.97958 418.76445 lineto +480.98715 418.76445 lineto +478.44499 425.08885 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +487.24955 421.42028 moveto +487.24955 421.88531 lineto +482.87827 421.88531 lineto +482.9196 422.5398 483.11595 423.03928 483.46731 423.38374 curveto +483.8221 423.72476 484.31469 423.89528 484.94507 423.89527 curveto +485.3102 423.89528 485.66327 423.85049 486.0043 423.76093 curveto +486.34876 423.67137 486.68978 423.53703 487.02737 423.35791 curveto +487.02737 424.25696 lineto +486.68634 424.40164 486.33671 424.51187 485.97847 424.58765 curveto +485.62022 424.66343 485.2568 424.70133 484.88823 424.70133 curveto +483.96506 424.70133 483.23307 424.43264 482.69226 423.89527 curveto +482.15489 423.35791 481.8862 422.63108 481.8862 421.7148 curveto +481.8862 420.76752 482.14111 420.01659 482.65092 419.46199 curveto +483.16417 418.90396 483.85483 418.62494 484.72289 418.62494 curveto +485.50137 418.62494 486.11625 418.87641 486.5675 419.37932 curveto +487.02219 419.8788 487.24954 420.55912 487.24955 421.42028 curveto +486.29882 421.14127 moveto +486.29192 420.62113 486.14553 420.20604 485.85962 419.89602 curveto +485.57716 419.58601 485.20169 419.431 484.73322 419.43099 curveto +484.20274 419.431 483.77732 419.58084 483.45697 419.88052 curveto +483.14006 420.18021 482.95749 420.60218 482.90927 421.14643 curveto +486.29882 421.14127 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +488.80998 416.51164 moveto +489.76071 416.51164 lineto +489.76071 424.55148 lineto +488.80998 424.55148 lineto +488.80998 416.51164 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +491.74483 416.51164 moveto +492.69556 416.51164 lineto +492.69556 424.55148 lineto +491.74483 424.55148 lineto +491.74483 416.51164 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +496.92216 419.43099 moveto +496.41235 419.431 496.00933 419.63079 495.71309 420.03036 curveto +495.41684 420.4265 495.26872 420.97076 495.26872 421.66313 curveto +495.26872 422.35551 495.41512 422.90149 495.70792 423.30107 curveto +496.00416 423.69721 496.40891 423.89528 496.92216 423.89527 curveto +497.42853 423.89528 497.82983 423.69549 498.12607 423.2959 curveto +498.42231 422.89632 498.57043 422.35207 498.57044 421.66313 curveto +498.57043 420.97765 498.42231 420.43511 498.12607 420.03553 curveto +497.82983 419.63251 497.42853 419.431 496.92216 419.43099 curveto +496.92216 418.62494 moveto +497.74888 418.62494 498.3982 418.89363 498.87012 419.43099 curveto +499.34203 419.96836 499.57799 420.71241 499.578 421.66313 curveto +499.57799 422.61042 499.34203 423.35446 498.87012 423.89527 curveto +498.3982 424.43264 497.74888 424.70133 496.92216 424.70133 curveto +496.092 424.70133 495.44096 424.43264 494.96904 423.89527 curveto +494.50056 423.35446 494.26633 422.61042 494.26633 421.66313 curveto +494.26633 420.71241 494.50056 419.96836 494.96904 419.43099 curveto +495.44096 418.89363 496.092 418.62494 496.92216 418.62494 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +500.5959 418.76445 moveto +501.54662 418.76445 lineto +502.73503 423.2804 lineto +503.91827 418.76445 lineto +505.03951 418.76445 lineto +506.22792 423.2804 lineto +507.41116 418.76445 lineto +508.36189 418.76445 lineto +506.84796 424.55148 lineto +505.72672 424.55148 lineto +504.48148 419.80818 lineto +503.23106 424.55148 lineto +502.10983 424.55148 lineto +500.5959 418.76445 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +513.42038 423.23907 moveto +514.51062 423.23907 lineto +514.51062 424.55148 lineto +513.42038 424.55148 lineto +513.42038 423.23907 lineto +513.42038 419.07963 moveto +514.51062 419.07963 lineto +514.51062 420.39205 lineto +513.42038 420.39205 lineto +513.42038 419.07963 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +519.5691 418.76445 moveto +520.51983 418.76445 lineto +521.70824 423.2804 lineto +522.89148 418.76445 lineto +524.01272 418.76445 lineto +525.20113 423.2804 lineto +526.38437 418.76445 lineto +527.3351 418.76445 lineto +525.82117 424.55148 lineto +524.69993 424.55148 lineto +523.45468 419.80818 lineto +522.20427 424.55148 lineto +521.08303 424.55148 lineto +519.5691 418.76445 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +531.41186 421.64246 moveto +530.6437 421.64247 530.1115 421.73031 529.81526 421.90598 curveto +529.51902 422.08166 529.3709 422.38135 529.3709 422.80504 curveto +529.3709 423.14262 529.48113 423.4113 529.70159 423.61109 curveto +529.92549 423.80744 530.22862 423.90561 530.61098 423.90561 curveto +531.13801 423.90561 531.55998 423.7196 531.87689 423.34757 curveto +532.19724 422.97211 532.35742 422.47435 532.35742 421.85431 curveto +532.35742 421.64246 lineto +531.41186 421.64246 lineto +533.30815 421.24977 moveto +533.30815 424.55148 lineto +532.35742 424.55148 lineto +532.35742 423.67309 lineto +532.1404 424.02445 531.87 424.28452 531.5462 424.45331 curveto +531.2224 424.61865 530.82626 424.70133 530.35779 424.70133 curveto +529.76531 424.70133 529.29339 424.53598 528.94204 424.20529 curveto +528.59413 423.87116 528.42017 423.42508 528.42017 422.86704 curveto +528.42017 422.216 528.63718 421.72514 529.07121 421.39445 curveto +529.50868 421.06376 530.15972 420.89842 531.02434 420.89842 curveto +532.35742 420.89842 lineto +532.35742 420.80541 lineto +532.35742 420.36794 532.21274 420.03037 531.92339 419.79268 curveto +531.63748 419.55156 531.23446 419.431 530.71432 419.43099 curveto +530.38363 419.431 530.06155 419.47061 529.74809 419.54983 curveto +529.43462 419.62906 529.13321 419.7479 528.84386 419.90635 curveto +528.84386 419.02796 lineto +529.19177 418.89363 529.52935 418.79373 529.8566 418.72828 curveto +530.18384 418.65939 530.50247 418.62494 530.81249 418.62494 curveto +531.64954 418.62494 532.27474 418.84196 532.68811 419.27598 curveto +533.10146 419.71001 533.30814 420.36794 533.30815 421.24977 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +535.27161 418.76445 moveto +536.22233 418.76445 lineto +536.22233 424.55148 lineto +535.27161 424.55148 lineto +535.27161 418.76445 lineto +535.27161 416.51164 moveto +536.22233 416.51164 lineto +536.22233 417.71555 lineto +535.27161 417.71555 lineto +535.27161 416.51164 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +539.14685 417.12134 moveto +539.14685 418.76445 lineto +541.10514 418.76445 lineto +541.10514 419.50333 lineto +539.14685 419.50333 lineto +539.14685 422.64486 lineto +539.14685 423.11678 539.21058 423.41991 539.33803 423.55425 curveto +539.46893 423.6886 539.73244 423.75577 540.12858 423.75577 curveto +541.10514 423.75577 lineto +541.10514 424.55148 lineto +540.12858 424.55148 lineto +539.39487 424.55148 538.8885 424.41542 538.60948 424.14329 curveto +538.33047 423.86772 538.19096 423.36824 538.19096 422.64486 curveto +538.19096 419.50333 lineto +537.49341 419.50333 lineto +537.49341 418.76445 lineto +538.19096 418.76445 lineto +538.19096 417.12134 lineto +539.14685 417.12134 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +542.36073 418.76445 moveto +543.31145 418.76445 lineto +543.31145 424.55148 lineto +542.36073 424.55148 lineto +542.36073 418.76445 lineto +542.36073 416.51164 moveto +543.31145 416.51164 lineto +543.31145 417.71555 lineto +542.36073 417.71555 lineto +542.36073 416.51164 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +550.10605 421.05859 moveto +550.10605 424.55148 lineto +549.15532 424.55148 lineto +549.15532 421.0896 lineto +549.15532 420.5419 549.04853 420.13198 548.83497 419.85985 curveto +548.6214 419.58773 548.30104 419.45166 547.87391 419.45166 curveto +547.36065 419.45166 546.9559 419.61528 546.65966 419.94252 curveto +546.36342 420.26977 546.2153 420.71585 546.2153 421.28077 curveto +546.2153 424.55148 lineto +545.25941 424.55148 lineto +545.25941 418.76445 lineto +546.2153 418.76445 lineto +546.2153 419.66351 lineto +546.44265 419.3156 546.70961 419.05553 547.01619 418.88329 curveto +547.3262 418.71106 547.68273 418.62494 548.08576 418.62494 curveto +548.75057 418.62494 549.25349 418.83162 549.59452 419.24498 curveto +549.93553 419.6549 550.10604 420.25944 550.10605 421.05859 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +555.82075 421.59079 moveto +555.82074 420.90187 555.67779 420.36794 555.39189 419.98903 curveto +555.10942 419.61012 554.71156 419.42066 554.19831 419.42066 curveto +553.6885 419.42066 553.29064 419.61012 553.00474 419.98903 curveto +552.72227 420.36794 552.58104 420.90187 552.58104 421.59079 curveto +552.58104 422.27628 552.72227 422.80848 553.00474 423.1874 curveto +553.29064 423.56631 553.6885 423.75577 554.19831 423.75577 curveto +554.71156 423.75577 555.10942 423.56631 555.39189 423.1874 curveto +555.67779 422.80848 555.82074 422.27628 555.82075 421.59079 curveto +556.77148 423.83327 moveto +556.77147 424.81844 556.55273 425.55043 556.11527 426.02924 curveto +555.67779 426.51149 555.0078 426.75262 554.10531 426.75262 curveto +553.77117 426.75262 553.45598 426.72679 553.15975 426.67512 curveto +552.8635 426.62689 552.57587 426.55111 552.29686 426.44777 curveto +552.29686 425.52288 lineto +552.57587 425.67444 552.85145 425.78639 553.12358 425.85873 curveto +553.3957 425.93107 553.673 425.96724 553.95546 425.96724 curveto +554.57894 425.96724 555.04569 425.80362 555.35572 425.47638 curveto +555.66573 425.15258 555.82074 424.66171 555.82075 424.00378 curveto +555.82075 423.53358 lineto +555.6244 423.87461 555.37294 424.12951 555.06637 424.2983 curveto +554.75979 424.46709 554.39293 424.55148 553.9658 424.55148 curveto +553.25619 424.55148 552.68438 424.28108 552.25035 423.74026 curveto +551.81633 423.19945 551.59931 422.48296 551.59931 421.59079 curveto +551.59931 420.69519 551.81633 419.97697 552.25035 419.43616 curveto +552.68438 418.89535 553.25619 418.62494 553.9658 418.62494 curveto +554.39293 418.62494 554.75979 418.70934 555.06637 418.87812 curveto +555.37294 419.04692 555.6244 419.30182 555.82075 419.64284 curveto +555.82075 418.76445 lineto +556.77148 418.76445 lineto +556.77148 423.83327 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +565.02833 416.51164 moveto +565.02833 417.30219 lineto +564.11894 417.30219 lineto +563.77792 417.3022 563.54024 417.37109 563.4059 417.50887 curveto +563.275 417.64666 563.20955 417.89468 563.20955 418.25292 curveto +563.20955 418.76445 lineto +564.77515 418.76445 lineto +564.77515 419.50333 lineto +563.20955 419.50333 lineto +563.20955 424.55148 lineto +562.25366 424.55148 lineto +562.25366 419.50333 lineto +561.34427 419.50333 lineto +561.34427 418.76445 lineto +562.25366 418.76445 lineto +562.25366 418.36142 lineto +562.25366 417.71728 562.4035 417.2488 562.70319 416.956 curveto +563.00287 416.65977 563.47823 416.51165 564.12928 416.51164 curveto +565.02833 416.51164 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +568.06136 419.43099 moveto +567.55155 419.431 567.14852 419.63079 566.85228 420.03036 curveto +566.55604 420.4265 566.40792 420.97076 566.40792 421.66313 curveto +566.40792 422.35551 566.55432 422.90149 566.84712 423.30107 curveto +567.14336 423.69721 567.5481 423.89528 568.06136 423.89527 curveto +568.56772 423.89528 568.96903 423.69549 569.26527 423.2959 curveto +569.56151 422.89632 569.70963 422.35207 569.70963 421.66313 curveto +569.70963 420.97765 569.56151 420.43511 569.26527 420.03553 curveto +568.96903 419.63251 568.56772 419.431 568.06136 419.43099 curveto +568.06136 418.62494 moveto +568.88808 418.62494 569.5374 418.89363 570.00932 419.43099 curveto +570.48123 419.96836 570.71719 420.71241 570.7172 421.66313 curveto +570.71719 422.61042 570.48123 423.35446 570.00932 423.89527 curveto +569.5374 424.43264 568.88808 424.70133 568.06136 424.70133 curveto +567.2312 424.70133 566.58015 424.43264 566.10824 423.89527 curveto +565.63976 423.35446 565.40553 422.61042 565.40553 421.66313 curveto +565.40553 420.71241 565.63976 419.96836 566.10824 419.43099 curveto +566.58015 418.89363 567.2312 418.62494 568.06136 418.62494 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +575.64134 419.65317 moveto +575.53455 419.59117 575.41744 419.54639 575.28999 419.51883 curveto +575.16598 419.48783 575.02819 419.47233 574.87663 419.47233 curveto +574.33926 419.47233 573.9259 419.64801 573.63655 419.99936 curveto +573.35064 420.34728 573.20769 420.84847 573.20769 421.50296 curveto +573.20769 424.55148 lineto +572.25179 424.55148 lineto +572.25179 418.76445 lineto +573.20769 418.76445 lineto +573.20769 419.66351 lineto +573.40748 419.31215 573.66755 419.05208 573.98791 418.88329 curveto +574.30826 418.71106 574.6975 418.62494 575.15565 418.62494 curveto +575.22109 418.62494 575.29343 418.63011 575.37266 418.64044 curveto +575.45188 418.64734 575.53972 418.65939 575.63618 418.67661 curveto +575.64134 419.65317 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +585.83582 417.43136 moveto +585.83582 418.53193 lineto +585.48446 418.2047 585.10899 417.96013 584.70942 417.79822 curveto +584.31327 417.63633 583.8913 417.55538 583.4435 417.55537 curveto +582.56166 417.55538 581.88651 417.82578 581.41804 418.36659 curveto +580.94956 418.90396 580.71533 419.68246 580.71533 420.70207 curveto +580.71533 421.71825 580.94956 422.49674 581.41804 423.03755 curveto +581.88651 423.57492 582.56166 423.84361 583.4435 423.8436 curveto +583.8913 423.84361 584.31327 423.76266 584.70942 423.60076 curveto +585.10899 423.43886 585.48446 423.19429 585.83582 422.86704 curveto +585.83582 423.95728 lineto +585.47068 424.20529 585.08316 424.39131 584.67325 424.51531 curveto +584.26677 424.63932 583.83619 424.70133 583.3815 424.70133 curveto +582.21375 424.70133 581.29403 424.3448 580.62232 423.63176 curveto +579.95061 422.91527 579.61476 421.93871 579.61476 420.70207 curveto +579.61476 419.462 579.95061 418.48544 580.62232 417.77238 curveto +581.29403 417.0559 582.21375 416.69766 583.3815 416.69765 curveto +583.84308 416.69766 584.27711 416.75966 584.68358 416.88366 curveto +585.09349 417.00423 585.47757 417.1868 585.83582 417.43136 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +588.50199 417.69488 moveto +588.50199 420.59356 lineto +589.81441 420.59356 lineto +590.3001 420.59357 590.67557 420.46784 590.94081 420.21637 curveto +591.20605 419.96492 591.33867 419.60667 591.33867 419.14164 curveto +591.33867 418.68006 591.20605 418.32354 590.94081 418.07207 curveto +590.67557 417.82062 590.3001 417.69489 589.81441 417.69488 curveto +588.50199 417.69488 lineto +587.45826 416.83716 moveto +589.81441 416.83716 lineto +590.67902 416.83717 591.33178 417.03351 591.7727 417.4262 curveto +592.21706 417.81545 592.43924 418.38726 592.43924 419.14164 curveto +592.43924 419.90291 592.21706 420.47817 591.7727 420.86742 curveto +591.33178 421.25667 590.67902 421.45129 589.81441 421.45129 curveto +588.50199 421.45129 lineto +588.50199 424.55148 lineto +587.45826 424.55148 lineto +587.45826 416.83716 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +593.72583 416.83716 moveto +594.77473 416.83716 lineto +594.77473 421.52362 lineto +594.77473 422.35035 594.92457 422.94627 595.22426 423.3114 curveto +595.52394 423.67309 596.00964 423.85394 596.68135 423.85394 curveto +597.34961 423.85394 597.83359 423.67309 598.13328 423.3114 curveto +598.43296 422.94627 598.5828 422.35035 598.58281 421.52362 curveto +598.58281 416.83716 lineto +599.63171 416.83716 lineto +599.63171 421.6528 lineto +599.6317 422.65864 599.38196 423.41819 598.88249 423.93144 curveto +598.38646 424.4447 597.65274 424.70133 596.68135 424.70133 curveto +595.70651 424.70133 594.96935 424.4447 594.46988 423.93144 curveto +593.97385 423.41819 593.72583 422.65864 593.72583 421.6528 curveto +593.72583 416.83716 lineto +fill +grestore +grestore +gsave [1.037105 0 0 0.964223 0 0] concat +gsave +0 0 0 setrgbcolor +newpath +448.62109 448.94113 moveto +448.62109 454.94001 lineto +449.88183 454.94001 lineto +450.94623 454.94001 451.72472 454.69888 452.21732 454.21663 curveto +452.71334 453.73438 452.96136 452.97311 452.96136 451.93282 curveto +452.96136 450.89942 452.71334 450.14332 452.21732 449.66451 curveto +451.72472 449.18226 450.94623 448.94113 449.88183 448.94113 curveto +448.62109 448.94113 lineto +447.57735 448.0834 moveto +449.72166 448.0834 lineto +451.21664 448.08341 452.31376 448.39515 453.01303 449.01863 curveto +453.71229 449.63868 454.06193 450.61007 454.06193 451.93282 curveto +454.06193 453.26246 453.71057 454.23902 453.00787 454.8625 curveto +452.30515 455.48599 451.20975 455.79773 449.72166 455.79773 curveto +447.57735 455.79773 lineto +447.57735 448.0834 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +458.3092 452.88871 moveto +457.54104 452.88871 457.00884 452.97655 456.7126 453.15223 curveto +456.41636 453.32791 456.26824 453.62759 456.26824 454.05128 curveto +456.26824 454.38886 456.37847 454.65755 456.59893 454.85734 curveto +456.82283 455.05368 457.12596 455.15186 457.50832 455.15186 curveto +458.03535 455.15186 458.45732 454.96584 458.77423 454.59382 curveto +459.09458 454.21835 459.25476 453.7206 459.25476 453.10056 curveto +459.25476 452.88871 lineto +458.3092 452.88871 lineto +460.20549 452.49602 moveto +460.20549 455.79773 lineto +459.25476 455.79773 lineto +459.25476 454.91934 lineto +459.03775 455.2707 458.76734 455.53077 458.44355 455.69956 curveto +458.11974 455.8649 457.72361 455.94757 457.25514 455.94757 curveto +456.66265 455.94757 456.19073 455.78223 455.83938 455.45154 curveto +455.49147 455.11741 455.31751 454.67133 455.31751 454.11329 curveto +455.31751 453.46225 455.53453 452.97139 455.96856 452.6407 curveto +456.40603 452.31001 457.05707 452.14467 457.92168 452.14466 curveto +459.25476 452.14466 lineto +459.25476 452.05166 lineto +459.25476 451.61419 459.11008 451.27661 458.82074 451.03893 curveto +458.53483 450.7978 458.1318 450.67724 457.61166 450.67724 curveto +457.28097 450.67724 456.95889 450.71686 456.64543 450.79608 curveto +456.33197 450.87531 456.03056 450.99415 455.74121 451.1526 curveto +455.74121 450.27421 lineto +456.08912 450.13987 456.42669 450.03998 456.75394 449.97453 curveto +457.08118 449.90564 457.39981 449.87119 457.70983 449.87119 curveto +458.54688 449.87119 459.17209 450.0882 459.58545 450.52223 curveto +459.99881 450.95626 460.20549 451.61419 460.20549 452.49602 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +465.52233 450.89942 moveto +465.41554 450.83742 465.29842 450.79264 465.17098 450.76508 curveto +465.04696 450.73408 464.90918 450.71858 464.75762 450.71857 curveto +464.22024 450.71858 463.80689 450.89426 463.51754 451.24561 curveto +463.23163 451.59352 463.08867 452.09472 463.08868 452.7492 curveto +463.08868 455.79773 lineto +462.13278 455.79773 lineto +462.13278 450.01069 lineto +463.08868 450.01069 lineto +463.08868 450.90975 lineto +463.28846 450.5584 463.54854 450.29833 463.86889 450.12954 curveto +464.18924 449.95731 464.57849 449.87119 465.03663 449.87119 curveto +465.10208 449.87119 465.17442 449.87636 465.25365 449.88669 curveto +465.33287 449.89358 465.42071 449.90564 465.51716 449.92286 curveto +465.52233 450.89942 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +466.49373 447.75788 moveto +467.44962 447.75788 lineto +467.44962 452.50635 lineto +470.2863 450.01069 lineto +471.50055 450.01069 lineto +468.43135 452.7182 lineto +471.62972 455.79773 lineto +470.38964 455.79773 lineto +467.44962 452.97138 lineto +467.44962 455.79773 lineto +466.49373 455.79773 lineto +466.49373 447.75788 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +476.95689 454.92967 moveto +476.95689 457.99887 lineto +476.001 457.99887 lineto +476.001 450.01069 lineto +476.95689 450.01069 lineto +476.95689 450.88908 lineto +477.15668 450.54462 477.40814 450.28972 477.71127 450.12437 curveto +478.01785 449.95559 478.38298 449.87119 478.80668 449.87119 curveto +479.50938 449.87119 480.07947 450.15021 480.51695 450.70824 curveto +480.95786 451.26628 481.17832 451.99999 481.17833 452.90938 curveto +481.17832 453.81877 480.95786 454.55248 480.51695 455.11052 curveto +480.07947 455.66855 479.50938 455.94757 478.80668 455.94757 curveto +478.38298 455.94757 478.01785 455.8649 477.71127 455.69956 curveto +477.40814 455.53077 477.15668 455.27414 476.95689 454.92967 curveto +480.19143 452.90938 moveto +480.19143 452.21012 480.04675 451.66241 479.7574 451.26627 curveto +479.47149 450.8667 479.07708 450.66691 478.57416 450.6669 curveto +478.07124 450.66691 477.6751 450.8667 477.38575 451.26627 curveto +477.09984 451.66241 476.95689 452.21012 476.95689 452.90938 curveto +476.95689 453.60865 477.09984 454.15807 477.38575 454.55765 curveto +477.6751 454.95379 478.07124 455.15186 478.57416 455.15186 curveto +479.07708 455.15186 479.47149 454.95379 479.7574 454.55765 curveto +480.04675 454.15807 480.19143 453.60865 480.19143 452.90938 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +482.65609 453.51392 moveto +482.65609 450.01069 lineto +483.60681 450.01069 lineto +483.60681 453.47775 lineto +483.60681 454.02545 483.7136 454.43709 483.92717 454.71266 curveto +484.14074 454.98479 484.46109 455.12085 484.88823 455.12085 curveto +485.40148 455.12085 485.80623 454.95723 486.10247 454.62999 curveto +486.40216 454.30275 486.552 453.85666 486.552 453.29174 curveto +486.552 450.01069 lineto +487.50273 450.01069 lineto +487.50273 455.79773 lineto +486.552 455.79773 lineto +486.552 454.90901 lineto +486.32121 455.26036 486.05252 455.52216 485.74595 455.69439 curveto +485.44282 455.86318 485.08974 455.94757 484.68672 455.94757 curveto +484.02189 455.94757 483.51725 455.74089 483.17279 455.32753 curveto +482.82832 454.91417 482.65609 454.30964 482.65609 453.51392 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +492.82474 450.89942 moveto +492.71795 450.83742 492.60083 450.79264 492.47338 450.76508 curveto +492.34937 450.73408 492.21158 450.71858 492.06002 450.71857 curveto +491.52265 450.71858 491.10929 450.89426 490.81994 451.24561 curveto +490.53403 451.59352 490.39108 452.09472 490.39108 452.7492 curveto +490.39108 455.79773 lineto +489.43519 455.79773 lineto +489.43519 450.01069 lineto +490.39108 450.01069 lineto +490.39108 450.90975 lineto +490.59087 450.5584 490.85094 450.29833 491.1713 450.12954 curveto +491.49165 449.95731 491.88089 449.87119 492.33904 449.87119 curveto +492.40448 449.87119 492.47682 449.87636 492.55605 449.88669 curveto +492.63527 449.89358 492.72311 449.90564 492.81957 449.92286 curveto +492.82474 450.89942 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +494.75202 454.92967 moveto +494.75202 457.99887 lineto +493.79613 457.99887 lineto +493.79613 450.01069 lineto +494.75202 450.01069 lineto +494.75202 450.88908 lineto +494.95181 450.54462 495.20327 450.28972 495.5064 450.12437 curveto +495.81298 449.95559 496.17811 449.87119 496.60181 449.87119 curveto +497.30451 449.87119 497.87461 450.15021 498.31208 450.70824 curveto +498.75299 451.26628 498.97345 451.99999 498.97346 452.90938 curveto +498.97345 453.81877 498.75299 454.55248 498.31208 455.11052 curveto +497.87461 455.66855 497.30451 455.94757 496.60181 455.94757 curveto +496.17811 455.94757 495.81298 455.8649 495.5064 455.69956 curveto +495.20327 455.53077 494.95181 455.27414 494.75202 454.92967 curveto +497.98656 452.90938 moveto +497.98656 452.21012 497.84188 451.66241 497.55253 451.26627 curveto +497.26662 450.8667 496.87221 450.66691 496.36929 450.6669 curveto +495.86637 450.66691 495.47023 450.8667 495.18088 451.26627 curveto +494.89497 451.66241 494.75202 452.21012 494.75202 452.90938 curveto +494.75202 453.60865 494.89497 454.15807 495.18088 454.55765 curveto +495.47023 454.95379 495.86637 455.15186 496.36929 455.15186 curveto +496.87221 455.15186 497.26662 454.95379 497.55253 454.55765 curveto +497.84188 454.15807 497.98656 453.60865 497.98656 452.90938 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +500.54939 447.75788 moveto +501.50012 447.75788 lineto +501.50012 455.79773 lineto +500.54939 455.79773 lineto +500.54939 447.75788 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +508.43423 452.66653 moveto +508.43423 453.13156 lineto +504.06295 453.13156 lineto +504.10429 453.78605 504.30063 454.28552 504.65199 454.62999 curveto +505.00679 454.97101 505.49937 455.14152 506.12975 455.14152 curveto +506.49488 455.14152 506.84796 455.09674 507.18898 455.00718 curveto +507.53345 454.91762 507.87447 454.78328 508.21205 454.60415 curveto +508.21205 455.50321 lineto +507.87102 455.64789 507.52139 455.75812 507.16315 455.8339 curveto +506.8049 455.90968 506.44149 455.94757 506.07291 455.94757 curveto +505.14974 455.94757 504.41775 455.67889 503.87694 455.14152 curveto +503.33957 454.60415 503.07089 453.87733 503.07089 452.96105 curveto +503.07089 452.01377 503.32579 451.26283 503.8356 450.70824 curveto +504.34886 450.15021 505.03951 449.87119 505.90757 449.87119 curveto +506.68606 449.87119 507.30093 450.12265 507.75219 450.62557 curveto +508.20688 451.12505 508.43422 451.80537 508.43423 452.66653 curveto +507.4835 452.38751 moveto +507.47661 451.86737 507.33021 451.45229 507.04431 451.14227 curveto +506.76184 450.83225 506.38637 450.67724 505.9179 450.67724 curveto +505.38742 450.67724 504.96201 450.82708 504.64166 451.12677 curveto +504.32474 451.42646 504.14218 451.84843 504.09395 452.39268 curveto +507.4835 452.38751 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +513.60639 454.48531 moveto +514.69663 454.48531 lineto +514.69663 455.79773 lineto +513.60639 455.79773 lineto +513.60639 454.48531 lineto +513.60639 450.32588 moveto +514.69663 450.32588 lineto +514.69663 451.6383 lineto +513.60639 451.6383 lineto +513.60639 450.32588 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +519.89462 450.01069 moveto +524.41058 450.01069 lineto +524.41058 450.87875 lineto +520.83502 455.03818 lineto +524.41058 455.03818 lineto +524.41058 455.79773 lineto +519.76545 455.79773 lineto +519.76545 454.92967 lineto +523.34101 450.77024 lineto +519.89462 450.77024 lineto +519.89462 450.01069 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +528.11015 450.67724 moveto +527.60034 450.67724 527.19731 450.87703 526.90107 451.27661 curveto +526.60483 451.67275 526.45671 452.217 526.45671 452.90938 curveto +526.45671 453.60176 526.60311 454.14774 526.89591 454.54732 curveto +527.19215 454.94345 527.59689 455.14152 528.11015 455.14152 curveto +528.61651 455.14152 529.01782 454.94173 529.31406 454.54215 curveto +529.6103 454.14257 529.75842 453.59831 529.75842 452.90938 curveto +529.75842 452.22389 529.6103 451.68136 529.31406 451.28178 curveto +529.01782 450.87875 528.61651 450.67724 528.11015 450.67724 curveto +528.11015 449.87119 moveto +528.93687 449.87119 529.58619 450.13987 530.05811 450.67724 curveto +530.53002 451.21461 530.76598 451.95866 530.76599 452.90938 curveto +530.76598 453.85666 530.53002 454.60071 530.05811 455.14152 curveto +529.58619 455.67889 528.93687 455.94757 528.11015 455.94757 curveto +527.27999 455.94757 526.62894 455.67889 526.15703 455.14152 curveto +525.68855 454.60071 525.45432 453.85666 525.45432 452.90938 curveto +525.45432 451.95866 525.68855 451.21461 526.15703 450.67724 curveto +526.62894 450.13987 527.27999 449.87119 528.11015 449.87119 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +536.84237 451.1216 moveto +537.08005 450.69447 537.36423 450.37928 537.69493 450.17604 curveto +538.02561 449.97281 538.41486 449.87119 538.86267 449.87119 curveto +539.46548 449.87119 539.93051 450.08304 540.25776 450.50673 curveto +540.58499 450.92698 540.74861 451.52635 540.74862 452.30484 curveto +540.74862 455.79773 lineto +539.79273 455.79773 lineto +539.79273 452.33584 lineto +539.79272 451.78126 539.69455 451.36962 539.49821 451.10093 curveto +539.30186 450.83225 539.00217 450.69791 538.59915 450.6979 curveto +538.10656 450.69791 537.71731 450.86153 537.43141 451.18877 curveto +537.1455 451.51602 537.00255 451.9621 537.00255 452.52702 curveto +537.00255 455.79773 lineto +536.04666 455.79773 lineto +536.04666 452.33584 lineto +536.04665 451.77781 535.94848 451.36617 535.75214 451.10093 curveto +535.55579 450.83225 535.25266 450.69791 534.84275 450.6979 curveto +534.35705 450.69791 533.97124 450.86325 533.68534 451.19394 curveto +533.39943 451.52118 533.25648 451.96554 533.25648 452.52702 curveto +533.25648 455.79773 lineto +532.30058 455.79773 lineto +532.30058 450.01069 lineto +533.25648 450.01069 lineto +533.25648 450.90975 lineto +533.47349 450.55496 533.73356 450.29316 534.0367 450.12437 curveto +534.33982 449.95559 534.69979 449.87119 535.1166 449.87119 curveto +535.53684 449.87119 535.89336 449.97798 536.18617 450.19154 curveto +536.4824 450.40511 536.70114 450.71513 536.84237 451.1216 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +546.80434 452.90938 moveto +546.80434 452.21012 546.65966 451.66241 546.37031 451.26627 curveto +546.0844 450.8667 545.68999 450.66691 545.18707 450.6669 curveto +544.68415 450.66691 544.28801 450.8667 543.99866 451.26627 curveto +543.71275 451.66241 543.5698 452.21012 543.5698 452.90938 curveto +543.5698 453.60865 543.71275 454.15807 543.99866 454.55765 curveto +544.28801 454.95379 544.68415 455.15186 545.18707 455.15186 curveto +545.68999 455.15186 546.0844 454.95379 546.37031 454.55765 curveto +546.65966 454.15807 546.80434 453.60865 546.80434 452.90938 curveto +543.5698 450.88908 moveto +543.76959 450.54462 544.02105 450.28972 544.32418 450.12437 curveto +544.63076 449.95559 544.99589 449.87119 545.41959 449.87119 curveto +546.12229 449.87119 546.69239 450.15021 547.12986 450.70824 curveto +547.57077 451.26628 547.79123 451.99999 547.79124 452.90938 curveto +547.79123 453.81877 547.57077 454.55248 547.12986 455.11052 curveto +546.69239 455.66855 546.12229 455.94757 545.41959 455.94757 curveto +544.99589 455.94757 544.63076 455.8649 544.32418 455.69956 curveto +544.02105 455.53077 543.76959 455.27414 543.5698 454.92967 curveto +543.5698 455.79773 lineto +542.61391 455.79773 lineto +542.61391 447.75788 lineto +543.5698 447.75788 lineto +543.5698 450.88908 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +549.36717 450.01069 moveto +550.3179 450.01069 lineto +550.3179 455.79773 lineto +549.36717 455.79773 lineto +549.36717 450.01069 lineto +549.36717 447.75788 moveto +550.3179 447.75788 lineto +550.3179 448.96179 lineto +549.36717 448.96179 lineto +549.36717 447.75788 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +557.25201 452.66653 moveto +557.25201 453.13156 lineto +552.88073 453.13156 lineto +552.92206 453.78605 553.11841 454.28552 553.46977 454.62999 curveto +553.82456 454.97101 554.31715 455.14152 554.94753 455.14152 curveto +555.31266 455.14152 555.66573 455.09674 556.00676 455.00718 curveto +556.35122 454.91762 556.69224 454.78328 557.02983 454.60415 curveto +557.02983 455.50321 lineto +556.6888 455.64789 556.33917 455.75812 555.98093 455.8339 curveto +555.62268 455.90968 555.25926 455.94757 554.89069 455.94757 curveto +553.96752 455.94757 553.23553 455.67889 552.69472 455.14152 curveto +552.15735 454.60415 551.88866 453.87733 551.88866 452.96105 curveto +551.88866 452.01377 552.14357 451.26283 552.65338 450.70824 curveto +553.16663 450.15021 553.85729 449.87119 554.72535 449.87119 curveto +555.50384 449.87119 556.11871 450.12265 556.56996 450.62557 curveto +557.02465 451.12505 557.252 451.80537 557.25201 452.66653 curveto +556.30128 452.38751 moveto +556.29438 451.86737 556.14799 451.45229 555.86208 451.14227 curveto +555.57962 450.83225 555.20415 450.67724 554.73568 450.67724 curveto +554.2052 450.67724 553.77978 450.82708 553.45943 451.12677 curveto +553.14252 451.42646 552.95995 451.84843 552.91173 452.39268 curveto +556.30128 452.38751 lineto +fill +grestore +grestore +gsave [1.037105 0 0 0.964223 0 0] concat +gsave +0 0 0 setrgbcolor +newpath +448.62109 480.18722 moveto +448.62109 486.1861 lineto +449.88183 486.1861 lineto +450.94623 486.1861 451.72472 485.94498 452.21732 485.46272 curveto +452.71334 484.98047 452.96136 484.2192 452.96136 483.17891 curveto +452.96136 482.14552 452.71334 481.38941 452.21732 480.9106 curveto +451.72472 480.42835 450.94623 480.18723 449.88183 480.18722 curveto +448.62109 480.18722 lineto +447.57735 479.3295 moveto +449.72166 479.3295 lineto +451.21664 479.32951 452.31376 479.64125 453.01303 480.26472 curveto +453.71229 480.88477 454.06193 481.85616 454.06193 483.17891 curveto +454.06193 484.50855 453.71057 485.48511 453.00787 486.1086 curveto +452.30515 486.73208 451.20975 487.04382 449.72166 487.04382 curveto +447.57735 487.04382 lineto +447.57735 479.3295 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +458.3092 484.1348 moveto +457.54104 484.13481 457.00884 484.22265 456.7126 484.39832 curveto +456.41636 484.574 456.26824 484.87369 456.26824 485.29738 curveto +456.26824 485.63496 456.37847 485.90364 456.59893 486.10343 curveto +456.82283 486.29978 457.12596 486.39795 457.50832 486.39795 curveto +458.03535 486.39795 458.45732 486.21194 458.77423 485.83991 curveto +459.09458 485.46445 459.25476 484.96669 459.25476 484.34665 curveto +459.25476 484.1348 lineto +458.3092 484.1348 lineto +460.20549 483.74211 moveto +460.20549 487.04382 lineto +459.25476 487.04382 lineto +459.25476 486.16543 lineto +459.03775 486.51679 458.76734 486.77686 458.44355 486.94565 curveto +458.11974 487.11099 457.72361 487.19367 457.25514 487.19367 curveto +456.66265 487.19367 456.19073 487.02832 455.83938 486.69763 curveto +455.49147 486.3635 455.31751 485.91742 455.31751 485.35938 curveto +455.31751 484.70834 455.53453 484.21748 455.96856 483.88679 curveto +456.40603 483.5561 457.05707 483.39076 457.92168 483.39076 curveto +459.25476 483.39076 lineto +459.25476 483.29775 lineto +459.25476 482.86028 459.11008 482.52271 458.82074 482.28502 curveto +458.53483 482.0439 458.1318 481.92334 457.61166 481.92333 curveto +457.28097 481.92334 456.95889 481.96295 456.64543 482.04217 curveto +456.33197 482.1214 456.03056 482.24024 455.74121 482.39869 curveto +455.74121 481.5203 lineto +456.08912 481.38597 456.42669 481.28607 456.75394 481.22062 curveto +457.08118 481.15173 457.39981 481.11728 457.70983 481.11728 curveto +458.54688 481.11728 459.17209 481.3343 459.58545 481.76832 curveto +459.99881 482.20235 460.20549 482.86028 460.20549 483.74211 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +465.52233 482.14551 moveto +465.41554 482.08351 465.29842 482.03873 465.17098 482.01117 curveto +465.04696 481.98017 464.90918 481.96467 464.75762 481.96467 curveto +464.22024 481.96467 463.80689 482.14035 463.51754 482.4917 curveto +463.23163 482.83962 463.08867 483.34081 463.08868 483.9953 curveto +463.08868 487.04382 lineto +462.13278 487.04382 lineto +462.13278 481.25679 lineto +463.08868 481.25679 lineto +463.08868 482.15585 lineto +463.28846 481.80449 463.54854 481.54442 463.86889 481.37563 curveto +464.18924 481.2034 464.57849 481.11728 465.03663 481.11728 curveto +465.10208 481.11728 465.17442 481.12245 465.25365 481.13278 curveto +465.33287 481.13968 465.42071 481.15173 465.51716 481.16895 curveto +465.52233 482.14551 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +466.49373 479.00398 moveto +467.44962 479.00398 lineto +467.44962 483.75245 lineto +470.2863 481.25679 lineto +471.50055 481.25679 lineto +468.43135 483.96429 lineto +471.62972 487.04382 lineto +470.38964 487.04382 lineto +467.44962 484.21748 lineto +467.44962 487.04382 lineto +466.49373 487.04382 lineto +466.49373 479.00398 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +479.84524 484.08313 moveto +479.84524 483.39421 479.70228 482.86028 479.41638 482.48137 curveto +479.13391 482.10246 478.73606 481.913 478.22281 481.913 curveto +477.71299 481.913 477.31513 482.10246 477.02923 482.48137 curveto +476.74677 482.86028 476.60553 483.39421 476.60554 484.08313 curveto +476.60553 484.76863 476.74677 485.30083 477.02923 485.67974 curveto +477.31513 486.05865 477.71299 486.24811 478.22281 486.24811 curveto +478.73606 486.24811 479.13391 486.05865 479.41638 485.67974 curveto +479.70228 485.30083 479.84524 484.76863 479.84524 484.08313 curveto +480.79597 486.32561 moveto +480.79596 487.31078 480.57723 488.04277 480.13976 488.52158 curveto +479.70228 489.00384 479.0323 489.24496 478.1298 489.24496 curveto +477.79566 489.24496 477.48048 489.21913 477.18424 489.16746 curveto +476.888 489.11923 476.60037 489.04345 476.32135 488.94011 curveto +476.32135 488.01522 lineto +476.60037 488.16678 476.87594 488.27873 477.14807 488.35107 curveto +477.4202 488.42341 477.69749 488.45958 477.97996 488.45958 curveto +478.60344 488.45958 479.07019 488.29596 479.38021 487.96872 curveto +479.69023 487.64492 479.84524 487.15405 479.84524 486.49612 curveto +479.84524 486.02593 lineto +479.64889 486.36695 479.39743 486.62185 479.09086 486.79064 curveto +478.78428 486.95943 478.41743 487.04382 477.99029 487.04382 curveto +477.28069 487.04382 476.70887 486.77342 476.27485 486.2326 curveto +475.84082 485.69179 475.62381 484.9753 475.62381 484.08313 curveto +475.62381 483.18753 475.84082 482.46931 476.27485 481.9285 curveto +476.70887 481.38769 477.28069 481.11728 477.99029 481.11728 curveto +478.41743 481.11728 478.78428 481.20168 479.09086 481.37046 curveto +479.39743 481.53926 479.64889 481.79416 479.84524 482.13518 curveto +479.84524 481.25679 lineto +480.79597 481.25679 lineto +480.79597 486.32561 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +486.10764 482.14551 moveto +486.00085 482.08351 485.88373 482.03873 485.75629 482.01117 curveto +485.63227 481.98017 485.49449 481.96467 485.34293 481.96467 curveto +484.80555 481.96467 484.3922 482.14035 484.10285 482.4917 curveto +483.81694 482.83962 483.67398 483.34081 483.67399 483.9953 curveto +483.67399 487.04382 lineto +482.71809 487.04382 lineto +482.71809 481.25679 lineto +483.67399 481.25679 lineto +483.67399 482.15585 lineto +483.87377 481.80449 484.13385 481.54442 484.4542 481.37563 curveto +484.77455 481.2034 485.1638 481.11728 485.62194 481.11728 curveto +485.68739 481.11728 485.75973 481.12245 485.83896 481.13278 curveto +485.91818 481.13968 486.00602 481.15173 486.10247 481.16895 curveto +486.10764 482.14551 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +491.83784 483.91262 moveto +491.83784 484.37765 lineto +487.46656 484.37765 lineto +487.5079 485.03214 487.70424 485.53162 488.0556 485.87608 curveto +488.4104 486.2171 488.90298 486.38762 489.53336 486.38761 curveto +489.89849 486.38762 490.25157 486.34283 490.59259 486.25327 curveto +490.93706 486.16371 491.27808 486.02937 491.61566 485.85025 curveto +491.61566 486.7493 lineto +491.27463 486.89398 490.925 487.00421 490.56676 487.07999 curveto +490.20851 487.15577 489.8451 487.19367 489.47652 487.19367 curveto +488.55335 487.19367 487.82136 486.92498 487.28055 486.38761 curveto +486.74318 485.85025 486.4745 485.12343 486.4745 484.20714 curveto +486.4745 483.25986 486.7294 482.50893 487.23921 481.95433 curveto +487.75247 481.3963 488.44312 481.11728 489.31118 481.11728 curveto +490.08967 481.11728 490.70454 481.36875 491.1558 481.87166 curveto +491.61049 482.37114 491.83784 483.05146 491.83784 483.91262 curveto +490.88711 483.63361 moveto +490.88022 483.11347 490.73382 482.69838 490.44792 482.38836 curveto +490.16545 482.07835 489.78998 481.92334 489.32151 481.92333 curveto +488.79103 481.92334 488.36562 482.07318 488.04527 482.37286 curveto +487.72836 482.67255 487.54579 483.09452 487.49756 483.63877 curveto +490.88711 483.63361 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +498.34826 483.91262 moveto +498.34826 484.37765 lineto +493.97698 484.37765 lineto +494.01831 485.03214 494.21466 485.53162 494.56601 485.87608 curveto +494.92081 486.2171 495.4134 486.38762 496.04377 486.38761 curveto +496.40891 486.38762 496.76198 486.34283 497.10301 486.25327 curveto +497.44747 486.16371 497.78849 486.02937 498.12607 485.85025 curveto +498.12607 486.7493 lineto +497.78505 486.89398 497.43541 487.00421 497.07717 487.07999 curveto +496.71892 487.15577 496.35551 487.19367 495.98694 487.19367 curveto +495.06377 487.19367 494.33178 486.92498 493.79096 486.38761 curveto +493.2536 485.85025 492.98491 485.12343 492.98491 484.20714 curveto +492.98491 483.25986 493.23982 482.50893 493.74963 481.95433 curveto +494.26288 481.3963 494.95354 481.11728 495.82159 481.11728 curveto +496.60008 481.11728 497.21496 481.36875 497.66621 481.87166 curveto +498.1209 482.37114 498.34825 483.05146 498.34826 483.91262 curveto +497.39753 483.63361 moveto +497.39063 483.11347 497.24424 482.69838 496.95833 482.38836 curveto +496.67587 482.07835 496.3004 481.92334 495.83193 481.92333 curveto +495.30145 481.92334 494.87603 482.07318 494.55568 482.37286 curveto +494.23877 482.67255 494.0562 483.09452 494.00798 483.63877 curveto +497.39753 483.63361 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +504.71916 483.55093 moveto +504.71916 487.04382 lineto +503.76843 487.04382 lineto +503.76843 483.58194 lineto +503.76843 483.03424 503.66164 482.62432 503.44808 482.35219 curveto +503.23451 482.08007 502.91415 481.944 502.48702 481.944 curveto +501.97376 481.944 501.56901 482.10762 501.27277 482.43486 curveto +500.97653 482.76211 500.82841 483.20819 500.82841 483.77311 curveto +500.82841 487.04382 lineto +499.87252 487.04382 lineto +499.87252 481.25679 lineto +500.82841 481.25679 lineto +500.82841 482.15585 lineto +501.05576 481.80794 501.32272 481.54787 501.6293 481.37563 curveto +501.93931 481.2034 502.29584 481.11728 502.69886 481.11728 curveto +503.36368 481.11728 503.8666 481.32396 504.20763 481.73732 curveto +504.54864 482.14724 504.71915 482.75178 504.71916 483.55093 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +510.23751 485.73141 moveto +511.32775 485.73141 lineto +511.32775 487.04382 lineto +510.23751 487.04382 lineto +510.23751 485.73141 lineto +510.23751 481.57197 moveto +511.32775 481.57197 lineto +511.32775 482.88439 lineto +510.23751 482.88439 lineto +510.23751 481.57197 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +516.38623 481.25679 moveto +517.33696 481.25679 lineto +518.52537 485.77274 lineto +519.70861 481.25679 lineto +520.82985 481.25679 lineto +522.01826 485.77274 lineto +523.2015 481.25679 lineto +524.15223 481.25679 lineto +522.6383 487.04382 lineto +521.51706 487.04382 lineto +520.27181 482.30052 lineto +519.0214 487.04382 lineto +517.90016 487.04382 lineto +516.38623 481.25679 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +528.22899 484.1348 moveto +527.46083 484.13481 526.92863 484.22265 526.63239 484.39832 curveto +526.33615 484.574 526.18803 484.87369 526.18803 485.29738 curveto +526.18803 485.63496 526.29826 485.90364 526.51872 486.10343 curveto +526.74262 486.29978 527.04575 486.39795 527.42811 486.39795 curveto +527.95514 486.39795 528.37711 486.21194 528.69402 485.83991 curveto +529.01437 485.46445 529.17455 484.96669 529.17455 484.34665 curveto +529.17455 484.1348 lineto +528.22899 484.1348 lineto +530.12528 483.74211 moveto +530.12528 487.04382 lineto +529.17455 487.04382 lineto +529.17455 486.16543 lineto +528.95753 486.51679 528.68713 486.77686 528.36333 486.94565 curveto +528.03953 487.11099 527.6434 487.19367 527.17492 487.19367 curveto +526.58244 487.19367 526.11052 487.02832 525.75917 486.69763 curveto +525.41126 486.3635 525.2373 485.91742 525.2373 485.35938 curveto +525.2373 484.70834 525.45431 484.21748 525.88834 483.88679 curveto +526.32581 483.5561 526.97685 483.39076 527.84147 483.39076 curveto +529.17455 483.39076 lineto +529.17455 483.29775 lineto +529.17455 482.86028 529.02987 482.52271 528.74052 482.28502 curveto +528.45461 482.0439 528.05159 481.92334 527.53145 481.92333 curveto +527.20076 481.92334 526.87868 481.96295 526.56522 482.04217 curveto +526.25175 482.1214 525.95035 482.24024 525.661 482.39869 curveto +525.661 481.5203 lineto +526.0089 481.38597 526.34648 481.28607 526.67373 481.22062 curveto +527.00097 481.15173 527.3196 481.11728 527.62962 481.11728 curveto +528.46667 481.11728 529.09188 481.3343 529.50524 481.76832 curveto +529.91859 482.20235 530.12527 482.86028 530.12528 483.74211 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +532.08874 481.25679 moveto +533.03946 481.25679 lineto +533.03946 487.04382 lineto +532.08874 487.04382 lineto +532.08874 481.25679 lineto +532.08874 479.00398 moveto +533.03946 479.00398 lineto +533.03946 480.20789 lineto +532.08874 480.20789 lineto +532.08874 479.00398 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +535.96398 479.61368 moveto +535.96398 481.25679 lineto +537.92227 481.25679 lineto +537.92227 481.99567 lineto +535.96398 481.99567 lineto +535.96398 485.1372 lineto +535.96398 485.60912 536.02771 485.91225 536.15516 486.04659 curveto +536.28606 486.18094 536.54957 486.24811 536.94571 486.24811 curveto +537.92227 486.24811 lineto +537.92227 487.04382 lineto +536.94571 487.04382 lineto +536.212 487.04382 535.70563 486.90776 535.42662 486.63563 curveto +535.1476 486.36006 535.00809 485.86058 535.00809 485.1372 curveto +535.00809 481.99567 lineto +534.31054 481.99567 lineto +534.31054 481.25679 lineto +535.00809 481.25679 lineto +535.00809 479.61368 lineto +535.96398 479.61368 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +539.17786 481.25679 moveto +540.12858 481.25679 lineto +540.12858 487.04382 lineto +539.17786 487.04382 lineto +539.17786 481.25679 lineto +539.17786 479.00398 moveto +540.12858 479.00398 lineto +540.12858 480.20789 lineto +539.17786 480.20789 lineto +539.17786 479.00398 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +546.92318 483.55093 moveto +546.92318 487.04382 lineto +545.97245 487.04382 lineto +545.97245 483.58194 lineto +545.97245 483.03424 545.86567 482.62432 545.6521 482.35219 curveto +545.43853 482.08007 545.11817 481.944 544.69104 481.944 curveto +544.17778 481.944 543.77303 482.10762 543.4768 482.43486 curveto +543.18055 482.76211 543.03243 483.20819 543.03243 483.77311 curveto +543.03243 487.04382 lineto +542.07654 487.04382 lineto +542.07654 481.25679 lineto +543.03243 481.25679 lineto +543.03243 482.15585 lineto +543.25978 481.80794 543.52674 481.54787 543.83332 481.37563 curveto +544.14334 481.2034 544.49986 481.11728 544.90289 481.11728 curveto +545.5677 481.11728 546.07062 481.32396 546.41165 481.73732 curveto +546.75267 482.14724 546.92318 482.75178 546.92318 483.55093 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +552.63788 484.08313 moveto +552.63787 483.39421 552.49492 482.86028 552.20902 482.48137 curveto +551.92655 482.10246 551.52869 481.913 551.01544 481.913 curveto +550.50563 481.913 550.10777 482.10246 549.82187 482.48137 curveto +549.5394 482.86028 549.39817 483.39421 549.39817 484.08313 curveto +549.39817 484.76863 549.5394 485.30083 549.82187 485.67974 curveto +550.10777 486.05865 550.50563 486.24811 551.01544 486.24811 curveto +551.52869 486.24811 551.92655 486.05865 552.20902 485.67974 curveto +552.49492 485.30083 552.63787 484.76863 552.63788 484.08313 curveto +553.58861 486.32561 moveto +553.5886 487.31078 553.36986 488.04277 552.9324 488.52158 curveto +552.49492 489.00384 551.82493 489.24496 550.92244 489.24496 curveto +550.5883 489.24496 550.27311 489.21913 549.97688 489.16746 curveto +549.68063 489.11923 549.393 489.04345 549.11399 488.94011 curveto +549.11399 488.01522 lineto +549.393 488.16678 549.66858 488.27873 549.94071 488.35107 curveto +550.21283 488.42341 550.49013 488.45958 550.77259 488.45958 curveto +551.39607 488.45958 551.86283 488.29596 552.17285 487.96872 curveto +552.48286 487.64492 552.63787 487.15405 552.63788 486.49612 curveto +552.63788 486.02593 lineto +552.44153 486.36695 552.19007 486.62185 551.8835 486.79064 curveto +551.57692 486.95943 551.21006 487.04382 550.78293 487.04382 curveto +550.07332 487.04382 549.50151 486.77342 549.06748 486.2326 curveto +548.63346 485.69179 548.41644 484.9753 548.41644 484.08313 curveto +548.41644 483.18753 548.63346 482.46931 549.06748 481.9285 curveto +549.50151 481.38769 550.07332 481.11728 550.78293 481.11728 curveto +551.21006 481.11728 551.57692 481.20168 551.8835 481.37046 curveto +552.19007 481.53926 552.44153 481.79416 552.63788 482.13518 curveto +552.63788 481.25679 lineto +553.58861 481.25679 lineto +553.58861 486.32561 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +561.84547 479.00398 moveto +561.84547 479.79453 lineto +560.93607 479.79453 lineto +560.59505 479.79454 560.35737 479.86343 560.22303 480.00121 curveto +560.09213 480.139 560.02668 480.38702 560.02668 480.74526 curveto +560.02668 481.25679 lineto +561.59228 481.25679 lineto +561.59228 481.99567 lineto +560.02668 481.99567 lineto +560.02668 487.04382 lineto +559.07079 487.04382 lineto +559.07079 481.99567 lineto +558.1614 481.99567 lineto +558.1614 481.25679 lineto +559.07079 481.25679 lineto +559.07079 480.85376 lineto +559.07079 480.20962 559.22063 479.74114 559.52032 479.44834 curveto +559.82 479.15211 560.29536 479.00399 560.94641 479.00398 curveto +561.84547 479.00398 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +564.87849 481.92333 moveto +564.36868 481.92334 563.96565 482.12313 563.66942 482.5227 curveto +563.37317 482.91884 563.22505 483.4631 563.22505 484.15547 curveto +563.22505 484.84785 563.37145 485.39383 563.66425 485.79341 curveto +563.96049 486.18955 564.36524 486.38762 564.87849 486.38761 curveto +565.38485 486.38762 565.78616 486.18783 566.0824 485.78824 curveto +566.37864 485.38866 566.52676 484.84441 566.52676 484.15547 curveto +566.52676 483.46999 566.37864 482.92745 566.0824 482.52787 curveto +565.78616 482.12485 565.38485 481.92334 564.87849 481.92333 curveto +564.87849 481.11728 moveto +565.70521 481.11728 566.35453 481.38597 566.82645 481.92333 curveto +567.29836 482.4607 567.53432 483.20475 567.53433 484.15547 curveto +567.53432 485.10276 567.29836 485.8468 566.82645 486.38761 curveto +566.35453 486.92498 565.70521 487.19367 564.87849 487.19367 curveto +564.04833 487.19367 563.39729 486.92498 562.92537 486.38761 curveto +562.45689 485.8468 562.22266 485.10276 562.22266 484.15547 curveto +562.22266 483.20475 562.45689 482.4607 562.92537 481.92333 curveto +563.39729 481.38597 564.04833 481.11728 564.87849 481.11728 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +572.45847 482.14551 moveto +572.35169 482.08351 572.23457 482.03873 572.10712 482.01117 curveto +571.98311 481.98017 571.84532 481.96467 571.69376 481.96467 curveto +571.15639 481.96467 570.74303 482.14035 570.45368 482.4917 curveto +570.16777 482.83962 570.02482 483.34081 570.02482 483.9953 curveto +570.02482 487.04382 lineto +569.06893 487.04382 lineto +569.06893 481.25679 lineto +570.02482 481.25679 lineto +570.02482 482.15585 lineto +570.22461 481.80449 570.48468 481.54442 570.80504 481.37563 curveto +571.12539 481.2034 571.51463 481.11728 571.97278 481.11728 curveto +572.03822 481.11728 572.11056 481.12245 572.18979 481.13278 curveto +572.26901 481.13968 572.35685 481.15173 572.45331 481.16895 curveto +572.45847 482.14551 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +579.76461 479.00398 moveto +579.76461 479.79453 lineto +578.85522 479.79453 lineto +578.51419 479.79454 578.27651 479.86343 578.14217 480.00121 curveto +578.01127 480.139 577.94582 480.38702 577.94583 480.74526 curveto +577.94583 481.25679 lineto +579.51143 481.25679 lineto +579.51143 481.99567 lineto +577.94583 481.99567 lineto +577.94583 487.04382 lineto +576.98993 487.04382 lineto +576.98993 481.99567 lineto +576.08054 481.99567 lineto +576.08054 481.25679 lineto +576.98993 481.25679 lineto +576.98993 480.85376 lineto +576.98993 480.20962 577.13977 479.74114 577.43946 479.44834 curveto +577.73914 479.15211 578.21451 479.00399 578.86555 479.00398 curveto +579.76461 479.00398 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +582.79763 481.92333 moveto +582.28782 481.92334 581.88479 482.12313 581.58855 482.5227 curveto +581.29231 482.91884 581.14419 483.4631 581.14419 484.15547 curveto +581.14419 484.84785 581.29059 485.39383 581.58338 485.79341 curveto +581.87962 486.18955 582.28437 486.38762 582.79763 486.38761 curveto +583.30399 486.38762 583.70529 486.18783 584.00154 485.78824 curveto +584.29778 485.38866 584.4459 484.84441 584.4459 484.15547 curveto +584.4459 483.46999 584.29778 482.92745 584.00154 482.52787 curveto +583.70529 482.12485 583.30399 481.92334 582.79763 481.92333 curveto +582.79763 481.11728 moveto +583.62434 481.11728 584.27366 481.38597 584.74559 481.92333 curveto +585.2175 482.4607 585.45346 483.20475 585.45346 484.15547 curveto +585.45346 485.10276 585.2175 485.8468 584.74559 486.38761 curveto +584.27366 486.92498 583.62434 487.19367 582.79763 487.19367 curveto +581.96746 487.19367 581.31642 486.92498 580.8445 486.38761 curveto +580.37603 485.8468 580.14179 485.10276 580.14179 484.15547 curveto +580.14179 483.20475 580.37603 482.4607 580.8445 481.92333 curveto +581.31642 481.38597 581.96746 481.11728 582.79763 481.11728 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +590.37762 482.14551 moveto +590.27083 482.08351 590.15371 482.03873 590.02626 482.01117 curveto +589.90225 481.98017 589.76446 481.96467 589.6129 481.96467 curveto +589.07553 481.96467 588.66217 482.14035 588.37282 482.4917 curveto +588.08691 482.83962 587.94396 483.34081 587.94396 483.9953 curveto +587.94396 487.04382 lineto +586.98807 487.04382 lineto +586.98807 481.25679 lineto +587.94396 481.25679 lineto +587.94396 482.15585 lineto +588.14375 481.80449 588.40382 481.54442 588.72418 481.37563 curveto +589.04453 481.2034 589.43378 481.11728 589.89192 481.11728 curveto +589.95737 481.11728 590.0297 481.12245 590.10893 481.13278 curveto +590.18816 481.13968 590.276 481.15173 590.37245 481.16895 curveto +590.37762 482.14551 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +591.34902 479.00398 moveto +592.30491 479.00398 lineto +592.30491 483.75245 lineto +595.14159 481.25679 lineto +596.35583 481.25679 lineto +593.28664 483.96429 lineto +596.48501 487.04382 lineto +595.24493 487.04382 lineto +592.30491 484.21748 lineto +592.30491 487.04382 lineto +591.34902 487.04382 lineto +591.34902 479.00398 lineto +fill +grestore +grestore +gsave [1.037105 0 0 0.964223 0 0] concat +gsave +0 0 0 setrgbcolor +newpath +447.57735 510.57547 moveto +449.13262 510.57547 lineto +451.10125 515.82514 lineto +453.0802 510.57547 lineto +454.63547 510.57547 lineto +454.63547 518.28979 lineto +453.61757 518.28979 lineto +453.61757 511.51586 lineto +451.62828 516.80687 lineto +450.57938 516.80687 lineto +448.59009 511.51586 lineto +448.59009 518.28979 lineto +447.57735 518.28979 lineto +447.57735 510.57547 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +459.30127 515.38078 moveto +458.5331 515.38078 458.0009 515.46862 457.70467 515.64429 curveto +457.40842 515.81997 457.2603 516.11966 457.2603 516.54335 curveto +457.2603 516.88093 457.37053 517.14961 457.59099 517.3494 curveto +457.81489 517.54575 458.11802 517.64392 458.50038 517.64392 curveto +459.02741 517.64392 459.44938 517.45791 459.7663 517.08588 curveto +460.08665 516.71042 460.24682 516.21266 460.24683 515.59262 curveto +460.24683 515.38078 lineto +459.30127 515.38078 lineto +461.19755 514.98808 moveto +461.19755 518.28979 lineto +460.24683 518.28979 lineto +460.24683 517.41141 lineto +460.02981 517.76276 459.7594 518.02283 459.43561 518.19162 curveto +459.11181 518.35697 458.71567 518.43964 458.2472 518.43964 curveto +457.65472 518.43964 457.1828 518.27429 456.83144 517.94361 curveto +456.48353 517.60947 456.30958 517.16339 456.30958 516.60535 curveto +456.30958 515.95432 456.52659 515.46345 456.96062 515.13276 curveto +457.39809 514.80208 458.04913 514.63673 458.91374 514.63673 curveto +460.24683 514.63673 lineto +460.24683 514.54372 lineto +460.24682 514.10625 460.10215 513.76868 459.8128 513.53099 curveto +459.52689 513.28987 459.12386 513.16931 458.60372 513.1693 curveto +458.27303 513.16931 457.95096 513.20892 457.63749 513.28814 curveto +457.32403 513.36738 457.02262 513.48622 456.73327 513.64467 curveto +456.73327 512.76628 lineto +457.08118 512.63194 457.41876 512.53205 457.746 512.46659 curveto +458.07324 512.3977 458.39187 512.36326 458.7019 512.36325 curveto +459.53895 512.36326 460.16415 512.58027 460.57752 513.01429 curveto +460.99087 513.44832 461.19755 514.10625 461.19755 514.98808 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +466.96909 515.32911 moveto +466.96908 514.64018 466.82613 514.10625 466.54023 513.72734 curveto +466.25776 513.34843 465.8599 513.15897 465.34665 513.15897 curveto +464.83684 513.15897 464.43898 513.34843 464.15308 513.72734 curveto +463.87061 514.10625 463.72938 514.64018 463.72938 515.32911 curveto +463.72938 516.0146 463.87061 516.5468 464.15308 516.92571 curveto +464.43898 517.30462 464.83684 517.49408 465.34665 517.49408 curveto +465.8599 517.49408 466.25776 517.30462 466.54023 516.92571 curveto +466.82613 516.5468 466.96908 516.0146 466.96909 515.32911 curveto +467.91982 517.57158 moveto +467.91981 518.55676 467.70107 519.28875 467.26361 519.76756 curveto +466.82613 520.24981 466.15614 520.49093 465.25365 520.49094 curveto +464.91951 520.49093 464.60432 520.4651 464.30809 520.41343 curveto +464.01184 520.3652 463.72421 520.28942 463.4452 520.18608 curveto +463.4452 519.26119 lineto +463.72421 519.41275 463.99979 519.52471 464.27192 519.59704 curveto +464.54404 519.66938 464.82134 519.70555 465.1038 519.70555 curveto +465.72728 519.70555 466.19403 519.54193 466.50406 519.21469 curveto +466.81407 518.89089 466.96908 518.40002 466.96909 517.74209 curveto +466.96909 517.2719 lineto +466.77274 517.61292 466.52128 517.86782 466.21471 518.03661 curveto +465.90813 518.2054 465.54127 518.28979 465.11414 518.28979 curveto +464.40453 518.28979 463.83272 518.01939 463.39869 517.47858 curveto +462.96467 516.93777 462.74765 516.22128 462.74765 515.32911 curveto +462.74765 514.4335 462.96467 513.71529 463.39869 513.17447 curveto +463.83272 512.63366 464.40453 512.36326 465.11414 512.36325 curveto +465.54127 512.36326 465.90813 512.44765 466.21471 512.61643 curveto +466.52128 512.78523 466.77274 513.04013 466.96909 513.38115 curveto +466.96909 512.50276 lineto +467.91982 512.50276 lineto +467.91982 517.57158 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +474.82809 515.1586 moveto +474.82809 515.62363 lineto +470.45681 515.62363 lineto +470.49814 516.27811 470.69449 516.77759 471.04585 517.12205 curveto +471.40065 517.46308 471.89323 517.63359 472.52361 517.63359 curveto +472.88874 517.63359 473.24182 517.58881 473.58284 517.49924 curveto +473.9273 517.40968 474.26833 517.27534 474.60591 517.09622 curveto +474.60591 517.99528 lineto +474.26488 518.13995 473.91525 518.25018 473.55701 518.32596 curveto +473.19876 518.40175 472.83535 518.43964 472.46677 518.43964 curveto +471.5436 518.43964 470.81161 518.17095 470.2708 517.63359 curveto +469.73343 517.09622 469.46475 516.3694 469.46475 515.45311 curveto +469.46475 514.50584 469.71965 513.7549 470.22946 513.2003 curveto +470.74272 512.64227 471.43337 512.36326 472.30143 512.36325 curveto +473.07992 512.36326 473.69479 512.61472 474.14605 513.11763 curveto +474.60074 513.61711 474.82808 514.29743 474.82809 515.1586 curveto +473.87736 514.87958 moveto +473.87047 514.35944 473.72407 513.94436 473.43817 513.63433 curveto +473.1557 513.32432 472.78023 513.16931 472.31176 513.1693 curveto +471.78128 513.16931 471.35587 513.31915 471.03551 513.61883 curveto +470.7186 513.91852 470.53604 514.34049 470.48781 514.88474 curveto +473.87736 514.87958 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +481.199 514.79691 moveto +481.199 518.28979 lineto +480.24827 518.28979 lineto +480.24827 514.82791 lineto +480.24826 514.28021 480.14148 513.8703 479.92791 513.59816 curveto +479.71434 513.32604 479.39399 513.18998 478.96685 513.18997 curveto +478.4536 513.18998 478.04885 513.3536 477.75261 513.68083 curveto +477.45637 514.00808 477.30825 514.45417 477.30825 515.01909 curveto +477.30825 518.28979 lineto +476.35235 518.28979 lineto +476.35235 512.50276 lineto +477.30825 512.50276 lineto +477.30825 513.40182 lineto +477.53559 513.05391 477.80255 512.79384 478.10913 512.6216 curveto +478.41915 512.44937 478.77567 512.36326 479.1787 512.36325 curveto +479.84352 512.36326 480.34644 512.56994 480.68746 512.98329 curveto +481.02848 513.39321 481.19899 513.99775 481.199 514.79691 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +484.04601 510.85965 moveto +484.04601 512.50276 lineto +486.0043 512.50276 lineto +486.0043 513.24164 lineto +484.04601 513.24164 lineto +484.04601 516.38317 lineto +484.04601 516.85509 484.10973 517.15822 484.23719 517.29256 curveto +484.36808 517.42691 484.6316 517.49408 485.02774 517.49408 curveto +486.0043 517.49408 lineto +486.0043 518.28979 lineto +485.02774 518.28979 lineto +484.29402 518.28979 483.78766 518.15373 483.50864 517.8816 curveto +483.22962 517.60603 483.09012 517.10655 483.09012 516.38317 curveto +483.09012 513.24164 lineto +482.39257 513.24164 lineto +482.39257 512.50276 lineto +483.09012 512.50276 lineto +483.09012 510.85965 lineto +484.04601 510.85965 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +489.88988 515.38078 moveto +489.12172 515.38078 488.58952 515.46862 488.29328 515.64429 curveto +487.99704 515.81997 487.84892 516.11966 487.84892 516.54335 curveto +487.84892 516.88093 487.95914 517.14961 488.1796 517.3494 curveto +488.40351 517.54575 488.70664 517.64392 489.089 517.64392 curveto +489.61603 517.64392 490.038 517.45791 490.35491 517.08588 curveto +490.67526 516.71042 490.83544 516.21266 490.83544 515.59262 curveto +490.83544 515.38078 lineto +489.88988 515.38078 lineto +491.78617 514.98808 moveto +491.78617 518.28979 lineto +490.83544 518.28979 lineto +490.83544 517.41141 lineto +490.61842 517.76276 490.34802 518.02283 490.02422 518.19162 curveto +489.70042 518.35697 489.30428 518.43964 488.83581 518.43964 curveto +488.24333 518.43964 487.77141 518.27429 487.42006 517.94361 curveto +487.07214 517.60947 486.89819 517.16339 486.89819 516.60535 curveto +486.89819 515.95432 487.1152 515.46345 487.54923 515.13276 curveto +487.9867 514.80208 488.63774 514.63673 489.50236 514.63673 curveto +490.83544 514.63673 lineto +490.83544 514.54372 lineto +490.83544 514.10625 490.69076 513.76868 490.40141 513.53099 curveto +490.1155 513.28987 489.71248 513.16931 489.19234 513.1693 curveto +488.86165 513.16931 488.53957 513.20892 488.22611 513.28814 curveto +487.91264 513.36738 487.61123 513.48622 487.32188 513.64467 curveto +487.32188 512.76628 lineto +487.66979 512.63194 488.00737 512.53205 488.33461 512.46659 curveto +488.66186 512.3977 488.98049 512.36326 489.29051 512.36325 curveto +490.12756 512.36326 490.75276 512.58027 491.16613 513.01429 curveto +491.57948 513.44832 491.78616 514.10625 491.78617 514.98808 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +497.36136 516.97738 moveto +498.45159 516.97738 lineto +498.45159 518.28979 lineto +497.36136 518.28979 lineto +497.36136 516.97738 lineto +497.36136 512.81795 moveto +498.45159 512.81795 lineto +498.45159 514.13036 lineto +497.36136 514.13036 lineto +497.36136 512.81795 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +504.98268 517.42174 moveto +504.98268 520.49094 lineto +504.02678 520.49094 lineto +504.02678 512.50276 lineto +504.98268 512.50276 lineto +504.98268 513.38115 lineto +505.18246 513.03669 505.43392 512.78178 505.73706 512.61643 curveto +506.04363 512.44765 506.40876 512.36326 506.83246 512.36325 curveto +507.53517 512.36326 508.10526 512.64227 508.54274 513.2003 curveto +508.98365 513.75834 509.2041 514.49206 509.20411 515.40144 curveto +509.2041 516.31084 508.98365 517.04455 508.54274 517.60258 curveto +508.10526 518.16062 507.53517 518.43964 506.83246 518.43964 curveto +506.40876 518.43964 506.04363 518.35697 505.73706 518.19162 curveto +505.43392 518.02283 505.18246 517.76621 504.98268 517.42174 curveto +508.21721 515.40144 moveto +508.21721 514.70218 508.07253 514.15448 507.78319 513.75834 curveto +507.49728 513.35876 507.10286 513.15897 506.59994 513.15897 curveto +506.09702 513.15897 505.70088 513.35876 505.41154 513.75834 curveto +505.12563 514.15448 504.98267 514.70218 504.98268 515.40144 curveto +504.98267 516.10071 505.12563 516.65014 505.41154 517.04972 curveto +505.70088 517.44585 506.09702 517.64392 506.59994 517.64392 curveto +507.10286 517.64392 507.49728 517.44585 507.78319 517.04972 curveto +508.07253 516.65014 508.21721 516.10071 508.21721 515.40144 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +514.13342 513.39148 moveto +514.02664 513.32948 513.90952 513.2847 513.78207 513.25714 curveto +513.65806 513.22614 513.52027 513.21064 513.36871 513.21064 curveto +512.83134 513.21064 512.41798 513.38632 512.12863 513.73767 curveto +511.84272 514.08559 511.69977 514.58678 511.69977 515.24127 curveto +511.69977 518.28979 lineto +510.74388 518.28979 lineto +510.74388 512.50276 lineto +511.69977 512.50276 lineto +511.69977 513.40182 lineto +511.89956 513.05047 512.15963 512.79039 512.47999 512.6216 curveto +512.80034 512.44937 513.18958 512.36326 513.64773 512.36325 curveto +513.71317 512.36326 513.78551 512.36842 513.86474 512.37875 curveto +513.94396 512.38565 514.0318 512.3977 514.12826 512.41492 curveto +514.13342 513.39148 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +517.15612 513.1693 moveto +516.64631 513.16931 516.24328 513.3691 515.94704 513.76867 curveto +515.6508 514.16481 515.50268 514.70907 515.50268 515.40144 curveto +515.50268 516.09382 515.64908 516.6398 515.94187 517.03938 curveto +516.23811 517.43552 516.64286 517.63359 517.15612 517.63359 curveto +517.66248 517.63359 518.06378 517.4338 518.36003 517.03421 curveto +518.65627 516.63464 518.80439 516.09038 518.80439 515.40144 curveto +518.80439 514.71596 518.65627 514.17343 518.36003 513.77384 curveto +518.06378 513.37082 517.66248 513.16931 517.15612 513.1693 curveto +517.15612 512.36325 moveto +517.98283 512.36326 518.63215 512.63194 519.10408 513.1693 curveto +519.57599 513.70667 519.81195 514.45072 519.81195 515.40144 curveto +519.81195 516.34873 519.57599 517.09278 519.10408 517.63359 curveto +518.63215 518.17095 517.98283 518.43964 517.15612 518.43964 curveto +516.32595 518.43964 515.67491 518.17095 515.20299 517.63359 curveto +514.73452 517.09278 514.50028 516.34873 514.50028 515.40144 curveto +514.50028 514.45072 514.73452 513.70667 515.20299 513.1693 curveto +515.67491 512.63194 516.32595 512.36326 517.15612 512.36325 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +525.54732 512.72494 moveto +525.54732 513.61366 lineto +525.27863 513.46555 525.00822 513.35532 524.7361 513.28298 curveto +524.46741 513.2072 524.19528 513.16931 523.91972 513.1693 curveto +523.30312 513.16931 522.82431 513.36565 522.48329 513.75834 curveto +522.14227 514.14759 521.97176 514.69529 521.97176 515.40144 curveto +521.97176 516.1076 522.14227 516.65703 522.48329 517.04972 curveto +522.82431 517.43896 523.30312 517.63359 523.91972 517.63359 curveto +524.19528 517.63359 524.46741 517.59742 524.7361 517.52508 curveto +525.00822 517.4493 525.27863 517.33735 525.54732 517.18922 curveto +525.54732 518.06761 lineto +525.28208 518.19162 525.0065 518.28463 524.7206 518.34663 curveto +524.43813 518.40864 524.13673 518.43964 523.81638 518.43964 curveto +522.94487 518.43964 522.2525 518.16579 521.73924 517.61809 curveto +521.22599 517.07039 520.96936 516.33151 520.96936 515.40144 curveto +520.96936 514.45761 521.22771 513.71529 521.74441 513.17447 curveto +522.26455 512.63366 522.97588 512.36326 523.87838 512.36325 curveto +524.17117 512.36326 524.45708 512.39426 524.7361 512.45626 curveto +525.01511 512.51482 525.28552 512.60438 525.54732 512.72494 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +532.16107 515.1586 moveto +532.16107 515.62363 lineto +527.78979 515.62363 lineto +527.83113 516.27811 528.02747 516.77759 528.37883 517.12205 curveto +528.73363 517.46308 529.22622 517.63359 529.85659 517.63359 curveto +530.22172 517.63359 530.5748 517.58881 530.91583 517.49924 curveto +531.26029 517.40968 531.60131 517.27534 531.93889 517.09622 curveto +531.93889 517.99528 lineto +531.59786 518.13995 531.24823 518.25018 530.88999 518.32596 curveto +530.53174 518.40175 530.16833 518.43964 529.79975 518.43964 curveto +528.87658 518.43964 528.14459 518.17095 527.60378 517.63359 curveto +527.06641 517.09622 526.79773 516.3694 526.79773 515.45311 curveto +526.79773 514.50584 527.05263 513.7549 527.56245 513.2003 curveto +528.0757 512.64227 528.76635 512.36326 529.63441 512.36325 curveto +530.4129 512.36326 531.02777 512.61472 531.47903 513.11763 curveto +531.93372 513.61711 532.16107 514.29743 532.16107 515.1586 curveto +531.21034 514.87958 moveto +531.20345 514.35944 531.05705 513.94436 530.77115 513.63433 curveto +530.48868 513.32432 530.11322 513.16931 529.64474 513.1693 curveto +529.11426 513.16931 528.68885 513.31915 528.3685 513.61883 curveto +528.05159 513.91852 527.86902 514.34049 527.8208 514.88474 curveto +531.21034 514.87958 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +537.41074 512.67327 moveto +537.41074 513.57233 lineto +537.14205 513.43455 536.86303 513.33121 536.57369 513.26231 curveto +536.28433 513.19342 535.98464 513.15897 535.67463 513.15897 curveto +535.20271 513.15897 534.84791 513.23131 534.61023 513.37598 curveto +534.37599 513.52066 534.25887 513.73768 534.25887 514.02702 curveto +534.25887 514.24749 534.34326 514.42144 534.51205 514.54889 curveto +534.68084 514.6729 535.02014 514.79174 535.52995 514.90541 curveto +535.85547 514.97775 lineto +536.53062 515.12243 537.00943 515.32739 537.2919 515.59262 curveto +537.5778 515.85442 537.72075 516.22128 537.72076 516.69319 curveto +537.72075 517.23056 537.50718 517.65598 537.08005 517.96944 curveto +536.65635 518.28291 536.07248 518.43964 535.32844 518.43964 curveto +535.01842 518.43964 534.69462 518.40864 534.35704 518.34663 curveto +534.02291 518.28807 533.66983 518.19851 533.29781 518.07795 curveto +533.29781 517.09622 lineto +533.64917 517.27879 533.99535 517.41657 534.33638 517.50958 curveto +534.6774 517.59914 535.01497 517.64392 535.34911 517.64392 curveto +535.79691 517.64392 536.14138 517.56814 536.38251 517.41657 curveto +536.62363 517.26156 536.74419 517.04455 536.7442 516.76553 curveto +536.74419 516.50718 536.65635 516.30912 536.48068 516.17133 curveto +536.30844 516.03354 535.92781 515.90092 535.33877 515.77347 curveto +535.00809 515.69596 lineto +534.41905 515.57196 533.99363 515.3825 533.73184 515.12759 curveto +533.47004 514.86925 533.33915 514.51617 533.33915 514.06836 curveto +533.33915 513.52411 533.53205 513.10386 533.91785 512.80761 curveto +534.30365 512.51138 534.85135 512.36326 535.56095 512.36325 curveto +535.91231 512.36326 536.24299 512.38909 536.55302 512.44076 curveto +536.86303 512.49243 537.14894 512.56994 537.41074 512.67327 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +542.9291 512.67327 moveto +542.9291 513.57233 lineto +542.66041 513.43455 542.38139 513.33121 542.09204 513.26231 curveto +541.80269 513.19342 541.503 513.15897 541.19298 513.15897 curveto +540.72106 513.15897 540.36626 513.23131 540.12858 513.37598 curveto +539.89434 513.52066 539.77723 513.73768 539.77723 514.02702 curveto +539.77723 514.24749 539.86162 514.42144 540.03041 514.54889 curveto +540.1992 514.6729 540.5385 514.79174 541.04831 514.90541 curveto +541.37383 514.97775 lineto +542.04898 515.12243 542.52779 515.32739 542.81025 515.59262 curveto +543.09616 515.85442 543.23911 516.22128 543.23911 516.69319 curveto +543.23911 517.23056 543.02554 517.65598 542.59841 517.96944 curveto +542.17471 518.28291 541.59084 518.43964 540.8468 518.43964 curveto +540.53677 518.43964 540.21298 518.40864 539.8754 518.34663 curveto +539.54127 518.28807 539.18819 518.19851 538.81617 518.07795 curveto +538.81617 517.09622 lineto +539.16752 517.27879 539.51371 517.41657 539.85473 517.50958 curveto +540.19575 517.59914 540.53333 517.64392 540.86746 517.64392 curveto +541.31527 517.64392 541.65973 517.56814 541.90086 517.41657 curveto +542.14199 517.26156 542.26255 517.04455 542.26255 516.76553 curveto +542.26255 516.50718 542.17471 516.30912 541.99904 516.17133 curveto +541.8268 516.03354 541.44616 515.90092 540.85713 515.77347 curveto +540.52644 515.69596 lineto +539.9374 515.57196 539.51199 515.3825 539.25019 515.12759 curveto +538.9884 514.86925 538.8575 514.51617 538.8575 514.06836 curveto +538.8575 513.52411 539.0504 513.10386 539.43621 512.80761 curveto +539.82201 512.51138 540.36971 512.36326 541.07931 512.36325 curveto +541.43066 512.36326 541.76135 512.38909 542.07137 512.44076 curveto +542.38139 512.49243 542.6673 512.56994 542.9291 512.67327 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +552.93756 514.79691 moveto +552.93756 518.28979 lineto +551.98684 518.28979 lineto +551.98684 514.82791 lineto +551.98683 514.28021 551.88005 513.8703 551.66648 513.59816 curveto +551.45291 513.32604 551.13256 513.18998 550.70542 513.18997 curveto +550.19216 513.18998 549.78742 513.3536 549.49118 513.68083 curveto +549.19493 514.00808 549.04681 514.45417 549.04682 515.01909 curveto +549.04682 518.28979 lineto +548.09092 518.28979 lineto +548.09092 510.24995 lineto +549.04682 510.24995 lineto +549.04682 513.40182 lineto +549.27416 513.05391 549.54112 512.79384 549.8477 512.6216 curveto +550.15772 512.44937 550.51424 512.36326 550.91727 512.36325 curveto +551.58208 512.36326 552.085 512.56994 552.42603 512.98329 curveto +552.76705 513.39321 552.93756 513.99775 552.93756 514.79691 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +557.47419 515.38078 moveto +556.70602 515.38078 556.17382 515.46862 555.87758 515.64429 curveto +555.58134 515.81997 555.43322 516.11966 555.43322 516.54335 curveto +555.43322 516.88093 555.54345 517.14961 555.76391 517.3494 curveto +555.98781 517.54575 556.29094 517.64392 556.6733 517.64392 curveto +557.20033 517.64392 557.6223 517.45791 557.93922 517.08588 curveto +558.25956 516.71042 558.41974 516.21266 558.41975 515.59262 curveto +558.41975 515.38078 lineto +557.47419 515.38078 lineto +559.37047 514.98808 moveto +559.37047 518.28979 lineto +558.41975 518.28979 lineto +558.41975 517.41141 lineto +558.20273 517.76276 557.93232 518.02283 557.60853 518.19162 curveto +557.28473 518.35697 556.88859 518.43964 556.42012 518.43964 curveto +555.82763 518.43964 555.35572 518.27429 555.00436 517.94361 curveto +554.65645 517.60947 554.48249 517.16339 554.48249 516.60535 curveto +554.48249 515.95432 554.69951 515.46345 555.13354 515.13276 curveto +555.57101 514.80208 556.22205 514.63673 557.08666 514.63673 curveto +558.41975 514.63673 lineto +558.41975 514.54372 lineto +558.41974 514.10625 558.27507 513.76868 557.98572 513.53099 curveto +557.69981 513.28987 557.29678 513.16931 556.77664 513.1693 curveto +556.44595 513.16931 556.12387 513.20892 555.81041 513.28814 curveto +555.49695 513.36738 555.19554 513.48622 554.90619 513.64467 curveto +554.90619 512.76628 lineto +555.2541 512.63194 555.59167 512.53205 555.91892 512.46659 curveto +556.24616 512.3977 556.56479 512.36326 556.87481 512.36325 curveto +557.71186 512.36326 558.33707 512.58027 558.75043 513.01429 curveto +559.16379 513.44832 559.37047 514.10625 559.37047 514.98808 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +565.02317 512.67327 moveto +565.02317 513.57233 lineto +564.75448 513.43455 564.47546 513.33121 564.18611 513.26231 curveto +563.89676 513.19342 563.59707 513.15897 563.28706 513.15897 curveto +562.81513 513.15897 562.46033 513.23131 562.22265 513.37598 curveto +561.98842 513.52066 561.8713 513.73768 561.8713 514.02702 curveto +561.8713 514.24749 561.95569 514.42144 562.12448 514.54889 curveto +562.29327 514.6729 562.63257 514.79174 563.14238 514.90541 curveto +563.4679 514.97775 lineto +564.14305 515.12243 564.62186 515.32739 564.90432 515.59262 curveto +565.19023 515.85442 565.33318 516.22128 565.33319 516.69319 curveto +565.33318 517.23056 565.11961 517.65598 564.69248 517.96944 curveto +564.26878 518.28291 563.68491 518.43964 562.94087 518.43964 curveto +562.63084 518.43964 562.30705 518.40864 561.96947 518.34663 curveto +561.63534 518.28807 561.28226 518.19851 560.91024 518.07795 curveto +560.91024 517.09622 lineto +561.26159 517.27879 561.60778 517.41657 561.9488 517.50958 curveto +562.28982 517.59914 562.6274 517.64392 562.96153 517.64392 curveto +563.40934 517.64392 563.7538 517.56814 563.99493 517.41657 curveto +564.23606 517.26156 564.35662 517.04455 564.35662 516.76553 curveto +564.35662 516.50718 564.26878 516.30912 564.09311 516.17133 curveto +563.92087 516.03354 563.54023 515.90092 562.9512 515.77347 curveto +562.62051 515.69596 lineto +562.03147 515.57196 561.60606 515.3825 561.34426 515.12759 curveto +561.08247 514.86925 560.95157 514.51617 560.95157 514.06836 curveto +560.95157 513.52411 561.14447 513.10386 561.53028 512.80761 curveto +561.91608 512.51138 562.46378 512.36326 563.17338 512.36325 curveto +563.52473 512.36326 563.85542 512.38909 564.16544 512.44076 curveto +564.47546 512.49243 564.76137 512.56994 565.02317 512.67327 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +575.17114 515.1586 moveto +575.17114 515.62363 lineto +570.79986 515.62363 lineto +570.8412 516.27811 571.03754 516.77759 571.3889 517.12205 curveto +571.7437 517.46308 572.23629 517.63359 572.86666 517.63359 curveto +573.23179 517.63359 573.58487 517.58881 573.9259 517.49924 curveto +574.27036 517.40968 574.61138 517.27534 574.94896 517.09622 curveto +574.94896 517.99528 lineto +574.60793 518.13995 574.2583 518.25018 573.90006 518.32596 curveto +573.54181 518.40175 573.1784 518.43964 572.80983 518.43964 curveto +571.88665 518.43964 571.15466 518.17095 570.61385 517.63359 curveto +570.07648 517.09622 569.8078 516.3694 569.8078 515.45311 curveto +569.8078 514.50584 570.06271 513.7549 570.57252 513.2003 curveto +571.08577 512.64227 571.77642 512.36326 572.64448 512.36325 curveto +573.42297 512.36326 574.03784 512.61472 574.4891 513.11763 curveto +574.94379 513.61711 575.17114 514.29743 575.17114 515.1586 curveto +574.22042 514.87958 moveto +574.21352 514.35944 574.06712 513.94436 573.78122 513.63433 curveto +573.49875 513.32432 573.12329 513.16931 572.65482 513.1693 curveto +572.12433 513.16931 571.69892 513.31915 571.37857 513.61883 curveto +571.06166 513.91852 570.87909 514.34049 570.83087 514.88474 curveto +574.22042 514.87958 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +581.35604 512.50276 moveto +579.26341 515.31877 lineto +581.46455 518.28979 lineto +580.34331 518.28979 lineto +578.65887 516.01632 lineto +576.97443 518.28979 lineto +575.85319 518.28979 lineto +578.10084 515.26194 lineto +576.04437 512.50276 lineto +577.16561 512.50276 lineto +578.70021 514.56439 lineto +580.23481 512.50276 lineto +581.35604 512.50276 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +582.80796 512.50276 moveto +583.75869 512.50276 lineto +583.75869 518.28979 lineto +582.80796 518.28979 lineto +582.80796 512.50276 lineto +582.80796 510.24995 moveto +583.75869 510.24995 lineto +583.75869 511.45386 lineto +582.80796 511.45386 lineto +582.80796 510.24995 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +586.68321 510.85965 moveto +586.68321 512.50276 lineto +588.64151 512.50276 lineto +588.64151 513.24164 lineto +586.68321 513.24164 lineto +586.68321 516.38317 lineto +586.68321 516.85509 586.74694 517.15822 586.87439 517.29256 curveto +587.00529 517.42691 587.2688 517.49408 587.66494 517.49408 curveto +588.64151 517.49408 lineto +588.64151 518.28979 lineto +587.66494 518.28979 lineto +586.93123 518.28979 586.42486 518.15373 586.14585 517.8816 curveto +585.86683 517.60603 585.72732 517.10655 585.72732 516.38317 curveto +585.72732 513.24164 lineto +585.02977 513.24164 lineto +585.02977 512.50276 lineto +585.72732 512.50276 lineto +585.72732 510.85965 lineto +586.68321 510.85965 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +594.84707 515.1586 moveto +594.84707 515.62363 lineto +590.47579 515.62363 lineto +590.51712 516.27811 590.71347 516.77759 591.06483 517.12205 curveto +591.41963 517.46308 591.91221 517.63359 592.54259 517.63359 curveto +592.90772 517.63359 593.2608 517.58881 593.60182 517.49924 curveto +593.94628 517.40968 594.28731 517.27534 594.62489 517.09622 curveto +594.62489 517.99528 lineto +594.28386 518.13995 593.93423 518.25018 593.57599 518.32596 curveto +593.21774 518.40175 592.85433 518.43964 592.48575 518.43964 curveto +591.56258 518.43964 590.83059 518.17095 590.28978 517.63359 curveto +589.75241 517.09622 589.48373 516.3694 589.48373 515.45311 curveto +589.48373 514.50584 589.73863 513.7549 590.24844 513.2003 curveto +590.7617 512.64227 591.45235 512.36326 592.32041 512.36325 curveto +593.0989 512.36326 593.71377 512.61472 594.16503 513.11763 curveto +594.61972 513.61711 594.84706 514.29743 594.84707 515.1586 curveto +593.89634 514.87958 moveto +593.88945 514.35944 593.74305 513.94436 593.45715 513.63433 curveto +593.17468 513.32432 592.79921 513.16931 592.33074 513.1693 curveto +591.80026 513.16931 591.37485 513.31915 591.05449 513.61883 curveto +590.73758 513.91852 590.55502 514.34049 590.50679 514.88474 curveto +593.89634 514.87958 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +600.21557 513.38115 moveto +600.21557 510.24995 lineto +601.1663 510.24995 lineto +601.1663 518.28979 lineto +600.21557 518.28979 lineto +600.21557 517.42174 lineto +600.01577 517.76621 599.76259 518.02283 599.45602 518.19162 curveto +599.15289 518.35697 598.78775 518.43964 598.36062 518.43964 curveto +597.66135 518.43964 597.09126 518.16062 596.65034 517.60258 curveto +596.21287 517.04455 595.99413 516.31084 595.99413 515.40144 curveto +595.99413 514.49206 596.21287 513.75834 596.65034 513.2003 curveto +597.09126 512.64227 597.66135 512.36326 598.36062 512.36325 curveto +598.78775 512.36326 599.15289 512.44765 599.45602 512.61643 curveto +599.76259 512.78178 600.01577 513.03669 600.21557 513.38115 curveto +596.97586 515.40144 moveto +596.97586 516.10071 597.11881 516.65014 597.40472 517.04972 curveto +597.69407 517.44585 598.09021 517.64392 598.59313 517.64392 curveto +599.09605 517.64392 599.49219 517.44585 599.78154 517.04972 curveto +600.07089 516.65014 600.21556 516.10071 600.21557 515.40144 curveto +600.21556 514.70218 600.07089 514.15448 599.78154 513.75834 curveto +599.49219 513.35876 599.09605 513.15897 598.59313 513.15897 curveto +598.09021 513.15897 597.69407 513.35876 597.40472 513.75834 curveto +597.11881 514.15448 596.97586 514.70218 596.97586 515.40144 curveto +fill +grestore +grestore +gsave [0.914554 0 0 0.850284 392.7318 105.8481] concat +gsave +0 0 0 setrgbcolor +newpath +78.658371 175.80817 moveto +78.658371 179.09528 lineto +80.146652 179.09528 lineto +80.697429 179.09528 81.12321 178.9527 81.423996 178.66754 curveto +81.724772 178.38239 81.875162 177.97614 81.875168 177.44879 curveto +81.875162 176.92536 81.724772 176.52106 81.423996 176.2359 curveto +81.12321 175.95075 80.697429 175.80817 80.146652 175.80817 curveto +78.658371 175.80817 lineto +77.474777 174.83551 moveto +80.146652 174.83551 lineto +81.127116 174.83552 81.86735 175.05818 82.367355 175.50348 curveto +82.871255 175.94489 83.123208 176.59333 83.123215 177.44879 curveto +83.123208 178.31208 82.871255 178.96442 82.367355 179.40582 curveto +81.86735 179.84723 81.127116 180.06794 80.146652 180.06793 curveto +78.658371 180.06793 lineto +78.658371 183.58356 lineto +77.474777 183.58356 lineto +77.474777 174.83551 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +87.113449 180.28473 moveto +86.242352 180.28473 85.638837 180.38434 85.302902 180.58356 curveto +84.966963 180.78278 84.798994 181.12262 84.798996 181.60309 curveto +84.798994 181.9859 84.923994 182.29059 85.173996 182.51715 curveto +85.4279 182.73981 85.771649 182.85114 86.205246 182.85114 curveto +86.802898 182.85114 87.281414 182.6402 87.640793 182.21832 curveto +88.004069 181.79254 88.18571 181.22809 88.185715 180.52496 curveto +88.185715 180.28473 lineto +87.113449 180.28473 lineto +89.26384 179.83942 moveto +89.26384 183.58356 lineto +88.185715 183.58356 lineto +88.185715 182.58746 lineto +87.939616 182.9859 87.632976 183.28082 87.265793 183.47223 curveto +86.898601 183.65973 86.449383 183.75348 85.918137 183.75348 curveto +85.246259 183.75348 84.711104 183.56598 84.312668 183.19098 curveto +83.918136 182.81207 83.72087 182.30621 83.720871 181.6734 curveto +83.72087 180.93512 83.966964 180.37848 84.459152 180.00348 curveto +84.955244 179.62848 85.693525 179.44098 86.673996 179.44098 curveto +88.185715 179.44098 lineto +88.185715 179.33551 lineto +88.18571 178.83942 88.021647 178.45661 87.693527 178.18707 curveto +87.369304 177.91364 86.912273 177.77692 86.322433 177.77692 curveto +85.947431 177.77692 85.582197 177.82184 85.22673 177.91168 curveto +84.87126 178.00153 84.529463 178.1363 84.20134 178.31598 curveto +84.20134 177.31989 lineto +84.595869 177.16755 84.978681 177.05427 85.349777 176.98004 curveto +85.720868 176.90192 86.082196 176.86286 86.433762 176.86285 curveto +87.382976 176.86286 88.09196 177.10895 88.560715 177.60114 curveto +89.029459 178.09333 89.263833 178.83942 89.26384 179.83942 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +91.490402 174.46637 moveto +92.568527 174.46637 lineto +92.568527 183.58356 lineto +91.490402 183.58356 lineto +91.490402 174.46637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +100.43181 180.03278 moveto +100.43181 180.56012 lineto +95.474777 180.56012 lineto +95.52165 181.30231 95.744306 181.86872 96.142746 182.25934 curveto +96.545087 182.64606 97.10368 182.83942 97.818527 182.83942 curveto +98.232585 182.83942 98.632975 182.78864 99.019699 182.68707 curveto +99.410318 182.58551 99.797037 182.43317 100.17986 182.23004 curveto +100.17986 183.24957 lineto +99.79313 183.41364 99.396647 183.53864 98.990402 183.62457 curveto +98.584147 183.71051 98.172038 183.75348 97.754074 183.75348 curveto +96.707196 183.75348 95.877119 183.44879 95.26384 182.83942 curveto +94.654464 182.23004 94.349777 181.40582 94.349777 180.36676 curveto +94.349777 179.29255 94.638839 178.44098 95.216965 177.81207 curveto +95.798994 177.17927 96.582196 176.86286 97.566574 176.86285 curveto +98.449382 176.86286 99.146647 177.14802 99.658371 177.71832 curveto +100.17399 178.28473 100.4318 179.05622 100.43181 180.03278 curveto +99.353683 179.71637 moveto +99.345865 179.12653 99.17985 178.65583 98.855637 178.30426 curveto +98.535319 177.9527 98.109538 177.77692 97.578293 177.77692 curveto +96.976727 177.77692 96.494306 177.94684 96.131027 178.28668 curveto +95.77165 178.62653 95.564619 179.10505 95.509933 179.72223 curveto +99.353683 179.71637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +110.73259 180.30817 moveto +110.73258 179.5152 110.56852 178.89411 110.2404 178.44489 curveto +109.91618 177.99177 109.46891 177.7652 108.89861 177.7652 curveto +108.32829 177.7652 107.87907 177.99177 107.55095 178.44489 curveto +107.22673 178.89411 107.06462 179.5152 107.06462 180.30817 curveto +107.06462 181.10114 107.22673 181.72418 107.55095 182.17731 curveto +107.87907 182.62653 108.32829 182.85114 108.89861 182.85114 curveto +109.46891 182.85114 109.91618 182.62653 110.2404 182.17731 curveto +110.56852 181.72418 110.73258 181.10114 110.73259 180.30817 curveto +107.06462 178.01715 moveto +107.29118 177.62653 107.57634 177.33747 107.92009 177.14996 curveto +108.26774 176.95856 108.6818 176.86286 109.16228 176.86285 curveto +109.95915 176.86286 110.60563 177.17927 111.10173 177.81207 curveto +111.60172 178.44489 111.85172 179.27692 111.85173 180.30817 curveto +111.85172 181.33942 111.60172 182.17145 111.10173 182.80426 curveto +110.60563 183.43707 109.95915 183.75348 109.16228 183.75348 curveto +108.6818 183.75348 108.26774 183.65973 107.92009 183.47223 curveto +107.57634 183.28082 107.29118 182.98981 107.06462 182.59918 curveto +107.06462 183.58356 lineto +105.98064 183.58356 lineto +105.98064 174.46637 lineto +107.06462 174.46637 lineto +107.06462 178.01715 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +113.63884 174.46637 moveto +114.71696 174.46637 lineto +114.71696 183.58356 lineto +113.63884 183.58356 lineto +113.63884 174.46637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +116.85564 180.99371 moveto +116.85564 177.02106 lineto +117.93376 177.02106 lineto +117.93376 180.9527 lineto +117.93376 181.57379 118.05485 182.04059 118.29704 182.35309 curveto +118.53923 182.66168 118.90251 182.81598 119.38689 182.81598 curveto +119.96891 182.81598 120.4279 182.63043 120.76384 182.25934 curveto +121.10368 181.88825 121.2736 181.38239 121.27361 180.74176 curveto +121.27361 177.02106 lineto +122.35173 177.02106 lineto +122.35173 183.58356 lineto +121.27361 183.58356 lineto +121.27361 182.57574 lineto +121.01188 182.97418 120.70719 183.27106 120.35954 183.46637 curveto +120.01579 183.65778 119.6154 183.75348 119.15837 183.75348 curveto +118.40446 183.75348 117.8322 183.5191 117.44157 183.05035 curveto +117.05095 182.58161 116.85564 181.89606 116.85564 180.99371 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +130.19743 180.03278 moveto +130.19743 180.56012 lineto +125.2404 180.56012 lineto +125.28728 181.30231 125.50993 181.86872 125.90837 182.25934 curveto +126.31071 182.64606 126.86931 182.83942 127.58415 182.83942 curveto +127.99821 182.83942 128.3986 182.78864 128.78532 182.68707 curveto +129.17594 182.58551 129.56266 182.43317 129.94548 182.23004 curveto +129.94548 183.24957 lineto +129.55876 183.41364 129.16227 183.53864 128.75603 183.62457 curveto +128.34977 183.71051 127.93766 183.75348 127.5197 183.75348 curveto +126.47282 183.75348 125.64274 183.44879 125.02946 182.83942 curveto +124.42009 182.23004 124.1154 181.40582 124.1154 180.36676 curveto +124.1154 179.29255 124.40446 178.44098 124.98259 177.81207 curveto +125.56462 177.17927 126.34782 176.86286 127.3322 176.86285 curveto +128.21501 176.86286 128.91227 177.14802 129.424 177.71832 curveto +129.93961 178.28473 130.19743 179.05622 130.19743 180.03278 curveto +129.11931 179.71637 moveto +129.11149 179.12653 128.94547 178.65583 128.62126 178.30426 curveto +128.30094 177.9527 127.87516 177.77692 127.34392 177.77692 curveto +126.74235 177.77692 126.25993 177.94684 125.89665 178.28668 curveto +125.53728 178.62653 125.33024 179.10505 125.27556 179.72223 curveto +129.11931 179.71637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +136.06267 182.09528 moveto +137.299 182.09528 lineto +137.299 183.58356 lineto +136.06267 183.58356 lineto +136.06267 182.09528 lineto +136.06267 177.37848 moveto +137.299 177.37848 lineto +137.299 178.86676 lineto +136.06267 178.86676 lineto +136.06267 177.37848 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +147.46501 178.02887 moveto +147.34391 177.95856 147.2111 177.90778 147.06657 177.87653 curveto +146.92594 177.84138 146.76969 177.8238 146.59782 177.82379 curveto +145.98845 177.8238 145.5197 178.02302 145.19157 178.42145 curveto +144.86735 178.81598 144.70524 179.38434 144.70525 180.12653 curveto +144.70525 183.58356 lineto +143.62126 183.58356 lineto +143.62126 177.02106 lineto +144.70525 177.02106 lineto +144.70525 178.04059 lineto +144.93181 177.64216 145.22673 177.34724 145.59001 177.15582 curveto +145.95329 176.96052 146.3947 176.86286 146.91423 176.86285 curveto +146.98844 176.86286 147.07048 176.86872 147.16032 176.88043 curveto +147.25016 176.88825 147.34977 176.90192 147.45915 176.92145 curveto +147.46501 178.02887 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +148.49626 180.99371 moveto +148.49626 177.02106 lineto +149.57439 177.02106 lineto +149.57439 180.9527 lineto +149.57438 181.57379 149.69548 182.04059 149.93767 182.35309 curveto +150.17985 182.66168 150.54313 182.81598 151.02751 182.81598 curveto +151.60954 182.81598 152.06852 182.63043 152.40446 182.25934 curveto +152.7443 181.88825 152.91422 181.38239 152.91423 180.74176 curveto +152.91423 177.02106 lineto +153.99236 177.02106 lineto +153.99236 183.58356 lineto +152.91423 183.58356 lineto +152.91423 182.57574 lineto +152.65251 182.97418 152.34782 183.27106 152.00017 183.46637 curveto +151.65641 183.65778 151.25602 183.75348 150.799 183.75348 curveto +150.04509 183.75348 149.47282 183.5191 149.0822 183.05035 curveto +148.69157 182.58161 148.49626 181.89606 148.49626 180.99371 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +161.67986 179.62262 moveto +161.67986 183.58356 lineto +160.60173 183.58356 lineto +160.60173 179.65778 lineto +160.60172 179.03669 160.48063 178.57184 160.23845 178.26324 curveto +159.99626 177.95466 159.63298 177.80036 159.14861 177.80035 curveto +158.56657 177.80036 158.10759 177.98591 157.77165 178.35699 curveto +157.43571 178.72809 157.26774 179.23395 157.26775 179.87457 curveto +157.26775 183.58356 lineto +156.18376 183.58356 lineto +156.18376 177.02106 lineto +157.26775 177.02106 lineto +157.26775 178.04059 lineto +157.52556 177.64606 157.82829 177.35114 158.17595 177.15582 curveto +158.52751 176.96052 158.9318 176.86286 159.38884 176.86285 curveto +160.14274 176.86286 160.71305 177.09724 161.09978 177.56598 curveto +161.48649 178.03083 161.67985 178.71637 161.67986 179.62262 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +169.29704 179.62262 moveto +169.29704 183.58356 lineto +168.21892 183.58356 lineto +168.21892 179.65778 lineto +168.21891 179.03669 168.09782 178.57184 167.85564 178.26324 curveto +167.61344 177.95466 167.25016 177.80036 166.76579 177.80035 curveto +166.18376 177.80036 165.72477 177.98591 165.38884 178.35699 curveto +165.0529 178.72809 164.88493 179.23395 164.88493 179.87457 curveto +164.88493 183.58356 lineto +163.80095 183.58356 lineto +163.80095 177.02106 lineto +164.88493 177.02106 lineto +164.88493 178.04059 lineto +165.14274 177.64606 165.44548 177.35114 165.79314 177.15582 curveto +166.1447 176.96052 166.54899 176.86286 167.00603 176.86285 curveto +167.75993 176.86286 168.33024 177.09724 168.71696 177.56598 curveto +169.10368 178.03083 169.29704 178.71637 169.29704 179.62262 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +171.45915 177.02106 moveto +172.53728 177.02106 lineto +172.53728 183.58356 lineto +171.45915 183.58356 lineto +171.45915 177.02106 lineto +171.45915 174.46637 moveto +172.53728 174.46637 lineto +172.53728 175.8316 lineto +171.45915 175.8316 lineto +171.45915 174.46637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +180.24236 179.62262 moveto +180.24236 183.58356 lineto +179.16423 183.58356 lineto +179.16423 179.65778 lineto +179.16422 179.03669 179.04313 178.57184 178.80095 178.26324 curveto +178.55876 177.95466 178.19548 177.80036 177.71111 177.80035 curveto +177.12907 177.80036 176.67009 177.98591 176.33415 178.35699 curveto +175.99821 178.72809 175.83024 179.23395 175.83025 179.87457 curveto +175.83025 183.58356 lineto +174.74626 183.58356 lineto +174.74626 177.02106 lineto +175.83025 177.02106 lineto +175.83025 178.04059 lineto +176.08806 177.64606 176.39079 177.35114 176.73845 177.15582 curveto +177.09001 176.96052 177.4943 176.86286 177.95134 176.86285 curveto +178.70524 176.86286 179.27555 177.09724 179.66228 177.56598 curveto +180.04899 178.03083 180.24235 178.71637 180.24236 179.62262 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +186.72282 180.22614 moveto +186.72282 179.44489 186.56071 178.83942 186.2365 178.40973 curveto +185.91618 177.98005 185.46501 177.7652 184.88298 177.7652 curveto +184.30485 177.7652 183.85368 177.98005 183.52946 178.40973 curveto +183.20915 178.83942 183.04899 179.44489 183.049 180.22614 curveto +183.04899 181.00348 183.20915 181.607 183.52946 182.03668 curveto +183.85368 182.46637 184.30485 182.68121 184.88298 182.68121 curveto +185.46501 182.68121 185.91618 182.46637 186.2365 182.03668 curveto +186.56071 181.607 186.72282 181.00348 186.72282 180.22614 curveto +187.80095 182.7691 moveto +187.80094 183.88629 187.5529 184.71637 187.05681 185.25934 curveto +186.56071 185.80621 185.80094 186.07965 184.77751 186.07965 curveto +184.3986 186.07965 184.04118 186.05035 183.70525 185.99176 curveto +183.36931 185.93707 183.04313 185.85113 182.72673 185.73395 curveto +182.72673 184.68512 lineto +183.04313 184.85699 183.35563 184.98395 183.66423 185.06598 curveto +183.97282 185.14801 184.28727 185.18902 184.60759 185.18903 curveto +185.31462 185.18902 185.84391 185.00348 186.19548 184.63239 curveto +186.54704 184.2652 186.72282 183.70856 186.72282 182.96246 curveto +186.72282 182.42926 lineto +186.50016 182.81598 186.21501 183.10504 185.86736 183.29645 curveto +185.51969 183.48785 185.10368 183.58356 184.61931 183.58356 curveto +183.81462 183.58356 183.16618 183.27692 182.674 182.66364 curveto +182.18181 182.05036 181.93571 181.23786 181.93571 180.22614 curveto +181.93571 179.21051 182.18181 178.39606 182.674 177.78278 curveto +183.16618 177.1695 183.81462 176.86286 184.61931 176.86285 curveto +185.10368 176.86286 185.51969 176.95856 185.86736 177.14996 curveto +186.21501 177.34138 186.50016 177.63044 186.72282 178.01715 curveto +186.72282 177.02106 lineto +187.80095 177.02106 lineto +187.80095 182.7691 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +193.84196 177.02106 moveto +194.92009 177.02106 lineto +194.92009 183.58356 lineto +193.84196 183.58356 lineto +193.84196 177.02106 lineto +193.84196 174.46637 moveto +194.92009 174.46637 lineto +194.92009 175.8316 lineto +193.84196 175.8316 lineto +193.84196 174.46637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +202.62517 179.62262 moveto +202.62517 183.58356 lineto +201.54704 183.58356 lineto +201.54704 179.65778 lineto +201.54704 179.03669 201.42594 178.57184 201.18376 178.26324 curveto +200.94157 177.95466 200.57829 177.80036 200.09392 177.80035 curveto +199.51188 177.80036 199.0529 177.98591 198.71696 178.35699 curveto +198.38102 178.72809 198.21306 179.23395 198.21306 179.87457 curveto +198.21306 183.58356 lineto +197.12907 183.58356 lineto +197.12907 177.02106 lineto +198.21306 177.02106 lineto +198.21306 178.04059 lineto +198.47087 177.64606 198.7736 177.35114 199.12126 177.15582 curveto +199.47282 176.96052 199.87712 176.86286 200.33415 176.86285 curveto +201.08805 176.86286 201.65837 177.09724 202.04509 177.56598 curveto +202.4318 178.03083 202.62516 178.71637 202.62517 179.62262 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +211.59001 180.28473 moveto +210.71891 180.28473 210.1154 180.38434 209.77946 180.58356 curveto +209.44353 180.78278 209.27556 181.12262 209.27556 181.60309 curveto +209.27556 181.9859 209.40056 182.29059 209.65056 182.51715 curveto +209.90446 182.73981 210.24821 182.85114 210.68181 182.85114 curveto +211.27946 182.85114 211.75798 182.6402 212.11736 182.21832 curveto +212.48063 181.79254 212.66227 181.22809 212.66228 180.52496 curveto +212.66228 180.28473 lineto +211.59001 180.28473 lineto +213.7404 179.83942 moveto +213.7404 183.58356 lineto +212.66228 183.58356 lineto +212.66228 182.58746 lineto +212.41618 182.9859 212.10954 183.28082 211.74236 183.47223 curveto +211.37516 183.65973 210.92595 183.75348 210.3947 183.75348 curveto +209.72282 183.75348 209.18767 183.56598 208.78923 183.19098 curveto +208.3947 182.81207 208.19743 182.30621 208.19743 181.6734 curveto +208.19743 180.93512 208.44353 180.37848 208.93571 180.00348 curveto +209.43181 179.62848 210.17009 179.44098 211.15056 179.44098 curveto +212.66228 179.44098 lineto +212.66228 179.33551 lineto +212.66227 178.83942 212.49821 178.45661 212.17009 178.18707 curveto +211.84587 177.91364 211.38884 177.77692 210.799 177.77692 curveto +210.42399 177.77692 210.05876 177.82184 209.70329 177.91168 curveto +209.34782 178.00153 209.00603 178.1363 208.6779 178.31598 curveto +208.6779 177.31989 lineto +209.07243 177.16755 209.45524 177.05427 209.82634 176.98004 curveto +210.19743 176.90192 210.55876 176.86286 210.91032 176.86285 curveto +211.85954 176.86286 212.56852 177.10895 213.03728 177.60114 curveto +213.50602 178.09333 213.7404 178.83942 213.7404 179.83942 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +223.97087 177.21442 moveto +223.97087 178.23395 lineto +223.66618 178.0777 223.34977 177.96052 223.02165 177.88239 curveto +222.69352 177.80427 222.35368 177.7652 222.00212 177.7652 curveto +221.46696 177.7652 221.06462 177.84723 220.79509 178.01129 curveto +220.52946 178.17536 220.39665 178.42145 220.39665 178.74957 curveto +220.39665 178.99958 220.49235 179.19684 220.68376 179.34137 curveto +220.87517 179.482 221.25993 179.61676 221.83806 179.74567 curveto +222.2072 179.8277 lineto +222.97282 179.99176 223.51579 180.22419 223.83611 180.52496 curveto +224.16032 180.82184 224.32243 181.23786 224.32243 181.77301 curveto +224.32243 182.38239 224.08024 182.86481 223.59587 183.22028 curveto +223.1154 183.57574 222.45329 183.75348 221.60954 183.75348 curveto +221.25798 183.75348 220.89079 183.71832 220.50798 183.64801 curveto +220.12907 183.5816 219.72868 183.48004 219.30681 183.34332 curveto +219.30681 182.23004 lineto +219.70524 182.43707 220.09782 182.59332 220.48454 182.69879 curveto +220.87126 182.80035 221.25407 182.85114 221.63298 182.85114 curveto +222.14079 182.85114 222.53141 182.7652 222.80486 182.59332 curveto +223.07829 182.41754 223.21501 182.17145 223.21501 181.85504 curveto +223.21501 181.56207 223.1154 181.33747 222.91618 181.18121 curveto +222.72087 181.02497 222.28923 180.87458 221.62126 180.73004 curveto +221.24626 180.64215 lineto +220.57829 180.50153 220.09587 180.28669 219.799 179.99762 curveto +219.50212 179.70465 219.35368 179.30426 219.35368 178.79645 curveto +219.35368 178.17927 219.57243 177.7027 220.00993 177.36676 curveto +220.44743 177.03083 221.06852 176.86286 221.87321 176.86285 curveto +222.27165 176.86286 222.64665 176.89216 222.99821 176.95074 curveto +223.34977 177.00934 223.67399 177.09724 223.97087 177.21442 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +228.77556 184.19293 moveto +228.47087 184.97418 228.17399 185.48395 227.88493 185.72223 curveto +227.59587 185.96051 227.20915 186.07965 226.72478 186.07965 curveto +225.86345 186.07965 lineto +225.86345 185.17731 lineto +226.49626 185.17731 lineto +226.79313 185.17731 227.0236 185.10699 227.18767 184.96637 curveto +227.35173 184.82574 227.53337 184.49371 227.73259 183.97028 curveto +227.92595 183.47809 lineto +225.27165 177.02106 lineto +226.41423 177.02106 lineto +228.46501 182.15387 lineto +230.51579 177.02106 lineto +231.65837 177.02106 lineto +228.77556 184.19293 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +237.33025 177.21442 moveto +237.33025 178.23395 lineto +237.02555 178.0777 236.70915 177.96052 236.38103 177.88239 curveto +236.0529 177.80427 235.71305 177.7652 235.3615 177.7652 curveto +234.82634 177.7652 234.42399 177.84723 234.15446 178.01129 curveto +233.88884 178.17536 233.75603 178.42145 233.75603 178.74957 curveto +233.75603 178.99958 233.85173 179.19684 234.04314 179.34137 curveto +234.23454 179.482 234.61931 179.61676 235.19743 179.74567 curveto +235.56657 179.8277 lineto +236.33219 179.99176 236.87516 180.22419 237.19548 180.52496 curveto +237.51969 180.82184 237.6818 181.23786 237.68181 181.77301 curveto +237.6818 182.38239 237.43962 182.86481 236.95525 183.22028 curveto +236.47477 183.57574 235.81266 183.75348 234.96892 183.75348 curveto +234.61735 183.75348 234.25017 183.71832 233.86736 183.64801 curveto +233.48845 183.5816 233.08806 183.48004 232.66618 183.34332 curveto +232.66618 182.23004 lineto +233.06462 182.43707 233.4572 182.59332 233.84392 182.69879 curveto +234.23063 182.80035 234.61345 182.85114 234.99236 182.85114 curveto +235.50016 182.85114 235.89079 182.7652 236.16423 182.59332 curveto +236.43766 182.41754 236.57438 182.17145 236.57439 181.85504 curveto +236.57438 181.56207 236.47477 181.33747 236.27556 181.18121 curveto +236.08024 181.02497 235.6486 180.87458 234.98064 180.73004 curveto +234.60564 180.64215 lineto +233.93767 180.50153 233.45524 180.28669 233.15837 179.99762 curveto +232.8615 179.70465 232.71306 179.30426 232.71306 178.79645 curveto +232.71306 178.17927 232.93181 177.7027 233.36931 177.36676 curveto +233.80681 177.03083 234.4279 176.86286 235.23259 176.86285 curveto +235.63102 176.86286 236.00602 176.89216 236.35759 176.95074 curveto +236.70915 177.00934 237.03337 177.09724 237.33025 177.21442 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +240.47087 175.15778 moveto +240.47087 177.02106 lineto +242.69157 177.02106 lineto +242.69157 177.85895 lineto +240.47087 177.85895 lineto +240.47087 181.42145 lineto +240.47087 181.95661 240.54313 182.30036 240.68767 182.4527 curveto +240.8361 182.60504 241.13493 182.68121 241.58415 182.68121 curveto +242.69157 182.68121 lineto +242.69157 183.58356 lineto +241.58415 183.58356 lineto +240.75212 183.58356 240.1779 183.42926 239.8615 183.12067 curveto +239.54509 182.80817 239.38689 182.24176 239.38689 181.42145 curveto +239.38689 177.85895 lineto +238.59587 177.85895 lineto +238.59587 177.02106 lineto +239.38689 177.02106 lineto +239.38689 175.15778 lineto +240.47087 175.15778 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +249.72868 180.03278 moveto +249.72868 180.56012 lineto +244.77165 180.56012 lineto +244.81853 181.30231 245.04118 181.86872 245.43962 182.25934 curveto +245.84196 182.64606 246.40056 182.83942 247.1154 182.83942 curveto +247.52946 182.83942 247.92985 182.78864 248.31657 182.68707 curveto +248.70719 182.58551 249.09391 182.43317 249.47673 182.23004 curveto +249.47673 183.24957 lineto +249.09001 183.41364 248.69352 183.53864 248.28728 183.62457 curveto +247.88102 183.71051 247.46891 183.75348 247.05095 183.75348 curveto +246.00407 183.75348 245.17399 183.44879 244.56071 182.83942 curveto +243.95134 182.23004 243.64665 181.40582 243.64665 180.36676 curveto +243.64665 179.29255 243.93571 178.44098 244.51384 177.81207 curveto +245.09587 177.17927 245.87907 176.86286 246.86345 176.86285 curveto +247.74626 176.86286 248.44352 177.14802 248.95525 177.71832 curveto +249.47086 178.28473 249.72868 179.05622 249.72868 180.03278 curveto +248.65056 179.71637 moveto +248.64274 179.12653 248.47672 178.65583 248.15251 178.30426 curveto +247.83219 177.9527 247.40641 177.77692 246.87517 177.77692 curveto +246.2736 177.77692 245.79118 177.94684 245.4279 178.28668 curveto +245.06853 178.62653 244.86149 179.10505 244.80681 179.72223 curveto +248.65056 179.71637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +256.60759 178.28082 moveto +256.87711 177.79645 257.19938 177.43903 257.57439 177.20856 curveto +257.94938 176.97809 258.39078 176.86286 258.89861 176.86285 curveto +259.58219 176.86286 260.10953 177.10309 260.48064 177.58356 curveto +260.85172 178.06013 261.03727 178.73981 261.03728 179.62262 curveto +261.03728 183.58356 lineto +259.95329 183.58356 lineto +259.95329 179.65778 lineto +259.95328 179.02887 259.84196 178.56208 259.61931 178.25739 curveto +259.39664 177.9527 259.0568 177.80036 258.59978 177.80035 curveto +258.04118 177.80036 257.59977 177.98591 257.27556 178.35699 curveto +256.95133 178.72809 256.78922 179.23395 256.78923 179.87457 curveto +256.78923 183.58356 lineto +255.70525 183.58356 lineto +255.70525 179.65778 lineto +255.70524 179.02497 255.59391 178.55817 255.37126 178.25739 curveto +255.1486 177.9527 254.80485 177.80036 254.34001 177.80035 curveto +253.78923 177.80036 253.35173 177.98786 253.02751 178.36285 curveto +252.70329 178.73395 252.54118 179.23786 252.54118 179.87457 curveto +252.54118 183.58356 lineto +251.4572 183.58356 lineto +251.4572 177.02106 lineto +252.54118 177.02106 lineto +252.54118 178.04059 lineto +252.78727 177.63825 253.0822 177.34138 253.42595 177.14996 curveto +253.7697 176.95856 254.1779 176.86286 254.65056 176.86285 curveto +255.12712 176.86286 255.53141 176.98395 255.86345 177.22614 curveto +256.19938 177.46833 256.44743 177.81989 256.60759 178.28082 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +271.7365 177.27301 moveto +271.7365 178.28082 lineto +271.4318 178.11286 271.12516 177.98786 270.81657 177.90582 curveto +270.51188 177.81989 270.20329 177.77692 269.89079 177.77692 curveto +269.19157 177.77692 268.6486 177.99958 268.26189 178.44489 curveto +267.87517 178.8863 267.68181 179.50739 267.68181 180.30817 curveto +267.68181 181.10895 267.87517 181.732 268.26189 182.17731 curveto +268.6486 182.61871 269.19157 182.83942 269.89079 182.83942 curveto +270.20329 182.83942 270.51188 182.7984 270.81657 182.71637 curveto +271.12516 182.63043 271.4318 182.50348 271.7365 182.33551 curveto +271.7365 183.3316 lineto +271.43571 183.47223 271.12321 183.5777 270.799 183.64801 curveto +270.47868 183.71832 270.13688 183.75348 269.77361 183.75348 curveto +268.78532 183.75348 268.00017 183.44293 267.41814 182.82184 curveto +266.8361 182.20075 266.54509 181.36286 266.54509 180.30817 curveto +266.54509 179.23786 266.83806 178.39606 267.424 177.78278 curveto +268.01384 177.1695 268.82048 176.86286 269.84392 176.86285 curveto +270.17594 176.86286 270.50016 176.89802 270.81657 176.96832 curveto +271.13298 177.03474 271.43962 177.1363 271.7365 177.27301 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +276.60564 180.28473 moveto +275.73454 180.28473 275.13102 180.38434 274.79509 180.58356 curveto +274.45915 180.78278 274.29118 181.12262 274.29118 181.60309 curveto +274.29118 181.9859 274.41618 182.29059 274.66618 182.51715 curveto +274.92009 182.73981 275.26384 182.85114 275.69743 182.85114 curveto +276.29509 182.85114 276.7736 182.6402 277.13298 182.21832 curveto +277.49626 181.79254 277.6779 181.22809 277.6779 180.52496 curveto +277.6779 180.28473 lineto +276.60564 180.28473 lineto +278.75603 179.83942 moveto +278.75603 183.58356 lineto +277.6779 183.58356 lineto +277.6779 182.58746 lineto +277.4318 182.9859 277.12516 183.28082 276.75798 183.47223 curveto +276.39079 183.65973 275.94157 183.75348 275.41032 183.75348 curveto +274.73845 183.75348 274.20329 183.56598 273.80486 183.19098 curveto +273.41032 182.81207 273.21306 182.30621 273.21306 181.6734 curveto +273.21306 180.93512 273.45915 180.37848 273.95134 180.00348 curveto +274.44743 179.62848 275.18571 179.44098 276.16618 179.44098 curveto +277.6779 179.44098 lineto +277.6779 179.33551 lineto +277.6779 178.83942 277.51383 178.45661 277.18571 178.18707 curveto +276.86149 177.91364 276.40446 177.77692 275.81462 177.77692 curveto +275.43962 177.77692 275.07438 177.82184 274.71892 177.91168 curveto +274.36345 178.00153 274.02165 178.1363 273.69353 178.31598 curveto +273.69353 177.31989 lineto +274.08806 177.16755 274.47087 177.05427 274.84196 176.98004 curveto +275.21306 176.90192 275.57438 176.86286 275.92595 176.86285 curveto +276.87516 176.86286 277.58415 177.10895 278.0529 177.60114 curveto +278.52165 178.09333 278.75602 178.83942 278.75603 179.83942 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +280.98259 174.46637 moveto +282.06071 174.46637 lineto +282.06071 183.58356 lineto +280.98259 183.58356 lineto +280.98259 174.46637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +284.31071 174.46637 moveto +285.38884 174.46637 lineto +285.38884 183.58356 lineto +284.31071 183.58356 lineto +284.31071 174.46637 lineto +fill +grestore +grestore +gsave [0.914554 0 0 0.850284 393.1927 75.71992] concat +gsave +0 0 0 setrgbcolor +newpath +83.439621 182.33551 moveto +83.439621 179.9859 lineto +81.506027 179.9859 lineto +81.506027 179.01324 lineto +84.611496 179.01324 lineto +84.611496 182.7691 lineto +84.154457 183.09332 83.650551 183.33942 83.099777 183.50739 curveto +82.54899 183.67145 81.9611 183.75348 81.336105 183.75348 curveto +79.968914 183.75348 78.898603 183.35504 78.125168 182.55817 curveto +77.355636 181.75739 76.97087 180.64411 76.970871 179.21832 curveto +76.97087 177.78864 77.355636 176.67536 78.125168 175.87848 curveto +78.898603 175.07771 79.968914 174.67732 81.336105 174.67731 curveto +81.906412 174.67732 82.447427 174.74763 82.959152 174.88824 curveto +83.47477 175.02888 83.949379 175.23591 84.38298 175.50934 curveto +84.38298 176.7691 lineto +83.945473 176.39802 83.480629 176.11872 82.988449 175.93121 curveto +82.496255 175.74372 81.978678 175.64997 81.435715 175.64996 curveto +80.365398 175.64997 79.560711 175.9488 79.021652 176.54645 curveto +78.486494 177.14411 78.218916 178.03473 78.218918 179.21832 curveto +78.218916 180.39801 78.486494 181.28668 79.021652 181.88434 curveto +79.560711 182.482 80.365398 182.78082 81.435715 182.78082 curveto +81.853678 182.78082 82.226724 182.74567 82.554855 182.67535 curveto +82.882974 182.60114 83.177895 182.48786 83.439621 182.33551 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +90.535324 178.02887 moveto +90.414226 177.95856 90.281413 177.90778 90.136887 177.87653 curveto +89.996257 177.84138 89.840007 177.8238 89.668137 177.82379 curveto +89.058758 177.8238 88.590009 178.02302 88.261887 178.42145 curveto +87.937666 178.81598 87.775556 179.38434 87.775558 180.12653 curveto +87.775558 183.58356 lineto +86.691574 183.58356 lineto +86.691574 177.02106 lineto +87.775558 177.02106 lineto +87.775558 178.04059 lineto +88.002119 177.64216 88.29704 177.34724 88.660324 177.15582 curveto +89.023602 176.96052 89.465008 176.86286 89.984543 176.86285 curveto +90.058757 176.86286 90.140788 176.86872 90.230637 176.88043 curveto +90.320476 176.88825 90.420085 176.90192 90.529465 176.92145 curveto +90.535324 178.02887 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +97.033371 180.03278 moveto +97.033371 180.56012 lineto +92.07634 180.56012 lineto +92.123213 181.30231 92.345869 181.86872 92.744308 182.25934 curveto +93.146649 182.64606 93.705243 182.83942 94.42009 182.83942 curveto +94.834148 182.83942 95.234538 182.78864 95.621262 182.68707 curveto +96.011881 182.58551 96.398599 182.43317 96.781418 182.23004 curveto +96.781418 183.24957 lineto +96.394693 183.41364 95.998209 183.53864 95.591965 183.62457 curveto +95.18571 183.71051 94.773601 183.75348 94.355637 183.75348 curveto +93.308759 183.75348 92.478681 183.44879 91.865402 182.83942 curveto +91.256026 182.23004 90.951339 181.40582 90.95134 180.36676 curveto +90.951339 179.29255 91.240401 178.44098 91.818527 177.81207 curveto +92.400556 177.17927 93.183759 176.86286 94.168137 176.86285 curveto +95.050944 176.86286 95.748209 177.14802 96.259933 177.71832 curveto +96.775552 178.28473 97.033364 179.05622 97.033371 180.03278 curveto +95.955246 179.71637 moveto +95.947428 179.12653 95.781412 178.65583 95.457199 178.30426 curveto +95.136882 177.9527 94.711101 177.77692 94.179855 177.77692 curveto +93.57829 177.77692 93.095868 177.94684 92.73259 178.28668 curveto +92.373213 178.62653 92.166182 179.10505 92.111496 179.72223 curveto +95.955246 179.71637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +104.41618 180.03278 moveto +104.41618 180.56012 lineto +99.459152 180.56012 lineto +99.506025 181.30231 99.728681 181.86872 100.12712 182.25934 curveto +100.52946 182.64606 101.08806 182.83942 101.8029 182.83942 curveto +102.21696 182.83942 102.61735 182.78864 103.00407 182.68707 curveto +103.39469 182.58551 103.78141 182.43317 104.16423 182.23004 curveto +104.16423 183.24957 lineto +103.77751 183.41364 103.38102 183.53864 102.97478 183.62457 curveto +102.56852 183.71051 102.15641 183.75348 101.73845 183.75348 curveto +100.69157 183.75348 99.861494 183.44879 99.248215 182.83942 curveto +98.638839 182.23004 98.334152 181.40582 98.334152 180.36676 curveto +98.334152 179.29255 98.623214 178.44098 99.20134 177.81207 curveto +99.783369 177.17927 100.56657 176.86286 101.55095 176.86285 curveto +102.43376 176.86286 103.13102 177.14802 103.64275 177.71832 curveto +104.15836 178.28473 104.41618 179.05622 104.41618 180.03278 curveto +103.33806 179.71637 moveto +103.33024 179.12653 103.16422 178.65583 102.84001 178.30426 curveto +102.51969 177.9527 102.09391 177.77692 101.56267 177.77692 curveto +100.9611 177.77692 100.47868 177.94684 100.1154 178.28668 curveto +99.756025 178.62653 99.548994 179.10505 99.494308 179.72223 curveto +103.33806 179.71637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +111.64079 179.62262 moveto +111.64079 183.58356 lineto +110.56267 183.58356 lineto +110.56267 179.65778 lineto +110.56266 179.03669 110.44157 178.57184 110.19939 178.26324 curveto +109.95719 177.95466 109.59391 177.80036 109.10954 177.80035 curveto +108.52751 177.80036 108.06852 177.98591 107.73259 178.35699 curveto +107.39665 178.72809 107.22868 179.23395 107.22868 179.87457 curveto +107.22868 183.58356 lineto +106.1447 183.58356 lineto +106.1447 177.02106 lineto +107.22868 177.02106 lineto +107.22868 178.04059 lineto +107.48649 177.64606 107.78923 177.35114 108.13689 177.15582 curveto +108.48845 176.96052 108.89274 176.86286 109.34978 176.86285 curveto +110.10368 176.86286 110.67399 177.09724 111.06071 177.56598 curveto +111.44743 178.03083 111.64079 178.71637 111.64079 179.62262 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +117.89861 182.09528 moveto +119.13493 182.09528 lineto +119.13493 183.58356 lineto +117.89861 183.58356 lineto +117.89861 182.09528 lineto +117.89861 177.37848 moveto +119.13493 177.37848 lineto +119.13493 178.86676 lineto +117.89861 178.86676 lineto +117.89861 177.37848 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +129.30095 178.02887 moveto +129.17985 177.95856 129.04704 177.90778 128.90251 177.87653 curveto +128.76188 177.84138 128.60563 177.8238 128.43376 177.82379 curveto +127.82438 177.8238 127.35563 178.02302 127.02751 178.42145 curveto +126.70329 178.81598 126.54118 179.38434 126.54118 180.12653 curveto +126.54118 183.58356 lineto +125.4572 183.58356 lineto +125.4572 177.02106 lineto +126.54118 177.02106 lineto +126.54118 178.04059 lineto +126.76774 177.64216 127.06267 177.34724 127.42595 177.15582 curveto +127.78923 176.96052 128.23063 176.86286 128.75017 176.86285 curveto +128.82438 176.86286 128.90641 176.86872 128.99626 176.88043 curveto +129.0861 176.88825 129.18571 176.90192 129.29509 176.92145 curveto +129.30095 178.02887 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +130.3322 180.99371 moveto +130.3322 177.02106 lineto +131.41032 177.02106 lineto +131.41032 180.9527 lineto +131.41032 181.57379 131.53142 182.04059 131.77361 182.35309 curveto +132.01579 182.66168 132.37907 182.81598 132.86345 182.81598 curveto +133.44548 182.81598 133.90446 182.63043 134.2404 182.25934 curveto +134.58024 181.88825 134.75016 181.38239 134.75017 180.74176 curveto +134.75017 177.02106 lineto +135.82829 177.02106 lineto +135.82829 183.58356 lineto +134.75017 183.58356 lineto +134.75017 182.57574 lineto +134.48844 182.97418 134.18376 183.27106 133.83611 183.46637 curveto +133.49235 183.65778 133.09196 183.75348 132.63493 183.75348 curveto +131.88102 183.75348 131.30876 183.5191 130.91814 183.05035 curveto +130.52751 182.58161 130.3322 181.89606 130.3322 180.99371 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +143.51579 179.62262 moveto +143.51579 183.58356 lineto +142.43767 183.58356 lineto +142.43767 179.65778 lineto +142.43766 179.03669 142.31657 178.57184 142.07439 178.26324 curveto +141.83219 177.95466 141.46891 177.80036 140.98454 177.80035 curveto +140.40251 177.80036 139.94352 177.98591 139.60759 178.35699 curveto +139.27165 178.72809 139.10368 179.23395 139.10368 179.87457 curveto +139.10368 183.58356 lineto +138.0197 183.58356 lineto +138.0197 177.02106 lineto +139.10368 177.02106 lineto +139.10368 178.04059 lineto +139.36149 177.64606 139.66423 177.35114 140.01189 177.15582 curveto +140.36345 176.96052 140.76774 176.86286 141.22478 176.86285 curveto +141.97868 176.86286 142.54899 177.09724 142.93571 177.56598 curveto +143.32243 178.03083 143.51579 178.71637 143.51579 179.62262 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +151.13298 179.62262 moveto +151.13298 183.58356 lineto +150.05486 183.58356 lineto +150.05486 179.65778 lineto +150.05485 179.03669 149.93376 178.57184 149.69157 178.26324 curveto +149.44938 177.95466 149.0861 177.80036 148.60173 177.80035 curveto +148.0197 177.80036 147.56071 177.98591 147.22478 178.35699 curveto +146.88884 178.72809 146.72087 179.23395 146.72087 179.87457 curveto +146.72087 183.58356 lineto +145.63689 183.58356 lineto +145.63689 177.02106 lineto +146.72087 177.02106 lineto +146.72087 178.04059 lineto +146.97868 177.64606 147.28142 177.35114 147.62907 177.15582 curveto +147.98063 176.96052 148.38493 176.86286 148.84196 176.86285 curveto +149.59587 176.86286 150.16618 177.09724 150.5529 177.56598 curveto +150.93961 178.03083 151.13297 178.71637 151.13298 179.62262 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +153.29509 177.02106 moveto +154.37321 177.02106 lineto +154.37321 183.58356 lineto +153.29509 183.58356 lineto +153.29509 177.02106 lineto +153.29509 174.46637 moveto +154.37321 174.46637 lineto +154.37321 175.8316 lineto +153.29509 175.8316 lineto +153.29509 174.46637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +162.07829 179.62262 moveto +162.07829 183.58356 lineto +161.00017 183.58356 lineto +161.00017 179.65778 lineto +161.00016 179.03669 160.87907 178.57184 160.63689 178.26324 curveto +160.39469 177.95466 160.03141 177.80036 159.54704 177.80035 curveto +158.96501 177.80036 158.50602 177.98591 158.17009 178.35699 curveto +157.83415 178.72809 157.66618 179.23395 157.66618 179.87457 curveto +157.66618 183.58356 lineto +156.5822 183.58356 lineto +156.5822 177.02106 lineto +157.66618 177.02106 lineto +157.66618 178.04059 lineto +157.92399 177.64606 158.22673 177.35114 158.57439 177.15582 curveto +158.92595 176.96052 159.33024 176.86286 159.78728 176.86285 curveto +160.54118 176.86286 161.11149 177.09724 161.49821 177.56598 curveto +161.88493 178.03083 162.07829 178.71637 162.07829 179.62262 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +168.55876 180.22614 moveto +168.55876 179.44489 168.39665 178.83942 168.07243 178.40973 curveto +167.75212 177.98005 167.30094 177.7652 166.71892 177.7652 curveto +166.14079 177.7652 165.68962 177.98005 165.3654 178.40973 curveto +165.04509 178.83942 164.88493 179.44489 164.88493 180.22614 curveto +164.88493 181.00348 165.04509 181.607 165.3654 182.03668 curveto +165.68962 182.46637 166.14079 182.68121 166.71892 182.68121 curveto +167.30094 182.68121 167.75212 182.46637 168.07243 182.03668 curveto +168.39665 181.607 168.55876 181.00348 168.55876 180.22614 curveto +169.63689 182.7691 moveto +169.63688 183.88629 169.38883 184.71637 168.89275 185.25934 curveto +168.39665 185.80621 167.63688 186.07965 166.61345 186.07965 curveto +166.23454 186.07965 165.87712 186.05035 165.54118 185.99176 curveto +165.20524 185.93707 164.87907 185.85113 164.56267 185.73395 curveto +164.56267 184.68512 lineto +164.87907 184.85699 165.19157 184.98395 165.50017 185.06598 curveto +165.80876 185.14801 166.12321 185.18902 166.44353 185.18903 curveto +167.15055 185.18902 167.67985 185.00348 168.03142 184.63239 curveto +168.38298 184.2652 168.55876 183.70856 168.55876 182.96246 curveto +168.55876 182.42926 lineto +168.3361 182.81598 168.05094 183.10504 167.70329 183.29645 curveto +167.35563 183.48785 166.93962 183.58356 166.45525 183.58356 curveto +165.65056 183.58356 165.00212 183.27692 164.50993 182.66364 curveto +164.01775 182.05036 163.77165 181.23786 163.77165 180.22614 curveto +163.77165 179.21051 164.01775 178.39606 164.50993 177.78278 curveto +165.00212 177.1695 165.65056 176.86286 166.45525 176.86285 curveto +166.93962 176.86286 167.35563 176.95856 167.70329 177.14996 curveto +168.05094 177.34138 168.3361 177.63044 168.55876 178.01715 curveto +168.55876 177.02106 lineto +169.63689 177.02106 lineto +169.63689 182.7691 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +175.6779 177.02106 moveto +176.75603 177.02106 lineto +176.75603 183.58356 lineto +175.6779 183.58356 lineto +175.6779 177.02106 lineto +175.6779 174.46637 moveto +176.75603 174.46637 lineto +176.75603 175.8316 lineto +175.6779 175.8316 lineto +175.6779 174.46637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +184.46111 179.62262 moveto +184.46111 183.58356 lineto +183.38298 183.58356 lineto +183.38298 179.65778 lineto +183.38297 179.03669 183.26188 178.57184 183.0197 178.26324 curveto +182.77751 177.95466 182.41423 177.80036 181.92986 177.80035 curveto +181.34782 177.80036 180.88884 177.98591 180.5529 178.35699 curveto +180.21696 178.72809 180.04899 179.23395 180.049 179.87457 curveto +180.049 183.58356 lineto +178.96501 183.58356 lineto +178.96501 177.02106 lineto +180.049 177.02106 lineto +180.049 178.04059 lineto +180.30681 177.64606 180.60954 177.35114 180.9572 177.15582 curveto +181.30876 176.96052 181.71305 176.86286 182.17009 176.86285 curveto +182.92399 176.86286 183.4943 177.09724 183.88103 177.56598 curveto +184.26774 178.03083 184.4611 178.71637 184.46111 179.62262 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +190.3322 180.99371 moveto +190.3322 177.02106 lineto +191.41032 177.02106 lineto +191.41032 180.9527 lineto +191.41032 181.57379 191.53142 182.04059 191.77361 182.35309 curveto +192.01579 182.66168 192.37907 182.81598 192.86345 182.81598 curveto +193.44548 182.81598 193.90446 182.63043 194.2404 182.25934 curveto +194.58024 181.88825 194.75016 181.38239 194.75017 180.74176 curveto +194.75017 177.02106 lineto +195.82829 177.02106 lineto +195.82829 183.58356 lineto +194.75017 183.58356 lineto +194.75017 182.57574 lineto +194.48844 182.97418 194.18376 183.27106 193.83611 183.46637 curveto +193.49235 183.65778 193.09196 183.75348 192.63493 183.75348 curveto +191.88102 183.75348 191.30876 183.5191 190.91814 183.05035 curveto +190.52751 182.58161 190.3322 181.89606 190.3322 180.99371 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +202.24431 177.21442 moveto +202.24431 178.23395 lineto +201.93962 178.0777 201.62321 177.96052 201.29509 177.88239 curveto +200.96696 177.80427 200.62712 177.7652 200.27556 177.7652 curveto +199.7404 177.7652 199.33806 177.84723 199.06853 178.01129 curveto +198.8029 178.17536 198.67009 178.42145 198.67009 178.74957 curveto +198.67009 178.99958 198.76579 179.19684 198.9572 179.34137 curveto +199.1486 179.482 199.53337 179.61676 200.1115 179.74567 curveto +200.48064 179.8277 lineto +201.24626 179.99176 201.78923 180.22419 202.10954 180.52496 curveto +202.43376 180.82184 202.59587 181.23786 202.59587 181.77301 curveto +202.59587 182.38239 202.35368 182.86481 201.86931 183.22028 curveto +201.38884 183.57574 200.72673 183.75348 199.88298 183.75348 curveto +199.53142 183.75348 199.16423 183.71832 198.78142 183.64801 curveto +198.40251 183.5816 198.00212 183.48004 197.58025 183.34332 curveto +197.58025 182.23004 lineto +197.97868 182.43707 198.37126 182.59332 198.75798 182.69879 curveto +199.1447 182.80035 199.52751 182.85114 199.90642 182.85114 curveto +200.41423 182.85114 200.80485 182.7652 201.07829 182.59332 curveto +201.35173 182.41754 201.48844 182.17145 201.48845 181.85504 curveto +201.48844 181.56207 201.38884 181.33747 201.18962 181.18121 curveto +200.9943 181.02497 200.56266 180.87458 199.8947 180.73004 curveto +199.5197 180.64215 lineto +198.85173 180.50153 198.36931 180.28669 198.07243 179.99762 curveto +197.77556 179.70465 197.62712 179.30426 197.62712 178.79645 curveto +197.62712 178.17927 197.84587 177.7027 198.28337 177.36676 curveto +198.72087 177.03083 199.34196 176.86286 200.14665 176.86285 curveto +200.54509 176.86286 200.92009 176.89216 201.27165 176.95074 curveto +201.62321 177.00934 201.94743 177.09724 202.24431 177.21442 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +209.93181 180.03278 moveto +209.93181 180.56012 lineto +204.97478 180.56012 lineto +205.02165 181.30231 205.24431 181.86872 205.64275 182.25934 curveto +206.04509 182.64606 206.60368 182.83942 207.31853 182.83942 curveto +207.73259 182.83942 208.13298 182.78864 208.5197 182.68707 curveto +208.91032 182.58551 209.29704 182.43317 209.67986 182.23004 curveto +209.67986 183.24957 lineto +209.29313 183.41364 208.89665 183.53864 208.4904 183.62457 curveto +208.08415 183.71051 207.67204 183.75348 207.25407 183.75348 curveto +206.2072 183.75348 205.37712 183.44879 204.76384 182.83942 curveto +204.15446 182.23004 203.84978 181.40582 203.84978 180.36676 curveto +203.84978 179.29255 204.13884 178.44098 204.71696 177.81207 curveto +205.29899 177.17927 206.0822 176.86286 207.06657 176.86285 curveto +207.94938 176.86286 208.64665 177.14802 209.15837 177.71832 curveto +209.67399 178.28473 209.9318 179.05622 209.93181 180.03278 curveto +208.85368 179.71637 moveto +208.84587 179.12653 208.67985 178.65583 208.35564 178.30426 curveto +208.03532 177.9527 207.60954 177.77692 207.07829 177.77692 curveto +206.47673 177.77692 205.99431 177.94684 205.63103 178.28668 curveto +205.27165 178.62653 205.06462 179.10505 205.00993 179.72223 curveto +208.85368 179.71637 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +215.50407 178.02887 moveto +215.38298 177.95856 215.25016 177.90778 215.10564 177.87653 curveto +214.96501 177.84138 214.80876 177.8238 214.63689 177.82379 curveto +214.02751 177.8238 213.55876 178.02302 213.23064 178.42145 curveto +212.90642 178.81598 212.74431 179.38434 212.74431 180.12653 curveto +212.74431 183.58356 lineto +211.66032 183.58356 lineto +211.66032 177.02106 lineto +212.74431 177.02106 lineto +212.74431 178.04059 lineto +212.97087 177.64216 213.26579 177.34724 213.62907 177.15582 curveto +213.99235 176.96052 214.43376 176.86286 214.95329 176.86285 curveto +215.02751 176.86286 215.10954 176.86872 215.19939 176.88043 curveto +215.28923 176.88825 215.38883 176.90192 215.49821 176.92145 curveto +215.50407 178.02887 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +225.57634 178.28082 moveto +225.84586 177.79645 226.16813 177.43903 226.54314 177.20856 curveto +226.91813 176.97809 227.35953 176.86286 227.86736 176.86285 curveto +228.55094 176.86286 229.07828 177.10309 229.44939 177.58356 curveto +229.82047 178.06013 230.00602 178.73981 230.00603 179.62262 curveto +230.00603 183.58356 lineto +228.92204 183.58356 lineto +228.92204 179.65778 lineto +228.92203 179.02887 228.81071 178.56208 228.58806 178.25739 curveto +228.36539 177.9527 228.02555 177.80036 227.56853 177.80035 curveto +227.00993 177.80036 226.56852 177.98591 226.24431 178.35699 curveto +225.92008 178.72809 225.75797 179.23395 225.75798 179.87457 curveto +225.75798 183.58356 lineto +224.674 183.58356 lineto +224.674 179.65778 lineto +224.67399 179.02497 224.56266 178.55817 224.34001 178.25739 curveto +224.11735 177.9527 223.7736 177.80036 223.30876 177.80035 curveto +222.75798 177.80036 222.32048 177.98786 221.99626 178.36285 curveto +221.67204 178.73395 221.50993 179.23786 221.50993 179.87457 curveto +221.50993 183.58356 lineto +220.42595 183.58356 lineto +220.42595 177.02106 lineto +221.50993 177.02106 lineto +221.50993 178.04059 lineto +221.75602 177.63825 222.05095 177.34138 222.3947 177.14996 curveto +222.73845 176.95856 223.14665 176.86286 223.61931 176.86285 curveto +224.09587 176.86286 224.50016 176.98395 224.8322 177.22614 curveto +225.16813 177.46833 225.41618 177.81989 225.57634 178.28082 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +234.70525 177.77692 moveto +234.12712 177.77692 233.67009 178.00348 233.33415 178.4566 curveto +232.99821 178.90583 232.83024 179.52301 232.83025 180.30817 curveto +232.83024 181.09333 232.99626 181.71247 233.32829 182.16559 curveto +233.66423 182.61481 234.12321 182.83942 234.70525 182.83942 curveto +235.27946 182.83942 235.73454 182.61285 236.07048 182.15973 curveto +236.40641 181.70661 236.57438 181.08942 236.57439 180.30817 curveto +236.57438 179.53083 236.40641 178.91559 236.07048 178.46246 curveto +235.73454 178.00544 235.27946 177.77692 234.70525 177.77692 curveto +234.70525 176.86285 moveto +235.64274 176.86286 236.37907 177.16755 236.91423 177.77692 curveto +237.44938 178.3863 237.71696 179.23005 237.71696 180.30817 curveto +237.71696 181.38239 237.44938 182.22614 236.91423 182.83942 curveto +236.37907 183.44879 235.64274 183.75348 234.70525 183.75348 curveto +233.76384 183.75348 233.02556 183.44879 232.4904 182.83942 curveto +231.95915 182.22614 231.69353 181.38239 231.69353 180.30817 curveto +231.69353 179.23005 231.95915 178.3863 232.4904 177.77692 curveto +233.02556 177.16755 233.76384 176.86286 234.70525 176.86285 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +243.81657 178.01715 moveto +243.81657 174.46637 lineto +244.8947 174.46637 lineto +244.8947 183.58356 lineto +243.81657 183.58356 lineto +243.81657 182.59918 lineto +243.59001 182.98981 243.3029 183.28082 242.95525 183.47223 curveto +242.61149 183.65973 242.19743 183.75348 241.71306 183.75348 curveto +240.92009 183.75348 240.2736 183.43707 239.77361 182.80426 curveto +239.27751 182.17145 239.02946 181.33942 239.02946 180.30817 curveto +239.02946 179.27692 239.27751 178.44489 239.77361 177.81207 curveto +240.2736 177.17927 240.92009 176.86286 241.71306 176.86285 curveto +242.19743 176.86286 242.61149 176.95856 242.95525 177.14996 curveto +243.3029 177.33747 243.59001 177.62653 243.81657 178.01715 curveto +240.14275 180.30817 moveto +240.14274 181.10114 240.30485 181.72418 240.62907 182.17731 curveto +240.9572 182.62653 241.40641 182.85114 241.97673 182.85114 curveto +242.54704 182.85114 242.99626 182.62653 243.32439 182.17731 curveto +243.65251 181.72418 243.81657 181.10114 243.81657 180.30817 curveto +243.81657 179.5152 243.65251 178.89411 243.32439 178.44489 curveto +242.99626 177.99177 242.54704 177.7652 241.97673 177.7652 curveto +241.40641 177.7652 240.9572 177.99177 240.62907 178.44489 curveto +240.30485 178.89411 240.14274 179.5152 240.14275 180.30817 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +252.72868 180.03278 moveto +252.72868 180.56012 lineto +247.77165 180.56012 lineto +247.81853 181.30231 248.04118 181.86872 248.43962 182.25934 curveto +248.84196 182.64606 249.40056 182.83942 250.1154 182.83942 curveto +250.52946 182.83942 250.92985 182.78864 251.31657 182.68707 curveto +251.70719 182.58551 252.09391 182.43317 252.47673 182.23004 curveto +252.47673 183.24957 lineto +252.09001 183.41364 251.69352 183.53864 251.28728 183.62457 curveto +250.88102 183.71051 250.46891 183.75348 250.05095 183.75348 curveto +249.00407 183.75348 248.17399 183.44879 247.56071 182.83942 curveto +246.95134 182.23004 246.64665 181.40582 246.64665 180.36676 curveto +246.64665 179.29255 246.93571 178.44098 247.51384 177.81207 curveto +248.09587 177.17927 248.87907 176.86286 249.86345 176.86285 curveto +250.74626 176.86286 251.44352 177.14802 251.95525 177.71832 curveto +252.47086 178.28473 252.72868 179.05622 252.72868 180.03278 curveto +251.65056 179.71637 moveto +251.64274 179.12653 251.47672 178.65583 251.15251 178.30426 curveto +250.83219 177.9527 250.40641 177.77692 249.87517 177.77692 curveto +249.2736 177.77692 248.79118 177.94684 248.4279 178.28668 curveto +248.06853 178.62653 247.86149 179.10505 247.80681 179.72223 curveto +251.65056 179.71637 lineto +fill +grestore +grestore +gsave [1.037105 0 0 0.964223 0 0] concat +gsave +0 0 0 setrgbcolor +newpath +448.62106 384.39758 moveto +448.62106 390.39646 lineto +449.8818 390.39646 lineto +450.9462 390.39646 451.72469 390.15533 452.21729 389.67308 curveto +452.71331 389.19083 452.96133 388.42956 452.96133 387.38927 curveto +452.96133 386.35587 452.71331 385.59977 452.21729 385.12096 curveto +451.72469 384.63871 450.9462 384.39758 449.8818 384.39758 curveto +448.62106 384.39758 lineto +447.57732 383.53986 moveto +449.72163 383.53986 lineto +451.21661 383.53986 452.31373 383.85161 453.013 384.47508 curveto +453.71226 385.09513 454.0619 386.06652 454.0619 387.38927 curveto +454.0619 388.71891 453.71054 389.69547 453.00784 390.31895 curveto +452.30512 390.94244 451.20972 391.25418 449.72163 391.25418 curveto +447.57732 391.25418 lineto +447.57732 383.53986 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +458.30917 388.34516 moveto +457.54101 388.34517 457.00881 388.433 456.71257 388.60868 curveto +456.41633 388.78436 456.26821 389.08404 456.26821 389.50774 curveto +456.26821 389.84531 456.37844 390.114 456.5989 390.31379 curveto +456.8228 390.51013 457.12593 390.60831 457.50829 390.60831 curveto +458.03532 390.60831 458.45729 390.4223 458.7742 390.05027 curveto +459.09455 389.6748 459.25473 389.17705 459.25473 388.55701 curveto +459.25473 388.34516 lineto +458.30917 388.34516 lineto +460.20546 387.95247 moveto +460.20546 391.25418 lineto +459.25473 391.25418 lineto +459.25473 390.37579 lineto +459.03772 390.72715 458.76731 390.98722 458.44352 391.15601 curveto +458.11971 391.32135 457.72358 391.40402 457.25511 391.40402 curveto +456.66262 391.40402 456.1907 391.23868 455.83935 390.90799 curveto +455.49144 390.57386 455.31748 390.12778 455.31748 389.56974 curveto +455.31748 388.9187 455.5345 388.42784 455.96852 388.09715 curveto +456.406 387.76646 457.05704 387.60112 457.92165 387.60111 curveto +459.25473 387.60111 lineto +459.25473 387.50811 lineto +459.25473 387.07064 459.11005 386.73306 458.82071 386.49538 curveto +458.5348 386.25426 458.13177 386.13369 457.61163 386.13369 curveto +457.28094 386.13369 456.95886 386.17331 456.6454 386.25253 curveto +456.33194 386.33176 456.03053 386.4506 455.74118 386.60905 curveto +455.74118 385.73066 lineto +456.08909 385.59633 456.42666 385.49643 456.75391 385.43098 curveto +457.08115 385.36209 457.39978 385.32764 457.7098 385.32764 curveto +458.54685 385.32764 459.17206 385.54466 459.58542 385.97868 curveto +459.99878 386.41271 460.20546 387.07064 460.20546 387.95247 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +465.5223 386.35587 moveto +465.41551 386.29387 465.29839 386.24909 465.17094 386.22153 curveto +465.04693 386.19053 464.90915 386.17503 464.75759 386.17502 curveto +464.22021 386.17503 463.80686 386.35071 463.51751 386.70206 curveto +463.2316 387.04997 463.08864 387.55117 463.08865 388.20565 curveto +463.08865 391.25418 lineto +462.13275 391.25418 lineto +462.13275 385.46715 lineto +463.08865 385.46715 lineto +463.08865 386.3662 lineto +463.28843 386.01485 463.54851 385.75478 463.86886 385.58599 curveto +464.18921 385.41376 464.57846 385.32764 465.0366 385.32764 curveto +465.10205 385.32764 465.17439 385.33281 465.25362 385.34314 curveto +465.33284 385.35003 465.42068 385.36209 465.51713 385.37931 curveto +465.5223 386.35587 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +466.4937 383.21434 moveto +467.44959 383.21434 lineto +467.44959 387.9628 lineto +470.28627 385.46715 lineto +471.50051 385.46715 lineto +468.43132 388.17465 lineto +471.62969 391.25418 lineto +470.38961 391.25418 lineto +467.44959 388.42783 lineto +467.44959 391.25418 lineto +466.4937 391.25418 lineto +466.4937 383.21434 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +479.39052 386.35587 moveto +479.28373 386.29387 479.16661 386.24909 479.03916 386.22153 curveto +478.91515 386.19053 478.77736 386.17503 478.6258 386.17502 curveto +478.08843 386.17503 477.67507 386.35071 477.38572 386.70206 curveto +477.09981 387.04997 476.95686 387.55117 476.95686 388.20565 curveto +476.95686 391.25418 lineto +476.00097 391.25418 lineto +476.00097 385.46715 lineto +476.95686 385.46715 lineto +476.95686 386.3662 lineto +477.15665 386.01485 477.41672 385.75478 477.73708 385.58599 curveto +478.05743 385.41376 478.44667 385.32764 478.90482 385.32764 curveto +478.97026 385.32764 479.0426 385.33281 479.12183 385.34314 curveto +479.20106 385.35003 479.28889 385.36209 479.38535 385.37931 curveto +479.39052 386.35587 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +485.12072 388.12298 moveto +485.12072 388.58801 lineto +480.74944 388.58801 lineto +480.79077 389.2425 480.98712 389.74197 481.33848 390.08644 curveto +481.69327 390.42746 482.18586 390.59797 482.81624 390.59797 curveto +483.18137 390.59797 483.53444 390.55319 483.87547 390.46363 curveto +484.21993 390.37407 484.56095 390.23973 484.89854 390.0606 curveto +484.89854 390.95966 lineto +484.55751 391.10434 484.20788 391.21457 483.84964 391.29035 curveto +483.49139 391.36613 483.12797 391.40402 482.7594 391.40402 curveto +481.83623 391.40402 481.10424 391.13534 480.56343 390.59797 curveto +480.02606 390.06061 479.75737 389.33378 479.75737 388.4175 curveto +479.75737 387.47022 480.01228 386.71929 480.52209 386.16469 curveto +481.03534 385.60666 481.726 385.32764 482.59406 385.32764 curveto +483.37254 385.32764 483.98742 385.5791 484.43867 386.08202 curveto +484.89336 386.5815 485.12071 387.26182 485.12072 388.12298 curveto +484.16999 387.84396 moveto +484.16309 387.32382 484.0167 386.90874 483.73079 386.59872 curveto +483.44833 386.2887 483.07286 386.13369 482.60439 386.13369 curveto +482.07391 386.13369 481.64849 386.28354 481.32814 386.58322 curveto +481.01123 386.88291 480.82866 387.30488 480.78044 387.84913 curveto +484.16999 387.84396 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +490.48922 386.34553 moveto +490.48922 383.21434 lineto +491.43995 383.21434 lineto +491.43995 391.25418 lineto +490.48922 391.25418 lineto +490.48922 390.38613 lineto +490.28943 390.73059 490.03625 390.98722 489.72968 391.15601 curveto +489.42654 391.32135 489.06141 391.40402 488.63427 391.40402 curveto +487.935 391.40402 487.36491 391.12501 486.924 390.56697 curveto +486.48652 390.00894 486.26779 389.27522 486.26779 388.36583 curveto +486.26779 387.45644 486.48652 386.72273 486.924 386.16469 curveto +487.36491 385.60666 487.935 385.32764 488.63427 385.32764 curveto +489.06141 385.32764 489.42654 385.41204 489.72968 385.58082 curveto +490.03625 385.74617 490.28943 386.00107 490.48922 386.34553 curveto +487.24952 388.36583 moveto +487.24952 389.0651 487.39247 389.61452 487.67838 390.0141 curveto +487.96773 390.41024 488.36386 390.60831 488.86679 390.60831 curveto +489.3697 390.60831 489.76584 390.41024 490.0552 390.0141 curveto +490.34454 389.61452 490.48922 389.0651 490.48922 388.36583 curveto +490.48922 387.66657 490.34454 387.11887 490.0552 386.72273 curveto +489.76584 386.32315 489.3697 386.12336 488.86679 386.12335 curveto +488.36386 386.12336 487.96773 386.32315 487.67838 386.72273 curveto +487.39247 387.11887 487.24952 387.66657 487.24952 388.36583 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +497.00997 389.94176 moveto +498.10021 389.94176 lineto +498.10021 391.25418 lineto +497.00997 391.25418 lineto +497.00997 389.94176 lineto +497.00997 385.78233 moveto +498.10021 385.78233 lineto +498.10021 387.09475 lineto +497.00997 387.09475 lineto +497.00997 385.78233 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +503.1587 385.46715 moveto +504.10943 385.46715 lineto +505.29783 389.9831 lineto +506.48108 385.46715 lineto +507.60231 385.46715 lineto +508.79072 389.9831 lineto +509.97397 385.46715 lineto +510.92469 385.46715 lineto +509.41076 391.25418 lineto +508.28952 391.25418 lineto +507.04428 386.51088 lineto +505.79387 391.25418 lineto +504.67263 391.25418 lineto +503.1587 385.46715 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +515.00145 388.34516 moveto +514.23329 388.34517 513.70109 388.433 513.40485 388.60868 curveto +513.10861 388.78436 512.96049 389.08404 512.96049 389.50774 curveto +512.96049 389.84531 513.07072 390.114 513.29118 390.31379 curveto +513.51508 390.51013 513.81821 390.60831 514.20057 390.60831 curveto +514.7276 390.60831 515.14957 390.4223 515.46648 390.05027 curveto +515.78683 389.6748 515.94701 389.17705 515.94701 388.55701 curveto +515.94701 388.34516 lineto +515.00145 388.34516 lineto +516.89774 387.95247 moveto +516.89774 391.25418 lineto +515.94701 391.25418 lineto +515.94701 390.37579 lineto +515.72999 390.72715 515.45959 390.98722 515.13579 391.15601 curveto +514.81199 391.32135 514.41586 391.40402 513.94739 391.40402 curveto +513.3549 391.40402 512.88298 391.23868 512.53163 390.90799 curveto +512.18372 390.57386 512.00976 390.12778 512.00976 389.56974 curveto +512.00976 388.9187 512.22678 388.42784 512.6608 388.09715 curveto +513.09827 387.76646 513.74932 387.60112 514.61393 387.60111 curveto +515.94701 387.60111 lineto +515.94701 387.50811 lineto +515.94701 387.07064 515.80233 386.73306 515.51299 386.49538 curveto +515.22707 386.25426 514.82405 386.13369 514.30391 386.13369 curveto +513.97322 386.13369 513.65114 386.17331 513.33768 386.25253 curveto +513.02421 386.33176 512.72281 386.4506 512.43346 386.60905 curveto +512.43346 385.73066 lineto +512.78137 385.59633 513.11894 385.49643 513.44619 385.43098 curveto +513.77343 385.36209 514.09206 385.32764 514.40208 385.32764 curveto +515.23913 385.32764 515.86434 385.54466 516.2777 385.97868 curveto +516.69106 386.41271 516.89773 387.07064 516.89774 387.95247 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +518.8612 385.46715 moveto +519.81193 385.46715 lineto +519.81193 391.25418 lineto +518.8612 391.25418 lineto +518.8612 385.46715 lineto +518.8612 383.21434 moveto +519.81193 383.21434 lineto +519.81193 384.41825 lineto +518.8612 384.41825 lineto +518.8612 383.21434 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +522.73644 383.82404 moveto +522.73644 385.46715 lineto +524.69474 385.46715 lineto +524.69474 386.20603 lineto +522.73644 386.20603 lineto +522.73644 389.34756 lineto +522.73644 389.81948 522.80017 390.12261 522.92762 390.25695 curveto +523.05852 390.39129 523.32203 390.45846 523.71817 390.45846 curveto +524.69474 390.45846 lineto +524.69474 391.25418 lineto +523.71817 391.25418 lineto +522.98446 391.25418 522.47809 391.11812 522.19908 390.84599 curveto +521.92006 390.57042 521.78055 390.07094 521.78055 389.34756 curveto +521.78055 386.20603 lineto +521.08301 386.20603 lineto +521.08301 385.46715 lineto +521.78055 385.46715 lineto +521.78055 383.82404 lineto +522.73644 383.82404 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +525.95032 385.46715 moveto +526.90104 385.46715 lineto +526.90104 391.25418 lineto +525.95032 391.25418 lineto +525.95032 385.46715 lineto +525.95032 383.21434 moveto +526.90104 383.21434 lineto +526.90104 384.41825 lineto +525.95032 384.41825 lineto +525.95032 383.21434 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +533.69564 387.76129 moveto +533.69564 391.25418 lineto +532.74492 391.25418 lineto +532.74492 387.79229 lineto +532.74491 387.2446 532.63813 386.83468 532.42456 386.56255 curveto +532.21099 386.29043 531.89063 386.15436 531.4635 386.15436 curveto +530.95024 386.15436 530.54549 386.31798 530.24926 386.64522 curveto +529.95301 386.97247 529.80489 387.41855 529.80489 387.98347 curveto +529.80489 391.25418 lineto +528.849 391.25418 lineto +528.849 385.46715 lineto +529.80489 385.46715 lineto +529.80489 386.3662 lineto +530.03224 386.0183 530.2992 385.75823 530.60578 385.58599 curveto +530.9158 385.41376 531.27232 385.32764 531.67535 385.32764 curveto +532.34016 385.32764 532.84308 385.53432 533.18411 385.94768 curveto +533.52513 386.3576 533.69564 386.96213 533.69564 387.76129 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +539.41034 388.29349 moveto +539.41034 387.60456 539.26738 387.07064 538.98148 386.69172 curveto +538.69901 386.31282 538.30115 386.12336 537.7879 386.12335 curveto +537.27809 386.12336 536.88023 386.31282 536.59433 386.69172 curveto +536.31186 387.07064 536.17063 387.60456 536.17063 388.29349 curveto +536.17063 388.97898 536.31186 389.51118 536.59433 389.89009 curveto +536.88023 390.26901 537.27809 390.45846 537.7879 390.45846 curveto +538.30115 390.45846 538.69901 390.26901 538.98148 389.89009 curveto +539.26738 389.51118 539.41034 388.97898 539.41034 388.29349 curveto +540.36107 390.53597 moveto +540.36106 391.52114 540.14233 392.25313 539.70486 392.73194 curveto +539.26738 393.21419 538.5974 393.45532 537.6949 393.45532 curveto +537.36076 393.45532 537.04558 393.42948 536.74934 393.37782 curveto +536.45309 393.32959 536.16547 393.25381 535.88645 393.15047 curveto +535.88645 392.22558 lineto +536.16547 392.37714 536.44104 392.48909 536.71317 392.56143 curveto +536.98529 392.63377 537.26259 392.66994 537.54505 392.66994 curveto +538.16854 392.66994 538.63529 392.50631 538.94531 392.17907 curveto +539.25533 391.85527 539.41034 391.36441 539.41034 390.70648 curveto +539.41034 390.23628 lineto +539.21399 390.57731 538.96253 390.83221 538.65596 391.001 curveto +538.34938 391.16979 537.98252 391.25418 537.55539 391.25418 curveto +536.84579 391.25418 536.27397 390.98378 535.83995 390.44296 curveto +535.40592 389.90215 535.1889 389.18566 535.1889 388.29349 curveto +535.1889 387.39788 535.40592 386.67967 535.83995 386.13886 curveto +536.27397 385.59805 536.84579 385.32764 537.55539 385.32764 curveto +537.98252 385.32764 538.34938 385.41204 538.65596 385.58082 curveto +538.96253 385.74961 539.21399 386.00452 539.41034 386.34553 curveto +539.41034 385.46715 lineto +540.36107 385.46715 lineto +540.36107 390.53597 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +548.61793 383.21434 moveto +548.61793 384.00489 lineto +547.70854 384.00489 lineto +547.36751 384.00489 547.12983 384.07379 546.99549 384.21157 curveto +546.86459 384.34936 546.79914 384.59737 546.79914 384.95561 curveto +546.79914 385.46715 lineto +548.36474 385.46715 lineto +548.36474 386.20603 lineto +546.79914 386.20603 lineto +546.79914 391.25418 lineto +545.84325 391.25418 lineto +545.84325 386.20603 lineto +544.93386 386.20603 lineto +544.93386 385.46715 lineto +545.84325 385.46715 lineto +545.84325 385.06412 lineto +545.84325 384.41997 545.99309 383.9515 546.29278 383.6587 curveto +546.59246 383.36246 547.06783 383.21434 547.71887 383.21434 curveto +548.61793 383.21434 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +551.65095 386.13369 moveto +551.14114 386.13369 550.73812 386.33348 550.44188 386.73306 curveto +550.14563 387.1292 549.99751 387.67346 549.99751 388.36583 curveto +549.99751 389.05821 550.14391 389.60419 550.43671 390.00377 curveto +550.73295 390.39991 551.1377 390.59797 551.65095 390.59797 curveto +552.15732 390.59797 552.55862 390.39818 552.85486 389.9986 curveto +553.1511 389.59902 553.29922 389.05477 553.29923 388.36583 curveto +553.29922 387.68035 553.1511 387.13781 552.85486 386.73823 curveto +552.55862 386.33521 552.15732 386.13369 551.65095 386.13369 curveto +551.65095 385.32764 moveto +552.47767 385.32764 553.12699 385.59633 553.59891 386.13369 curveto +554.07082 386.67106 554.30678 387.41511 554.30679 388.36583 curveto +554.30678 389.31311 554.07082 390.05716 553.59891 390.59797 curveto +553.12699 391.13534 552.47767 391.40402 551.65095 391.40402 curveto +550.82079 391.40402 550.16975 391.13534 549.69783 390.59797 curveto +549.22935 390.05716 548.99512 389.31311 548.99512 388.36583 curveto +548.99512 387.41511 549.22935 386.67106 549.69783 386.13369 curveto +550.16975 385.59633 550.82079 385.32764 551.65095 385.32764 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +559.23094 386.35587 moveto +559.12415 386.29387 559.00703 386.24909 558.87958 386.22153 curveto +558.75557 386.19053 558.61778 386.17503 558.46622 386.17502 curveto +557.92885 386.17503 557.51549 386.35071 557.22614 386.70206 curveto +556.94023 387.04997 556.79728 387.55117 556.79728 388.20565 curveto +556.79728 391.25418 lineto +555.84139 391.25418 lineto +555.84139 385.46715 lineto +556.79728 385.46715 lineto +556.79728 386.3662 lineto +556.99707 386.01485 557.25714 385.75478 557.5775 385.58599 curveto +557.89785 385.41376 558.28709 385.32764 558.74524 385.32764 curveto +558.81068 385.32764 558.88302 385.33281 558.96225 385.34314 curveto +559.04148 385.35003 559.12931 385.36209 559.22577 385.37931 curveto +559.23094 386.35587 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +563.64872 383.53986 moveto +564.69245 383.53986 lineto +564.69245 391.25418 lineto +563.64872 391.25418 lineto +563.64872 383.53986 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +568.41786 383.53986 moveto +569.29625 383.53986 lineto +566.60941 392.23591 lineto +565.73102 392.23591 lineto +568.41786 383.53986 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +573.47635 384.24773 moveto +572.71852 384.24774 572.1157 384.5302 571.6679 385.09512 curveto +571.22353 385.66005 571.00135 386.42993 571.00135 387.40477 curveto +571.00135 388.37617 571.22353 389.14433 571.6679 389.70925 curveto +572.1157 390.27418 572.71852 390.55664 573.47635 390.55664 curveto +574.23417 390.55664 574.83354 390.27418 575.27446 389.70925 curveto +575.71881 389.14433 575.941 388.37617 575.941 387.40477 curveto +575.941 386.42993 575.71881 385.66005 575.27446 385.09512 curveto +574.83354 384.5302 574.23417 384.24774 573.47635 384.24773 curveto +573.47635 383.40035 moveto +574.55796 383.40036 575.42257 383.76377 576.07018 384.49058 curveto +576.71777 385.21397 577.04156 386.18536 577.04157 387.40477 curveto +577.04156 388.62074 576.71777 389.59213 576.07018 390.31895 curveto +575.42257 391.04233 574.55796 391.40402 573.47635 391.40402 curveto +572.39127 391.40402 571.52322 391.04233 570.87218 390.31895 curveto +570.22458 389.59558 569.90078 388.62418 569.90078 387.40477 curveto +569.90078 386.18536 570.22458 385.21397 570.87218 384.49058 curveto +571.52322 383.76377 572.39127 383.40036 573.47635 383.40035 curveto +fill +grestore +grestore +gsave +<< +/ShadingType 2 +/ColorSpace /DeviceRGB +/Coords [13.768714 1253.7404 69.307785 1253.7404] +/Extend [true true] +/Domain [0 1] +/Function << +/FunctionType 3 +/Functions +[ +<< +/FunctionType 2 +/Domain [0 1] +/C0 [1 1 1] +/C1 [1 1 1] +/N 1 +>> +] +/Domain [0 1] +/Bounds [ ] +/Encode [ 0 1 ] +>> +>> +newpath +6.6715565 346.99756 moveto +456.9358 346.99756 456.9358 346.99756 456.9358 346.99756 curveto +eoclip +gsave [8.209048 0 0 0.09472844 -109.1859 228.2326] concat +shfill +grestore +grestore +1 0.36862746 0.3764706 setrgbcolor +[] 0 setdash +5.4563475 setlinewidth +0 setlinejoin +0 setlinecap +newpath +6.6715565 346.99756 moveto +456.9358 346.99756 456.9358 346.99756 456.9358 346.99756 curveto +stroke +gsave [1.037105 0 0 0.964223 0 0] concat +gsave +0 0 0 setrgbcolor +newpath +448.5826 357.69497 moveto +448.5826 360.59366 lineto +449.89502 360.59366 lineto +450.38072 360.59366 450.75618 360.46793 451.02143 360.21647 curveto +451.28666 359.96501 451.41928 359.60676 451.41929 359.14173 curveto +451.41928 358.68015 451.28666 358.32363 451.02143 358.07216 curveto +450.75618 357.82071 450.38072 357.69498 449.89502 357.69497 curveto +448.5826 357.69497 lineto +447.53887 356.83725 moveto +449.89502 356.83725 lineto +450.75963 356.83726 451.41239 357.0336 451.85331 357.42629 curveto +452.29767 357.81554 452.51985 358.38735 452.51986 359.14173 curveto +452.51985 359.90301 452.29767 360.47826 451.85331 360.86751 curveto +451.41239 361.25676 450.75963 361.45138 449.89502 361.45138 curveto +448.5826 361.45138 lineto +448.5826 364.55157 lineto +447.53887 364.55157 lineto +447.53887 356.83725 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +453.65659 358.76454 moveto +454.60732 358.76454 lineto +454.60732 364.55157 lineto +453.65659 364.55157 lineto +453.65659 358.76454 lineto +453.65659 356.51173 moveto +454.60732 356.51173 lineto +454.60732 357.71564 lineto +453.65659 357.71564 lineto +453.65659 356.51173 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +461.40192 361.05869 moveto +461.40192 364.55157 lineto +460.45119 364.55157 lineto +460.45119 361.08969 lineto +460.45119 360.54199 460.3444 360.13208 460.13084 359.85994 curveto +459.91727 359.58782 459.59691 359.45175 459.16978 359.45175 curveto +458.65652 359.45175 458.25177 359.61538 457.95553 359.94261 curveto +457.65929 360.26986 457.51117 360.71595 457.51117 361.28087 curveto +457.51117 364.55157 lineto +456.55528 364.55157 lineto +456.55528 358.76454 lineto +457.51117 358.76454 lineto +457.51117 359.6636 lineto +457.73852 359.31569 458.00548 359.05562 458.31206 358.88338 curveto +458.62207 358.71115 458.9786 358.62504 459.38163 358.62503 curveto +460.04644 358.62504 460.54936 358.83172 460.89039 359.24507 curveto +461.2314 359.65499 461.40191 360.25953 461.40192 361.05869 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +463.27237 356.51173 moveto +464.22827 356.51173 lineto +464.22827 361.2602 lineto +467.06495 358.76454 lineto +468.27919 358.76454 lineto +465.21 361.47205 lineto +468.40837 364.55157 lineto +467.16829 364.55157 lineto +464.22827 361.72523 lineto +464.22827 364.55157 lineto +463.27237 364.55157 lineto +463.27237 356.51173 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +473.05866 363.23916 moveto +474.1489 363.23916 lineto +474.1489 364.55157 lineto +473.05866 364.55157 lineto +473.05866 363.23916 lineto +473.05866 359.07973 moveto +474.1489 359.07973 lineto +474.1489 360.39214 lineto +473.05866 360.39214 lineto +473.05866 359.07973 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +483.11364 359.65326 moveto +483.00685 359.59126 482.88973 359.54648 482.76228 359.51892 curveto +482.63827 359.48792 482.50048 359.47242 482.34892 359.47242 curveto +481.81155 359.47242 481.39819 359.6481 481.10884 359.99945 curveto +480.82293 360.34737 480.67998 360.84856 480.67998 361.50305 curveto +480.67998 364.55157 lineto +479.72409 364.55157 lineto +479.72409 358.76454 lineto +480.67998 358.76454 lineto +480.67998 359.6636 lineto +480.87977 359.31225 481.13984 359.05217 481.4602 358.88338 curveto +481.78055 358.71115 482.1698 358.62504 482.62794 358.62503 curveto +482.69338 358.62504 482.76572 358.6302 482.84495 358.64053 curveto +482.92418 358.64743 483.01202 358.65948 483.10847 358.6767 curveto +483.11364 359.65326 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +484.02303 362.26776 moveto +484.02303 358.76454 lineto +484.97375 358.76454 lineto +484.97375 362.23159 lineto +484.97375 362.7793 485.08054 363.19093 485.29411 363.46651 curveto +485.50768 363.73863 485.82803 363.8747 486.25517 363.8747 curveto +486.76842 363.8747 487.17317 363.71108 487.46941 363.38383 curveto +487.76909 363.05659 487.91894 362.61051 487.91894 362.04558 curveto +487.91894 358.76454 lineto +488.86967 358.76454 lineto +488.86967 364.55157 lineto +487.91894 364.55157 lineto +487.91894 363.66285 lineto +487.68815 364.01421 487.41946 364.276 487.11289 364.44823 curveto +486.80976 364.61702 486.45668 364.70142 486.05366 364.70142 curveto +485.38883 364.70142 484.88419 364.49474 484.53973 364.08138 curveto +484.19526 363.66802 484.02303 363.06348 484.02303 362.26776 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +495.64877 361.05869 moveto +495.64877 364.55157 lineto +494.69804 364.55157 lineto +494.69804 361.08969 lineto +494.69804 360.54199 494.59125 360.13208 494.37769 359.85994 curveto +494.16411 359.58782 493.84376 359.45175 493.41663 359.45175 curveto +492.90337 359.45175 492.49862 359.61538 492.20238 359.94261 curveto +491.90614 360.26986 491.75802 360.71595 491.75802 361.28087 curveto +491.75802 364.55157 lineto +490.80213 364.55157 lineto +490.80213 358.76454 lineto +491.75802 358.76454 lineto +491.75802 359.6636 lineto +491.98537 359.31569 492.25233 359.05562 492.5589 358.88338 curveto +492.86892 358.71115 493.22544 358.62504 493.62847 358.62503 curveto +494.29329 358.62504 494.79621 358.83172 495.13724 359.24507 curveto +495.47825 359.65499 495.64876 360.25953 495.64877 361.05869 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +502.36586 361.05869 moveto +502.36586 364.55157 lineto +501.41513 364.55157 lineto +501.41513 361.08969 lineto +501.41513 360.54199 501.30835 360.13208 501.09478 359.85994 curveto +500.88121 359.58782 500.56085 359.45175 500.13372 359.45175 curveto +499.62046 359.45175 499.21571 359.61538 498.91948 359.94261 curveto +498.62323 360.26986 498.47511 360.71595 498.47511 361.28087 curveto +498.47511 364.55157 lineto +497.51922 364.55157 lineto +497.51922 358.76454 lineto +498.47511 358.76454 lineto +498.47511 359.6636 lineto +498.70246 359.31569 498.96942 359.05562 499.276 358.88338 curveto +499.58602 358.71115 499.94254 358.62504 500.34557 358.62503 curveto +501.01038 358.62504 501.5133 358.83172 501.85433 359.24507 curveto +502.19535 359.65499 502.36586 360.25953 502.36586 361.05869 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +504.27248 358.76454 moveto +505.22321 358.76454 lineto +505.22321 364.55157 lineto +504.27248 364.55157 lineto +504.27248 358.76454 lineto +504.27248 356.51173 moveto +505.22321 356.51173 lineto +505.22321 357.71564 lineto +504.27248 357.71564 lineto +504.27248 356.51173 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +512.01781 361.05869 moveto +512.01781 364.55157 lineto +511.06708 364.55157 lineto +511.06708 361.08969 lineto +511.06708 360.54199 510.96029 360.13208 510.74673 359.85994 curveto +510.53315 359.58782 510.2128 359.45175 509.78567 359.45175 curveto +509.27241 359.45175 508.86766 359.61538 508.57142 359.94261 curveto +508.27518 360.26986 508.12706 360.71595 508.12706 361.28087 curveto +508.12706 364.55157 lineto +507.17117 364.55157 lineto +507.17117 358.76454 lineto +508.12706 358.76454 lineto +508.12706 359.6636 lineto +508.35441 359.31569 508.62137 359.05562 508.92795 358.88338 curveto +509.23796 358.71115 509.59448 358.62504 509.99751 358.62503 curveto +510.66233 358.62504 511.16525 358.83172 511.50628 359.24507 curveto +511.84729 359.65499 512.0178 360.25953 512.01781 361.05869 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +517.73251 361.59089 moveto +517.7325 360.90196 517.58955 360.36803 517.30365 359.98912 curveto +517.02118 359.61021 516.62332 359.42075 516.11007 359.42075 curveto +515.60026 359.42075 515.2024 359.61021 514.91649 359.98912 curveto +514.63403 360.36803 514.4928 360.90196 514.4928 361.59089 curveto +514.4928 362.27638 514.63403 362.80858 514.91649 363.18749 curveto +515.2024 363.5664 515.60026 363.75586 516.11007 363.75586 curveto +516.62332 363.75586 517.02118 363.5664 517.30365 363.18749 curveto +517.58955 362.80858 517.7325 362.27638 517.73251 361.59089 curveto +518.68323 363.83336 moveto +518.68323 364.81854 518.46449 365.55053 518.02703 366.02934 curveto +517.58955 366.51159 516.91956 366.75271 516.01706 366.75271 curveto +515.68293 366.75271 515.36774 366.72688 515.0715 366.67521 curveto +514.77526 366.62698 514.48763 366.5512 514.20862 366.44786 curveto +514.20862 365.52297 lineto +514.48763 365.67453 514.7632 365.78649 515.03533 365.85882 curveto +515.30746 365.93116 515.58476 365.96733 515.86722 365.96733 curveto +516.4907 365.96733 516.95745 365.80371 517.26748 365.47647 curveto +517.57749 365.15267 517.7325 364.6618 517.73251 364.00387 curveto +517.73251 363.53368 lineto +517.53616 363.8747 517.2847 364.1296 516.97813 364.29839 curveto +516.67155 364.46718 516.30469 364.55157 515.87755 364.55157 curveto +515.16795 364.55157 514.59614 364.28117 514.16211 363.74036 curveto +513.72808 363.19955 513.51107 362.48306 513.51107 361.59089 curveto +513.51107 360.69528 513.72808 359.97707 514.16211 359.43625 curveto +514.59614 358.89544 515.16795 358.62504 515.87755 358.62503 curveto +516.30469 358.62504 516.67155 358.70943 516.97813 358.87821 curveto +517.2847 359.04701 517.53616 359.30191 517.73251 359.64293 curveto +517.73251 358.76454 lineto +518.68323 358.76454 lineto +518.68323 363.83336 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +526.64041 361.64256 moveto +525.87224 361.64256 525.34004 361.7304 525.04381 361.90607 curveto +524.74756 362.08175 524.59944 362.38144 524.59944 362.80513 curveto +524.59944 363.14271 524.70967 363.41139 524.93013 363.61118 curveto +525.15403 363.80753 525.45716 363.9057 525.83952 363.9057 curveto +526.36655 363.9057 526.78852 363.71969 527.10544 363.34766 curveto +527.42579 362.9722 527.58596 362.47444 527.58597 361.8544 curveto +527.58597 361.64256 lineto +526.64041 361.64256 lineto +528.53669 361.24986 moveto +528.53669 364.55157 lineto +527.58597 364.55157 lineto +527.58597 363.67319 lineto +527.36895 364.02454 527.09854 364.28461 526.77475 364.4534 curveto +526.45095 364.61875 526.05481 364.70142 525.58634 364.70142 curveto +524.99386 364.70142 524.52194 364.53607 524.17058 364.20539 curveto +523.82267 363.87125 523.64872 363.42517 523.64872 362.86713 curveto +523.64872 362.21609 523.86573 361.72523 524.29976 361.39454 curveto +524.73723 361.06386 525.38827 360.89851 526.25288 360.89851 curveto +527.58597 360.89851 lineto +527.58597 360.8055 lineto +527.58596 360.36803 527.44129 360.03046 527.15194 359.79277 curveto +526.86603 359.55165 526.463 359.43109 525.94286 359.43108 curveto +525.61217 359.43109 525.2901 359.4707 524.97663 359.54992 curveto +524.66317 359.62915 524.36176 359.748 524.07241 359.90645 curveto +524.07241 359.02806 lineto +524.42032 358.89372 524.7579 358.79382 525.08514 358.72837 curveto +525.41238 358.65948 525.73101 358.62504 526.04104 358.62503 curveto +526.87808 358.62504 527.50329 358.84205 527.91665 359.27607 curveto +528.33001 359.7101 528.53669 360.36803 528.53669 361.24986 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +537.55827 358.93505 moveto +537.55827 359.83411 lineto +537.28958 359.69633 537.01056 359.59299 536.72122 359.52409 curveto +536.43186 359.4552 536.13217 359.42075 535.82216 359.42075 curveto +535.35024 359.42075 534.99544 359.49309 534.75776 359.63776 curveto +534.52352 359.78244 534.4064 359.99946 534.4064 360.2888 curveto +534.4064 360.50927 534.49079 360.68322 534.65958 360.81067 curveto +534.82837 360.93468 535.16767 361.05352 535.67748 361.16719 curveto +536.003 361.23953 lineto +536.67815 361.38421 537.15696 361.58917 537.43943 361.8544 curveto +537.72533 362.1162 537.86828 362.48306 537.86829 362.95497 curveto +537.86828 363.49234 537.65471 363.91776 537.22758 364.23122 curveto +536.80388 364.54469 536.22001 364.70142 535.47597 364.70142 curveto +535.16595 364.70142 534.84215 364.67042 534.50457 364.60841 curveto +534.17044 364.54985 533.81736 364.46029 533.44534 364.33973 curveto +533.44534 363.358 lineto +533.79669 363.54057 534.14288 363.67835 534.48391 363.77136 curveto +534.82493 363.86092 535.1625 363.9057 535.49664 363.9057 curveto +535.94444 363.9057 536.28891 363.82992 536.53004 363.67835 curveto +536.77116 363.52334 536.89172 363.30633 536.89173 363.02731 curveto +536.89172 362.76896 536.80388 362.57089 536.62821 362.43311 curveto +536.45597 362.29532 536.07534 362.1627 535.4863 362.03525 curveto +535.15562 361.95774 lineto +534.56658 361.83374 534.14116 361.64428 533.87937 361.38937 curveto +533.61757 361.13103 533.48668 360.77795 533.48668 360.33014 curveto +533.48668 359.78589 533.67958 359.36564 534.06538 359.06939 curveto +534.45118 358.77316 534.99888 358.62504 535.70848 358.62503 curveto +536.05984 358.62504 536.39052 358.65087 536.70055 358.70254 curveto +537.01056 358.75421 537.29647 358.83172 537.55827 358.93505 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +541.62987 359.43108 moveto +541.12005 359.43109 540.71703 359.63088 540.42079 360.03045 curveto +540.12455 360.42659 539.97643 360.97085 539.97643 361.66322 curveto +539.97643 362.3556 540.12282 362.90158 540.41562 363.30116 curveto +540.71186 363.6973 541.11661 363.89537 541.62987 363.89537 curveto +542.13623 363.89537 542.53753 363.69558 542.83378 363.29599 curveto +543.13001 362.89642 543.27813 362.35216 543.27814 361.66322 curveto +543.27813 360.97774 543.13001 360.43521 542.83378 360.03562 curveto +542.53753 359.6326 542.13623 359.43109 541.62987 359.43108 curveto +541.62987 358.62503 moveto +542.45658 358.62504 543.1059 358.89372 543.57782 359.43108 curveto +544.04974 359.96845 544.2857 360.7125 544.2857 361.66322 curveto +544.2857 362.61051 544.04974 363.35456 543.57782 363.89537 curveto +543.1059 364.43273 542.45658 364.70142 541.62987 364.70142 curveto +540.7997 364.70142 540.14866 364.43273 539.67674 363.89537 curveto +539.20827 363.35456 538.97403 362.61051 538.97403 361.66322 curveto +538.97403 360.7125 539.20827 359.96845 539.67674 359.43108 curveto +540.14866 358.89372 540.7997 358.62504 541.62987 358.62503 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +548.78615 356.51173 moveto +548.78615 357.30228 lineto +547.87676 357.30228 lineto +547.53574 357.30229 547.29806 357.37118 547.16372 357.50896 curveto +547.03282 357.64675 546.96737 357.89477 546.96737 358.25301 curveto +546.96737 358.76454 lineto +548.53297 358.76454 lineto +548.53297 359.50342 lineto +546.96737 359.50342 lineto +546.96737 364.55157 lineto +546.01148 364.55157 lineto +546.01148 359.50342 lineto +545.10209 359.50342 lineto +545.10209 358.76454 lineto +546.01148 358.76454 lineto +546.01148 358.36151 lineto +546.01148 357.71737 546.16132 357.24889 546.46101 356.95609 curveto +546.76069 356.65986 547.23605 356.51174 547.8871 356.51173 curveto +548.78615 356.51173 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +550.33109 357.12143 moveto +550.33109 358.76454 lineto +552.28938 358.76454 lineto +552.28938 359.50342 lineto +550.33109 359.50342 lineto +550.33109 362.64495 lineto +550.33108 363.11687 550.39481 363.42 550.52227 363.55434 curveto +550.65316 363.68869 550.91668 363.75586 551.31282 363.75586 curveto +552.28938 363.75586 lineto +552.28938 364.55157 lineto +551.31282 364.55157 lineto +550.5791 364.55157 550.07274 364.41551 549.79372 364.14338 curveto +549.5147 363.86781 549.37519 363.36833 549.37519 362.64495 curveto +549.37519 359.50342 lineto +548.67765 359.50342 lineto +548.67765 358.76454 lineto +549.37519 358.76454 lineto +549.37519 357.12143 lineto +550.33109 357.12143 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +553.54495 358.76454 moveto +554.49568 358.76454 lineto +554.49568 364.55157 lineto +553.54495 364.55157 lineto +553.54495 358.76454 lineto +553.54495 356.51173 moveto +554.49568 356.51173 lineto +554.49568 357.71564 lineto +553.54495 357.71564 lineto +553.54495 356.51173 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +559.83319 359.65326 moveto +559.7264 359.59126 559.60929 359.54648 559.48184 359.51892 curveto +559.35783 359.48792 559.22004 359.47242 559.06848 359.47242 curveto +558.53111 359.47242 558.11775 359.6481 557.8284 359.99945 curveto +557.54249 360.34737 557.39954 360.84856 557.39954 361.50305 curveto +557.39954 364.55157 lineto +556.44364 364.55157 lineto +556.44364 358.76454 lineto +557.39954 358.76454 lineto +557.39954 359.6636 lineto +557.59933 359.31225 557.8594 359.05217 558.17975 358.88338 curveto +558.5001 358.71115 558.88935 358.62504 559.34749 358.62503 curveto +559.41294 358.62504 559.48528 358.6302 559.56451 358.64053 curveto +559.64373 358.64743 559.73157 358.65948 559.82803 358.6767 curveto +559.83319 359.65326 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +561.22311 361.66322 moveto +561.22311 362.36249 561.36606 362.91192 561.65197 363.3115 curveto +561.94132 363.70763 562.33745 363.9057 562.84038 363.9057 curveto +563.3433 363.9057 563.73943 363.70763 564.02879 363.3115 curveto +564.31813 362.91192 564.46281 362.36249 564.46281 361.66322 curveto +564.46281 360.96396 564.31813 360.41626 564.02879 360.02012 curveto +563.73943 359.62054 563.3433 359.42075 562.84038 359.42075 curveto +562.33745 359.42075 561.94132 359.62054 561.65197 360.02012 curveto +561.36606 360.41626 561.22311 360.96396 561.22311 361.66322 curveto +564.46281 363.68352 moveto +564.26302 364.02799 564.00984 364.28461 563.70327 364.4534 curveto +563.40013 364.61875 563.035 364.70142 562.60786 364.70142 curveto +561.90859 364.70142 561.3385 364.4224 560.89759 363.86436 curveto +560.46011 363.30633 560.24138 362.57262 560.24138 361.66322 curveto +560.24138 360.75384 560.46011 360.02012 560.89759 359.46208 curveto +561.3385 358.90405 561.90859 358.62504 562.60786 358.62503 curveto +563.035 358.62504 563.40013 358.70943 563.70327 358.87821 curveto +564.00984 359.04356 564.26302 359.29847 564.46281 359.64293 curveto +564.46281 358.76454 lineto +565.41354 358.76454 lineto +565.41354 366.75271 lineto +564.46281 366.75271 lineto +564.46281 363.68352 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +575.55119 361.05869 moveto +575.55119 364.55157 lineto +574.60046 364.55157 lineto +574.60046 361.08969 lineto +574.60046 360.54199 574.49367 360.13208 574.28011 359.85994 curveto +574.06653 359.58782 573.74618 359.45175 573.31905 359.45175 curveto +572.80579 359.45175 572.40104 359.61538 572.1048 359.94261 curveto +571.80856 360.26986 571.66044 360.71595 571.66044 361.28087 curveto +571.66044 364.55157 lineto +570.70455 364.55157 lineto +570.70455 356.51173 lineto +571.66044 356.51173 lineto +571.66044 359.6636 lineto +571.88779 359.31569 572.15475 359.05562 572.46132 358.88338 curveto +572.77134 358.71115 573.12786 358.62504 573.53089 358.62503 curveto +574.19571 358.62504 574.69863 358.83172 575.03966 359.24507 curveto +575.38067 359.65499 575.55118 360.25953 575.55119 361.05869 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +580.08782 361.64256 moveto +579.31965 361.64256 578.78745 361.7304 578.49122 361.90607 curveto +578.19497 362.08175 578.04685 362.38144 578.04685 362.80513 curveto +578.04685 363.14271 578.15708 363.41139 578.37754 363.61118 curveto +578.60144 363.80753 578.90457 363.9057 579.28693 363.9057 curveto +579.81396 363.9057 580.23593 363.71969 580.55285 363.34766 curveto +580.8732 362.9722 581.03337 362.47444 581.03338 361.8544 curveto +581.03338 361.64256 lineto +580.08782 361.64256 lineto +581.98411 361.24986 moveto +581.98411 364.55157 lineto +581.03338 364.55157 lineto +581.03338 363.67319 lineto +580.81636 364.02454 580.54595 364.28461 580.22216 364.4534 curveto +579.89836 364.61875 579.50222 364.70142 579.03375 364.70142 curveto +578.44127 364.70142 577.96935 364.53607 577.61799 364.20539 curveto +577.27008 363.87125 577.09613 363.42517 577.09613 362.86713 curveto +577.09613 362.21609 577.31314 361.72523 577.74717 361.39454 curveto +578.18464 361.06386 578.83568 360.89851 579.70029 360.89851 curveto +581.03338 360.89851 lineto +581.03338 360.8055 lineto +581.03337 360.36803 580.8887 360.03046 580.59935 359.79277 curveto +580.31344 359.55165 579.91041 359.43109 579.39027 359.43108 curveto +579.05958 359.43109 578.73751 359.4707 578.42404 359.54992 curveto +578.11058 359.62915 577.80917 359.748 577.51982 359.90645 curveto +577.51982 359.02806 lineto +577.86773 358.89372 578.20531 358.79382 578.53255 358.72837 curveto +578.85979 358.65948 579.17842 358.62504 579.48845 358.62503 curveto +580.3255 358.62504 580.9507 358.84205 581.36407 359.27607 curveto +581.77742 359.7101 581.9841 360.36803 581.98411 361.24986 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +588.75804 361.05869 moveto +588.75804 364.55157 lineto +587.80731 364.55157 lineto +587.80731 361.08969 lineto +587.8073 360.54199 587.70052 360.13208 587.48695 359.85994 curveto +587.27338 359.58782 586.95303 359.45175 586.52589 359.45175 curveto +586.01264 359.45175 585.60789 359.61538 585.31165 359.94261 curveto +585.01541 360.26986 584.86729 360.71595 584.86729 361.28087 curveto +584.86729 364.55157 lineto +583.91139 364.55157 lineto +583.91139 358.76454 lineto +584.86729 358.76454 lineto +584.86729 359.6636 lineto +585.09463 359.31569 585.36159 359.05562 585.66817 358.88338 curveto +585.97819 358.71115 586.33471 358.62504 586.73774 358.62503 curveto +587.40256 358.62504 587.90548 358.83172 588.2465 359.24507 curveto +588.58752 359.65499 588.75803 360.25953 588.75804 361.05869 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +594.47273 359.64293 moveto +594.47273 356.51173 lineto +595.42345 356.51173 lineto +595.42345 364.55157 lineto +594.47273 364.55157 lineto +594.47273 363.68352 lineto +594.27293 364.02799 594.01975 364.28461 593.71318 364.4534 curveto +593.41004 364.61875 593.04491 364.70142 592.61777 364.70142 curveto +591.91851 364.70142 591.34841 364.4224 590.9075 363.86436 curveto +590.47003 363.30633 590.25129 362.57262 590.25129 361.66322 curveto +590.25129 360.75384 590.47003 360.02012 590.9075 359.46208 curveto +591.34841 358.90405 591.91851 358.62504 592.61777 358.62503 curveto +593.04491 358.62504 593.41004 358.70943 593.71318 358.87821 curveto +594.01975 359.04356 594.27293 359.29847 594.47273 359.64293 curveto +591.23302 361.66322 moveto +591.23302 362.36249 591.37597 362.91192 591.66188 363.3115 curveto +591.95123 363.70763 592.34737 363.9057 592.85029 363.9057 curveto +593.35321 363.9057 593.74934 363.70763 594.0387 363.3115 curveto +594.32804 362.91192 594.47272 362.36249 594.47273 361.66322 curveto +594.47272 360.96396 594.32804 360.41626 594.0387 360.02012 curveto +593.74934 359.62054 593.35321 359.42075 592.85029 359.42075 curveto +592.34737 359.42075 591.95123 359.62054 591.66188 360.02012 curveto +591.37597 360.41626 591.23302 360.96396 591.23302 361.66322 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +597.38175 356.51173 moveto +598.33248 356.51173 lineto +598.33248 364.55157 lineto +597.38175 364.55157 lineto +597.38175 356.51173 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +605.26658 361.42038 moveto +605.26658 361.8854 lineto +600.8953 361.8854 lineto +600.93663 362.53989 601.13298 363.03937 601.48434 363.38383 curveto +601.83914 363.72486 602.33172 363.89537 602.9621 363.89537 curveto +603.32723 363.89537 603.68031 363.85059 604.02133 363.76102 curveto +604.36579 363.67146 604.70682 363.53712 605.0444 363.358 curveto +605.0444 364.25706 lineto +604.70337 364.40173 604.35374 364.51196 603.9955 364.58774 curveto +603.63725 364.66353 603.27384 364.70142 602.90526 364.70142 curveto +601.98209 364.70142 601.2501 364.43273 600.70929 363.89537 curveto +600.17192 363.358 599.90324 362.63118 599.90324 361.71489 curveto +599.90324 360.76762 600.15814 360.01668 600.66795 359.46208 curveto +601.18121 358.90405 601.87186 358.62504 602.73992 358.62503 curveto +603.51841 358.62504 604.13328 358.8765 604.58454 359.37941 curveto +605.03923 359.87889 605.26657 360.55921 605.26658 361.42038 curveto +604.31585 361.14136 moveto +604.30896 360.62122 604.16256 360.20614 603.87666 359.89611 curveto +603.59419 359.5861 603.21872 359.43109 602.75025 359.43108 curveto +602.21977 359.43109 601.79435 359.58093 601.474 359.88061 curveto +601.15709 360.1803 600.97453 360.60227 600.9263 361.14652 curveto +604.31585 361.14136 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +610.1804 359.65326 moveto +610.07361 359.59126 609.95649 359.54648 609.82904 359.51892 curveto +609.70503 359.48792 609.56724 359.47242 609.41568 359.47242 curveto +608.87831 359.47242 608.46495 359.6481 608.1756 359.99945 curveto +607.8897 360.34737 607.74674 360.84856 607.74674 361.50305 curveto +607.74674 364.55157 lineto +606.79085 364.55157 lineto +606.79085 358.76454 lineto +607.74674 358.76454 lineto +607.74674 359.6636 lineto +607.94653 359.31225 608.2066 359.05217 608.52696 358.88338 curveto +608.84731 358.71115 609.23656 358.62504 609.6947 358.62503 curveto +609.76015 358.62504 609.83248 358.6302 609.91171 358.64053 curveto +609.99094 358.64743 610.07878 358.65948 610.17523 358.6767 curveto +610.1804 359.65326 lineto +fill +grestore +grestore +grestore +showpage diff --git a/tags/lttv-0.11.3-23102008/doc/user/user_guide/docbook/lttv-color-list.png b/tags/lttv-0.11.3-23102008/doc/user/user_guide/docbook/lttv-color-list.png new file mode 100644 index 00000000..98abddbe Binary files /dev/null and b/tags/lttv-0.11.3-23102008/doc/user/user_guide/docbook/lttv-color-list.png differ diff --git a/tags/lttv-0.11.3-23102008/doc/user/user_guide/docbook/lttv-color-list.svg b/tags/lttv-0.11.3-23102008/doc/user/user_guide/docbook/lttv-color-list.svg new file mode 100644 index 00000000..4428218a --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/user/user_guide/docbook/lttv-color-list.svg @@ -0,0 +1,377 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + White : mode unknown or unnamed + + + + + + + + + Yellow : running in a trap + Orange : servicing an IRQ + Dark yellow : waiting for CPU + Dark purple : zombie + Dark green : waiting for fork + Magenta : process has exited + Pale blue : running in a system call + Green : running in user mode + Dark red : waiting for I/O + + Pink : running a softirq handler + + diff --git a/tags/lttv-0.11.3-23102008/doc/user/user_guide/docbook/lttv-numbered-5.eps b/tags/lttv-0.11.3-23102008/doc/user/user_guide/docbook/lttv-numbered-5.eps new file mode 100644 index 00000000..37206e49 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/user/user_guide/docbook/lttv-numbered-5.eps @@ -0,0 +1,8659 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: GIMP PostScript file plugin V 1.17 by Peter Kirchgessner +%%Title: lttv-numbered-5.ps +%%CreationDate: Tue Nov 30 17:10:43 2004 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%Pages: 1 +%%BoundingBox: 14 14 467 364 +%%EndComments +%%BeginProlog +% Use own dictionary to avoid conflicts +10 dict begin +%%EndProlog +%%Page: 1 1 +% Translate for offset +14.173228346456693 14.173228346456693 translate +% Translate to begin of first scanline +0 349.80741818181815 translate +452.15999999999997 -349.80741818181815 scale +% Image geometry +1100 851 8 +% Transformation matrix +[ 1100 0 0 851 0 0 ] +% Strings to hold RGB-samples per scanline +/rstr 1100 string def +/gstr 1100 string def +/bstr 1100 string def +{currentfile /ASCII85Decode filter /RunLengthDecode filter rstr readstring pop} +{currentfile /ASCII85Decode filter /RunLengthDecode filter gstr readstring pop} +{currentfile /ASCII85Decode filter /RunLengthDecode filter bstr readstring pop} +true 3 +%%BeginData: 577335 ASCII Bytes +colorimage +Zi>SdJRa7@JRa7@JRa7@K4BE~> +Zi>T!JTZNdJTZNdJTZNdK6;\~> +Zi>U%J^/hlJ^/hlJ^/hlK?eu~> +Zi>SdJRa7@JRa7@JRa7@K4BE~> +Zi>StJTHB`JTHB`JTHB`K6)P~> +Zi>U%J^/hlJ^/hlJ^/hlK?eu~> +ZiC#2oh'dAJR3n6g0Oi5WIqfh"bWe,K;jM&?35bYKCW@KWI\B,?@cFT?@W~> +ZiC#Eoi-K^JT66\g2R1VZB;b="d-*QOg:,KE<;9.On*MlZ@R"HEIi,%EI\~> +ZiC$MopgTYJ^/hlgfidoC+c@O95c@>~> +ZiC#.!$(u5rDNVq!$$PnJQdV.i*-/VrE9o;=q%4qP*59s7p9)lmufR* +=pgXm@Tm6:Qs/tI21\q^>(KkM>(?~> +ZiC#C!$_DVs(hRM!H005Dh2hXDr>R6ZMe\DZ1FGfmV`)eqm,I)qm$3`okt8Do:)6@RosZJZ1FGQ +qelh?Z?L54ZDMR3o0e.JJT$*XTPo>~> +ZiC$I!Bl\DR/`ZV4b:b,b(7^nb5^K"aogSr];4l;!7q&,l,rn_!7phc%,gll]D&q5b0&Z:]79_# +$Jsdfb0K#'b4kX[J]`PdJ]aY.J,~> +ZiC#.!!rR>rbhdXrc\>JJQdV.JQgu8s6fqFr`TDRP".Ct=pKP?\*rd8Il6Lp#GcjYn$ddM8+p*r +V!ie_msd2u>(;]*&>Z-#=pht3DcVMZW'-r+&:n#9>(Kke>5G&l>5tDt>5tDs>(?~> +ZiC#A"q6ldR[fn>rh':f%"W8HD1QQ^D?'2-6N1g(Ng`^0mVN2hR_87`KnVkYD$R;!]^rdiA68JA% +^>PYL%C!bD/G<%:)L4>rG;n]o6Y,9o;kQgKc#AXJSfsT\8D)HqecA;rb_\>rG@&~> +ZiC$E!t`/^_>M=QaoE!(`du-\`o+l:pAfR>`WOZ"@XIO_#L^0%p\0o:Hg8TUhNZ"R`KZ.Np;RU! +pQUp:P^CEid^`gAhVS4N$Kh)b^"2qkLu+@NJ]<8\J]>4>qVM+hs5*aps5*^oJ,~> +ZiC#.!!rJ0rODn6JQdV.JQgu8s6fq!r`TDRP$2]$U^`K'J#%RDUm@='EJp-%r`TLrP4.iN2)Y6+%% +MBBAsfBi>%X.tQtH;#+Z5GOIj-aGJY&Ha@Uan[Sq1@HmsfiUJQRtNJ!*tmn*d(O=s&RhQpnFO;D +"\o2.i5OQtDkaAses#UnE'BJ"E6]@"80O!.TQ'JQdV.d9?D8qca$g"Jf4sn*g3YJ,~> +ZiC#?!Y!/krQ>,`!R;+)COp8PCZ'".o)KX,CD;bs@sG5^YF^0%Y`4XHTqRg&Q_Ap.CBde,oCEea +5l.\nW./T,U9Lk;GG;=;GPu;#5ah4t/3mL$WiD)"EfR0"NcM>pKn],S@[hh+RU\VSo +ZiC$@!"f.dqVhJdJ\coRJ\g9\s7ZLFrkS]`\V7'_h@8,Zd,*-rhUpK1b1,(crkSf/\+fVKGB`o?% +,/k?b1b_/_91Kgg<0B1+a:EiP#?LsdbNX+`Pp9\gsX0spQC^6P_I]1d(+jSp\3PD_&q,"WL2iBB +4B_^GKSXrg\Rqn`=,"Q$uOp\4X&J,~> +ZiC#.!!r?!!!n-ZJQdV.i*-0L!"f-5'r:;]P4-<+g4c!aP*5:4T#J+[SlAMb=pUqmmgrZ+n*d(O +Ih%S&Yth+SUjk7tih5;$;Z0c'V!idj>(OZl_ZG15`W!b7H_BhWJ+'D+RT>mGOu;^sIl1D5@c#[! +hUWZ8n*`-0ih5;$`UA01hM:RLH#CO9_7t2en&^_r7tFip>(Kl*>5I^b=pCekn"fF@V#=JI~> +ZiC#;!"AW!!"=EkJS0OHi+N)]!#Y]J's7(qRIePAhiOT(R@3ZMUrp9nV-R=)B*bX2o,)+QoCK!] +KasC6[TforXG&pBjeh4`R6^B8Kg99`Dr]D? +jkD%XoCG5Mjeh4 +ZiC$G_pnFBOS?5h]opA/[<)*:OE8+K_=-6% +oAVcpp\/aco +ZiC#*!!r?!!!n-VJQ@>&i)]mH!"f-1'r(/HJ+&>Tm=g>aIt-p:n"B.'2>pQ!U@3Rfmuf4O6\/9h +ZiC#9!"AW!!"=EiJRsCDi+;r[!#Y]H's."\L%CIjnVrV$Kn],Q?H)6C=?'F^AK%ZAo,)%MoCG(" +@=OR6k;MMp5VbOdo;;-:55f%9XRh-0AZ"j]Qg'Klm?a1(NL06jL%F?1>s+eOKc'FQ(d@q1,J*E! +o9^_1oCEg^o;;-:O7U$6cG9%\;i0ncbit.To9VBq;1W;0AV"F@AH;?"X8qkgAH8h2o)PliAUj~> +ZiC$8!"eo)!"a^lJ[p?Bi48m^!%IoX("i8BOS>98oqJ8VOHB3uHf";7D.KIA\Jj,ppF3Y`p\.9` +Iup>tmq21c;I4;5pU0bXGQ#&qf_:JI\^pGLV!tRooq\DQYI4+dOS@tXI;QmrO=Bqu.<#3r,Mr0^ +pS9sWp\--.pU0bXXnTfof$a<-D5-^Sf']`NpRkDpP_$Ar\Uh<>\H)C%ec>I@\H(4[pAieB\UX~> +ZiC#&!!r?!!!n-RJPq%si)9UD!"f--'VOoEJ+%l:msfiUN/[4T=Ms4+>!`*b)PHQf&5lEpmgqq, +l^7/0']SR98lF[;S:?#*rDFc2msd&iRcoclk'MO?NfN7KKC=;>n(=RJ6_Nmt&F]'%rDFh`n(-qn +/Q2'+&E[!206f +J,~> +ZiC#7!"AW!!"=EgJRa7@i+)fY!#Y]F's$q[L%C"Po7htiP*5Ek@`moEAl)t9@iDE>o,(tIoCEf# +bjD"no,Qrb3`T\Oo:Uek+T5dmX7M$.A#8RXQ0EaLm?Nt$IYdTLL%F*&D,[M,Kc'FQ*;s@kL\'0g +82t,No,-T\TS8(hoC&6An+2r4P3g;RKi3d!Kf<:g@tA-@A)CbrL\e+L@fZ,u@fZ,s@fWV0o)Pli +@t4~> +ZiC$4!"eo)!"a^hJ[L':i3iUZ!%IoT'\<#?OS=iupQC^6S=K>:KBDU>LQqbF)V4ZW.CHSXpF2ct +pT=2a1%Y-/TXFCcW0!*$rO!d!pQ0=$eFfE)mZA3R.l](pYrlKQIbel.JNQgrO!ikpYPXF +@^Yt).J*;4OHt!7q/D!7q(U +J,~> +ZiC#&!!rQ'qtg@!JPq%sJPtE(s6geSTV-S'msfiUIt-p:D;<%Hn"Q8_PdRDurDFQ,n*^fc;Xa&c +;U4`un*^g`g7Y*:T\>ocIkgn"+JA1Q0iEA7]t^sO?GX=i[8;MEIt-p[OJ63Umsbnol\#.6+]d1_ +c4iQBn*^g\g73YB3Ss4*DsuD(.WN/6Ndk/mmsd'C;Lq_s;Vk:OT_e-S;?61`;?61_;?if_n"B.< +T`%c=~> +ZiC#5!"AW!!"=EeJRO+;e<_m`TV`kfR%a@0#os@0#or@0Wb'o;_ER +Wr6@V~> +ZiC$0!"eo)!"a^dJ['d2i3E=V(m*>"_pnFBOS=iupQ10ucd2?jYL9Aea/o6E)V"NU.C$/PpF3B0 +pT=2a.G!X`f!3fcW0!*VrNRKppQ06s_qrM\_.JJZaj._7R.l](pT^O3Wm^X'.H^5drNRNRlM'!a +OKnQS.G!LWOH=DLpMZ;jieM!Pgr5eFIBiO.D1SsCZ%91:YlN;Mp\V2qp\_8rp\V3"f(\pep\38; +Z%)~> +ZiC#&!!rT(!;lZf!!n-RJPq%si)0QJrI5SbBTP_dO@NE77n&,eIt)3Q?9<(N9Lo([$[JCp&5lE! +Ih5M-OT'_;&7U'5OG:bPF`KmQWLBokheO@N,k9Pok]&:]Xg1&Xum +LPT7R:/[a7&710KIt'27La5`_CI'aWF,5Hu=D3dd0nE5R;Lqa&;?3Bdn,#u_n,/gYn,$#_mf]:] +T^V@J;Le~> +ZiC#3!"AW!!"=EcJR-<-OU$(5Is)3;m2,P)Wrq +?!jO_(2JkcKnDFRM_&#(D+-TuH]!T3?ZD?)3e:Xh?\)S@?N@)%oD;kpoDGTgoD;npo)u"!W:TWZ +?[r~> +ZiC$,!"eo)!"a^`JZXL*i2m"-rK%eCT!;OKZ!;^^>]mTMOH=gEHX`M4H'nT5$bt,O.BT`#O=@p\V)mpB8pHdeEM7 +Xag~> +ZiC#&!!rT$qY'sqJPq%sJPtB'r%\4;#r+_30gS)[5tO*91&j084\@jr8,Q9b1&b)N1&k,O62`MC +;G&r(0iCb,r\==K+@._J;Gp7S1G_6j:-UNj5>FL!5s[4k6TIY&0elQl0erT(!]_tqr_ibN!`DcR +r\5!_3AF,u0f;Bt:d[)p0gS)[6%Mpb;Lqa&;?2"=T_hX`T_hXeT`.jhT`%c=~> +ZiC#/!"AW!!"=E_JQm\0hcp":s%!e]84YX082a"95W:SZ4[)8376XNF>Q6V\>Q6V]>6.2Qr\b*f +?*jH2E(J;+@nC]>?b0!3]]c4=%>;47or5>84YX/8jH0C2E"c/3&^\=!^JY1r`fC\(L'm' +3&W[!4uQ><2EFN9=@kS52Fp@s8:aur>Cg#8>6'3MVYaToVYaTtVZ'g"VYs_L~> +ZiC$(!"eo)!"a^\JZ44"hl6Nds+gtoLko]"LhDO2GQ)7TEs-u[IrgkPWW-W1WW-W2W;qVMB*YE+ +F(]-bS9OsFr2CfpB8EOdU53Z'Ln^W"D/G*'LhDO"B8DRnP_`sbWJ#&"r2BaEB7'WWs(;D4S9OsF +&ZKhaS=F5/NiAb1DeE^=LhDNpWI_%"WSXTS]DBhMWVXp;WW19CWW19BWIO~> +ZiC#"!!rS/rjDd:!k#QEJPLbkJPLbkJPLbkJPMA'J,~> +ZiC#-":W5gaN48$!"=E]JQ[P,JQ[P,JQ[P,P$*;~> +ZiC$$!"Ah[qq_;]JYdpoJYdpoJYdpoJYeO+J,~> +ZiC"s!#Y]-r_EJk!_uW1JP(JcJP(JcJP(JcJP)(tJ,~> +ZiC#+"<:]S@:B%C!$$PkJQID(JQID(JQID(P#m/~> +ZiC#t!%n/%qhG/'JY7ReJY7ReJY7ReJY81!J,~> +ZiC"sof7RqJP(JcJP(JcJP(JcP"L6~> +ZiC#)og"(.JQ78$JQ78$JQ78$P#[#~> +ZiC#polktWJXh:]JXh:]JXh:]P+7%~> +Zi>S6JMi!9JMi!9JMi!9K/J/~> +Zi>S;JNA?CJNA?CJNA?CK0"M~> +Zi>S_JR3n6JR3n6JR3n6K3j'~> +Zi>T<]9!"(JU`6#JU`6#JU`6#r-n^%d?"?)J,~> +Zi>T;]8lq'JUN)tJUN)tJUN)tr-\R"d>n9'J,~> +Zi>T<]9!"(JU;rpJU;rpJU;rpr-JEtd?"?)J,~> +ZN#LD^$YpG!.j!XhLXO7hLXO7hYc42me5,t!R55,~> +ZN#LC^$PjE!.imUgO\+1gO\+1g\fe,mIo#r!R,/+~> +ZN#L@^$5X?!.i^Peq)D'eq)D'f)4)"lL`Qj!Qel&~> +ZN#L4^"rd[!.j!XhLXO7hLXO7hYc71maL"nS5+S~> +huBRK.onJ_Pb1J_Pb1J_U+Ws6\:i!Lc`5~> +hu +ZN#L4^"rd[!.j!XhLXO7hLXO7hYc71maL"nS5+S~> +jSoJarVH?ap@\(Uo*4j[p@n@ZrT!p/gV)>BRK.onJ_Pb1J_Pb1J_U+Ws6\:i!Lc`5~> +jSoJarVH?ap@\(Uo*4j[p@n@ZrT!p/f"KW8PQ69cJ_#D'J_#D'J_'bMs6A(a!L-3,~> +ZN#L4^"rd[!.j!XhLXO7hLXO7hYc71maL"nS5+S~> +kPkngr;$*[oC;;;mHjc:$1.'In+#u@pA+XKs+/q +kPkngr;$*[oC;;;mHjc:$1.'In+#u@pA+XKs+/b7ecD!8J_#D'J_#D'J_#D'r7:o5e(+Pa8q6~> +ZN#L4^"rd[!.j!XhLXO7hLXO7hYc71maL"nS5+S~> +l2M4lqtTjTn*TH*kiV!fjSn3>j9k#2kNV="n+$&Eqrn'2gV)>BRK.onJ_Pb1J_Pb1J_U+Ws6\:i +!Lc`5~> +l2M4lqtTjTn*TH*kiV!fjSn3>j9k#2kNV="n+$&Eqrn'2f"KW8PQ69cJ_#D'J_#D'J_'bMs6A(a +!L-3,~> +ZN#L4^"rd[!.j!XhLXO7hLXO7hYc71maL"nS5+S~> +li.LpqY0XOmH`uujPo+Th;-lcg]-%;h;7)Kj5oLimI9`@qs+34gV)>BRK.onJ_Pb1J_Pb1J_U+W +s6\:i!Lc`5~> +li.LpqY0XOmH`uujPo+Th;-lcg]-%;h;7)Kj5oLimI9`@qs+34f"KW8PQ69cJ_#D'J_#D'J_'bM +s6A(a!L-3,~> +ZN#L4^"rd[!.j!XhLXO7hLXO7hYc71maL"nS5+S~> +m/J@.p[mtBl/q$bhV?i;f@JL%eC2pse^i@)g>(QCj6#Umn+6;Om/MXk^"WRV!.imUgO\+1gO\+1 +g\fh+mF0ejRSA;~> +m/J@.p[mtBl/q$bhV?i;f@JL%eC2pse^i@)g>(QCj6#Umn+6;Om/MXf^"*4K!.i^Peq)D'eq)D' +f)4,!lI4;bPY-H~> +ZN#L4^"rd[!.j!XhLXO7hLXO7hYc71maL"nS5+S~> +mf*mtq=X:Fl/q!_gtC<0e'ZLec2>csc-FY^daZk$gtq&Pl0Rp3qBRK.onJ_Pb1J_Pb1 +J_U+Ws6\:i!Lc`5~> +mf*mtq=X:Fl/q!_gtC<0e'ZLec2>csc-FY^daZk$gtq&Pl0Rp3q +ZN'[Uqel_0hT]uqhJ^[)h?(`\W;YQU[ucj!DrBIQ`Jsb5!LD""h>qQ>leVW=rGMmgXOZ1)T%3Dg +!nEl\l.uH2!.j!XhLXO7hLXO7hYc71meGW?VOl?;h?)6!Dti)hDp@h/!Luo8~> +n,F$up[dhQb\c:m!Q'clD#opFnCmt\bj4!`V#G39DLAJ4gSCCho%O5pSSdWG!La">gO\+1 +gO\+1gO\,Wg]-[9gB+1Ae,J)'e"(@MgAu08n_4)59n3~> +n,F$up[dh +ZN'[U!!_B +=K((p?Hr&t!muERl.uH2!.j!XhLXO7hLXO7hYc71meGW?DdL]fh?(r.!;G=N!57pZ!Luo8~> +nGa4"p@7P6jP\hGeBuRbaMu-7^q[Rrrk/9E'#)/t_o9^>c-XtlgY_&Sm-s]GnGi7;!pmn^k?3&.PYFCO36tH![q/9=/O_h?d/)q!mc3K +l.Z6-!.imUgO\+1gO\+1g\fh+mJ,E;D-tNdgB,T+!;G4K!5A!X!Lc`5~> +nGa4"p@7P6jP\hGeBuRbaMu-7^q[Rrrk/9E'#)/t_o9^>c-XtlgY_&Sm-s]GnGi76!1#UUh]n9fu.5>=BNlUY?!%:l4jD +l.,m"!.i^Peq)D'eq)D'f)4,!lM/p3CL5-^ecNs"!;G%F!57pR!L-3,~> +ZN'[U!QhIcUu:)8gt:2s>&2^?g@*l!!57^T$ZB4ff$UKm +FkUb?c19]mg:as,HJ_kt7J_kt7J_kt7r8.JAptc'j?HrK+!mP$OpYGoN^%'q[S5+S~> +nc'@#p$h;0in`;gO\+1gO\+1gO\,Wg]-[9gB)5!bl6>ub[1AEgAq;gn_4)59n3~> +nc'@#p$h;0in`;caldcGZ=MN/'UeGdVCL5-RecO:- +SE\T*PQ69cJ_#D'J_#D'J_'bMs6BC1!c),armh.m?3'`N! +ZN'[U!_uF+^mSo%kC_8qGdSVG8_sc#;Iqg735*Ph+]HW8?BPOP\iUT^^e> +c!G>ehAFtSK6)g[gtpbmK6)+(g>:Zd^#$;LDgSbZhCNSOc/.=.B4]S2?H&M5hVQeSVV]:UFa9L? +VU_8VhVN1]K5Z+IhVPemDgSbah>s,HJ_kt7J_kt7J_kt7r8.JAptc'j?HrK+(!U%bhVQtVF*!Ss +hVJ7Gc#:.>DiiQFh>s-AJ,~> +o)CE?p$_2-i7ur5cH=/E^V%+d['Hs?Y-"h-Xf\b0Yd1XE\\,\o`lcTTf%]*Cl0e3@o)JI=!ULUeNDlb\c+hf9gW!P1/0>VVBjHNn`$#CD0`AUgFR2KbhLn$ARa#'?cAS3gY:2JUt`bKF*O4;UsklOgY6ST +K5Z%DgY9)`D0`A\gB!`CJ_Pb1J_Pb1J_Pb1r7h8=ptGje?d/N((!L"bgY:>IF)dAlgY2_Bb\a_2 +DiiHCgB!a;J,~> +o)CE?p$_2-i7ur5cH=/E^V%+d['Hs?Y-"h-Xf\b0Yd1XE\\,\o`lcTTf%]*Cl0e3@o)JI8! +ZN'[U!O#Ut_`o%kCt:2ekCPYP-5]`?pGeli=b9$Z9JF5GaZ6JgQcA+8=V +c!G>ehAFh&.0qYqg>:D>.0p2_f`^#"ts!#UCZhCNSOc/.=.B4KA.?G0Z`hVQL`Dr.Qe$kul7 +Dp;SghVJ6s.0(3LhVOJ?!#UCah>s,HJ_kt7J_kt7J_kt7r8.JAptc'j?HrK+(!U%bhVQge#RF&J +hVJ7G]`=96!(,!+h>s-AJ,~> +oD]X'p$_2-hqHW.bfI`<]XYATYH=k)Vl$?iV&#r+WN*)(ZF.3S^Ve+8d+-k-jQZ+)r:Bs_gAq;g +qqDRo!8H6.^>%8(0FB4ce+266[S:VjgQG9.gV*[BgXB.2#YE4o0QGk%$piY&4C.I2!mG"grS&1< +MC8feR+^6CMC8`==kDE_!5@3J+9375n_5U3?d.9Zdq)t6D-t"aD;3gh?Z"Pt8.68>7r?IW!8H6. +Q7N.UP2"`O+9375ptGh!8H8W<=B.)bM1bO^:jT- +'EgkJptGh<9n3~> +oD]X'p$_2-hqHW.bfI`<]XYATYH=k)Vl$?iV&#r+WN*)(ZF.3S^Ve+8d+-k-jQZ+)r:Bs_ec>ca +qpl4i!7o^$^"1Vn/I3e]cLTO,YtAf[erWKrf"D(7f$7/!#YN+f0ltps#W^Je3Ekq'!m+barRMh2 +KI$mUOOD_*KI$m0;U3kE!56sB(]Y,#n^]7,?HLdLc=:2&CL4\\CY%(Y?>J,j8IQ;:7Vg+P!7o^$ +PUQSLPh+EE(]Y,#psoJ1!.i^Peq)D'eq)D'f)4,!lM/p3CL5-^eeQ;5!7o`H;@!P#`n&]@]tFE) +&-,&:psoJ18q6~> +ZN'[U!3K*Ib*SPF,\_:t"SDLpZhVM.&`l:#!hK(^6[0R&ph?]tI +h?(r.DuJN1O?J*0F%2hZO?J*5]d[IJh>k77!0l')!7p_t/T4h3f$9(/J)5*M]j>X6hT]AFX@Za3 +_.OfRUhJ3DRc2a*[S,h.9h>s,HJ_kt7J_kt7J_kt7r8.JAptc'j>/gKl'ua>VhVM.& +`l:#!hVJ7G]`:(4]eMkKh>s-AJ,~> +o`#d*p$h8-hqHW-bJqB3\[A`GX/MnkU7e-MrgamtT:hmQV5UDsZ*h*S_8XRCeCi^=l0n9Co`+[? +!i3,O?e<0ECQVUO?e<5\gV.EgAnt6!1)*&!715j/T"Y-e'!P,J(ngI^0ba3gWX#AX@cj2\ml:g +b[1A)gAj(Jf51rNcN'9[RK1\0gB!`CJ_Pb1J_Pb1J_Pb1r7h8=ptGje=i10e'uF)RgY5Rs_nJ5q +gY2_B^&U:0\hHSJgB!a;J,~> +o`#d*p$h8-hqHW-bJqB3\[A`GX/MnkU7e-MrgamtT:hmQV5UDsZ*h*S_8XRCeCi^=l0n9Co`+[: +!L*f#_66W(C=+\R#\[ +a^+tuec7G +ZN'[Uqu@oi");8k4c/--;HeN7Ac/.hKm!%/V-c/*AF]j>X6hT]AFHWI]6hRQ6s +c!CD,h>jD/hRrpGc2`gShU_JY!Ls.@hLXO7hLXO7hLXP]hZ*$=h>hKi!#+5?!7'HX`W4<@!56&G +^#%VR!7pu&!Luo8~> +p&>p-p@.D0hqHT+b/M0.\$N6gX?/GgY9FBcaEu*H!(i]V@;)GgB,T+ +D>r9-f4eO5gU^O5f4eO5gY8/6gY2_BcNK-SgXG]I!,_Z7-Q%!KgML<3?Z"QPb[546=h*5@\4^\_ +?NAj8!2A6&\cCL2"H([_e+hYuRK.onJ_Pb1J_Pb1J_U+Ws6]U9!,_N3'ALt9bM1dc!70C"^:jTY +gY9"6e+q`!RSA;~> +p&>p-p@.D0hqHT+b/M0.\$N6Fac[FWp:8o)OT +!7o^$SCZ9!!7o-iN6^#"psoJ1!.i^Peq)D'eq)D'f)4,!lM/p1C\[rDf$498f%.M7`mt.']`>Ik +eu\qtq:5S28q6~> +ZN'[U!s,HJ_kt7J_kt7J_kt7r8.JAptc'j?HrK+"O16QhT#Em"hiam]tFFEh>mVr +q;).B:4N~> +pA[)Nq"!e6i7c],ai(s*[BQa2UnF9IQ^*_rO,f6[NfK0^P*D<$S=lXTX0AtA^;J%;e(NX?lgacC +s7b7K!5A?b#JL2mgAnt*@Jg*+?-;`m$bfPRN`2D$gV*[B_tsE?]%\'n6'>)^D+X3N!mG"grn@e+ +@;XcRb$Al1@;Z"u$b?>mgAntBU-T>ERdo,6D-tNagEUQB^0ba3gWEf=D-b<=gW3T9b[1A)gAnh> +gV*[BgS-tF0Uhdk!La">gO\+1gO\+1gO\,Wg]-[9gB)5!bl6?"b[1A)_tsE;]%[5!^&\/e!nRFd +q:bq=9n3~> +pA[)Nq"!e6i7c],ai(s*[BQa2UnF9IQ^*_rO,f6[NfK0^P*D<$S=lXTX0AtA^;J%;e(NX?lgacC +s7b(F!589\#JC,gec/g'`$b9#FN)#Vgf"D(7^&%d9[FQ1b6B"NPCJ+'H!m+barmhFu +?>J-C`EI'!?>KPk$afucecL*f#:g.CKe^.f#:g.a^+tuec<,/ +f"D(7eso&<0pMIa!L*S8eq)D'eq)D'eq)EMf)P%1ecKVnao9ioa^+tu^&%d5[FP>k]`A#^! +ZN'[U!k7?Xkqp)!7(/l!cMDiqq`T:?G0Z`hVQ(HDgPNqhVPe8Dp;Sg +hVJ7GhVPpGhU\gchOFSkq;).B!.j!XhLXO7hLXO7hYc71meGW?DdL]fhA4@B!8c9,fa[GKa? +]tFEZhVPpGf(n/'S5+S~> +pAZ$,oBk`"gXXWl`4idhYH"IoSXPk.OH#0TM#;uFLP^qKO-5crSY;mZY-YXP_oU-Qg>M,[o)/4] +pY,]K^AHXo^&ZpB!5?orB@Ybg_T)*")L"hE=hreH^&Zp:!70ESe$@`_?!RlZMDR/Mn(Rof?Z#es +)V8\/b1jL/GLpP7b1k[>'\h@B^=0+:gR8,co%O4_?d/E%,]-o&?Z"QP^/8`kgO\+1gO\+1gO\,Wg]-[9gB)5!bl6?4b[1A)df@_KgXEI_gAnt% +!4pq>[K+ePgB!a;J,~> +pAZ$,oBk`"gXXWl`4idhYH"IoSXPk.OH#0TM#;uFLP^qKO-5crSY;mZY-YXP_oU-Qg>M,[o)/4] +pXT?F^&-@i]`?X7!56ZjB@>AZ]ts3h)KS>5$;3OKJ>3?n(%Q^?>KPk +)U`8%`R_S#F4+])`R`V1&D#D4^!J-C\P?p];RP*,[7Y5V?3&R- +!56t7]`?X/V:O4^!64ic!L*S8eq)D'eq)D'eq)EMf)P%1ecKVnao9j,a^+tuc2c/@f$:DKec +ZN'[U!k7GhRrpGed/,'PQ9;1h>s,HJ_kt7J_kt7J_kt7r8.JAptc'j?HrK+(!U%bhVLjs`m.I:hVJ7G +]`:(4]eN.Sh>s-AJ,~> +p\u31p@%8+gt'ip`4rjiY,S4iS!T>"N/<:AJUi7LIN*ceJV/Z4MiX$eR\-CSXg>OO_o^6TgZ%Gb +p&=^cpY,]K^AHXo^&ZpB!5@3N98LQG6AP'b"/%UdH2cP3^&ZpB8PR+C;)I%^Sd6Ur`rl[on(Rof +?Z#br)3@+5^2ZW8gQ4[M_n%7-gY2_BcNp8oOp',%gB)5!bkp-?D-t4m7C(-](i?3@?b3m]&8fWu +!8H6.^>%DV!8Gg*U=58.chZ;rRK.onJ_Pb1J_Pb1J_U+Ws6]U9!c;;grnA13?NAj88PR+C;)I%^ +!5?/I352s\7JPU +p\u31p@%8+gt'ip`4rjiY,S4iS!T>"N/<:AJUi7LIN*ceJV/Z4MiX$eR\-CSXg>OO_o^6TgZ%Gb +p&=^cpXT?F^&-@i]`?X7!56sB7YAO54bN7T".V4WF8j`(]`?X76;"r28hnoJS-U:h]`8>`n(%Q^ +?>KMj)2p_*\Sjs-er2b=]sT;!f%'i7c3TrcMu_/oecKVnansX7CL4he7^:'Z(i,p9?FdUV&8TBo +!7o^$^"1cJ!7oF$SBd&ncM?#lPQ69cJ_#D'J_#D'J_'bMs6BC1!c),armhh+?3&R-6;"r28hnoJ +!56&G0tjtL6MT+4PY-H~> +ZN'[U!2J*Z3UY`*c!G>e +hAFh&.0qYqg>:D>.0p2_f`^#"\Z!#Usjh?%V%c26?BDdL\i70jT68oNRf8IQ;>8oMs\!8cQ4 +^#%VZ!8cRk&-*Q3ptc%A!.j!XhLXO7hLXO7hYc71meGW?DdL]fhA+:A!8cS`8dG\h`noPP]tFDm +&-,>Jptc%A:4N~> +p\u3-o'>Dof[A!_^UgeTWMH/TQ&pr\Kn4]#H?aZ5FrPXMH@10mKSYSKPaS,i3+dtf=X176bLdtf=V$sD3:gAntBL(jV-P4@9.D-tNagEUQBbc4&<(gfjOgSdRM(0sFIb[1A) +gAntBgV*[BgP-CB*0U*O!La">gO\+1gO\+1gO\,Wg]-[9gB)5!bl6?3b[1A)gWrS"#XQMcgAnt% +!.>C]8^I1d!Lc`5~> +p\u3-o'>Dof[A!_^UgeTWMH/TQ&pr\Kn4]#H?aZ5FrPXMH@10mKSYSKPaS, +ZN#L4i8+^PW6*oQ]iodFh>s,HJ_kt7J_kt7J_kt7r8.JAj5'gM!;#%JS5+S~> +q#;?2p$V#%g=4Bf_7R+YWMH,RP`L]VJpr#iF`MJ?rb`]oE,p&EH@:?uM2mdeSYN-d[(=&mbgY8' +k3_g?q#>p"i7eLJW6!`J]N][DgB!`CJ_Pb1J_Pb1J_Pb1r7h8=j4aUK!;"qGRSA;~> +q#;?2p$V#%g=4Bf_7R+YWMH,RP`L]VJpr#iF`MJ?rb`]oE,p&EH@:?uM2mdeSYN-d[(=&mbgY8' +k3_g?q#>ori78.@UrCp9\QX4>ecD!8J_#D'J_#D'J_#D'r7:o5j447E!;"bBPY-H~> +ZN'^Vp]0F4!,qT3RGJ1E8or8!DapD^h>s,HJ_kt7J_kt7J_kt7r8.JAq;)*ipAjpD!PJL)h>s-A +J,~> +q#MMR%:"OYI2!ZaNrDl +j6?(/q#C-Dp]0F1!,_H1RG.t@88lSjDa^2ZgB!`CJ_Pb1J_Pb1J_Pb1r7h8=q:bmdpAjpA!PSR* +gB!a;J,~> +q#MMR%:"OYI2!ZaNrDl +j6?(/q#C-?p]0F,!,M +ZN#L4hqeR13=$$VSD*,^!Ls.@hLXO7hLXO7hLXP]hZ*$(h>to"oA0M<:4N~> +q>WYVp$V#$g=+9c^UUSNVP'BBNei=:H$+(BBkCj`?sd5I?t!PVBP_X0G^Y1"NK]d&V5pl1^;S4C +f\biYo`"[dJ_T,;#MGV.$48hag#h#iRK.onJ_Pb1J_Pb1J_U+Ws6\k$!R"L7gB!a;J,~> +q>WYVp$V#$g=+9c^UUSNVP'BBNei=:H$+(BBkCj`?sd5I?t!PVBP_X0G^Y1"NK]d&V5pl1^;S4C +f\biYo`"[dJ_&`0#+R[Z#W\:#k10Qt!.i^Peq)D'eq)D'f)4,!lJpFr`_uEZ!L-3,~> +ZN#L4^"rd[!.j!XhLXO7hLXO7hYc71maL"nS5+S~> +q>VK2o'>Amf$DIT]="f>Tpq=.M1^8%F)G`)@UEAD=T2AB=BSm8@:a.jEd)q`LlR^gTVnou]"uG4 +e_K3Mnbi4_J_Rlm!La">gO\+1gO\+1gO\,Wg]-ZigB!a;J,~> +q>VK2o'>Amf$DIT]="f>Tpq=.M1^8%F)G`)@UEAD=T2AB=BSm8@:a.jEd)q`LlR^gTVnou]"uG4 +e_K3Mnbi4_J_%Nc!L*S8eq)D'eq)D'eq)EMf)P$aecD"-J,~> +ZN#L4^"rd[!.j!XhLXO7hLXO7hYc71maL"nS5+S~> +qYreZp[@>)g=+9b^UUPKUn*j6MM-G'F)>T$?X$W4;Gg7d:Jaqg='K!@BPhd6I=mB;QCO_LYd_ +qYreZp[@>)g=+9b^UUPKUn*j6MM-G'F)>T$?X$W4;Gg7d:Jaqg='K!@BPhd6I=mB;QCO_LYd_ +ZN#L4^"rd[!.j!XhLXO7hLXO7hYc71maL"nS5+S~> +qYrhXo^1euf[7gX] +qYrhXo^1euf[7gX] +ZN#L4^"rd[!.j!XhLXO7hLXO7hYc71maL"nS5+S~> +qYrhVo'>Ale]u4N\[&93SX,=nJpVWYBk(FN;bfqZD3DK_gpKL=8PW/b>[hAaFF&LnNg6-0WN`kG +`m*#fip-(3qYu-$^"WRV!.imUgO\+1gO\+1g\fh+mF0ejRSA;~> +qYrhVo'>Ale]u4N\[&93SX,=nJpVWYBk(FN;bfqZD3DK_gpKL=8PW/b>[hAaFF&LnNg6-0WN`kG +`m*#fip-(3qYu,t^"*4K!.i^Peq)D'eq)D'f)4,!lI4;bPY-H~> +ZN#KG]USs%J_kt7J_kt7J_kt7r8%JBc-i*^:4N~> +qYqQ0nEJrde',bD[]cX'RZi\aIX#jIA6rA:9h7\!NAR@pI\[CjE" +db3RBn+cbZJXX6?!!%T)J_Pb1J_Pb1J_U+W!U\+ZRK-'4~> +qYqQ0nEJrde',bD[]cX'RZi\aIX#jIA6rA:9h7\!NAR@pI\[CjE" +db3RBn+cbZJX!g3!!%T$J_#D'J_#D'J_'bM!U@eTPQ4=+~> +Zi>RV\c@< +qu8t]p?q,%f[7jY]ZWpA=l@!4i/]gO\+1gO\+1gO\,WgAl*;!.Y~> +qu8t]p?q,%f[7jY]ZWpA=l@!4i/]eq)D'eq)D'eq)EMec9L4!.Y~> +JcC<$JcC<$JcC<$JcC<$[f6>WJ,~> +qu8t\o^1etf?_OS\[&93S<]+iIs>sJ@pE&18OGX":bO=JXoHNr1,h<^85 +qu8t\o^1etf?_OS\[&93S<]+iIs>sJ@pE&18OGX":bO=JXoHNr1,h<^85 +ZN#L4J_kt7J_kt7J_kt7L>Ds$:4N~> +qu9"[oBbPof$;=N\?Ms,RZi\`I +qu9"[oBbPof$;=N\?Ms,RZi\`I +`r?+JHgU>&J_kt7J_kt7J_kt7J_l. +qu9"Zo'>>keBQ"I\$)a'R?EG[HZX+9?<9rp69@"U-R'QLVuOd\-7gr65tFdP?"@_lH@h!8Ce^D% +[CsN&e(`mInc&FcJ_Pb1J_Pb1J_Pb1J_Pq6!Lc`5~> +qu9"Zo'>>keBQ"I\$)a'R?EG[HZX+9?<9rp69@"U-R'QLVuOd\-7gr65tFdP?"@_lH@h!8Ce^D% +[CsN&e(`mInc&FcJ_#D'J_#D'J_#D'J_#S,!L-3,~> +`r?27N$Wf:o)F9tJ_kt7J_kt7J_kt7L>Ds$:4N~> +qu9"Yo'58ieBGnF[]ZO$R$!5WH?3k4>ZFNh5<(AI,97R5V>nLS+t53)5"/1F>@VDfH%:^3O]NAu +VnBpjdb<[EnGW7aJ_Pb1J_Pb1J_Pb1J_Pq6!Lc`5~> +qu9"Yo'58ieBGnF[]ZO$R$!5WH?3k4>ZFNh5<(AI,97R5V>nLS+t53)5"/1F>@VDfH%:^3O]NAu +VnBpjdb<[EnGW7aJ_#D'J_#D'J_#D'J_#S,!L-3,~> +`;]u6N[8o8o`'L!J_kt7J_kt7J_kt7L>Ds$:4N~> +qu9"Yn`o,ge',bD[B?C!Q]R#SH#dY0>?"%22cG^tR1QCa^V +)HaAhdb<[EnGN1`J_Pb1J_Pb1J_Pb1J_Pq6!Lc`5~> +qu9"Yn`o,ge',bD[B?C!Q]R#SH#dY0>?"%22cG^tR1QCa^V +)HaAhdb<[EnGN1`J_#D'J_#D'J_#D'J_#S,!L-3,~> +_Z'c4O=#2;pA]^#J_kt7J_kt7J_kt7L>Ds$:4N~> +qu9"Yn`o,ge',bD[B6?"9b4Z4o?*u>IoTDueE*[NBp4@;b>>%),aG^kL/QCXkR +Y%et`_V*o3nGN1`J_Pb1J_Pb1J_Pb1J_Pq6!Lc`5~> +qu9"Yn`o,ge',bD[B6?"9b4Z4o?*u>IoTDueE*[NBp4@;b>>%),aG^kL/QCXkR +Y%et`_V*o3nGN1`J_#D'J_#D'J_#D'J_#S,!L-3,~> +_#FQ3OsP;:q#>p%J_kt7J_kt7J_kt7L>Ds$:4N~> +qu9"Yn`o,ge',bD[B?C!Q]R#SH#dY0>?"9b4uP&A+;kb!U&W%I+!rQs4@Dh@>%),aG^kL0QCaqS +[(F"n+(W[]nGW7aJ_Pb1J_Pb1J_Pb1J_Pq6!Lc`5~> +qu9"Yn`o,ge',bD[B?C!Q]R#SH#dY0>?"9b4uP&A+;kb!U&W%I+!rQs4@Dh@>%),aG^kL0QCaqS +[(F"n+(W[]nGW7aJ_#D'J_#D'J_#D'J_#S,!L-3,~> +^Ae?2PU:M:qYu-'J_kt7J_kt7J_kt7L>Ds$:4N~> +qu9"Yo'58ieBGnF[]ZO#R#m/UH?3k4>ZFNg5<(>G+rh@1V#S@P+Xf!%5"/.E>@M>eH%:^3Q_((V +[(O<"b^UgOg]%*MJ_Pb1J_Pb1J_Pb1J_Pq6!Lc`5~> +qu9"Yo'58ieBGnF[]ZO#R#m/UH?3k4>ZFNg5<(>G+rh@1V#S@P+Xf!%5"/.E>@M>eH%:^3Q_((V +[(O<"b^UgOg]%*MJ_#D'J_#D'J_#D'J_#S,!L-3,~> +]`/-0Pp^Y:r;V?)eD:3+f"'Z:f)O%peq)\/hLXO7hLXO;h>s-AJ,~> +qu9%[o'>>keBPtH[]cX&R? +qu9%[o'>>keBPtH[]cX&R?cr\^pT%J_#D'J_#D'J_#P+!L-3,~> +])Mp/QRHn +qu9+_oBbPoe]u1L\?Ms,RZiY_IR@pI] +[_B`*eD'$Km$B,@kl1U5gZdGo\sg5!g]%6SbLaG^$eN^Z*"3Ai#T/0>fC7Mr\sg5"gBXm$749QH +AZFkH#fMd.2CL=WLVi0-#Hi&8-n%2hWpJIA\sg3fgB+sT\q/UegO\+?gB!a;J,~> +qu9+_oBbPoe]u1L\?Ms,RZiY_IR@pI] +[_B`*eD'$Km$B,@kl1U5f'1`e[?S)ff)G^M`k9n&c2iS8r;Zp:Hd9q+ecN7D[ID`PX_U6P.61n7 +jjj^RN)heb0i!D8jjj[=CI0I!3FeoPecN7D[>NN:[?S(Meq)D'erSA_PY-H~> +\Gla/R43++IN;nP6:Bh?(C8mdf3B``b* +qu94bo^1bsf?_OR\[&61S<](gIs>sJ@pE#084#Es0eFk'XoHKo0fD*Z7o!#f@VKe*IYE`ES"cmd +\A-)0e_TBX"]$0R-gAq-7gD7;fTUqO6N.uh-F)bo*?!1$!76s$q +0S.u0gO\,1gB!a;J,~> +qu94bo^1bsf?_OR\[&61S<](gIs>sJ@pE#084#Es0eFk'XoHKo0fD*Z7o!#f@VKe*IYE`ES"cmd +\A-)0e_T5hD#^! +[f6L,Rjr?to\KS,qq_V0`jJuo@X#rJmG@lNqq_S/`jK!70tl0(h?]3QIs66\:*b&+h?(C8me#?: +dUW4U"4$W@cJ@=ZDemqXkhZlr6B'dakMFe@cGml;F#hOsoA0P^c0k2[%BV'h[Ed(KcHaG+8e@ib +h@@M?7Z>1/daHF`[ +qu8t]p?q,$f[7gX]9h.K33&E?CYlDp'2`j8o9N"u!AScC4J;92OSYWZWp\t0qqO8sQ`UqmbIJ_n-f>k1"G@bp6ea;)lXKMHX#M7#%=dCnQcg0MA+JU(BP +Hck(E]$0R6gB#N*r;Zu\rrC6ohV/0MC":hglIus'cT!b$I;rCe9he2J5q"S9e(OBJ!kQVCo@j_W +-t0R-cdU4`_kp-BU?gPD`+qc!^s19Sc,[2:0O)9WgCCA7@CFA*e'H4TVG/aK^@9kY]$0Q!gB+t1 +mH`O,qruiI]XH)*^:(2ASsG=iI +qu8t]p?q,$f[7gX]9h.K33&E?CYlDp'2`j8o9N"u!AScC4J;92OSYWZWp\t0qqO8sQ_=Z:YC\urof#FgiF(04,dd>TdTVVJA#M-ns;NroDcKj$eQue(:Ljrks +J'-=D[E7_+ecEm!r;Zr>pAi4hecT]#cI1q-#gP.13D):K&.naF!\q^?o@1/daHF`[ +[/U:+SKqR_p>,h'hY[.pF\^"2Q,#b[-aZ*giu>)UP[!klkH +p"fhV7Ql^,rQYK5!7(T#"-`cc^#e)RDZFFug?RMrc7ua*kPsE=ed'L?]tKXbJ(F8K!klkHo\L+u ++`kr[hWOA%pZL,@=qJC7h@S+V8taVdip641n(u0E2.k,#h@R[MF3`@nkO80>kLR>)2.k,'h?(C8 +mdf6)kO%++ipl4!hXTb/iq)".^!6!2hYmfGh@YubIs5sPB3nV65rgYK+W(arquH6Co7R+KJ_kt7 +J_p+W!Luo8~> +qYrhTnEJoce'#\C[]cU&R?EJ^IWoaG@pN/69LhE85sIN?s3Eu!7nH?NH!,'^0g$IH-f?Yj6H#Zti5/ +n+6/6e%Uf"A'"2p!kQVCo%W]tmFqX"n`T;on`Ko2o@j>XbjF`Nrql!8&\b]o^::JKU7.7&L3[cQ +@9H>(8HD+2nD!O7!,[;AJ_Pb1p=fV:9n3~> +qYrhTnEJoce'#\C[]cU&R?EJ^IWoaG@pN/69LhE85sIN?s3Eu!7nH?NkLR=M&@LoV&BSi_T[MQRlgsl>hT:[5@`@`c&A96.cIUk8n+ZS>f"Hu!@`@lg +!k$/8o%*?omFD9on`T;jn`Ko/o@ +ZMt']M'ZKJptc%AhYR6XmolOD[:XX)g@*nu]`@Ksrs/!rn'7FX^"2T-#c*9]_90HY?Alt_!klkH +p>,nQ78Bu]c3j:GcJICXHYWVhf'2#rg=C%38nZT+m+qfp6FHgUs6p!N$Js@Z`l>^"P\,^)p"fb` +c0k5\"]AOLf&uf+s8VfqkJfhTmG8&70]H,;H!Pp;Gp+ZLkJ*V +n_O>\c0k/Zn`T;r!93P3mG@Br!93V,!klkHle_RGleW5DMnQg7[BZj5V4a?KPa%,[K`-5t)"?D\ +KE/:Rh?1$9KX$*bJ_kt7pYGq@:4N~> +qYqQ2o'58je]u1L\?W'/Sf6?k!f#2$qu6rfU6qq=X(&JjGFagCR(^cUCE&;>^5f +;,'W:JjGFegB+t1mIK$%kO%"(iUQ*ug[XG,iUbe*]$0R-g\q$8gC_ +qYqQ2o'58je]u1L\?W'/S/0j`!E,cjrs8'kT\d(gF2?;necM(_de`tKmo&W"S2%FsmFM<9#2\P/ldh#Zb16X% +am/TN"/nX\V>\F5c3j:G`n&]@HXd&XcKWmbd`uJp7UsThm+)0]H,;H!Pp;Gp+ZLkJ*Rn^[cL +am/HJn`T;j!8d8/mFLgj!8d=u!k$/8ldl"7ldc*rp[8%^pU0MArIb0QqUPbNKS6IYeq)D'f([_r +PY-H~> +ZN's]"P\h]?,cZs!gVX6qu6rr>+k$&NI.bUn)")b!U0@Vrs/:%k2s'`ONHnH#cNio^V-Rs:l!3M +!klkHpYH&*LHg!rr6G>j$KbB%f%t1*K/$QOleVutS8E%!<-_T9f%f]M(>?0+cKFBVhV.;YhU]i[ +`kAdUH\'/kp>,kac0k8]&]HVcdbaTms.H%bqtKF-G9t)oh?VGV@'8/Gp\k*k#Q=Jgi29=kcgT^' +`ANS%7SZiZs8Ptp8kP2C:34bPh?(C8mdf6)kO%++ipl4!hY$(3mJl>Liq)".^!6!2hYmc\F6h]_ +T` +qYqQ5o^1bsf?hXV]ljNEL+V9\Pa"LbAbp\k$i#PD6[jg&NS_X6(`X.n/&i7GJX +MoscU]$0R9gBQ,\:6jJnjo4HCkmR6$A^T1FuFEQdq-!kQVCo%W]tmFqX"n`T;op#l@Ws5sC@o@j>XbjF`N +rqc]]le<2mcLC8glg!]rjPo.Tgt:3-d*BkZ`)H0["`6-t!&gYMgB46g2Jn$"J_Pb1pY,_;9n3~> +qYqQ5o^1bsf?hXV] +ZN'm["P\n`>f?Qs"'V/EqtpBuAq->:KlN!jn)")s!nul8qu6k4R%TCfA$PR\!9!k9#dKi1\Yt?k +0mi0ArSltr!klkHpYH(m?r3u=VZ!1fU^NDf>0>RQB246_T[rMe%*OK&9O)4pZ?ocDeD0NL(=lfo +kND0R[AhI2hTiO6[CruUTQ-4Kp>,kac0k8]"Gf@chXCIDs$Hbc"o%KK`@ZH*h?C5rL",8Zqu6lt +oBXtQ<35QF&r8iqhG-tIW;lmrW(osPep15ro%jG]c0k/Zn`T;r!93P3mG@g)s766XrT=@OlJ17Z +iq)".^!6!2hZ!i]F6_T_Up]am"T.rfo/Z9r"im'bKUTsXhZ'j3hLXO7hY5k-S5+S~> +qYqW9p@%5(g=+9b^:1>HURd^4M1^5#EboAu?9j^uY7ers!,%TT"/!Wq"gDh#?+8 +Yc?C4f#agmJ*G_dh<*Gn]$0R9gBGB$BZo)MrkK#\`5])u=i](CARDA9T%!)]%EXE&;e9g>`.M6[ +cdph>gD8788p=cL;di[%=&ht#AP#6T4>f6^fCmr#]$0R7gC]'+`ng+\rVo;TrVQ9VkJAoun(SJY +A;/Fnp&+ghs8Mrko'=eNNB&q`Tlh+pnHW;lmrVGBpTe9P&oo%O5WbjFuUn`T;o!9*J2mG%U& +s)S1BrT=?d3B=5)iUbe*]$0R-g].-Nlg!4*`7FP[#6"Gpp\4,^n(S#A2?3_8V#&jGD1UGmgO\,R +gB!a;J,~> +qYqW9p@%5(g=+9b^:1>HURd^4M1^5#EboAu?qtpBuAq->:IpRE8nC@cmFWc%lqtpBtAsf6MF%WD$ec=A% +g'G$mjQYa^_ejthf)==becN8$lM&j6]j+=1[@WcaS.)3X<`[&bdUPn%!1_L)edTNO84HQ^J!t4R +QHn"Ked0Pn(d]2R'KIW@&-=RP'`A:=-]6@GecN8$lLi^2ImM35n,E@e6N@&hp[@:a/U-[7"fbsa +eE6N&rs&Alldg>ZVXN(KImM35:/\$hs8T$r:.go)GuCO1ecN8$lLNNrkO$h#hXTdrf)=e3Er`?@ +k67tJ3N_K$o@ +ZN'gY$ep[i>/T4Rih?Hsn,*+i\ZVUD +=scIRDfM`Ch@GRAWMu,HS>;gNP_41$GB7Z?'ep\t3m6 +N@&hp[@;%G<,-K"d3T+iplpCrs&Allf+pHMsoL+7Z?'e;H!Dl##\'\ii6+=o%jG]c0k/Zn`T;r! +93P3mG@j*%J/T=lLXE+mHW2t[HHQ@h?(C8md&^1#]o7e$`(31i4lt=hZ)NM_>CqG\H;No2=B<)a +-q[7R`G<^s2T]IJ_kt7pYGq@:4N~> +q>WYSo'>Amf$DFR]lHTVeNXQA'[0H$Ai6!kQVCpY-OI>boWQTU;1:StLj"@kCMHVI@KVT+0c5s.1V@DU[lgB+t1 +mIf3G7Z5m_p\Y!j77I1Pp[@;$G<53I#*pM#rVKGd +Rc:>MMXoL']$0R4g[XIogAp4&kO%%'k7)J4!',,&kH-`E!-@>^o@j>XbjF`N!W23IgC(sNs5:], +^>AnI^qfo_#/%OW\Fo5pmb7o@2?3_8V#/pHUk.9OgO\,RgB!a;J,~> +q>WYSo'>Amf$DFR]1bG +qtIMdecimWKS5`/qpte`J_#D'J_'SH!L-3,~> +ZN'aW$J^[j=i97Z$jgkLqu6m!m)c&IGId5"#2nS'da$SDqu6ishR/SOPk*ZCi1>4gYI1pDP_FTp +0+nZt%!h9t&E6;>\9)^g]F(-S![Dce4hVJ7CDZIW) +n(nS>8;#+8c,IH2]s;ugSCZKdhAFsMT\A8^l`L3em`E#Umf2:h[BZoq@$KC>h?(C8me,E>*4aqp +p\pic6N<\^SF>XtX;(-["at10k4?dnTE_",lf,lkH10Sn*4aqpUo=4k#Q(V(G\H;No1%!g$a-q[7R`C!9J_kt7nD429:4N~> +q>VK5p$V#$g!\*`^::GKV4X0>NJN17G][k>BOtX\?N+4M?=75QB5;F,GC4ssN09R"UoLZ.]u8(@ +f\biXp%\R_gBuVn-s49t]a4p^qu$I!rV>]sSSrPWnCn/j^:L8Sg&(^I#6"5NY)?5Ao%Oc4TnBMY +^;eLDaMa][6U*=MI.#`#hbpDUXI>HHU7@?qB2_>h*qJ%e]$0R9gDFd5`jh%?92/-PLGi/<_I_9P +f`9Qe!5?kMgCW)%9TIfbh:U6+e&6rFR*jRWgDJIB:2Np2=aC&IfQh88mJj+t5s.1\3g84jgB+t1 +mIf3G+Lp7rpRn7T77El@SF>XsX;(-X#(:=1k4?cirh9[lT:Nseil#5Kn(S$h\D?VLVZ$Mo#lFFl +T#0L*@!D1r!kQVCo%X$(s)\&"mFq^$kH4Dus6&e(roa<&rW!3aF#a4#3N_K'o@j>XbjF`N!:/k' +(;n]&i5!(@hZ)KK^qROq]XkV_[e&fhmFqf?2?3_8Uk.9OgO\,KgB!a;J,~> +q>VK5p$V#$g!\*`^::GKV4X0>NJN17G][k>BOtX\?N+4M?=75QB5;F,GC4ssN09R"UoLZ.]u8(@ +f\biXp%\R_edBoa-W[iS9`bB7qtpBtq;f;XBQ(S\pXTDRam/QM +"<=u/n+ZgSs$HaY"e>&EhQ.F[ecg>7bNAE>qk+.b`TuO&@!1qh"WY)0n"co[rs/FsSAF4(@!2%k +!k$/8o%*[#s)\&"mFD?qkH4Dus6&e#roa<&rW!3aF#a4#3N_K$o@:hI\bGj+mFDHWR"U-[_1C0geq)EAecD"-J,~> +ZN'aW%H?7"OXXAa;ZI/5o(2bWrrr5`ab=rLp"o`,rn[t:kk!NI`Tm'dqu-NtoA[#YPk*ZCiiSZm +d`8baIs5s3-57""%X7?t&Ee-#f%.sRXK84bHX8]T0bEp4!klkHpYHa*>+B_)2+CaL@OY?c`Ff2! +g;*d"IfR11nD4Am>Yr&OY+klb"d&B<_up\@,Yn)rE<#k-URGAG^lo:5YD"dnW6b@5P]h?=J1ipZX=rVm0#p\4.Bi6,SNo%jG] +c0k/Zqr\'&!!%*;kO%+.iodF(pAb89kND6hr9"HqXgQ[.`heX-kNCjrh?(C8me,H2me(W$W:\pI +U9jIkrV$BblS7b_"im'bKUTr4hLXO7hXTG'S5+S~> +q#;?/n`o/if$DIT]XG#CURmj9NJN49H?O=IChdWqrad']B5)1"EclYVJqo;IQ^skLY-bgWaNi>j +ip#t1q#C0E(YREhMC([-8,rs%m-XKGs8Dfai4XJW[e8Akg=Y!+cFCaCYG/D_mHsTHs8D]XeYZ3, +o%Oc4XJO\3hUBWc_SDk"KS!u!Hg]W"hg=\Rl/CCIe'H%DWgJ]]*:hhc]$0R9gDFdC_44mq799JE +2H@'"AH:!,^&Z]^!4^AEgBP]%=3/q/\c&r?XF"*6cI_XA(ss%9=&Dt!?,HXAf#6tUfS+$'4?>2< +ERsm=!kQVCp"KW_a5QL[6iBFY6U=$*jk79Pn(S%iCt.\>S4*?F#Xh!FQg*DM@WCkk"qnu8n+HMR +rr!,up[de:ho]DJo%O5WbjFuUqr\'&!!%*;kO%"+iTI='pAb89kND6er9"*%3W&j[3N_K-iUbe* +]$0R7g\5XZlN#u;gB#1@pAYHtqtp-^p%._<=mXlHUc&2Q2PboYJ_Pb1n_4)59n3~> +q#;?/n`o/if$DIT]XG#CURmj9NJN49H?O=IChdWqrad']B5)1"EclYVJqo;IQ^skLY-bgWaNi>j +ip#t1q#C0@(WjA&HQ<<>*WQFHh;7T"s82QWg:)-?[Ir)ecbmH0X]A8>01q2OhVR]#s82EMcCdXk +o%"$!Z*36Ul/)Bk!5JN3!g6H2ma_U!J+N-tlf\9,#1h5A]sWqNo%!lMam/WO(i*ac+WQFc!ij7#Qb?M +deD;o[E7_,ecZQ(ipZX;63Qc?g:[d\ecg,1eEHLoqEb@QleorqDsu6\&B<_up\Xph#lFAjn*AoT +@W1hg!k$/8o%*^$"*4GBF8P.fecXdlXZ?+U!d!PfmFM3u!HU:!!!G +ZN'dX!Tr_IiY1+20hDcR#slGMg@tXIrV,$(Fh.5&(ujcAio8nQmeu:k`R<&OakHLhs82E(h@Sjoil/mEWM#oSOEX_[&.ej4h?(C8me>QTDe#/02/T#"!%CsV +`Fif``P#/I[=eOjcLBZoPXBr0RXtEa#&m.$NlTT%o%j]$(tR-dg>^B!f)>UUpYO'+[B6Ks:j(Fb +h?(C8me,E>*4=tqoDY9[63j$JR-Wb_W"e^W"at+1l0l[jS-PLM_<0Of??u"r"WXr5l_'pLqZurf +TU`dP^Ku3Vh?(C8mdf64k5]T%!!%*V(G\H;6_1$d["a-q[7R`C!9J_kt7o%jD;:4N~> +q#;?4o^1i"g=+)p`ZamiibgP2% +k3Vg9s8(INdDl1&'=N*95m[Ms2kd7ip%S+Jl-+`u[eAGmf#u"K_S*D'j1_.WZEC"'`:`lpn)LiP +P4I?>h6!aJX +)JdmtgB+t1mIf3G)Re\lnX?&@6UR<0R-EMYV@r@P#'st,kO- +q#;?4o^1i"g=+)p`ZamiibgP2% +k3Vg9s8(:`OCD7#F%ml!*ikotU[J&/gZ[#e\CLBG>HTrr:B4bIgT_J'Dle8I- +Pk*B1g9J'Sn`Jc`rmh;#`P9*b>'o\g"Q"Y"n*g2;s5!n0b/TYho%!lMam/WO(i*ND8hjtG[/_LR +XhA4S!55p<;RLiO[Ee9m"ctriS8Cms63]Lt22Arhddkrkc2^]]Je!k$/8o%*a%!-A)=!-A)$mFDEskNAs-q>^OVF8k@if)+V2XZ?@\!BI:]k5aB1ecN8$ +lLi`o!!$g,edL@+s5:Z*^#&eG^VTf\s1&4*n$VK4"i6RZKUBT,eq)D'f(.AmPY-H~> +ZN'dXro+[]oBt._(^L*A#:>bAhY[3GpYq9U\b=ksi8Wb[naQ&Ds7Ya?ilRj`Z/5BTp\2s2Pk*ZC +i2iHh^S7$RHZ*\'1DC0)#]K.f&E6T`S=kt"N0K6MAjmsV&-2:X!klkHpYHa*]<2!M8@Kgh"';.c6:cadam.+hQd%Z$Lc9V[C*-9OA'fJ +pYGtbc0k8]"?h7okNi-=s$?t^n*AuedVh/7h?AmFZ/k0rT=1?oA0P^ +c0k8]p[7]q!3#PH%A:!+i4lt=hZ)HI_7dP\\c9&=n)lFjh?:lfKS6U]hLXO7hX]M(S5+S~> +p\u3-na#8mf?qd\^U^\QW2#rPP`L`XKReJtH$=H1FW,IKH@:K5(@c,e&Rc,[K$U6oX-o%O5WbjG/Z(i=OC\a3iQ1gBl17ED'#%;,^JPrVm5;APG?N3&`K- +CXr16!kQVCp"L'@J`#STn+6-:61O@.iRuOq2"00'MdN^Jkj@p>o*FpUm,m-RT2LI5gBB]jhrRL* +rUUAnH^:q7m]"#2W['];`=mF`EUc&2QD1UGmgO\,MgB!a; +J,~> +p\u3-na#8mf?qd\^U^\QW2#rPP`L`XKReJtH$=H1FW,IKH@:t#YK/IANL/!!!!$"+*6#qjPo%8H#'h]eekYs)C]!.C3+)g6TmRT!$QF7k2PCSYsO]G +ee-Q9Z0L`9i83,;f$qp[]r5+]ma_Np@b0](rnd\7s4RV(`kdl_o%!lMam/WO(i*$)2*mb?`;j)H +]jkR#!*W9$Z*A?M[EeC++X&R%,rL=c`Qm!(eca7u63n"u&--qQrrSuR)#X^B"p/Gc +pXTDRam/QM"?CtkkNi-=s$?t^n*AuedVgl/ecgh6Z/k0rT=1;o@JCp[>NTec`gVKS6IYeq)D'f(.AmPY-H~> +ZN'dX#MRVNb_@JJ"oJ?&"<+5mrV$EVP&\&+h?D]cce@a]qu6i^]pP=tgAUaIekVJ"h@Q3.Wg\Qs +Nd5#9>!jVP&.!4Ah@D#8?=$Ar8lADV3@#.E&-2:X!klkHpYHa*4+0Cq]?8iC`PlIrdc,1\B>)!C +CB1cfn_Oi2CKR0X:dJ282+^)j]uej\n_OT#(t-RLdaGtRdf'1QpY* +G;75!ir89D4U:<8inDhk-$Sh7"fbOMf&GiilNcY7hU]Ya:9*d>&pH(Pi^uh5SEoL2S45u,`G\MO +o%jG]c0k/ZroX9&q#CD9ro`\*!93t?!NeB]!!,*^r9*J(r9"=VlEdV+_s6Xgk5aN5h?(C8md&a, +md8j3TX"4j!;lci!;$3Y!Tu3&h?:lfKUTr4hLXO7hX]M(S5+S~> +p]!8Pp$_,(gss`m`4iafXf/"eR[0+sMMHn;J:DuqI!pElJ:`H0MN*a`R@^1OXKo=K_oU-QgYq>_ +q"FaagBZEGW24V]!sJN%$Nh3@dHToYj3WKt[eAG]g<6h-a5#b6g]#k7^5[8nYgM_"jl+!JP4@9< +Ss7ULa3)WC\[JuMTpgKpG4"#p1mYS]ZE147ZEU:#R[.Y#o%O5WbjG/Z(i;p><`QZMdqQV7AT@87 +H[0hkft*CJ[a4Ku&_Li-[[gu"="?UC7YeUpcdph@gBl15Db!Gn:J#Q?rVm5`O@_Pq3&W9%CXr16 +!kQVCp"L((1'lNle=j5&8'DG@%tgCeceaPTQ^S=Jp2 +S=2rF#a4\3<2(AroX:? +o@j>XbjF`NqetlV!Pm.[rseu*qtp3`p%._Bn*T. +p]!8Pp$_,(gss`m`4iafXf/"eR[0+sMMHn;J:DuqI!pElJ:`H0MN*a`R@^1OXKo=K_oU-QgYq>_ +q"Faaed'W^2)76+!#\a2\Xi]GVg=!5mPk!?) +SdsW:gtpuCeC)jn`PJQXHgTAd4gEGdrm(Pls3:bd_7c'Vo%!lMam/WO(i)'Q4]8W&dV$8,@p717 +CMRRn]p)t+[Ee"9<)_pXTDR +am/QM"EuTBio9t's#gVQk2P+C\Ku1_echpMIFdE?r9FUOjPJJ,Db?bmee30]al5obS=Jp2S=E&< +N5]^-U@Qk:[E7_)f)Fh4F85e9F8k@iec=S*k5h\FrVusZXo.ALf)4\9F#a4\3<2(AroX: +ZN'aW$.d53W/Xh(',:lR!!33?ec#4EjGn/hp"fl+ccOVtq>UC!o]iuSR'G62rV$BA8sJl&&;Wjr15;OQ##]K+e":QP--li.%!>H+E$ig:uh?(C8me>QTPWN>J>/AM@g9-gddc0)g1t#b=>Os)>S]bd^WG!\nD5#5*502.b.Xe.p]'jVs8Vi<[B69sHTQH*pYGtbc0k8] +&[Nh?M#69;^@si;MX?hV-Q(Q9T-/n(n/O+EuE97LMiJi^QC(6A(Rg +C+!klkHo%sE0!-@l7!d!PfmG7j&r9"3b3<2(AqrdA'r9"=b_2Q(QXi'6!k5aN5h?(C8md&a, +s6\YOTX"OR]tM8As5(T2^&GPH[B[!In)?(fh?:lfKUTr4hLXO7hXTG'S5+S~> +pAZ'-oBbVtg=4Ei_nERdY,S7jS=#S)O,SsPL5#V[)MNp$M2[IWPaIu6Uo:E$\%TZ"c-tA'jm2O; +pAasC$.HJSJU0s2&.o3I!"&cFbj"b"k]\)#lf#YLVGE/iMjltmch7Q7@n_4V2 +Atl2rUTq;/W19$7Mf1@mmFqd_AW;V'L&HW(Jc:-"<=\TS!kQVCpY-OJ6@3!@^<>3]c"1\RnaZ)6 +kM+(RCE0e/n_4W,Bkb"o4)qQ0*(c""`R!,hn_4o.(gf^%8OQe&p]&mus8V2/6o[(\-Q-sUpY,b\ +bjG)X&[3C%e_/d;iJWWOgXjikB,\AMgBPW19;^=qhu;R?i8*)9b*>5,b4"'eYTS/?r^R>fiSd9s +772]m5\eI4gB+t1mIK$3k5]St!!76)kO%"(iV_jAXZ6AAXo%;Kg\g77!!PBbXZ6B&roX:?o@j>X +bjF`NqZQ<@(;J8shS-_@ir7`J_Rd7e[^ +pAZ'-oBbVtg=4Ei_nERdY,S7jS=#S)O,SsPL5#V[)MNp$M2[IWPaIu6Uo:E$\%TZ"c-tA'jm2O; +pAas>$-no)'fI3D"U+u,!"&cE_VX)!cFJ"0[Ir)ed^*jH4^_Mm8ina93HM.:Q3b,IOgn^\8. +Fh,?%]>rFHb.4ddZ^+4-mFD@^LomrYXo>7"HPl>)!k$/8pXU1A2dT*W\]34Kb%"JgdaHF_`P&3o +72WGXn^\6!B23@u-Z?1j$9*aN_p6d$ecO,.2>mdf0tm>]c-?:ghHCWm#Qk28DpnC2!k$/8p!s_V ++EuDjhW!OQMrNIEd`#i9[I;ZN_.H?;f\GE`iWn>of#s@DB#X5i"0&hXf;"[E7_"f)#I7 +edL@+s5(H&_;bLO_8ZDerOE$Dp[>NUec`gVKUBT,eq)D'f(%;lPY-H~> +ZN'^V&),.NkPtS\erN9C&/#0[dJikBif%fdo\L)5dEC88s8W&]etAD@g@P7:!mXE&n_Oh$+>G>m +2*EZ6(*Y4Y+qt6"mG7pF#Rh.?#QYJZrhAF:85@RHPb1b]f:;+s$cH=/CTRUDj +@_pXfh@\SG_qJm*Q9d[&]jNRX_opWdn(no8@[OjF_4[e-s7=sqoDejI[Afg^BN1GUpYGtbc0k8] +"k%T;ONu/LecjLI`IAS6mbS0#Ue=gncI2[B#1V#AU.RSCmbSQ(G9E!#KSYTlR4k5qc,Xnq5JhYlUGoA[BqkNC-m^#%r8iq)".^!6!2hY[??h@$!]s5(H& +_;bLO^VTo[!4Dg2"1daS1$ma"a-q[Ma+<)uhLXPQh>s-AJ,~> +p&>p(na#;og!e6f_nN[gYH+RrT:D=8PECodNW+kSNK0'^PEhN)T;&3_YHtaP_T0pLf\Y]SnGMtZ +ptHS7^6lsqf@.$D0-1Z;"qec7iSDY+CUWli(>-a0YL2%Se%;JoNjRF$lf[1KRJ>C +5,%N$!kQVCo%X3-olL/rmFqX"qr\$_F05<%mG%U&%!)D/XlnlL3B=5)kMc$e!kQVCleD:>mFrDj +d/Vt:^;Ak,gV1J5[^NQKZEUe%hc9$t"JdpU2PboYJ_Pb1nCmu49n3~> +p&>p(na#;og!e6f_nN[gYH+RrT:D=8PECodNW+kSNK0'^PEhN)T;&3_YHtaP_T0pLf\Y]SnGMtZ +psp5/XV=rB:.6uT!uqga"q\H$d`St9B!q9^%Fi.#2d00Q5qaAeK<`l(h?DKU`hb\[n^\8"5@S>> +J$I/8_5)ErC/cKtmFD:T62O[`?>odG!Bg_ZecN8$lM&jH^JmF,:6*?Ben7Ucb0J/P_7c@c@MiF^ +cLf["eYg%-B16Va4dNK\08mN_cID=6!n(7(rU@a4rQPU-S@"p-<>eG%Mq[E7_,ecj0: ++HXmor72&"cGb8I@d_eOc^m^1YKkSpecsXNaeDBhSF4r?a`ASdKS5/?f%,Y5KR\JD4.Pil!k$/8 +o%*j(olL/rmFD9oqr\$_F05<%mFM7!%!)D/XlnlL3B=5)kMGg]!k$/8ldkq5mFD^&oDd[\]tr_. +hSI1EqmcgBp[>NVec`gVKUBT,eq)D'f'q5kPY-H~> +ZN'[U(#$Xqqr[&8ig?CqiOFV]j7W$%MJ]oqh@/2cg@astmd/r#T\f;=p]'gdc:4&gh@QW0H[.mk +(_R5a&/%N^GB'MIhYD'jGDH'6o%jG]c0k>_$egb,;HQ\(db\b7SGf)dS="Y4.4(Y0g@sG;h5sZe +8`&0:4blX-Zc/JRlL)d!V3$=/F34.4!klkHo\K_a;CQstr5AlV +X`GWg;R,ZH"jMfc;O4rn`lR* +kO%.)k5t[:[Jf7lcFqflkMl*i!klkHle_O=mG7l:Z24J$rV?He!;$0X"7,.,1%!g#a-q[Ma+<)u +hLXPPh>s-AJ,~> +p&>p/p@.A.hV$B'ai(s*[]us7VPBfTS!fV/Q2[$JPno^RR[p(FVQ$]&[CO#haN`/ch;dbgqXjg^ +gD/8#e^M+e:d+6q>gDJVN/kn]#3aB\GM,@9*`mB322D-I!@DpmogB+t1mI]-E\ksr& +YenE"`5KHkE?YTaY3t$Vb(VuVQ+=iQ`PfX$NCag7J&Ld0&\:ghH*G0*`l,g2Wc:TE.t>M.gB+t1 +mIK$%kO%"(iUQ*ug\p=8!!%*=k67tJF6D`Fo@j>XbjF`NqZQ<@!PZqZrt,2-qtg*^p%._Bn*TH* +kMK;,gB=<1!&gX(gO\+1g[EktRSA;~> +p&>p/p@.A.hV$B'ai(s*[]us7VPBfTS!fV/Q2[$JPno^RR[p(FVQ$]&[CO#haN`/ch;dbgqXjg^ +eeQIACJH`=2'iNmdak@?A&Za4Z#"$[o@=Z)D`q>k0ek7+#aEd#inE,7b/'`2Pk!<9W-s/F:gC*< +^V-RP=G;6GT^C>+G6*2TrHeQhC\$$X[E7_.edTuc4&AsQA'3A88sl"FS-YLHF'^:=TY](Nee?T3 +;@Z1IdUt@J1K(BhP*3Z#cID74!jN^brWj&u]t%_p-!n.$?6oj_"9M]Ap=9;Qam/NL"LUuRHa3Lf +#/RE_+TtQBma_Q\JNHbq]D]\R_mb_"&h/7Fma_NK:+:Opr5AoWX`I)I.=K&'ecN8$lLNNrkO$h# +hXTdrf)=e3!!%*=k67tJF6D`Co@ +ZN'[U$JN>bf!Vp2ifotmrV$NhnaG=jCUs&n$f'"Zil/=Af]&-cir/TToC).n6Bplo!cJ>QrWiQ6 +#]IlB!klkHp>-2#KgIXW3Qg(0B/t&3D['ZS/L>n[deV`7h5sZl&-*m)hV=JdCJnEPb0\Sbf%fWK +!nL\>rj`T=`mVQ2\\6,,[Af[?Bj-nXp>,kac0k5\'%4$g2,8XkXK7e58hF3X#Y'0SnD4i2_1bBF +ArWUfU5*,<6T#9"@%>L2h@n>B=uB')S?/fZG=jGS2%1j?SB^]_!klkHo%rp"mG7j&n`T;rrT=7^ +PeI"Xs5*e=!93V,!klkHle_O=mG8;FZ2`[o]tr_,g:P&/[JR31kM@r]h?:lfKUTsXhZ(WIhLXO7 +hY5k-S5+S~> +o`#a+o^D&)hV$E)b/M0/\?rKBWi)\fTV%eQS/.ZeTVA6[WN<>1\%BGoaj/Afh;d_ep[eFZgD.kg +dADVMR^-@^g["+ciSWA=_Kfh'o@k#.Wi`muKT;q8@?0+.l/gm[f?prhP3q!*FZb0QQ3)OuFirG? +]$0R8gD@tb91hT5a48^O:R[Z8QBmJX5pUlkc.VsJ(>>cf'bC?fe(`N?7X/0B]ZAFGd*pV#mFrE* +=$TJI5sB@4;*INLEHtJJ1+"/&P1TE^!kQVCo\1#)Y$3YUI@I=>R<_@V=V;'1H+NM"'@jgM7R(IC +UTC2H@66h6%OYVPb4+.!c`UJ-9mrM4XI4?*/jhK+-XW=bo\0GYbjFuUn`T;o!9*J2mG%U&!W[;D +r9+*^s5sC@o@j>XbjF`NqZQ<@%DC*fhS-_>hYPm:^:(EJZ3@J8ZK9nBmb7l?2?5Y2qV2%GJ_Pb1 +J_TqR!Lc`5~> +o`#a+o^D&)hV$E)b/M0/\?rKBWi)\fTV%eQS/.ZeTVA6[WN<>1\%BGoaj/Afh;d_ep[eFZeePFq +?7?Es(a&g*dc0!3da#hFWbQ.Ro@=Yo>Ve_e&/6*,!.3/Rg=F]p_7?'rPjR$'G?1[A]`P-5GKSJ< +[E7_-edKBF4Yo)Y_:$bA2/E/'#%CUb+a:K>q:6,-Vc"3>!$BM;e<2sq6!Ch:rlP8eeEka%c;#K! +$j[8>:)jNR+\l2i'F4gC-&g:GecN8$lL`X?af&5p:4Je8XI+3(/QNDg7X)90ee?B?ImZG4NMNYM +LL<201CPX;QGr@E'?R1R0i!4gXK7e58hE=b$n#AI`Uq^_[E7_)f(%qjec=RskO$k!k5kX)Xo.A^ +XoIPahXf;"[E7_"f)#I7edL@+s5(H&_;bLO_8Z8]"240d[I2TK_3fnC_>;a__1C0geq)EHecD"- +J,~> +ZN'[U("0eR)r@XFKbf&"tj3GWCf2(^+;J%a.]r8%S4X]6)IrVus6a8Xp&cX94ZIiq)".^!6!2hY[??h@-'^s5(H&_;P.A]=kWNZ2^p.Z00sun(n2fRY7-Mq;2.VJ_kt7 +J_p.X!Luo8~> +oD^QBo^:u)hV$E*bK%N8]=5/PY,nY%VPU)aUSO]^VPgDoY->7A]"Z&$b0SSjhW*hep@A7XgD.#H +S9n"XFamV>c/[jAgY(*&^34.to@k#"RZEA;AU&NN>Be?Pj58VCdESmXP3q!*FZb0QQ3)OuFirG? +]$0R8gD8MbO'm:#`RWYhAPJJN8P)3)4FOf_fDOA+g8\*e&H2Y@'u\k]fu[PrH +oD^QBo^:u)hV$E*bK%N8]=5/PY,nY%VPU)aUSO]^VPgDoY->7A]"Z&$b0SSjhW*hep@A7XeeN63 +,p3mo,n'cG_:m>cb/M'%Uh4/Fo@=YP4Vf4[0/j^V!(t)]daH:S\ZqMZPjR$'G?1[A]`P-5GKSJ< +[E7_-eeZcQN*^^m_9UWW@l7n/0ek7+3HqmMdeqZ!eYc:Z&,lP0'Ys_!$HHC5>Y^3pbg=efldc?i +YpgM0&-s*irWiK*+e2(_eWW^$sSdah`0n7S-?M76sO]OH2[E'JG0^p!s2Pam/HJn`T;j!8d8/mFM7!!-@ks +!8d=u!k$/8ldkq5mFD[#nGh@Y]tr_.hS6o(\HK=Nn)q+Iec`gVKUBUOf)Mk'eq)D'f([_rPY-H~> +ZN'[U%Df<>O?ok^TSSE?lhC#LmfVF#BY!`k%aV\_Q=tEKZ(Qo/_<^jerU0g54d>?j!H/2?$ipQN +b2*35c0k8]'\q?eaiVubhVQgr8o"E->$@H0cJ&6R"0Yi:%K$2,&?l&h#hkkF?=Zs$db@rI=2`R;aG8g"CCddQ$*fZ0e9Bk(k*T"E1i +EAKHd0j<#oh@e8MN,N39DiCs?bKOr^2&%d2f_4/'^!6!9hXTdrh>lR*kO%-rk5aN5h?(C8md&a, +md8j5T!&+lrqc]irU^!R!9X7>!ST9th?:*PKUTsXh?1g'R^%G#J_kt7pYGq@:4N~> +o)BL&o^:u)hqQ`1c,mrA^:Un`Za$^:XfVN&s/Zm:YHbFA\@]Jk`Q?BPe_8m?kjJ-6s7b7_R!j72 +32Wj.=^ebnh:^?-cc`n@B"%FE.Iec"5)X]Q#@qu?a/Xo@&^g=;ZiC2m:@d+-h6gBH2f/j<&t +rAk9F2E!BC-6OR8YhA^(!kQVCn_4Z%\qk95@;gt)_U$+>80A7BcgTU$e\n/.BOPS$S%-P]D_a-a +0j2ikgChiDLi6^1DhtR5aN8?U1_ha0eb7`!]$0R4g[XIogAp4&kO%$ok5aK4gB+t1mH`O(Dsc9p +]?L6OqY9gXoC;;CYhq(ltgB;@O!&gYLgB46g2Jn$"J_Pb1pY,_;9n3~> +o)BL&o^:u)hqQ`1c,mrA^:Un`Za$^:XfVN&s/Zm:YHbFA\@]Jk`Q?BPe_8m?kjJ-6s7b(Z3=n&W +0r^C0$m;Lob0%Q6[B5s,@^YgY'o+([9M')mDb:E\W6WQ._7?hMO>3!eecL%eP5_B]P"Bl?ecN8$ +lLi^BeBl7P_913Wf$7bp@fTD->*QG6df%`!WE'H:qu?a.WVtBRd[3R%<_S\!dcf6cdYJ_W+;">S +rr`H)'Mbd[p!s2Pam/EI&]qG5C0k4]K;cK*]c9We#Zf(9ee6 +ZN'[U%)&X)Ue]A0d]nb=kO\Z@"RXusB"@Ni%F)5NS9^4UimP)j[HI/[mfVn$3L&pf!cJ,ErWiQ0 +!cQ6-$.aG\!3s5Raj/W=h?r)MO]s:L#ljr7NVqW=gt%ul4Z@f;g?7;qg4sl4*dL+X`fUbu44VS07ddGugdf8/0 +fUbu44VTiFf_=5(^!6!9hXTUmh>lL(ipGUmiW.p.h?(C8me,H2F7nr$k4IL=Oa!b$U7e0LR$a2( +OT(4:LB3#InD=.t!1N_g".Fn3a+<)uhLXPXh>s-AJ,~> +nc'@$o^D),iS<)8d*0VN_SeO-asGg=4!(Br9A4e^;X``MbbZmFq\b*&@LU!\tap)^r4RNrS%\5[uX]B'+"X=%?(/=$/![o;FWcqcILV3gB>uT4?,P&2@),l +-mT]dIAkcigB+t1mI/g+ci;`)cY5@@+p_c+mb7`lqp,W"gXVfk83I[nA^Bf!qTo2r#h>GB83I\G +Raq/e!kQVCo%W]nmFqWtn_rlin_jK)o@j>XbjG)XpZqKj!2]>B&$r7S_S3Uj['?d7WhcA\SGnuW +QiWF@nD!pB!&jXi"#^-;Uk.9OgO\,RgB!a;J,~> +nc'@$o^D),iS<)8d*0VN_S[k,4eeO?KEKY6ofZU[;2425"_7?hMV2-jjmFD>_.S9V?!_Fkcb16X%am/KK +!7_"rrRM;#F"mZo[CsDqdf%`(e[(3:.2*!f!"F_]ed0dNQ:a"jH-4RuecaEJ.23?i#R1D3!$Q]d +eF_;n[E7_&f)!Mged' +ZN'[U$duGjbuW]himt6>gAKe1gsUac\b"YdfYb,5OdbFrimt6>gAKe0g<)F?mG7i?qJQA2b2*35 +c0jlR#M*Mf\]WLWgA'M.Yq6Xi$=EYd#Mdn#AU1K-g?.6'g5pYkH]4>RKR[o@8p?BJg@O/&^!6!, +h?Lu44]2t"`oPt]d[!d$1DC,7jPC-iQ;U4/%VO5ah?(C8mY]j&^!6!s-AJ,~> +nGa1#p$h;1j5/MAe'Q=]a2Gj1^V.;X]+Vci^;.V(`lZHNdam./io]Rqq="@XgD.T2[`D7piRc,< +Ur(3k^qRCgXDMLVo@k#*]!T51P2"dDa-ia?aMbm,\?r%iP3q!(W;:W:W63ir]$0R-gBcK[AH90\ +e(*.HgBFc3'EAJHqqDG2f?fmqHEGKDg?@9(g!u0i,Ub2t/h/=p)B;n=e^rOIgB+t1mH*(,_du#0 +'F;V;gBZ&h;FiSX#fG`X#LAW482C`echH/q]$0Q!gB+t1mIf6.qt1'lDtMco_m-VUYcXt*Vkp/_ +St)8GQiN!4g[f7^Uk.9OgO\,QgB!a;J,~> +nGa1#p$h;1j5/MAe'Q=]a2Gj1^V.;X]+Vci^;.V(`lZHNdam./io]Rqq="@XeeP-bZ,K2\g&%V!QSecN8$lAF-o[E7_,f'<;9C[fmYeC)^hcHH4/pr*6HnCI35!5X6; +J_#D'p=98/8q6~> +ZN'XT$.@D]3A"ZP95h?Mcdd*C"hgu[B%g:_iW:/45<2*l;1\_?T1!nHH$jk^3i`a_.C +n,Fj9p[[_9jl,%Lf@83nbf\#H`Pf[3_Sa:0`Q$!@bKeJ`f%JmkIJ +1V+8(2VS#^#MRbVc-+A[f]Cisf=Gs--5m^8'I$\"\C^9*!n68ujkC!c_dbkA=2X+R#M$q[;'uoV +_rTPWe]F"P-Spb.pY,bujm@r[!n68up"T6,oksi,J_Pb1J_RKb!Lc`5~> +n,Fj9p[[_9jl,%Lf@83nbf\#H`Pf[3_Sa:0`Q$!@bKeJ`f%Jm +ZN'OQ"cRqPF)Q!pCB/&IAccLo^[L"`[=rmIF)Q!pC].iG@\C!_rSI\=mcON+leV]odb=KQ#J2d5 +gVO1;eG%f"h#?"-gu@/mg:_uWrbhmg^"V30h?)j-k2bA&d`-'4_:%B##2%.eB4Jd+j5(!mba<%+ +ak6@E!p8q5MV\EsmcNB_!R9DNhLXO7hR),?S5+S~> +mJddpo^M52jPetMf[eO!cd'eZbPoZab7DhBcdC4lf\50>jQGjtp&=@Yo@jG)HZ*bgBY5D +EnJ,3*msc;#27_[e(!"'jP'die[g2-CBK!pe_0HH!p&b0jP'ja_eW\rcf +mJddpo^M52jPetMf[eO!cd'eZbPoZab7DhBcdC4lf\50>jQGjtp&=@Yo@="nF),RhB)?$=@"#TS +ec`!uDej.dB)?$ +ZN'OQ!ResY\H0Imn(n,ubJ3YZ!6)D'r87;-r86r!!8@8*#i0k][,0Z*$cC?Js4[M(s4Y]RrmK`] +!8.;!i8+RgdaHd!hUpZeb^hEcJ_kt7J_m`i!Luo8~> +m/IXrq"!n>kiLg]h:pW7f$r0trmLhq%Fiq[f\50=ioK:gme$GCs7=tGcbf:^!PH_HgB,pH]^rQ3 +_R\D-g=Om(dEp1ac-6+/$-^Z>cHjneeC`LGg],t&gBcMZ0!X0cPRG>#gB60_g"PE>!SQ*'eH+:T +c.i6Di7e7^rm0NW"4b`DeUcK-gB5QI05Z9pJ_Pb1ZeGML9n3~> +m/IXrq"!n>kiLg]h:pW7f$r0trmLhq%Fiq[f\50=ioK:gme$GCs7=eBah@;R!P->@ecO18\b!3/ +]t)]#e^Dgibfe/NaN+4u$-1-/b0/#Sd*pP8ec=.oed0fN/ZmUTOUAbJf)*n>f)EeLec=%qal(_P +ccO;TJ_&K)"3#[@COt&feq)DYecD"-J,~> +ZN#L4p"o]*r8@A/r8@S3l.uN[3bR+Eh>qYsJ_kt7U#$!tJLM682r0&AJ_kt7Zeb_Q:4N~> +lMh@mp[[e>l0%-ei8 +ai_iQd*pOule;NW3G-h@gAu8lJ_Pb1U"]dnIjl!52VN`:J_Pb1ZeGML9n3~> +lMh@mp[[e>l0%-ei8M+K_$Rur +`Q#s@bKeJaldc0M3+L>7ecBW`J_#D'U"0FeI4,^22:[9/J_#D'Zdo/A8q6~> +ZN#L4pYPr-ro!b6p>Z&0ro!h6m+qf#/$&U)!5sHFJ_m0Y#g@iL'F"O;+Oku!J_kt7ZJGVP:4N~> +kl2%hq"+%Dm-gO\+1gToQ7RSA;~> +kl2%hq"+%Dm- +ZN#L4q;2//ro!h8r8RJ2rSmh8s5 +jo5Vaq=F.Gmd9B,r9F=J#41dHnb2hYjo9ndqqD\8e^DdfaiD?:^qRIn]",>[rjMj9%_0']]Y2+t +_oBdAcI(1ro%O8e6<_cfgO\+OgB>WJ.ME$b!!*bLJ_Pb1J_RHa!Lc`5~> +jo5Vaq=F.Gmd9B,r9F=J#41dHnb2hYjo9n_qpl>.d*9bS`PTF(]=PM\[^EKKrj)R1%^`XP\@K5b +^VRk/b085_o%!o[6!2?^eq)DEec`s?.2)m`!!*bGJ_#D'J_%*W!L-3,~> +ZN#L4qqhA1s5 +ir9Pfqt^!Zo^_SCnac>Gp\FaGs+/r&gCVudccsSO_nj("]!o,UZa-k6YRn(FZa@0M\\,Vk_T'[@ +cdUM!C!"Z#ZgO\+1gT]E5RSA;~> +ir9Pfqt^!Zo^_SCnac>Gp\FaGs+/c!ee$9UbK.Z=^V%.e[^36DYHG#*X:VM:YHY:<[C<]Y^;7b. +bKeQ.ecLp]Rt9.Aet1G"`H!jM&J"mB!"YlVeq)D'f!*^+PY-H~> +ZN#L4r8.J2s53n9ro3q;rT*b8rT*t,nu:J]>ghLXOOh?Bo%$ig`jJ_kt7J_mTe +!Luo8~> +g].^+]=>;UZ*:C4X88h.X/rJ-Yd:aH]"Ghq`lZKPe_9fQ"3jS9c%4W# +gR6e"W#lEJ%"j,cJ_Pb1YM0)H9n3~> +g]. +ZN#Khrk/cU^qmn*`5Ta:aN;QHbPo`[c2YusbK@uLaN) +ZN#Kes1/oR['?a5Vkg#XS=,\.PECodNJrdRrJD8HN/WdXOckrpR@Be@USb#lYd:n:\cRU2I=\t\ +]#2)Z\_+It[p^_,$4':U\q.GD\urUFRSA;~> +ZN#K`s0WQHYH4\"U7[sEQ^*_rO,]'TM26qBrItu@LkpqHNK0*`Q'[r0StW!ZX0/i+[/tt)H@`JT +[DTBP[+MbjZ +Zi:#TJPHtU:fCG!?"%8UBl%^-FEVkRH[L6jIt.HJrdXrr&:8_JG'%bDD/*]o@U<8@<) +Zi:#TJP?eP91hcG7RTU15X.Fq4?GSa3B0"t2uY`72ubl;3=\)34?Ykm5X@b*77Tm?9`7Zf8K\g= +JPC\ia\De/s%E/[s%EAciCsPC&gg7!!"hnU9n?#i9s%+i-@c~> +Zi:#TJP$PI84Q-;6U +Zi:#D^44(>'le>=H$k*nKSPDBNfT?eQ'Rf*R[]fESH#&qR[KP0Q'@GnNf8jNKn=f&H?XIMDf8Yl +7"K9-Douu(E,fo?F`hkOGPl[jGBS.PFE;JAk&1NC5SdI?D]T:"JT$*XJT%T-J,~> +Zi:#B^44(<'l.MrA7AqQ>Zt62t:Jaqf[CfIA7fLkD/E5e +6\0**D9HerD#\5PBkV-lq.BV="DDLaCM`0N#])^@=)Ddp$YP$-JSfsTX)3@~> +Zi:#@^44(:'PV2k@piVJ>?Fs,;c6Ff:/+DT8kSqOs%4)#9MJ8X:f:4l<`iO1?=75QAnl*s;\_WM +COp9EC]A/LBEMmZAS([Jrac4EB4u!okA(?<4r%(5CE*[mJSTgPJSV<%J,~> +Zi@`,!,oj5s5++?iSieVj5f:_roX7DrTO"?roj@Es5jgQjQ,@]io8qTi8<1#42ZX+J_nQ+rSRM1 +rSdV2rnlqr"2m<3fDaV-0Yi7rJ_kt7Wnr7~> +Zi@`+!,]a1(>%$\ai2*/\?rKBWMQA^SXPq3P`h/jr/V_WOckonR$jJ9U8=fiYd:gO^r4==daMhi +_1C?lgW\C[g"+[$cd'bXaiMQDrPnlXs2YGfbK\>[e(*.:gB5;T@FY;2!AM3 +Zi@`(!,KU*("1CM`PK3s['6X2V4jNOR?s/$OcPLuN#7P7OHPioR%'\@Uo:AuZa[Q^`5p3LA3%;R +eq)DredL!TccsYTa2Z*:_nuAgs2#)[`5]g +ZN%Yq!!($qs5+=EiSrkWj5f=`k2tjikii!0q!.hBs60LGs5s@Cs5XIGio8qT[P3-cJ_kt7bMN6U +ro!_5qr7P4rS[_5leV]%.]E=&!SXW+hLXO7hPo@^~> +ZN%Yn!!('o(td?`ai2'.\$N9=VkfuVR?s/$O,]'UMM_:f(l=0/NK93cQC+27USb&oZF7B[`5nl/ +HeO&*J_SH(%b]C]d*BhVaMu08_SO%'rkAoX_84"+`Q-'Bc-Oeeg$RMqMCPJ$gB#p+J_Pb1J_R0Y +J,~> +ZN%Yi!!('j(t6gR`PK0qZ`gF-US+-FQBRGkN/EFHLPGb](l!g%MN!OVP*ME)T;&6`Y-PLJ^r3$" +Gh%ArJ_&)s%+NYLbK7cC_nj.&]tF6Ss1A]Q^VRe*`lQ?Id+%I6!f&4urRM"tVLd +ZN%\r!Ls/0h@AH$iSrkWjQ,Fbk3(pkkl0iGlM0]DlMp,Kkl9iGjpC/1jQ#7WFu6khJ_kt7bhiBW +ro!e7r8RV6qr7V6s5 +ZN%\o!La#0gDS\occX5B]XP2LWhlG]R[95#Nf/aLKnP)1rdboVf^;.V(`lQ?KdF?k>gB5Mc(1~> +ZN%\j!L*T*eeuuabJqB1\$N6;VP0TMQ]mMjMhm+@Jq8H%rdG]6J:W<)L51YFOHPotSY;mZXg,=E +A1jn.J_#D'cIO5id*BhU`l#X,]Xt_a[^NTO['[6K[^NZT]">Yk_8O@9bg=l0ecWiX<6KjDJ_#D' +U=Oh~> +ZN%\r!Ls/1h@JN%iSieVjQ,Fbk3(pkl0834rTj:Grp0RKs69RI$g6[7jlGL>2K[g6hgsX8hTk!X +hu;R6iVqj6j8A!9j8J!;iW%j9ht#V*QmePWhLXO7hOr_U~> +ZN%\o!La#0gDSPgbJqB2\$E09UnF9GQ'%)bLkUJ3Isuclrd#H/I!pKpJq\u;NKKHmS=cUUXg+BJ +@`$:6eUcJ+gX+[df@80laiD?:^V%1h\$i]NZi%*>Za@-K\@T>d^r"(3bKePeo@j>4.%GkCJ_Pb1 +T\Ft~> +ZN%\j!L*T*eelcW`kf^qI@i[^EHHZ*1A/Xq._?Z*LaF\%0,b_8O@9c-c.6!g4puJ_#D'J_$LF +J,~> +ZN%\r!Ls/2h@nf)i8N\UjQ,Fbk3(sll07Kulg+Q:q!J(Irp0UL%dE3@kND!gKeU!qiSi_PJ_kt7 +d,+f[s5,nu;,,>ehLXO7hOiYT~> +ZN%\o!La#1gGmg5bf@T5\$E09UnF3EPE1WXKn=f&H?j^WFEDSGFEMePH$k*mKSPJHP*VQ.Uo.cM +PJ"`ObL"pngO\,.gCi2idE]nT_nj(!\@/cMYck43XSf+2XKA\1Z*UmJ]"Gep`Q69LeCjTN"3s_: +bCSE!gO\+OgOK~> +ZN%\j!L*T+eeli[a25O"Z`^=)Tq%I6OGo!LJq&2pG]n3.EWC+YEYrn@GBnRbJV8f;O-5j!TVGsA +O12d<`llk_eq)E$ee6KZbfRlA^V%.d['Hp=XK/A#W;NP*W2Zi!Xfo"9[^`r`_8OC;cd_^?"3F;1 +`du]leq)DEepm~> +ZN%\r!Ls/3hZ)IGi8N\Uj5f=ak3(sll0@R"lg4!;mJ-,KmJlPQli68MkmZe=bs6"!jQ#7Yi8ELD +hLXP4hZ)L5i;_d9irA';jSe3;k5=??jSn3>iWS5ui8 +ZN%\o!La#2gDehqcH*o;\?i?XK&7tVPX6e';_S(W2co#YHbIC\\,_qa3)]Uf_O8' +TIH"SgO\+1gR[)L~> +ZN%\j!L*T,ef3,bai(s*['-L,Tq%I5O,JdGJ:2]dF)l29D/B/d)/O/.EH?;JI"-a&MN3jdM(MAr +Y-YXO_oU,Seq)E%ee6HXbK%Q;]XkPYZ*(1.W2HJeUALVsU8+N]Vl?`!Yd1[H]>)5&aj&<1ecM?i +NIfZ3eq)DCepm~> +ZN%\r!Ls/3hA"l*iSrkWjQ5OdkNM0plK[^%m-X3=meQ>Nmf2\Sm/QA]lKRQ"0!FF!jQ#:[iS`UE +hLXP5hZ)L5hur#uioB(ujT"?>k54?>k5OKBjT"9?irA!;huM[3h?2;76cjo9J_kt7SDJb~> +ZN%\o!La#2gD\Vhb/M-,[BQ[-TUV:2NJW@>I +ZN%\j!L*T,ef2uZ`kf9qZ)jjsS`TUq^ES"'DG'UtV_SY)UMV5L>pYHkUI]YVP- +bgH@>"3k"7_1C0geq)DBepm~> +ZN%\r!Ls/4h@nf)i8N\UjQ5LckNM0plKdd&m-Xf?rpT^QrU9dSs6UEblg*j#Y2/:'k2k[aio8qT +hgsX8hU^Q`hZ_rsiSrkWroF+@roX1Bqrmt@roX7Bs5a4?s5F.>i8EMkh?'f1LOn<5hLXOPhLG~> +ZN%\o!La#3gDnkpc,[]6[]um1Tq%I4NJW=H@LR&N,4QE +TV\Zm[_'>pbgG-qgO\,3gDJPkcH4&B]t1YXYH4_#UnOEOS=5k5R$X0=QkPmWS=ZCKUo:;rYdCpQ +^Ve.:d+/-M!idc)J_Pb1J_QXJJ,~> +ZN%\j!L*T-efV$MM?\1G]e"CC1q0g@:*@?>o[0X@:Nh\C2Ip3G^P$pM.qm7 +S=uj^Z*q9\a3<(beq)E)eh>Isai2*0\[JcGX/MkhTUhU@R$X,'P`q;qPa.Q$R%'V)8(bL$7>!iIN"J_#D'J_$:@J,~> +ZN%\r!Ls/4hA5#,iSrnXjQ5Oekih9rlg*p)mI'E2n,DhPnc/+Yn,MhUm1AXMlg!a!ki_*ijQ,@\ +iS`UEhLXP8hZ)I@i8NYSj5]4]jlPXeroa7DqW[tBroa=Ds5j:A$fg7+iS`YOhUlO(_h$ZqhLXOO +hLG~> +ZN%\o!La#3gDnbib/D$)Z`U1$SX>S#M1gA)G&qP:B4PFX?!CM3=WCUL>[CiKB52:'Fa8IhLl@L` +S=uj^Z+%E`aNi@fgO\,4gGRU2cH4&A]XY>QXfA7nTUqX?Q^*btP*(lgOcbfjPa7]*S=ZINW2m,- +[^s2ia3;o]eRpqYJ_Pb1J_QUIJ,~> +ZN%\j!L*T-ef<&[`k]0nYGn=iR?WbjL4ObtF)Yr/AR]"P>?P)-=8uA@>$PBBA8#arEd)n]Ko(hR +R%:"NXg>RP`6-JYeq)E*ehtn#ai2*/\?rKAWMZD^S=5e0PEM#gNfB$WNK&s[OckrqR%'\@Uo18r +ZF7?Y_T0jIct,5LJ_#D'J_$7?J,~> +ZN%\r!Ls/5hA>)-i8N\UjQ5OdkNM0qlKdg(mI'H3nF6GIqXXXUs7$'Ws6^Kdm-F!&l0. +ZN%\o!La#4gHF6=c,[Z5[]ld-T:(q)MM6P+G&hG6ARSnL=]SO%;c6Om +ZN%\j!L*T.eihO.aMPU!Z)jjsS@(cNBl.m7I"@!0 +O-Q0,V5pi/]>;P3J_#D'g=@%fcH4&@]=5,MX/D_cS=,_.P)kQ\MMV.b(l4$,N/j!_Q'e&4Tr"`j +Z*h-V_T/N+Gh%ArJ_#D'R+?c~> +ZN'jZ!.4PB!1_)-iSrnYjQ5OekiqBtlg4$+mdKW6naZVLqXa^Ws7$inn*f]4mHj0( +lKRNqk2tddj5T%UhgsX8hV-idh[SN&io9"ZjQ5OdkND'mrosCHqs44GrosIHs6'FE$0C7/j5].U +Fu6j_hLXO7hNm#K~> +ZN'jW!."D@!1qHi!La#4gHO38b/D$(ZE:$uSW(kdTs +LP^qKOHPotSY2dWXK]+C]t\88Yh=T^J_Pb1QeR#~> +ZN'jR!.4PB!1_ +ZN'jZ"P1lDXT8@Ge(t*:`r+ZoS,gdis5+sWiSrnYjlYahkiqBum-X3.n*fc9nac8Dq"ad_q"F@P +rq$0\(%Le_n*]T1m-Es$l0. +ZN'jW"OkE3WW<%=!8#`T!>*!CgB!`Cc.bJ:eBZ.P]XG&EUn3s:NJE+6GB.P6@p`G@;c@b&VmW^q +BLYNa9M\Pd>$bZNCiOWFJVK)GQ^snNYI2!Ya3E+bgO\,8gGdj9ccX5B]XG,JWMH2XR$EhpMhm+@ +Jq/?"I=-EjI=?ZsK8#) +ZN'jR"O>$,V#^M7e(+O2^&6F^PQ8tZ5Ln*%_nEOaXJVY[Q&gfUIsQ3TC1^m[=B&-qEKm?-Y*Wch +7S$-G:fCJ$?tJ,~> +ZN'aW!PJK[h>mVir8%IE!6j:;hr*JQj5]4^k3(smlK[^%mI'H3nF?)?oD\Ud!W;ZjoGI2jo'u5= +n*]T0m-Es$ki_*ijQ,@\iS`UEhLXP=h@\Z'iSieVjQ,Fbk3(sll07Kurp0RMqsF@Krp0ULs60sU +kND!hjFf4rio/hQJ_kt7J_laMJ,~> +ZN'aT!PSQ\gAq;gr7_7@!6s7:f[A!`^U^\QVkKTGOG\aAH#mk;A7&M?;-*PiruK9_6UjX@:fUY) +@V9P!G^Y4$Ng-!*VQ@)5^;S1@J_Pb1h:jm>d*'GF]XG,JWMH/UQ]mJgLkUG1IX??bGBS.PG'A1V +H@13nKSPJGP*MH,U8Fr&-C)<_aNi:dgO\+1gR-`G~> +ZN'aO!PJK[ec>car71n5!6s(5e'5qL]="iAURda7N/*"4G&_>2@U3)7:K7/druK6\6:==9:/b5! +?tF(lFaARmMij?rU8Y6%]"l>/J_#D'h:=O4bJqB2\$E09V4a?FPE:`ZKn=f&H['aWFE;MDF*)PJ +G^4[cJV8f:Nfo]sSt`)l-'GjS`6-GXeq)D'esP$=~> +ZN'aW!PJL2hC\em(jk+u(]Y+oXPVg04U<$Sc/.0A0aK4ef&!r@&.D==hV+>O#RF&JhA?5(SGh:F +S,gdi52Y;WioB+]k3(pkl0@U$mI'E2nF?)?oCW(]qtg-br;Zfqp%@tLoCDJBnF,i6mHj0(l07Ep +k2k[aio/hQJ_kt7h;/k'i8ESRj5f:_k2tjjl07Kulg*p(rU'LMrU'XOs6K^Ms60sUkNC&RC#.h/ +io/hQJ_kt7J_ldNJ,~> +ZN'aT!PSR3gF`Jh(j4Sj)up[qW8$($4:3-VbhLa60FB4ce(_62&.D@=gXhZD$4'5HgDBo%Rf1tA +RK1Ue5h=<(_nEOaX/2DTP)Y6JHZj@DARJ_B;+spgjgLeAT@!W$kfSDfg;UKo;+] +StrBj[_0JucId^&J_T):)VEK_aMPU!ZEC1&T:;.0Nf&RDIscNcF`VSBrGNTnEH?5FH$b!kKnt\L +PaJ#'/l/OV]>2D.ce*g'J_Pb1RG35~> +ZN'aO!PJL2eh-re(j"8](]Y+gUt4+e3=$UOak"n%/I3e]cIT6u$j]J-f$]X3#RF&BeeeAuPl9/6 +PQ8tZ5gdco^U^\QVkKTFO,AU>G]R_8@pN28:J+LajgLb?T$[N;ha0Ln77g6Q=C#BKD/slLJr#GO +R\6OZZ+%Hcb0tpmJ_&`0):QjO_nNXeY,\=kSsoD&I51EcZDKH[^R#M2[R^ +R>`^lVm3\B_8a[IJ_#D'J_$7?J,~> +ZN'aW!PJL2hCS^^SBfC[A(IIVB@a"VF2a)$LYeCnLrOp)Mq2N5[CK%Jg6bWk`l:#!hVJ7Gr8%IE +!6j::hr*JQj5f=akNM0qlKdg(mdKZ8nac;Do_nL_pE'22s7uB]p%7kHo'u5 +ZN'aT!PSR3gFWCVT$,F[A^@(NA'q/IEPdYuL"hnfL;84"Mq)H5['i\AeWrpa^qMongY2_Br7_7@ +!6s7`eBZ.P]XG#CURda6MhQY+F)>Pu>ZO]q84?TA2`3BG0STQ,ME3pf7nZZY>@D,[Ed)qaM3!pk +Tr>-$]"uG3eUcJ+gYgg+g=4Ei`4idhYH"CjR[0+rM1pJ.H$47KDJa0(C&VcaBkhI$E-$/HH[g[' +MiX'0++0ufZ*q<^a3<"`gO\+1gR6fH~> +ZN'aO!PJL2eh$kNSArPKA'USEA'U`:CV>NeK@Z8^K=uUlLXKg-Z*d>>dZdLZ]tHEdf%'i7r71n5 +!6s([d)s8>\$#\9i7RU9;2Dd0C089H+ME*gb77g6Q=C,KODfg;UL5_:^ +SYW9i[_0Jud"0c!f&5+!e^2IX^q-qXX/;S\Q]dAdL4Xi"GB7bAChdWsBDuK]B4u!pD/aN~> +ZN'aW!PJL2h>mVHrn\O+f&!e7?Hmh#^#%>>Z.`u+f&"hbB?k-ZZ2NA`CM25ThSoQ@h>k7eh>s,H +c/'Jui8EVSj5f=akNM0qlg4$,mdKZ8o(2MGp%J+Rp\jk.rVufhp@\+NoCDG@n*f]3m-Es$ki_*j +jQ,@\iS`UEhLXP@hA5#,iSrkWjQ5OdkNM0plKdd&m-X3.meuVPnGVhUmf2Ydm-F!&lJ673kN:me +j5].Xi89+BJ_kt7RGNG~> +ZN'aT!PSR3gE$?dgY:>ke(_/.?d*du]%bW0Yh*T#e(`)RB?t9^Y5QrZBk>cJgV*[6gAntcgB!`C +cJ'Gpe'5qK]!ST;Tpq:,Lk:"tE,&oi=]8'e6U!Oh1+t3X.N[IHZo'j,6:X^I=']nFE)/5BP(db@fBdT@Us%_Bl%^/G'SOg +L5UgTOe/>HXg>ON_oU-TJ_Pb1J_QXJJ,~> +ZN'aO!PJL2ec>c@rmhspcIT)t?HICl[FWX"XjUilcIU*DA&`"JXo6ZTB4K6^Vn:CJ_#D'J_$:@J,~> +ZN'aW!PJL2h>mVirn[sG.4Ijh?HmCpc2HK"dUW1T$!tlmF"&5D^!^Ef!!Bpk!58UfkiqBum-X60nF?)?oCV_Lp@n@WrV7N5s8;Wcp@\(MoCDG@n*]T0lg!a!ki_*ijQ#7Y +i89+BJ_o>As5+RLiSrnYjQ5Oekiq?slg*p)mI'E2n,DhRnc&%Wn,MegmHj3*lK[WtkN:pgjQ#7Z +iS`UEhLXO7hO<;O~> +ZN'aT!PSR3gAq;crn@aB/1='i?d*@mb5L&qcXH_O$!k]fE@;r@]$Fg^!!Bge!5ABc!La#6gE5+s +bf.?,ZE0mpR?EM`J9c3QB45"E:J+#>3]/TB.45$>*m4M`0JkdR6V1'R>@;)\F*W7hMisI!UoLZ. +]u/">J_Pb1iS,@+eBZ.P]sk;KVkT]JP)Y9NIsQ9YDJNlr@UNMIrEC7H?!h#MAnc+$FEr:cKo;%W +R@g:TYdM*Y`llc`J_Pb1J_Q[KJ,~> +ZN'aO!PJL2ec>cYrmhC?.4IR`?HHtd`Vn?ga^4lF$!P-m^,`,Kg%d/i,IM6:XaK=C#HPE-?V\LlR^gTr4uu +\\H/-J_#D'iRU0BccX2?\[/H;URmj:Nf&OAI!B^NChRBi?sR#@=]ed/>$G9=@:NqbD/sfGIY*?6 +Od;N2VQ@&2]>;M2eUc;&eq)DAepm~> +ZN'aW!PJL2hCS`@hVQNXXkqn7?Hm[p^#%>-T\=0sg>:Q&akFSMhRuZsdVEXPhVQhkhVJ7Gr8%IE +!6s@@hr*JQj5f=akNM0qlKdg(mdKZ8o(2MHp%J.Tq>(!c*rc3:q"OLUp%7kHnaQ#8mHs9+lKRNq +k2k[aio/hQJ_kt7i8,:-i8N\Tj5f=akND'nlKdd&mHs?1n*oiFnbhtWnc/+Yn,MefmHj3*lKRQs +kN:mejQ#7Yi89+BJ_kt7RbiP~> +ZN'aT!PSR3gFWE>gY:$SY1q\2?d*^s]\ChsSC_Ife_A]m`n.rAgU^*jcY7%BgY:2_gY2_Br7_7@ +!7'=bf?qaZ]sk8HUn3s:MhQY*Ebo>p=]8$b69I.[.jlPa*Z[03s)TYD2*4&m9N"u!A86+.I=mE= +QCXeMYdV3]aj5OmJ_T/<4kJ*)`PB'lYGn=hR$3PeKR\;jEG]E$@:*5B=&r=$<)lq!=Bf'>@qTRq +F*N+aL5V1[S"Z^\Z+%E`aNiCggO\+1gRHrJ~> +ZN'aO!PJL2eh$m8f%/+HXk)&'?HI7h\^o&fR*oV[dalj[_:#m-f!S+[b%GA8f%/-Kf%'i7r71n5 +!7'.]d`f_H\[/E9Tph4+LOsnrDeW]e=&DUZ5WUbU.OH>]*?6s0s)KP@1c[`f8l/Pn@VBY$H@Ud0 +P*qr=XKf:L`QNh`J_&f24jqQo_7R.[X/2JXQ&poYJUD]_DJEfo?X6f:MMQ^skLXg>RP`6-MZeq)D'esk6@~> +ZN'aW!PJL2hCS`@hVQNX[GGs)?Ho93cb0Em>hVJ7?r8%IE +!6s@chr*JQj5f=akNM0qlg4$,mdKZ8o(2MHp@n@Wq>0sbr;Q`pq>'g[p@\(Lo()>?n*]T0lg!`u +kN:mej5T%UhgsX8hVd6,hr*JQj5f=ak3(smlK[^%mHs?1n*ol;rUTmXrpp*Zs6p`kmdBK/m-Es$ +kih3ljlGI]io/hQJ_kt7J_lmQJ,~> +ZN'aT!PSR3gFWE>gY9mOZIa'r>KEI,EPdYuHeFQTLrG<>LXg0?]!=tCeWNR]`Q(h4gY2_:r7_7@ +!7'=cf$MOV]XG&EURda5MM-D%E,&oh=&DUZ5<1MO-mKcP04N]9`?HW[0fM6_8P`>k@VB\&H\.'7 +Q(+MHYI2$[aNiLjgO\,=gE"ehaMPU!Z)a^oR[&qkKn"DkEG]>u?sHi7;c-=dr_=G/:f::p>@(`M +C2S*;I=d66P*hi9WNW\?^rFUIJ_Pb1J_Q[KJ,~> +ZN'aO!PJL2eh$m8f%.h@Xk%On>/d"$DnUriH.7dBK>E@,K@4O5[B2o2dYpYJ_8])%f%'i/r71n5 +!7'.^dEBMD\?`35T:(n%LOjbnD.mB_;P5J_#D'J_$=AJ,~> +ZN'aW!PJL2h>mVirn])>#S;>C+EQD43C3k+9)69qYBp\p@\+NoCDG@n*]T0lg!`u +kN:mej5T%UhgsX8hVm<1hr*JQioB+]k3(sml0@U$mHs?1n*ol;o()DDqt0mZs763['_(SZmd9B, +lg!`ukN:pgjQ#7Yi89+BJ_kt7S)/Y~> +ZN'aT!PSR3gAq;grnAl=#S;8@,'2G+1^=U2BA:ss.L7;QdG(m"$4^.GgXV<6#RE`:gY4[jRf1tA +RK1Xf*S/QY_S!=]WM>uLO,8I9F`1u'>ZFQk69I.[.O:>YrugER*$-=R0/YjY85gHXB>bf7H0['$C'SX5LtL4O\pEG]>u?X$Q0:JFGQ84c]X:e=>V;,pb( +@:a1lFEr@hM3!miTV\]o\%T]$ce3m(J_Pb1SD/P~> +ZN'aO!PJL2ec>carmiN6#S;&;+E,hu0`qt*ACf1f.0q5Lak!Rd#RjV9f$]L+#REc1f%)PUPl9/6 +PQ9"[*RW'L^::GLV4X-#S0e5s$qW.3t5XrugER*$$4O/i5US7SH`_?Y+"nG^b=( +OdDW6Wj&qE_o^DZeq)E4ej%[/a2,BrYc=OlR?NYeK78&dDJE`k?!('':.n/K7Rp9P:.RuO:K(=u +?=RYcEd)n^L5_7]S=uj_ZamiibLD*oJ_#D'SCW2~> +ZN'(D#0_WI'*L8MiSFU*!6s@chr*JQj5f=akNM0qlg4$,n*ol +ZN'(A#0D?D&d1,HiS+C%!7'=ce^)=R]XG#CU77F/Lk9trDJ3H_ec?,*DZss8Vu*2]gHX99b/D$'Z)a^oRZrhhK78&dD.mEc>#eEo +8Ol086+?moe3c\s9M\Sg>[_5[E-6MXL5V4]SYN0f[Ca8qcIRR$J_Pb1SD/P~> +ZN'(<#/u!=&d1)BiRS$o!7'.^d*'AA\$fCh@$W;b]_H3]&H:+d)Qrs8Vu*2]m35IUUle3ZPn8kr5`>$b]PD/slLK8>POR@g=VZ*q<^ajG[jJ_#D'SCW2~> +ZN'1G#g@iK'*\F:+Oo?+!Ls/7hE]uViSrnYjlYail0@U$mI'H4nac;Dp%J.Tq>'mar;HWos8Mol +q=s^Xp%7kHnaQ#8mHj0(l0. +ZN'1D#g%QF&dA=9+49$$!La#6gHsH9aMPTtYGe1bQ&^ZOHZX.<@9Z`,83o"@:sFtG^Y4$O-H*+VQ@)6^W"CEJ_Pb1J_Q^LJ,~> +ZN'1?#fV3?&d878+3`Zo!L*T0ej@a+`4`XbX/)>RP)G$CG]IV3?W^6#7R&mh/LDSY'Fc'\eGnu$ ++!iEm3'K`';-7+7C2nHGK8PbWS>3'f[_0K!dXfu#f&P=&d)s8>\$<$3TUM+*Lk:&"Ec#Gu?!($# +8k)044Z[Q]ruKNM5!_S.9i4no?Y!niFaARmN00HsU8Y6&]>;M3J_#D'J_$@BJ,~> +ZN':J"O2KH&dJ:7!>NH/h>s,Hc/(_Ci8N\UjQ5OekiqBum-X60nF?)@o_%qOq"X[]qu$ElrVliq +qYBp\p@\(Lo'u5 +ZN':G"Nl3C&dJ:7!>NB-gB!`CcJ(\9cH*l8[BHO'S!B"iJU;KVB45"E:.R]52(g:#)\`bf"Voel +^)n[Q1H@]i9iG5'B5M^8J;0&IR\6R\[(=)ocId^&J_T8?6.s`3`k]0mXf%k^Q&gcSI +ZN':B"NGj<&I&+5!>N3(ecD!8cIP>/b/D!&Z)a[lQ]d8\IX#mLARAP<9L_9-1bC't)AEYe"Voel +^)nXO1,qHc92SesA86+.I=mE=Q^jhMYdV3]ajYglJ_&o56.F3$_7R.[WM?#NP)P-GH?=%=@pW89 +:.dr@4ukPckOm$ud5 +ZN'=K"j:mA',1oT!!*Y2hVJ:'!6s@Bhr*JQj5f=akNM0qlg4$,n*ol +ZN'=H"itU;&eb`R!!*Y0hV/("!7'=ce^)=R]XG#CU7@L0Lk9trDJ3H_niA,97R5%1o8l&Gk?tO4qG^b='OHuE2WNW_B_T:$TJ_Pb1J_QaMJ,~> +ZN'=C"iP74&eb`R!!*V+hUV^l!7'.^d*'AA\$fCh@$W;b]bI3]&H;+rh@1%13Mj +s7mld/MfCO78$N\?=[hkG^Y7&OI)N5Wj&qE_o^DZeq)E5ej7a.`k]0mXf%k_Q&gcSI<]dKAmehC +:eO8D4ZG8QWW.aXs3NDT2EF#h8PW2d?"7SeFaJ[pN09R"V5pl1]u/"AJ_#D'J_$CCJ,~> +ZN'1G"fNOQ!"FYs,Hc/'N!i8N\UjQ5OekiqBum-X60n+#r=oCV_Lp\=R\qYU6hs8O&6q"OLT +o^qbGnaQ#8mHj0(l0. +ZN'1D"f<@N!"FS:gB!`CcJ(\:ccO&:[]la+SNS"Zd`[Ca;scJ!j(J_T8?6.XE)_n +ZN'1?"em%H!"FJ7ecD!8cIP>0bJh3*Z)jdoR$*D_J9c3QB44tC:.R]62D?U+*uYn.&JALOs#2AZ +1cdom9iG2%AScC3IYE]BQ^sqPZ*q?`b12'oJ_&o56.*lp^UUSNVP'BANJE+5F`1u(?!($"7m]?u +1bW;:d3Ysqc7(>"2EF&k8l&Gk@:sFtG^b@)OdDW6WN`hC_T:,Ueq)D'et(BB~> +ZN'7I#2=8TM[p.tgti(%!6s@Ahr*JQj5f=akNM0qlg*s*mdKZ8o(2MHp@n@Wq>0seq]>\8rqQ9_ +p@\(Lo'u8=n*]T0lg!`ukN:mej5T%UhgsX8hW!BVhr*JQj5f=akNM0qlg4$,mdKZ8o(2MHp@e7U +rr2cjrVuilq"XUWp%7nJo'u5 +`r?&6.O9%_S!=\WM5oKO,8L:G&V2,>uslr +7R0$m0Jh_c80=r5bp=ek1,qEa8P`>j@:sFuH%1R-P*hl +`r?&T">?+Hj +6p+#O-Q6/W33M>_8joReq)D'et(BB~> +ZN':J#JN'4g3 +c2RtHr;-6aq#'jhq"adarV$9XgBYAKCtEh31>1I;!La#6gHjK>bJ_-(Z)a[lR$*A]IsH*PAmeeA +:.R]6:bjUO,pFHR5Oe]b/29%F5t=XJ=^P`UEHlqcMNF0qUoLZ-]YhkWW10b,UtH)3^?/.;H[@ +c2RtHr;-6aq#'jhq"adarV$9Xed&Z@C!q&(1">"0!L*T0ej7d/`k]0lXf%k^P`CQOI!0ID@pN27 +9L_<0:baLL,U"6O5Oe]b.kihA5=J4B=']:.R]62D?YSs-+E=WW10a,Uk?&3Bfi':fgq4BlA-AJqoANR\?X]Zb!rlc@OPt +eq)DCepm~> +ZN'=K#D"J2hVLO6dG*LYS,ggj5i:MYioB([jlYail0@U$mI'H4naZ2Ao_&(Zqtg-aqY^?mrqZ<^ +p@\(Lo()>?n*]T0lg!a!kND!hjQ#7Yi89+BJ_oGD6/UVYio9"ZjlYail0@U$mI'H4nac;Dp%J+R +q>UBjqu$EmrqcKeq"OLTo^hYDnF,f5mHj0(l0. +dJjOPrVH<_o^hYDnGM_\nF?)@p%S:[rqcZ^gBW3ZY1q\r(=([h!La#6gHjQAbf7E.Z`L$sR?NVc +JU2EUBOY4I;+jAEYhbab=&it,o`+PY0fD*X77p?U>@D/]F*W7hMisI!UoUc0^;S1CJ_Pb1j4clL +c,[W2Z`U*tRZi_cJ9c3QB44tC:.RZ42(r,2h]EOFVuOpZ+=8Wp3'K`&:fgq5C2eBFKSknYSYW9i +[_9T$d=L&'gO\+MgOK~> +dJjOPrVH<_o^hYDnGM_\nF?)@p%S:[rqcZ^ed$URWn,`b(!>:^!L*T0ej7j2a2,BqYGe1cQB6uW +IX#mLAmeeA:J!r=YMGXa=&`k*o`+PX0JkdR6V1$O=^P`UEHch_LlRaiTr5$!]"l>2J_#D'j46NB +aMPQtYGn7dQ]R)WIreq)DCepm~> +ZN'CM"3*N6fDaV-2Sdu%JcC<$JcC<$JcC<$JcEjl!.TM~> +e,KgSqY0[RnF#]0lKRO2kPs`Pl0@X&n+$&Dq>L6kn(Rr^4^lk>gAs4Gf\=[N)>EXaiS)`'`4`R] +VkBH@MhHM%E+rfe<`-=$ruU974?u>-:K:V-BPqs?Jr,SUT;J`t]>D\;f\bl[q18Qss5a2Hrq>gD +i7QH!_Rd(SUn*d2L4=DdBOP%A91(g&hZ$[p&e:c +e,KgSqY0[RnF#]0lKRO2kPs`Pl0@X&n+$&Dq>L6kn(%TU4C6A5ec@VEXaiS)`'`4`R] +VkBH@MhHM%E+rfe<`-=$ruU974?u>-:K:V-BPqs?Jr,SUT;J`t]>D\;f\bl[q18Qss5a2Hrq>gD +i7QH!_Rd(SUn*d2L4=DdBOP%A91(g&hZ$[p&e:c +ZN'FN!fe_,rS@S1WhS>hhr*JQj5f=akNM0plKdg(mdKZ7nac;Co_84[rqulpq>U*fp&FY"oCDGA +nF,f4m-F!&l0.UfkiqBum-X60nF?)@o_%qPqu?Wlr;HWo +s8Molq=s^Xp%7nJo'u5 +ec--Xq=aFLmHa$"jl>@Zi8FUl%H-4)j5f@dlKn!1p%\Lbs7"bEN%(S$gB$!+UYA&acH*l8[]ld- +SsY\!Kn"AhDJ3Kb=Aqsd8og`If\=]TGrdiD4?u;*9i>%t@:sFuH%(F(OHuE2W3H6V#,;.>'c\DH0/bpZ8P`>j@VB\'I"R9: +QCO_LYdV3^aj>UnJ_Pb1T%a6^J,~> +ec--Xq=aFLmHa$"jl>@Zi8FUl%H-4)j5f@dlKn!1p%\Lbs7"S@M(#%qecF9rUXh]Wb/D$(Z)jgq +RZrhgJphi_Ch@'Z<`)O]89(HFf\=]SGW@W@4$H#$92JVl?Y*tkG'edqN09R"UoUc/]Yhk@J_#D' +jOQZJcH*i6[BHO&S<].lJphf]Bk(FL:e=#<2_gg%W>H3T"eu%='H82D/i5UT7nlob?tO7sH%:X. +P*hl +ZMsp]oD\pY<+GLfs8RT=rtX*O#RF>ks8UY:#RF>ks8UY:!!lKkp](8Krr4#9XYgMQ\c;^(TIgR< +\c;]pN$/<0;XaYcmnsE(H2dgiO"WCT7@aCl?tX=uH\(YcqqeWW^?GUsp=[3rg%GCFr8>,b\E!>` +!+,X(%$W4d*#*\us7^"/&9IjN"Rf4o&9IaKs+gXS!R4C;!!7)P:Ab(mDsmT(!"f#,!QG-3s8LmG +KE8XGrS@PBJcC<$JcC<$JcDqRrnd4!!q2XSJ,~> +fDc?ZqY'LKm-3ZniSWJHg=cJX'A(mhg"P3:hVdDTkN_I)o_ACbg[kFIlVqBbn,NE;nc'?U70j<% +kPtS670j<%kPtS65m.HnlhLJ]rJ-7/ruq9@1^l^^s8Vbb/dt"RrVu2B+pJ@!:>@;_>ck$ +p@%2&f[.aV]!AB4S<](gIWfXB?s$8u6=TlZ1E@2?#GV7@)'LC^2a9c, +fDc?ZqY'LKm-3ZniSWJHg=cJX'A(mhg"P3:hVdDTkN_I)o_ACbf(8nDlVqBbn,NE;nc'?T63mip +kPtS563mipkPtS563%9hn+Zk^L]7;sqlM^a6+R$qpRj)H6+R$qlAQkh#ZC-ks6ihk%rUaqhIlce +!`6"1qZ&!WEbIlT";O!n90XO&"Wfcr8kLiL";j^FE-?Y^M3+'qU^FE<^rKs`!!dB@s7^"/&9IjN +"Rf4o&9IaKs+gXS!R4C;!!7)P:Ab(mDsmT(!"f#,!QG-3s8LU?EriE"rRLu6JcERd6N6]IkMY"> +ahbQoX/)8NNJ;n*DeEH[;+a/ar9Q3['b1JCs2l3&-nmSE7SZrgA8?74Jr,VXTVo!%^;\@HguRem +JcC<$JcE%UrmpX]!p>e?J,~> +ZMsp]o`"uu0=1'G!e3GLo)BGO>'G$gPlLc1>'G$gPlLc-:31JOSGN6gqrYRmKDtm@fR7(G>`%PA +bB7?<@Z07G]4,/hF&&8*s-Q].F'-N`s*S%-NKM9]gRt@@XHnk>s4k]0aMZR%s6.bUda.+Vs6\.Q +bJ;Eor;Qotn)_GfrVn!k=+,@:ZiC'18onoCaoDC.;Km1[`T$ppk.OfJs8V/KrbDX908J>OrbDd= +08K8uk.RPrqu6`hDZKG0rn[X.oYCT[R-+A(!:GF +f`)N[p[dk>kiLg]gtLE3eC2jnr6Q#&daQaug"Y?@j6#Xonb)b5o`"uu0=1'G!e3GLo`#a,rHhgo +F]Xh-rHhgoF]Xh-rHV1]F&/8$rri5]]N0^bs*!%D?>s2-jT!DHAp%U@i;U?!@WZZeRfEDTk3F[,XJVT5IMMGpN[CjE"<%hNb +A#oY0Z:]+T9$726QrS?O;SqAlqrYOlK`D)4Rf7(*<%j$\Rf7(.<%j%#qrYPc$N'i)o5+L"s8LaQ +K(Qh[J +f`)N[p[dk>kiLg]gtLE3eC2jnr6Q#&daQaug"Y?@j6#Xonb)b0o`"uu0=1'G!e3GLo)BGO>'G$g +PlLc1>'G$gPlLc-:31JOSGN6gqrYRmKDtmofR7(G>`%PAbB7?<@Z07G]4,/hF&&8*s-Q].E_j4/ +i)VI1;GVeHKg\3j4rF)O[ms8SHaGB6[$irAo<]iKdcs5]XJCBRTiLYS?tCC!lmL\CW-]qbr2rrVe1!;- +ZMsp^pAY6b@TDd]rr[a8J%t[V(\[8.[I`gR+ogsA[I`gR+ogg9V"=#5-2RWEn?m*^J,]I-.bn[;@@Bs8W&glK[AnF3FOQ +lK[AnF3FUQ[;AeAqu6_^5lgQWrn[[/s7Yj_hI^uorS@PBJcC<$JcC<$JcDqRrn[[/s7Yj`blI4& +:4N~> +gA_faq=X7DkiC^Zg=Xs(d*BkYb/sV's2bhpbg"J]eCN=.hrEkamdem+p\t?c@TDd]rr[a8J%t[V +(\[5-[e&sR+TLg?[e&sR+TLU7V"=&6-Mm`Fn?m*]J,]IsSm/PdTjY2'N+'isZYQ]7HtI1HIfm +:fgt8D01/XMisO&W3EbG`m3,jjQuPjs+13$s0D\(gAup+p?`",!:-(JJ,~> +gA_faq=X7DkiC^Zg=Xs(d*BkYb/sV's2bhpbg"J]eCN=.hrEkamde^&p\t?c@TDd]rr[a8J%t[V +(\[8.[I`gR+ogsA[I`gR+ogg9V"=#5-2RWEn?m*^J,]IsSm&GbTO+l"N*scqZYQZ7HW\J\mYiIl +pE?4$l??7nj"u"o[^!3GWgJs.I4J-FHlOM,?9TSu<\#=`8KfN]<\Ygn?V<"HH@Ua/P)jQa2?:8% +`m$8ccdghDs8P6+mcI\^s7[\0oBL[+XT.dtD(0u7s8DNRlJe%LbPh#3l3ZI7F3FUQ[;AeAqu6_^ +5lgQWrmh*ks7Y^[g159]rRLu6JcEOc)>EU`i7ZN#_Rm.UV4Ep4LOXPfBjt7E9LZYfru5c13^H;5 +='fKUFF8_!P+&)CYd_BecILb3m.Gr0JcC<$ZN'pT!H\;7l3,3/kEJSh~> +ZMsp^p\t<00rF79s*k*bJ%t^W(\[8.^&.Po*<5F<^&.Po*<5/>p](9$'`.h^oZa7.J,fQEI<"WR +fOg-9Fa*T[ibaPCWjDU&s0WR@lN$DRs2H,[s"oFpRCr@nru8MHqZ$>F]_;Bn_:&8Ck5P(td/jdK"Sglt]1Mu$*'Ae2Log@No*F(Ms03RDlN$DRs2Gb0s7FR5:4N0@"3#Phqu-O$`HeZa +s7FR5S,iKe"2.=&!;-r<0p>,nb:3Yk!h>ltps+13$s+13$s0)J%h>r<0p>,qB!:QFQJ,~> +g]%rbp[dhs*F=^VG2R?s7q(^li5RMO7dX=pAa,iMt3jPqZ$T& +)#rn%mJm4'(@C6"?Y3o%\@8`CUn1IEL5(!V1hgi1.5tO#6sFO5oZa7-J,B6J`H\N]rVm2CGcgiBoZa8%!;uj!\k/[t +o`+pF!J:@FgB5#]FlWGT!:>@;_#H^qnEJocd`]P@[&p3sQ]HrRH#d\1>ZFNhLT./GTUq^DgAgE1 +U/Ns56q^E\?tXD$I>*WDS"cme\A6/2f&#NUpOW?qs+13Us8LaRK)bQ!"I]>VRSA;~> +g]%rbp[dh*o$\@8`CUn1IFLPC*W1hgi1-oP@!6sEP;/R)I204f!21.$MFMijZFNhLT./GTUq^DgAgE1 +U/Ns56q^E\?tXD$I>*WDS"cme\A6/2f&#NUpOW?qs+13Us8LRMF8tsb"I&oLPY-H~> +ZMsp_q>UQkE(]6Vs8RQNs1a&up&>bF6@3o6MuWfq6@3o6MuW\Bs7a31HhdUlmVdUTs6t#VrVu/" +Jbb"+bO2c&J,T$5qqA_eHiN[SV"h-\,H=8ie?Ops6kBNrr;Z,b5VDLk/>!ls8:*P +kkEl+^A7d0k=,CM(\g+?MKhXas5F%Us8UXQs77)'oBLf*J,]HLmVdUQrr_J=L[+j<"6bGolM^_a +mbRsBrrh%NUtu+Ds8LjUK`Cc&"2.HRk5F-Bmete2KnGoFqsaUdmXOp0!qs+-LT!%F2s+13$ +s+13$s69R_h>r<0p>,qB!:QFQJ,~> +h#A,dp@7P6j58VCe'Q=\`l,^.]tCqe\c0)Q\[oDc^;.V)aNMlVe_/d;k/d#`qY^?qpM`adl2Ud5 +"96,c^\@aErr$\"H$;*'s8?e#H$;*'s823`pUjXUq#>C%D1DTcnSrsSs64?Oq1W2"n$PE2rUfg\ +ft:I7s77))oBUl,nD>-jBNmHB]="f?KHko-N-G=bGB@IM2J?]48LPcNA7RVI=_q([5_b2YQCFSG +XdK1)`m)[E?u]mMUAs6Rq>^KD#6493VXs,'!eC:M!psiSr;Ql^Knnpkrr_GMM$7tD.[-W;+a5?3&3'6,pTPlc7:P*4$cA3 +h#A,dp@7P6j58VCe'Q=\`l,^.]tCqe\c0)Q\[oDc^;.V)aNMlVe_/d;k/?`\qY^?qpM`adl2Ud5 +"96,c^\.UADaJep22hM7DaJep22hM3mf3$p>'p&EEpdjqs8VS.IJs32D1VM:?H_cE5C`Y(pA'p;@(o6cgIfSus(gLfZ2L2pBY,I("IZ]S5!'PRKB.c&TAn4L^,"6'R2^hgFQo,+:pl@/e`rr3&fDh%]b"6bGolM^_bk_4fWrVlreh>mNS"PQVH +h>m3Jrmh*ks7Y"G[7YMsrRM8>rRdcsK:LHgnG`OWJ+`gCp:%g0rr_&JY3^`>qYrkWn`o,ge',bD +[]ZO$R$!8YHZX+:?W^/t6p<3k]"uJ7fAG`Yq18Qs +s+13Us8LRMF8tsb"I&oLPY-H~> +ZMsp`qYpW>1mmJ+s*k.N^OO#krs`^+HZq3&s8QgsHZq3&rr3;HK6)\-s36%CMuEZ,mVdUTs715Z +rVu/"JcEW&@!-3?KE(u4VL*B:k5YI::31Vc$ifsqfDkB.>Q+R&R&g5d#P!#Nrr;Z(`r>uHjM8:[ +rr;&k[(Nf5^%q[/k<]+I(\jiXs6b@gs5j=Ys8UXQs8RjLF*CprKDtlPmVdUQrrTHD^&7m3Z#@>R +rrVWF!<3!&o3ue8g&V$Cs8LjUK`Cu,s/,bO"2.HRk5F-FmaHYrF`Ur3aoB86me?_aL[P3)3V`UX +L[P35OIZ"D".W2\m-O`QmXO0p!lMYDq#:E5L[O1%!qs+hKlh?9>K +n!m.'~> +h>]/*p@.D2in`;;d*0VN_S"R-4ML +Ifo_cJ%taX&,uNC5'_9+MuW`k5'_9+MuN__b(Fbdjo>mL#^MRV3UB-Y1%Z`?;aSRs+aHoH!L].rr3&eD1DNa"9,K@]`%j4ri^1Or;Qic +gAq6Q#P2MJo@F!p&>,b;4m1@rrMM:i;Wkr]D)+#!lKf^ir9)R[_Ll&62L0 +h>]/*p@.D2in`;;d*0VN_S"R-4ML +Ifo_cJ%t[V%;J%QG;5$Us(X]DG;5$Trs?f"F+`WTc$t5$rVpp+Dh%fenoK6Xs6=HPs2/SZF_W^b +s8V/WHZOi%s8RjLF*Cpqo]:q=fu.GG^U^\Q5Ydi9P(*HsIX>re3H&bN:b3k_Cim7n +S"QX[ZCh39b0eRh]^+R,/cXr5qu?]I"TSLW:31Vc#_W-V!q'uVr;Qi'DnZ)K!jQ[Wr;Qidh>mQT +#4l;FpY#WEp&G$B!H\;=f)MEAecWrrVo'^Z5>[qXWt4gssZh^ULGHU7.:)Kmn5bBjt:H:.R]7 +3&<3>/$T'#0f;!W78$N\?Y4+qH@h!8Q_((V[(F5udFmI@n,%\9JcC<$ZN'pT!H\;=ec9L`ec_3; +kEJSh~> +ZMspar;QlrIR)`Os8RQM^OO#lruCk+%(Z90QlQ.t%(Z90QlQ/+Z"AKnp](9[J0\##i;)$Mi]CfkL';/!RO%0rrV1ol2L]!qK9.UKX4oYH^l-fS2'#k@[pB]XUs0WjHs5'8qMi]BPrr3&fDh%`c"6t5_mf!.f +l?e$Kr;Qidh>mQT#LWlfHYWV.`V0?2h>r<0rS@XR2`IW5h?1GdGNSk\%.9Kh\aJn#>)E-/#i>=U +":3lapUU)!"UNubqq0]srrR[akj/6LiW.p:!eYR^q#:DJ#i=S@!q:2ZJcC<$JcC<$df9='!JLLO +h?%TqDuJMqS,i#J:4N~> +hZ"Agp@.D1iS<&5cH=/D^:Un_ZEUL5X/c)s)60d?YHbIC\\5esaNW#]ftQM1mdp;PIR)`Os8RQM +^OO#lruCe%%_DK/QluFt%_DK/QluG/Z>"`qp&G';It*CTru:?BJ,fQFIrarVg1ucLn%OjXaftHG%etNPj^:9@AOJ883N)DB]n^]!T*t:rrRgnmeHe`K*^;mrrVY.JE-Yqr:K@;hU^#o_7?kPUn*g4Lk0knCh@$W +;b]eL4utSY1UI,/3'9Gp8l/Pn@VKe)I>!NARA$O^[_9W'e(WgGnGRqi2lRK2ZB9n3~> +hZ"Agp@.D1iS<&5cH=/D^:Un_ZEUL5X/c)s)60d?YHbIC\\5esaNW#]ft-5-mdp;PIR)`Os8RQM +^OO#lruCk+%(Z90QlQ.t%(Z90QlQ/+Z"AKnp](9[J0\#;r4$oMi]C^fY$8#etEDg^:9FDOeSA4NW_OoJo!2/F^AErBJ;AbG&UYT6V0"0P*VT1 +VQ6u1UB&E^e]"r[n,106s7ZfUs8TJHs8UrGIuBC;PlC[amVdURrr_P5J+!:8"6t5_mem(dmbRsC +rs-sX@s_tkHc>^Yrmh*ks8CLNTJ\U"rmh1Z8orkfedUi2?FfLl^KQKkK*^?.rr`Vas7a)!rri\b +s81I(p&>)CC$=m_!TF+;rrRgomeHe`K*^>nrrV\0J)gPpr:K@;hU^#o_7?kPUn*g4Lk0knCh@$W +;b]eL4utSY1UI,/3'9Gp8l/Pn@VKe)I>!NARA$O^[_9W'e(WgGnGRqecKUe +C]2fePQ9m28q6~> +ZMspbrVluM3f/[I`gN+oh-B[I`gN+ogg^J):1iq#:E`Dh%cd';/gLpSq], +s2CMVm\"=.oT!%WjSnHErsamgpZ>Y+s3k,rp[",Urr3"oJGfEcOJ^e/d]V4#ahI95[__nAiQUKX +b,a1pn*U)PrrM7Arr3ed?d8?1ImO,76%o.,@UfB;Mlla9>'ms^rrVV,J,TBK^LX/Hg]hWe6rSB9sLK`%&s80F?_*n?JgAfT_IuD;G +s,-l$g?sIjs3d!YMp;9[!-d/X\%ht"U4\QJpAP"'a%/:jm/,+pKq?rqs1bSbON%%q%.bJ=s80'V +K;ePEW3uX)(7;r4J's7)W;$>lqltp/ +W;$>ldZAs4XQK>6NVreXp:%efs+13$s+14(s8LjUK`D&.!,qi:!,qkn"IoJ\S5+S~> +hu=Pjp@.D0i7li1bfI]:]=5/PY,eP"V5'cZrh14(USOcbWiWD0[^j)f`QHNU\_Z<0m`IPIrT4%] +Im8Ftp&>a#mgc_rn!#$rmgc_rn!#$rkq[hGrp0@Z!psiSrr3Y*A%hT:A?u6TS^HhuCq]J#>-I#d +li6u`NNVs9?$lOl9=ENqC,X/N]j!7nVkT`M-T>[k=Yj3-7QP="0g']0@o.#k=?LDeN/j^7YdV0? +!6s!G\OjC#l[M>&s$VJ7o4)A3s,F2Sj^!5Hrr3&eD1DNa!l&9@r;Qi5A>B&%!q"_Brr2uTrdP#e +!.W#^s8LaRK)bi)$`#Z\!&gY-gU^+%j8I^em>"iirVu`+]>6r:FkH`@O+3)+s8S&YFkH)2s8UN0 +IuV\PN<"+RijQW$gnKLba7fK3&][C&VsF6,QA(m]rVsX4e;qQDrs\E?s8Vu%LP)W$s/<=(rt^s< +s8CN=KpL*_s1bVENJOn)`^W"elh^Vc`^TrNfXRs\ru7*Gm=3QBJZ/8As80*WK<"\Gs3d$ZL99S4 +Ll_K6!qs(;li06Lp?q,%f[7jZ]X4fjM8D>r8mRK2ZB9n3~> +hu=Pjp@.D0i7li1bfI]:]=5/PY,eP"V5'cZrh14(USOcbWiWD0[^j)f`QHNU[GBm,m`IPIrT4%] +Im8Ftp&>a%o*i%umZ\mpo*i%umZ\mpl7meFrp':Y!q'uVrr3Y)@__Z;@^,mOSC6huC;'8"=Kgfb +lMpl_Mlla9>'p4h8@I3mC,F#L]j!:oVkT`M,r]Lj=Ya*,7ltO&1-Ko3@S^ii=$(5cN/sd8YdV0B +!6s!G\44'tl@2,"s$;>8o3u2/s,4#Rk$!,Frr3&fDh%`c!l&9Ar;Qi5A>K,&!q+nFrr2uTrdP#g +!.Vu]s8LRMF8u6j$_T?W!&^G#f!S+khYl"[lA&QhrVuc,]>6o9GM<)FOFN2-s8RuWGM;J7s8UN/ +IuDSOMZ@tTjL;o(hP,Xaa7fK3&][F(W9jE-QA(p_rVsX4er[lHrs\K=s8Vu$LP)Q"s/<@)rt_!< +s8CN=KpL'^s1bSCMheY(`C2kem/$_d`C0cNg:=0]ru7'EmXNZCJ#N,As80'VK;ePEs3d!YKrjG4 +LQ263!qs+ +ZMt-hs8W%W3Rd$g!e3GLo)B;G>'"a_PlLc->'"a_PlJ8oA,U3VTmiC]FDDMb\J;>2'#A@VLLKElJ%X!G&M6ru0=nW3iM=nGh(@Z*LRes8V#\W3N5;oDchE +p\k*mjYQo9([PdIG]R'!s8TK)Is4Z0s8S!PGB6sAm/I"dmVdURrrSKoao)/?Q=.i3rrVWF!;c]r +h>m3Jrn[g3s5)V02uN^\2l>HF:3Yk!hD+H*=CRAuf_#3_70%PcpJOV1C.@stMZ@tTh>mTM;J1>g +:A9YHGM:hjJ)H/mF^f1+rVmJM83'kXiakd#=HYZ'Cs8T$$HZMrPs7]E(HZ<5`qo-PP +rrVh_VLebus+13$s4[MPh?8N3hVN2K!!dH!hOFT7S5+S~> +i;X\mp[RS2i7c`.bJqB3\[8WDWi)YdT:MLARf8]lR[]h=TVJ?^X08k=]>);+cF)<\iIEf-rT*t[ +Im:^?rtGD1H!>E6;j@=\H!>E6;j@,G'-oss8TN)IsFf3s8S'TGB7!@li-ncm;7@Prr`7b?c`3G"9+NlaSYu=mFqX;rrLjSo`+pF +"bQd/gS?b+!!Y?WgU^+%j8I^emBfr)OL*R3s2IM.FkHFc8pP,>p&C!OFkGFKs7]N-H"g"IN<"+R +fkl5qLh([=CrH;P&][C&VsDZQ@Wc+1gAcX]DelrZs!+Im3i2lPR)1e:VO[@GD#6`Vf`;o9uQA +s$-8Il/LIHbf.<*Yc4@eQ&^WNHZX.=@U3)7:J45I6UF+,6q0aB;-$n/ASZ:1IY +i;X\mp[RS2i7c`.bJqB3\[8WDWi)YdT:MLARf8]lR[]h=TVJ?^X08k=]>);+cEZ$XiIEf-rT*t[ +Im:^=rt0HYF)t6Ds8R:HF)t6Ds1&,>rG23.!q'uVrr3VbK5#[AMraoWCgqO!RdAg70k^H#s8Q#U +:3Ub_gAg25)o1hAu)]?]>:QKdb*F( +;HS*`>.O\5[8MKu:2&?BrrVV,J,TBKQ=.i4rrSKoanu)>mbRs?rrLsVo`+pA"`s^p +et=o!!!Yr>,(o5`Vf`;oUMfDs$-8I +l/LIHbf.<*Yc4@eQ&^WNHZX.=@U3)7:J45I6UF+,6q0aB;-$n/ASZ:1IY +ZMt*hs5$(iqVqPT^[_=7lC`WZW:U&hlC`WZW:U&6qg\D@!qs++np\t6Ms6afTW*l^r +bD!>$rt'Zumer;T_sjm8UAo^'F5$B`s!7%4q^nK4mZ]$ldL>"4W%%?8P\/&,_-E#tIm:F9eQRV* +IrFcMs!3'ohZ)^Pdf5[hqq_=3mcoQVqtht +iVtk9q"!e6i7lf/b/M0.\$E0:VPBfTR[BD*PEM&jP*2#oQ^O>7Tr"]hYdCsT_oT(1]K'ljn+H\A +s8Tk(rt"FiJVC&os8VDZJVC&os31@iq#:Eh[_MhA&H1.JKqI0#s7aNoLTT\@S'1'K8beIE)KJFN1m(sH`OTf_8a-$fA5KOb)q11 +]`%m1qlkd.])M^4n"##hbl%JBp9qa8rr`89Y3Z&d"9-*=lhg\`p?dA+rrMM:o`+pF"G6[.gSF^` +"f0na9m,Lpg]-Xo[=)h(Bp[cXIfS7'okV#3`W5'!rrCgRgAq9B"K;"IH2%==g]-YXJ#HeYnB-Ek +r;R=XD=.7_?,?!6?).FL3d'0Lq#;W,s8+LCnaD\fpX0W,s/7@;qitj9nA/@hqgUi>p=*8us*nnQ +q#;Uh!8IP8'[m#K1ATZ)Npl:f./WlqT^hK8,kpa1W:f>R\GQ!nr;QirioTJ**rGNojPAD5aMGHo +XJVVWP)G$DH#mh9@pW;<;GU"[r^n2):/Otl>[V,YE-?V\Ll[gkUT(K,^;\=Fg>V8ar.4m!s+13T +s8LaTK)aT[qJH8e"I]>VRSA;~> +iVtk9q"!e6i7lf/b/M0.\$E0:VPBfTR[BD*PEM&jP*2#oQ^O>7Tr"]hYdCsT_oSq-]K'ljn+H\A +s8Tk(rt"CfItO]ks8VAWItO]ks3:Fjq#:Eh\%hqB&H11KK:^lus7XEkKrjG:k,/$*s8PCQS:?IA +s8W)HMfa3>f[A$bA"gE=Y,\@kKO%;VI?0D>@S'1'JrG\GE)BACNM31tI&j]g_8a0%fA5KOb`I=0 +^&J$7qltd+\c2U3m[Scbc2@SCp:%g8rrU$>m/6kb_63/%rrVo^J,90GmXOm/rmh3ns45boqeHA. +f!S+khYl"[lL)$-CM.@Ii;\9,hZ*=c[JS&Gk5YG]hZ)F4s6frepY'ugrrCpUmVdTR@'&k@Ks^dX +&:\WdrK:L)n??/ls*m%Mg@tCB,jt^0/YM_'PU6(P%,V#_.J*G$B$'PY>`S]25I^@:>(cj)DsmE# +,_#gNs5k$Ds-PPiN&lA$hcq^nK4mZ]$lmf9?]kc22jp@eFa!r_3BlMhq&o'>Amf$DFR]!JK8 +T9te"Kn"AhD.mEc=]J?q9hYIX*DB4*<**=4ASZ7.I"I08Q(4VKYd_?cbgY;)kjSG#s+13$s0;V' +ecU!nf%0\5s4.=;!9]S=J,~> +ZMt$gRl&6r<0p>,nb:3Yk!hD+BZkk*N6Io)[4IfS@*s1sqePQ9S?rrCpUh>mTM??_$l +f)PaMhZ*"^Itqh!s7tU8r;R7VDsm7?GP;'rBSHJ9ABFKD,3f/PIt)A:iqkTps%.kCl?,18Is3hk +n:,'4s6afTs*ntTq#;Le!8dbU8q6k(MuVW6lMpnL!:&hLGCP*\!93`ZGCtsel@Jtds+13$s3L`E +h>r<0p>,qB!:QFQJ,~> +iVseloBk`"gXXZn`P9!lYcOatTU_C7PE:f`MuAMNMi]YVV3dFR+4kO%uPs8LaR +K)bQ!"1h3Lj8I^elK.7%fBi +hrXe9%Y&?_oQ56Ks5IdJs*nS#o`$-$!d0!BDZJhp304)3KE(7\4*lC#2f\A;B'''[D1DS8D=.,u ++b'LHs8P]BrB-)>gApL>IpL/8uO14?1ruV.3mcWN]d`]SB[]la+SZk*,;uTZ7;cQn'?=IM^E-?SZLQ.LcTVeit]"uJ6f%fV +RSA;~> +iVseloBk`"gXXZn`P9!lYcOatTU_C7PE:f`MuAMNMi]YVV3dFR+4kO%uPs8LRM +F8tsb"1:aBhYl"[kiV+$fX$'(SGRd9hZ*VlPaI[8hZ*TUhZ)F4s7]iDK:LNmrrCpUmVdT1CZ>Bi +i9't;%Y&EboQ>BOs5I^Gs*nS$o`$-#!-`pBDZJeo23@i0KE(4Z4*uI$2f\>:B'0-]Dh%e:DsmE# ++b'LKs8P]@s#l;?h>lj@s69ULl2Z$XIr>>HpKi&rO1FQ5ruV.3mcWN]d`]SB[]la+SZk*,;uTZ7;cQn'?=IM^E-?SZLQ.LcTVeit]"uJ6f%f +Zi:-j7;qmNs+gXRh>r<0p>,nb:3Yk!hD+5gT(N'_l>(VmTQ_5)j1 +>5/$shZ*"^It_Rqs7t0ur;R7VDsm7?GP;'rBSHJ9Dr1-d,3f/TIt)fXlMm)/hAFuHl?,7 +ir9qpp@%8+h:Bus`P9!lYH"InSXGb*Nf/aLKnK>U)M<]tLl.1RPF%c2Uo:E$\#V8c`mi`#k3V[: +NW0jmoBti%h:U0#aMbj*\$N9=W2?>`T:MLArL+LkSXuLKV5LAsZF7?Y_oL!Kf%f6Im.@adrn@I* +s7Y1L\kR>-rS''hU7_,Ys649Ap4!(ss7]TEQ`:&?s8N)Rs4[PRqnf4cF]nJ=!8IP@D1@7RirAi( +gALs\Ir4TFFER9Vib=,HIr3s4s!$\-H%1#JcC<$JcDtSrn@I*s7Y1MRK2ZB +9n3~> +ir9qpp@%8+h:Bus`P9!lYH"InSXGb*Nf/aLKnK>U)M<]tLl.1RPF%c2Uo:E$\#V2a`mi`#k3V[: +NW0jmoBti%h:U0#aMbj*\$N9=W2?>`T:MLArL+LkSXuLKV5LAsZF7?Y_oL!Kf%f6Im.@adrmh*k +s7Y"G[7YMsrRN^aU7V#Ws6=BDpO<2"s7]QDR&C#As8N)Us5!bUqn](`G?Xb@!8dbDDh!ISirAi( +gALs\IrFcIFa*QZib4&GIrF39s!$_.H[gNlBBoM]3SkV,s6=_iI=HZbW:YRXmf2]nJ,b":meHf+ +IfS@*s.Csp(uG;L!:'Ual2^,7!.03FHY;X@_5)j9BW1gcs#p/IlJp[McH!c4Z`U.!RZrhhK78&d +Deiuq?s[)A=]np4?!h&PBl.j5H[pj0OdDZ8Wj&tH`66T^hra>#JcC<$JcDtSrmh*ks7Y"HPQ9m2 +8q6~> +[/U3,196!+L&_//!JLLHh?1GdGNSk\1$Xj"Y3>;m@#Xu4!8@JA!7(V\!8@JQ!8@J0!<;e+hYXPX +oD\gEs6afTU0OkjbB^&arsjNsmer5P_sjm8UAo^]hXpglq^JK8o;IiZqthh@oCJo5s7^0_qpnkpq>1*mrT**ls+13$ +s+14(s8LjUK`Cc&"IoJ\S5+S~> +j8U+uq!m\3hUp9#`k]0nYH"FlS!T:uMhct:Isl]krd#K0I"$TuL5CkNPaS,;VlY%dGf.YLe_B*I +me2V>(AdXiki1FNe'?+T_7dCfZELC1W2?Ddr1Xt%Uo(&iXKSq;\\,_raj&8cgu7G_oDZ2urn@I* +s7Y1L\kR>-rS''kO&`FJm_iLjqL8Lss6Tm4rPAQds8N)Ns4[PRok3.WXoe.n!8%8;S7e_K3MnGRq +j8U+uq!m\3hUp9#`k]0nYH"FlS!T:uMhct:Isl]krd#K0I"$TuL5CkNPaS,;VlY%dFi2>Ie_B*I +me2V>(AdXiki1FNe'?+T_7dCfZELC1W2?Ddr1Xt%Uo(&iXKSq;\\,_raj&8cgu7G_oDZ2urmh*k +s7Y"G[7YMsrRN^cOArCHm_rRmqgSV"s6fs5s1n]hs8N)Qs5!bUpLi@[YlOCq!8@J@Dh"KdY40V[ +W;QZ*IrFcROBf]mY[!q&IrF39s!%=hW:TVZKE(u2'Q6SCs8.smc1B2G_>f"'mf2]nJ,b"8m.gT) +IfS4&s5k#%:B1@N!0$@6LFN,t.B)k0W.Y-EBAWO:=F'`=rrW/[k3;mdr:KC=hq6B#`4iadX/2GV +PE(HOIX6-VD/*]p@q,FG*+NMkBl%^/GC4srMij;S7e_K3MnGRq +[Jp@"DXln@s+ULPh>r<0p:UR0:3Yk!hD+HGG?Sp+ApMT"OmTQ;JLPk +aP%rrPb'Z2Xb:c[BJNA9#Zi +IrFcTmVdUTO@Gu2q>VY#+`#g3s%Wj$s8UpUCNjhgT`>%b>'G0gOT52UC3sr,W;ZSm!q`"[JcC<$ +JcC<$gAh0/!JLLH]`ai*n!m.'~> +j8U+ro^:o$g=4Bf_7R+YWhc8UQ&pr\KReJsG]e+Lrc/s!F`r%WIY*<3Ng#j$U7P#VT"`.cdF[48 +lLFb1rtbG%mHETeg""HnaMbm-]!o&OYHFt-WrAt5WiN5'YHbFB\\,\pa32fYf\PQMm.(%^s8LaR +K)bPU"0,(VRSA;~> +j8U+ro^:o$g=4Bf_7R+YWhc8UQ&pr\KReJsG]e+Lrc/s!F`r%WIY*<3Ng#j$U7P#VR_H__dF[48 +lLFb1rtbG%mHETeg""HnaMbm-]!o&OYHFt-WrAt5WiN5'YHbFB\\,\pa32fYf\PQMm.(%^s8LRM +F8tsA"/JP1hYl"[lHsfZDf]N9bQ!YICYJMGCPR]hDu]h\CYIVFs8,]/HY6CVrul13gi%\tK4&h2 +CrQ>P%Y&Ebs35[qF^TdSs*ntDoD\ue>'G0gOT,7]8J&0+rtE_,BQn!,a8^X-mf2]nJ,bU"U?q^U +,*3s`gAh1J#d"(+h>h>8F%)c%s.EP>G>aP&qbh0UEF,UBqYpWhS>PNM*;Asej4r22aMPTuYGn=h +R?NYfKReGpFE)59CMNi_)es2,E-$2KIY*?6Od;N2Vld;9^W"FFg#(rZpOW?qs+13Rs8LRMF8tsA +"I&oLPY-H~> +ZMspfJcCH(rn[[/J+ZP<-[4_OhD+HsfV!qNSCIH9c_,dAs1O&?`Lqk`s.FkqmXP9:c&7(5f)PcC +J(itm^]3#ZK94.Ir;R8A\+]k!c^'3Rf_tib\*ikf"o#*NK;A,VVaTRY\qs+; +jSqCBq!mY0h:Brp_nEL_Whl>VP`L]VJUMfeF)Z#5CMIU"Ci+*1FE`(]K8>MMQ^jeKXdQc9`m)ud +i90M"rf$jlq=F%=jPS_Dda$%V`501"\[SuRZa0S8s0<:lRmIU:`s8LaR +Jq*Gf![`'MrS''or7@TqKUgKes3I?hf`/[^UEP$rrr.uKnH8orr3&4J+rq>Fa&.[JV8l>P*_]4VQ@&3]u/">f%oBOnG\"=JcC<$YQ+UV!J5go9`kC] +RSA;~> +jSqCBq!mY0h:Brp_nEL_Whl>VP`L]VJUMfeF)Z#5CMIU"Ci+*1FE`(]K8>MMQ^jeKXd6Q6`m)ud +i90M"rf$jlq=F%=jPS_Dda$%V`501"\[SuRZa0S8s0<:lRmIU:`s8LRM +F+'Dp1EK9XRTs1c&:s7Wq:s3He.m.pZ) +c_,dAs8R]kmf3=D!6L"KW;$>lpT0""V"=Wdc&7(,Zg.Ad!qs+rq>Fa&.[JV8l>P*_]4VQ@&3]u/">f%oBOnG\"=JcC<$YQ+UQ!HWb`8co(V +PY-H~> +ZMspgJcCH(rn[X.o3MHpk5F-:mXbE;rrVdmDuTb;h>i-,JcC<$JcE=]rn[X.oR?rCn!m.'~> +jSqC?o^1i"g!\*`^UUSNVP'BBO,8O>HZjFJCM@Bl@q/tXA7]FhD/jZCI=d65P*_`7WKss)_TC-T +h;dejqhtIgqt0@Bk2G.Le^DadaMl'4^:h.i])B/Q]">Vh^r"(2b0A>`f\GEHl0e9EZiC$Z!.sfH +!Me]RgApVls8W*$rUX01rr3#R!6>+"p[RP0h:L&t`P8sjY,S4hR['"pM2$Y5IXHHeH$FU\H[UBp +KSYPIPF.l6VQ6u2]Y_b8eD&sFmeMG6JcC<$Y5eLU!.sim!s%YU9n3~> +jSqC?o^1i"g!\*`^UUSNVP'BBO,8O>HZjFJCM@Bl@q/tXA7]FhD/jZCI=d65P*_`7WKO[%_TC-T +h;dejqhtIgqt0@Bk2G.Le^DadaMl'4^:h.i])B/Q]">Vh^r"(2b0A>`f\GEHl0e9EZiC$U!-@a4 +!M80Hec=uds8N$"o2]f/rrLsVa8\0on`o2kf?qd\^U^_SWMH/TQ'%&_L4b#*I!U'`G^4U^I=Hg$ +Ll7=XR%0kIX0K.H_T:$PgYq>`q18Qss+13Qs8LRLF7aqa!9]S=J,~> +ZMspgJcCH(rn[X.o=Y1Uk5F-:mZ%5Im&^)6!J/)crr_8%3R[p,!Tl+fs+13$s+13]s8LjUK`Cc6 +"O-r8S5+S~> +jSp7qn`o/if$DFR]lIqI>[CfJAnl4(GC4ssN09R"V38*n^;\=E +g#2&]p58k_rUo[HkiC[Wf[\Erbf\#H`5BIi_%FQ%`5]m@bg4\df\> +jSp7qn`o/if$DFR]lIqI>[CfJAnl4(GC4ssN09R"V2qmk^;\=E +g#2&]p58k_rUo[HkiC[Wf[\Erbf\#H`5BIi_%FQ%`5]m@bg4\df\>XB9ic@-.rr3#]0us;RrUoUBinW,3b/D$)['$C)TU_C6OH#-QKnFu.J:N3% +K7nu9MiX$eR\$:PXKf4H_8jgLg#(oWo7?pms+13Ps8LRMF8tt!"N^Z,PY-H~> +ZMsphJcCH(rn[X.o=Y1Uk5F-:mZ%5Im&^)6!J/)crrV20W;Z_spT];LJcC<$JcE=]rn[[/s7Y:P +S,i#J:4N~> +jo7RDp$V#$g!\*`^:1>HUn*j7MM-J)FDko*@9m2cO,n`p<)lt%>[LuSCiOWGJr#GOR\5_D[(F/q +cdpq5lgk"3s!Rg@o'Yf*j58YFf$r-ocHOJSaiVWGai_fNc-Oedf%Jj9io]LmnbfWkrn@F)o_,Hl`Bu5!J82err_50W;Z_q!VN\:ru1b*m,m6[e^)@U^:CVSX/;V_S!TA%O,]'TM>i;K +M2I7POHYuuSY2dXXg5FK_8jdJf\YZRnG@e:JcC<$XoJCT!J:@FgB +jo7RDp$V#$g!\*`^:1>HUn*j7MM-J)FDko*@9m2cO,n`p<)lt%>[LuSCiOWGJr#GOR\5VA[(F/q +cdpq5lgk"3s!Rg@o'Yf*j58YFf$r-ocHOJSaiVWGai_fNc-Oedf%Jj9io]LmnbfWkrmh'jo;r&9 +hYl"*lAbfEm&^)6!J/)crrV20W;Z_spT_a<)Z''lk2=tDccO,>\[8Q@VP9ZOQ^!VnNJi[NreMGK +MN!RWPEqW-TVSQhZF@K_a3;uahW*njqgncus+13Ps8LRMF8tsb"I&oLPY-H~> +ZMsphJcCH(rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjUK`Cc&"IoJ\S5+S~> +jo6:roBbSqf?_OT]Ye6t=^>HJCiOZIK8G\USVsMP\%]i* +e(WdEn+qk?&H)7lmHWioi835Af@AC"daA'@s3V>)eCE1(gYLfJk3;7%q#7Jsrn@F)o +jo6:roBbSqf?_OT]Ye6t=^>HJCiOZIK8G\USVX;M\%]i* +e(WdEn+qk?&H)7lmHWioi835Af@AC"daA'@s3V>)eCE1(gYLfJk3;7%q#7Jsrmh'jo;r&9hYl"* +l@JuHru(S$lfI$XeBc7T^UghXXf8.kSsu.6P`q5lrf@tZP*;/tR[p+HVlR#-\@oc"bgG%uiooh+ +JcC<$JcDhOrmh*ks7Y"HPQ9m28q6~> +ZMspiJcCH(rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjUK`Cc&"IoJ\S5+S~> +jo7R?n`f&feBQ"J\?W'/S<].kJpVWYBk(IPo`+sQai`oKs6!QY:/Y.u@:j=rG^kF+P*q0)XgG^W +ajA\tk3_hrrsnquna>`,jl>=Wh;$`;g&BY(faQThh;7)LjlbprnFupas8LaQK(HDOd,Y)Z!:>@; +])NWHp@.A.hq?N*b/M0/\$N9>W268^St)7;rKnChR[]k?TqnTdY-GCG^;J%9db!:6kO%tos+13$ +s/Z2!gAup+p=f_=!:-(JJ,~> +jo7R?n`f&feBQ"J\?W'/S<].kJpVWYBk(IPo`+sQai`oKs6!QY:/Y.u@:j=rG^kF+P*q'&XgG^W +ajA\tk3_hrrsnquna>`,jl>=Wh;$`;g&BY(faQThh;7)LjlbprnFupas8LRLF7ZL7b23*J!:#.8 +])NWHp@.A.hq?N*b/M0/\$N9>W268^St)7;rKnChR[]k?TqnTdY-GCG^;J%9db!:6kO%tos+13$ +s/Z2!ecBjlp=9A2!9]S=J,~> +ZMspiJcCH(rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjUK`Cc&"IoJ\S5+S~> +k5RaGp@%2&g!\$\]X=o?TUCt$KRS,bC1LXR;sL;e69dUrQ2gls6qC!K@Zr87tAioB+^kj%O&nb<$`s8LaQK(HDOd,Y)Z!:>@;\c3KE +p$_2,hqHW-bJqE5]!erKXK&1pUS=I[TG=/qUo()kY-G@D]YMJ-cI1>"iof_$JcC<$JcDbMrn@I* +s7Y1MRK2ZB9n3~> +k5RaGp@%2&g!\$\]X=o?TUCt$KRS,bC1LXR;sL;e69dUrQ2gls6qC!K@Zr87tAioB+^kj%O&nb<$`s8LRLF7ZL7b23*J!:#.8\c3KE +p$_2,hqHW-bJqE5]!erKXK&1pUS=I[TG=/qUo()kY-G@D]YMJ-cI1>"iof_$JcC<$JcDbMrmh*k +s7Y"HPQ9m28q6~> +ZMspjJcCH(rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjUK`Cc&"IoJ\S5+S~> +k5RaEo^1bsf?_OS]!AB4SX,=mJU2ETAmeeA:/jSF4#f)S1pm;Z6U=17;-.%5C2eBGKoD4KTr>0' +]uA4EgZ%Jfr.G"UrV?-WnF#Z/lKS61$0ga@m-X94p\b#is8LaQK(HDOd,Y)Z!:>@;\GmBCp$_2- +i7li1c,mo?]t1YZZ*1:1WMlcpV\Z51WiWA-ZF.3S^Ve+8cd^V'iof_#rdk*#s+13Ms8LaRK)bQ! +"I]>VRSA;~> +k5RaEo^1bsf?_OS]!AB4SX,=mJU2ETAmeeA:/jSF4#f)S1pm;Z6U=17;-.%5C2eBGKoD4HTr>0' +]uA4EgZ%Jfr.G"UrV?-WnF#Z/lKS61$0ga@m-X94p\b#is8LRLF7ZL7b23*J!:#.8\GmBCp$_2- +i7li1c,mo?]t1YZZ*1:1WMlcpV\Z51WiWA-ZF.3S^Ve+8cd^V'iof_#rdk*#s+13Ms8LRMF8tsb +"I&oLPY-H~> +ZMspjJcCH(rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjUK`Cc&"IoJ\S5+S~> +k5RaDoBYJme]u1L\?Ms,S!8kcIWoaF@pE#08OG[%1bgU6/9h-o:cgs49N,)$Ao2X:Jr,S?T;J`t +]>Me=g#2)_qLSYOs8Moip%.bEr:'aV"7u6^rMBPngAlis[K4b8rS%>>JcE4Z(&[anlK.![f[J0i +a2Gg.]=>;VZa$b5Y8+:HZEpsJ]"Gep`lZKQe_8m?kjA$AJcC<$JcD_Lrn@I*s7Y1MRK2ZB9n3~> +k5RaDoBYJme]u1L\?Ms,S!8kcIWoaF@pE#08OG[%1bgU6/9h-o:cgs49N,)$Ao2X:Jr,SMe=g#2)_qLSYOs8Moip%.bEr:'aV"7u6^rMBPnec9d_XT?T$rRLu6JcE4Z(&[anlK.![f[J0i +a2Gg.]=>;VZa$b5Y8+:HZEpsJ]"Gep`lZKQe_8m?kjA$AJcC<$JcD_Lrmh*ks7Y"HPQ9m28q6~> +ZMspjJcCH(rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjUK`Cc&"IoJ\S5+S~> +k5RaCn`o,geBGnF[]cU%R?EG[I!':>JcE1Y'`I^olf[9ag=F]tbK.Z>^qRIm +\[_UIs0r]Q]=ktq`5]pDd+$_'i8s4kpATXCJcC<$W;lkO!J:@FgB +k5RaCn`o,geBGnF[]cU%R?EG[I!':^qRIm +\[_UIs0r]Q]=ktq`5]pDd+$_'i8s4kpATXCJcC<$W;lkJ!H\;7ec_3;kEJSh~> +ZMspjh>dMinc'lpS`_"\c2U/^&\!4i%- +!TJV+rr@iQru(IJOF`_Dqu?WGRY@Eis8VT#M2Ae2s8UrIrI4gXrr3,;LP)Q&p&G$J!/0sW!S6E3 +h>ltps+13$s+13$s0)J%h>r<0p>,qB!:QFQJ,~> +k5RaBnEJocd`]P@[B6uj`l5rphT-mKfS*4c+".krtH6q^E\?taJ%IYE`1S"cpf +\A6/2f&#NUp?M\X^[_=%dGlou3#EQI#F-rRIf +rn@F)o +k5RaBnEJocd`]P@[B6uj`l5rphT-mKfS*4c+".krtH6q^E\?taJ%IYE`.S"cpf +\A6/2f&#NUp?M\X^[_=\!kJ?UqgTFMs81B9KqR6$s7!XWM7N'Ss81-.KqR5qrr@iQrrBb2 +IfR%WrrM,/qu6YOrVmc-`K5Y:chmb@es_;ch#IED]8;BTmf3=DT)F-=\c2U3_2!ZAqt:!gec9d_ +XT?T$rRLu6JcE.X'E7asmHN]igtC6+ccjPP`P]R/_#1qZ^qmq,`lQ?KdF?e&hW!_an+hP7JcC<$ +VZ6YH!H\;7ec_3;kEJSh~> +ZMspkh>[P+5JQdh'RO#-F&nP*s*IpnF&nP*s)1q^>%[U1"T.>pI!rbDeE +s8U&AF*C6]p&G$J!/0sW!S6E3h>ltps+13$s+13$s0)J%h>r<0p>,qB!:QFQJ,~> +k5RaAn*&]_dE9A=[&g*pQB$`NG]@G,>?"9b4uY/E,97U9>Q=_<-7q&96:k!T?=n%sI"R?*R\?[a +\%fr.e_Tos2b.S)E]52s0@)[E*\@^s0uQOC1=OKs3#Rt +F^ps2-jSu`"B51n^ +rr9<#;Z:G;YlFaC=`nj[YkA%qgAlis[K4b8rS%>>JcE(V-i3H'lf[ +aN;TKcHt"if\>9Bjlu1'r.4m!s+13Hs8LaRK)bQ!"I]>VRSA;~> +k5RaAn*&]_dE9A=[&g*pQB$`NG]@G,>?"9b4uY/E,97U9>Q=_<-7q&96:k!T?=n%sI"R?'R\?[a +\%fr.e_Tir?W"?upBaSH&VV;Km1[`W,sM3WZmf0P???'5,jSu\uB5(bZs8U8bA9D@> +ir&fZqrZJ*#lXc(Ai]j+!3uM&!qU=1rr3)pet`TMru/LY?uo^uc2ZFE??'5,jSu\uB5(bZs8TB$ +;Z:G;Z2ajC>'"m[Z1\.rec9d_XT?T$rRLu6JcE(V-i3H'lf[aN;TK +cHt"if\>9Bjlu1'r.4m!s+13Hs8LRMF8tsb"I&oLPY-H~> +ZMspkh>[S,5C^H@rtkCs&@M,tQm)Lt&@M,tQm)M$%))DS#kRom"Rrg9.=_?s,+_uDlC_Ifqi"q) +m^$]1oQs]`nEu5]s7[\0oBL[+s8P6DrosRZs82f\)qn&Ss8P5cZKe)hYk`cZg>6Fiq>6+YamQKN +XSi)$n?n'M!<)ou4co[.!;HHl!gGtNrr3)[Lf+6Pru:TW=Ng3I=H`]U>-dFd?Ej6TAD5mPIJNo7 +B"e3/#l"B! +kPmmJp@%2&f[7gX]!JH5SX#4iIs5jF@9HK$6Td1W-6F';0(/bG*$Zpf3']u/I3Ug +@']Zg@EIuECT[2S=j-@(N$eT!,,"PnHX$OM5`b0qlh1;Rrp(NV9UPk[5$PAYTjY2%J7[EiB70IN +N+'isZYQ]@rrhi!M,=9OrrkZKl0I[.rr3*!PX5BLrr_5:;#pUq-hDXebON8KP5h+XZg+5lZMB#^ +g"g:gp]$k!]]nbJp&G%nH0b!bRe?^[gAlis[K4b8rS%>>JcE%U&cMIpmcruqi8*/?f$r0rd*L&; +c4J=KdF-Opf@o$;ioTCjnFup5s+13$s.o\ogAup+p=f_=!:-(JJ,~> +kPmmJp@%2&f[7gX]!JH5SX#4iIs5jF@9HK$6Td1W-6F';0(/bG*$Zpf3']u/( +YID6cc.1V1lgsEA"+NOb^\.UCp?iL"oBqths6^H+oBqths76Z4pVdF0qYp]f[;@@Brr416>-dFd +?Ej(TFjll(#p\oaDJcC<$V#UGF!H\;7ec_3;kEJSh~> +ZMspkh>dM)!l'6"pAYs&mL[)/pRE6'mL[)/pRE6'o7-Z;dL>aDs"d&?1G;SD*Z9rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjUK`Cc&"IoJ\S5+S~> +kPmmJp@%2&f[7gX]!AB4S<](gIWo^D?s$8u69?tS,TRUJhZ(kD)'LC^2a9c,1 +f\5*9hr3YYl0Rm4rIP!"s+13Es8LaRK)bQ!"I]>VRSA;~> +kPmmJp@%2&f[7gX]!AB4S<](gIWo^D?s$8u69?tS,TRUJhZ(kD)'LC^2a9c,' +YI;0ac.(P0lgsEAs*k*bJ%t^W(\[8.^&.Po*<5F<^&.Po*<5V[qu>(QoD8J>oZa7.J,fQEI<"WR +fOg-9Fa*T[ibaPCWjDU&s0WR@lN$DRs2GcQrto[(J#N,>ru6l/p]'gjEU<_XArZVs^&@/5J,/b[ +GPD-sD2J2j"SBsDS,iNf"W20-J#N,@rr]r,U]CDo"7!.]!<3!GnuB%ns4V"?q0d8Ls4CqCq743; +k\ktHs,06gIt+Eks7[)(LP),Np&G$B!-@b6!Qs9tec=uds02M5r:][KlKI?hi838Cg"=pTf)O>1 +f\5*9hr3YYl0Rm4rIP!"s+13Es8LRMF8tsb"I&oLPY-H~> +ZMspkh>dM)"96,c^\.U9DaJep22hM7DaJep22hG5!e4*Wp\uf4Dh%fen8WmTs6=HPq1W.um^59/ +rUfg\g:^[:s77)'oBLf*s7[LKIr@cKrVm;S3S+61iW+E*s6=HPrr3DABj.b@q1W.um^59/q>UKP +!<3!-pDdsuDag?!s4U5Dh>mKR!T!hUrt#)#pA +kPmmJp@%2&f[7gW]!AB4S<](gIWfXB?s$8u69?tS,TSAhs3r#$)'C=\2a0]+?(_*7-ars8V*ZI/O$/CkD]erk2uKYQ"?O +?d.uH5_/\1!S[VRrseWBD1-CpUAt8I:86JCr;QfP!<3!GrUfg\fnsOXnSrsSs64?Os8W)3BNhV= +qM@X`I;qfPs5sB#H#lWSp&G$G!.sgN!RTm*gApVls/uA1qt9OKm-^m +mI9iJJcC<$JcDDCrn@I*s7Y1MRK2ZB9n3~> +kPmmJp@%2&f[7gW]!AB4S<](gIWfXB?s$8u69?tS,TSAhs3r#$)'C=\2a0]+'p;@(o6cgIfTNC*,C%B7A0VZ%+7/es8V'YIJs32D1V]d%)6L[YlFNQ?H_cE +5C`M/!T!hUrse]BCO^7kT`>&G:8H_Hr;QfS!<3!/rUfg\g5B^Zn8WmTs6=HPrr3\IBj.b@qh[[_ +Ir@cKs5j<"GB6BPp&G$B!-@b6!Qs9tec=uds/uA1qt9OKm-^mmI9iJ +JcC<$JcDDCrmh*ks7Y"HPQ9m28q6~> +ZMspkh>dM)"96,c^\.U9C-?of0oQ)3C-?of0oQ#1#_,`Uqu=ojIuF:*/FIG+s8VY2IJs32D1V`) +C1)1!1P>`-jJI9FKBE46LJDo7?3pT*R*u$&IkCX!!FpKUrs.ZdIJs32D1V]d%)6LWWr;t8C1)1! +1P>N'!T!hUrsndjdf8`b/cYEOF8+Agqu-Nqh>mKR%HOC5HaWG8F++#el>;+OrtN4^mYEUjs8LjTK_)kXf&lqf!:GFKn!m.'~> +k5Ra@n)rW]dE08:Z`BmlQ&UNIGAh/'=].jZ4>\W:0\QK[+V,8++t56+5=\IK?"@bmH\.-&R@pL^ +\%]l,e_K6Oo]lJVIfo_cJ%taX&,uNC5'_9+MuW`k5'_9+MuN__rI7aMqu=rkIuF:*/F@;(s8VY1 +I/O$0D1_f*Ch%[)15#W,jJR?GJ`Zq3LJ`2>>mUK'RF;'&JM-s%!rZPQrr3er!.=_El>;.Qs8K`H +=0)<"_e`h,CGLP>JcDhO$N9elnaGl2lKIEor8n=KkiqC! +mdTlErIP!"s+13As8LaRK)bQ!"I]>VRSA;~> +k5Ra@n)rW]dE08:Z`BmlQ&UNIGAh/'=].jZ4>\W:0\QK[+V,8++t56+5=\IK?"@bmH\.-#R@pL^ +\%]l,e_K6Oo]lJVIfo_cJ%t[V%;J%QG;5$Us(X]DG;5$Srs4*P>5S?EIt*CTs"*SVJ,fQ>F++#e +l>;+P_eNS%C,:MBs5^&(F+`WTs+a?jG?tRTpR'D/mXHherrHVVrr35c!.FhGl>;+OrsZYVlcp&G$B!-@b6!Qs9tec=uds/c5-rV?'Tn*TH+ki_.,jpC57l0@X'nFZPT +JcC<$JcD>Armh*ks7Y"HPQ9m28q6~> +ZMspkh>dM)!rmb"o`#@;HhZu43WK*rHhZu43W/m^V$QhqS,`O+rVmi)Dh%fepNLu]s5n*Ls6bsl +$r0EMs8U2A6#5rgrrq7G!#UA#qu6]:#lXc(+hdmT#O_Y>qu>eoKDkfXdT1kps6bsl$r0EMq>UKP +!;ZX$`X)V-&B=Iq!;HHl!T!hSrsd(N6*9n]pNLu]s5n*LrVluJltps+13$s+13$s0)J%h>r<0p>,qB!:QFQJ,~> +k5RaAn*&]_dE9>#S*`4Z4rRfDj_\',;E-,qLi55tFgR?=n%rI"R?*R\?[a +\%fr.e_TqQmueq-:J$?As2nD0D]VpL&M#ZdTM.us6l*r +&5PlQq>UKM!;ZX%`sD_.'Z9Ok!;$*errLjSr;R5K=$j"]s7LM[q>]VpL&M#RdTM.uqu6uC#liob +n,NF2#ke5sgAlis[K4b8rS%>>JcD_L#6"AkoC;>=rpC*[mdBQ5nacGNrdk*#s+13>s8LaRK)bQ! +"I]>VRSA;~> +k5RaAn*&]_dE9>#S*`4Z4rRfDj_\',;E-,qLi55tFgR?=n%rI"R?'R\?[a +\%fr.e_Teo +KE(A$+Uh+mBO$-!8Nk7GZ-ec>ICrrLsVr;R5J<^Ek^s7^_aqu>eoKDkfPdT1kpqu6uB#lj&fn,NF5 +#ke5sec9d_XT?T$rRLu6JcD_L#6"AkoC;>=rpC*[mdBQ5nacGNrdk*#s+13>s8LRMF8tsb"I&oL +PY-H~> +ZMspjh>dG'p&>Zrk5b8Vs4.2Mk5b8Vs4.2Mmn3TZ!?$rr3#=&H;\1XWdWB((b9@s35/CpRJ&Zs*o+]s6f1Ls35JTq>UKP!<3!! +cCFn6"3(oP^&%d0N;ihXh>mNS"0kU_oDS\$LMPoLc"<@;S>?2bIrk5D[E\^N$-!PVrs7ffs2GWC +s8LjTK_)kXf&lqf!:GFKn!m.'~> +k5RaBnEJocd`]P@[&p3sQ]R#SH#d\2>ZFNh5WV0^s5Z0Z)]^%H.PNbD6q^E\?tXD$I>*W/S"cme +\A6/2f&#NTpZheWJ+`gXn`'WDs8UONs5O+Rs8UONs6E8Rs8)fVq#:E_D1DQb'8^M`s3>>Hs6o:N +s3>VYs0>I^nbiCmhtR0NSji\*T)S`j`t\OQ!NmRCrtZ;aiW%2;RJ!#CrV_.anc/(Dqu=r]UA=fi +gAq6Q!RIA:rr^:Us1\@1!07'Y!S[VQrr]Q'O7E2K&W(;^s3>>HopVWRrI&\UkI/\8rs?^[s8NMj +s8U+Np&G$G!.sgN!RTm*gApVls/,hrrX/W%qtg*`qYU3hrdk*#s+13:s8LaRK)bQ!"I]>VRSA;~> +k5RaBnEJocd`]P@[&p3sQ]R#SH#d\2>ZFNh5WV0^s5Z0Z)]^%H.PNbD6q^E\?tXD$I>*W,S"cme +\A6/2f&#NTpZheWJ+`gSoB-,Js8UXMs5sCVs8UXMs6iSZrrE,_q#:E`Dh%cd'8LA_s35/Cs6f1L +s35JTs05=\oDJUohY7'MS3m8$SGrNh`Xr.L!NdOCrtZ/]ir@88R.m,Hs8RRioDe7Dqu=oZT_\Tg +h>mQT!R@57rr^7Ps1S:0!0$pW!T!hTrr]N$ORrGN&Vk/]s35/CpRJ&Zs*o+]k-`J5rs?[Vs8NAf +s8U(Mp&G$B!-@b6!Qs9tec=uds/,hrrX/W%qtg*`qYU3hrdk*#s+13:s8LRMF8tsb"I&oLPY-H~> +ZMspjh>[P+!.XS>'GtoShGQ]?rtoh?hGQ]?rt(F`N#;+G&;?HN5;:9!)kB%lNc +A'Y&(s8NNgA+.\[[K"eXPjd0`F8l1?;KMd9)KT)Pmtb;ls+aLg]i'dgs-ui`hK*;_s38[_rVloT +!<3!&ec:s'ei@gLrrLsVr;QfS!<3!#l>'nTrmi4Ws1)='mtb;ls+aLg]i'dgl>'#.n('L?s8QS$ +hVL8.p&G$J!/0sW!S6E3h>ltps+13$s+13$s0)J%h>r<0p>,qB!:QFQJ,~> +k5RaCn`o,ge',bD[]ZO$R$!8YHZX+:?W^/t6qQ#1kXHjC,:"We0/YgW7o!&g@VTn-Itiu5S><3k +]"uJ7fAG`Yq)#O+.!psiSrr3VB>FOr7?crD> +:oN,iB@uH_A'b,*s8NNjAa@MZZN&MZQLEEbFoMCA;/uO6)Kf8Qm>#&krIn+a]MXOas.)ubgN.#[ +s3/LYrVloQ!<3!&f*%E.f/RpNrrLjSr;QfP!<3!#l"OYRrmr:Ys12F'm>#&krIn+a]MXOalYTA5 +n(0OAs8QY)hqL,)p&G$G!.sgN!RTm*gApVls+13$s+13$s0)J%gAup+p=f_=!:-(JJ,~> +k5RaCn`o,ge',bD[]ZO$R$!8YHZX+:?W^/t6qQ#1kXHjC,:"We0/YgW7o!&g@VTn-Itiu3S><3k +]"uJ7fAG`Yq +qu-Nqh>mQT#1`d=n('L?r;QfS!;uith>mQT"6sZ3cMlB;lMnCuSF;8?ao@,o_7`a)s6=Al_=,r8 +F8u8M9&ADbC\@`,ec9d_XT?T$rRLu6JcC<$JcC<$JcDqRrmh*ks7Y"HPQ9m28q6~> +ZMspjh>dM)nc'0[*IIrFcRrrLsVrVm#. +.0'p+qu6]R!;uith>mQT!R4C;!#L.bqlM^]4h:Umg1q66GN/Z&g2@Z*&:=EV"nu1+#]'2-s8LjT +K_)kXf&lqf!:GFKn!m.'~> +k5RaDo'>Ale]l+K\$2j+RZi\aIWoaF@Tui-9@j.@MeQj:AnYjn6TRJ$92\l!AScF6JVfJ>Su&Qr +]>D_UJP])VfaCCh8,j8]/3D\`- +C%q<#rn@F)oVRSA;~> +k5RaDo'>Ale]l+K\$2j+RZi\aIWoaF@Tui-9@j.@MeQj:AnYjn6TRJ$92\l!AScF6JVfJ;Su&Qr +]>D_mKR!T!hUrrLA>qu@O_s8/oU#X,`us4V6"'R/R:s4VB*!"aMVrrr.##RG5? +p&G$B!-@b6!Qs9tec=uds+13$s+13$s0)J%ecBjlp=9A2!9]S=J,~> +ZMspjJcCH(rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjUK`Cc&"IoJ\S5+S~> +k5Q=so^(\rf$DFR\[&93SX,=mJU2BRAm\\>_Ad +rIP!3s8LaQK(HDOd,Y)Z!:>@;JcC<$JcC<$YQ+UV!J:@FgB +k5Q=so^(\rf$DFR\[&93SX,=mJU2BRAm\\>_Ad +rIP!3s8LRLF7ZL7b23*J!:#.8JcC<$JcC<$YQ+UQ!H\;7ec_3;kEJSh~> +ZMspiJcCH(rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjUK`Cc&"IoJ\S5+S~> +k5QM%p?q,%f[7jZ]X4f@D/]F*`CmNg5>JcC<$JcC<$JcDqRrn@I*s7Y1MRK2ZB9n3~> +k5QM%p?q,%f[7jZ]X4f@D/]F*`CmNg53kWj0(K +`m3,ij6Q;fs,m?\ec9d_XT?T$rRLu6JcC<$JcC<$JcDqRrmh*ks7Y"HPQ9m28q6~> +ZMspiJcCH(rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjUK`Cc&"IoJ\S5+S~> +jo7R>nEJree',eF\$2j+S<]+iJU;KVBOY7L;bp%W770C.6UXC89i4no?tF+nGCG4'P*h'&XgG^W +ajAYrk3_qps,m?\gAlis[K4b8rS%>>JcC<$JcC<$JcDqRrn@I*s7Y1MRK2ZB9n3~> +jo7R>nEJree',eF\$2j+S<]+iJU;KVBOY7L;bp%W770C.6UXC89i4no?tF+nGCG4'P*gs#XgG^W +ajAYrk3_qps,m?\ec9d_XT?T$rRLu6JcC<$JcC<$JcDqRrmh*ks7Y"HPQ9m28q6~> +ZMsphJcCH(rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjUK`Cc&"IoJ\S5+S~> +jo6D!o'>Amf$DFR]!JK8T9te"Kn"AhD.mEc=]J?q9hYIX*DB4)<**=4ASZ7.I"I08Q(3c3Yd_?c +bgY;)kjSG#s,m?\gAlis[K4b8rS%>>JcC<$JcC<$JcDqRrn@I*s7Y1MRK2ZB9n3~> +jo6D!o'>Amf$DFR]!JK8T9te"Kn"AhD.mEc=]J?q9hYIX*DB4)<**=4ASZ7.I"I08Q(3Z0Yd_?c +bgY;)kjSG#s,m?\ec9d_XT?T$rRLu6JcC<$JcC<$JcDqRrmh*ks7Y"HPQ9m28q6~> +ZMsphJcCH(rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjUK`Cc&"IoJ\S5+S~> +jo6D#p$V#$g!\'^]sb/EUR[X3MM-G'F)G]&?sHi8<)`co*)fj=>$bZMCN+ECJVT5KR@oSBZb!uo +cdgh2lLKN+OT5=7!.sgN!RTm*gApVls+13$s+13$s0)J%gAup+p=f_=!:-(JJ,~> +jo6D#p$V#$g!\'^]sb/EUR[X3MM-G'F)G]&?sHi8<)`co*)fj=>$bZMCN+ECJVT5KR@oJ?Zb!uo +cdgh2lLKN+OT5=2!-@b6!Qs9tec=uds+13$s+13$s0)J%ecBjlp=9A2!9]S=J,~> +ZMsphJcCH(rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjUK`Cc&"IoJ\S5+S~> +jSqC$5!3>?kNEASH"$G'eaoN00HuUlqsk^;S4C +g#(rZpOW@+s8LaQK(HDOd,Y)Z!:>@;JcC<$JcC<$YQ+UV!J:@FgB +jSqC$5!3>?kNEASH"$G'eaoN00HuUlM[g^;S4C +g#(rZpOW@+s8LRLF7ZL7b23*J!:#.8JcC<$JcC<$YQ+UQ!H\;7ec_3;kEJSh~> +ZMspgJcCH(rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjUK`Cc&"IoJ\S5+S~> +jSp7uo^1euf[@s]^::GKV4X0>Nei=:H?F4EC1h*f@fBdU@Us(bCi=B=I"@$1Od;N3W0Oa%_8spP +guIYgrIP!1s8LaQK(HDOd,Y)Z!:>@;JcC<$JcC<$YQ+UV!J:@FgB +jSp7uo^1euf[@s]^::GKV4X0>Nei=:H?F4EC1h*f@fBdU@Us(bCi=B=I"@$1Od;N3W04O"_8spP +guIYgrIP!1s8LRLF7ZL7b23*J!:#.8JcC<$JcC<$YQ+UQ!H\;7ec_3;kEJSh~> +ZMspfJcCH(rn[X.o=Y1Uk5F-:maD+:IrFcErrVWF!;QQp!8`;,JcC<$JcC<$!<<&T!JLLHh?9>K +n!m.'~> +j8U+nnEJuge^)=R]XG#CUn4!I!BaQDf'9)rb3>df0AuD=-im!q"_Bq#:?pgO]BUs+13$s+11Ms8LaRK)bQ!"I]>V +RSA;~> +j8U+nnEJuge^)=R]XG#CUn4!I!BaQDf'9)rb3 +ZMspfJcCH(rn[X.o=Y1Uk5F-:maD+:IrFcErrVWF!;QQp!8blu"kH!?#ZC,Bs+13$s+13`s8LjU +K`Cc&"IoJ\S5+S~> +j8U(ro^1f!g!e3c^q-nUWM?&QP`L`WJq&/nG'%d'E>rq>Fa&.[JV8l>P*VW3VQ@%n]u/">f%oBO +nG\"=O8o46!.sgN!RTm*gApWhrrR[emI:,WmFqX9rrE,Sb5VXs5m.Hnl[f(es+13$s1SI3gAup+ +p=f_=!:-(JJ,~> +j8U(ro^1f!g!e3c^q-nUWM?&QP`L`WJq&/nG'%d'E>rq>Fa&.[JV8l>P*VW3VQ@%j]u/">f%oBO +nG\"=O8o41!-@b6!Qs9tec>!`rrR[gmdU5XmbRs=rrE,Vb5VXr63%9hmt(Lis+13$s1SI3ecBjl +p=9A2!9]S=J,~> +ZMspeJcCH(rn[X.o=Y1Uk5F-:maD+:IrFcErrVWF!;QQp!8blu"W1G+hF^Cks+13$s+13`s8LjU +K`Cc&"IoJ\S5+S~> +ir;%5n`o2kf?qd\^U^_SWMH/TQ'%&_L4b#*I!U'`G^4U^I=Hg$Ll7=XR%0kIX0K+,_T:$Pg>V5_ +q18R+s8LaQK(HDOd,Y)Z!:A57!e5(SnG`RWgAq$K! +ir;%5n`o2kf?qd\^U^_SWMH/TQ'%&_L4b#*I!U'`G^4U^I=Hg$Ll7=XR%0kIX0K+(_T:$Pg>V5_ +q18R+s8LRLF7ZL7b23*J!:!e5.VnG`RXh>m?N! +ZMspeJcCH(rn[X.o=Y1Uk5F-Um_)$p+cu-lpRj)D&8V%?rrB/EN;rW^&-u2&qu8^R+TN@Ls8Tc$ +#U,;ts*mdQ(_gW6s8/oY$n\.Hs1qG='OI"(s8REW!(,QBrrAQ<#U->Ms.pk=(l\Io('(Hk(_C?2 +s2@kE(i*$!s8/oY$n\.Hr;QidDh%Q^#1`eCs8U@EJcC<$JcC<$^&S)g!JLLHh?9>Kn!m.'~> +ir;":p$V&'gt'ip`4idhY,S7jS=#P&NJ`LFK7\Z)J:W9(KSG>AO-5ftSt`-aZ*q +ir;":p$V&'gt'ip`4idhY,S7jS=#P&NJ`LFK7\Z)J:W9(KSG>AO-5ftSt`-aZ*q<=a3E)dhrX1q +JcC`0rmh'jo;r&9hYl"ElFfUl+cu-lpRj)D&8V%?rrB/EN;rW^&-u2&qu8^R+TN@Ls8Tc$#U,;t +s*mdQ(_gW6s8/oY$n\.Hs1qG='OI"(s8REW!(,QBrrAQ<#U->Ms.pk=(l\Io('(Hk(_C?2s2@kE +(i*$!s8/oY$n\.Hr;QidDh%Q^#1`eCs8U@EJcC<$JcC<$^&S)_!H\;7ec_3;kEJSh~> +ZMspdJcCH(rn[X.o=Y1Uk5F-UYuZA6Ue.*VW(r`ChD'*Orr=d]n,HQhkMAg'r;Tdc!3Q"'!:RI! +J)[7qSH"'YORp:\L]=GVSDoc*Uqof6l^J,cTNSDoc*V#:5oqoR+XrrMlNrr3"h.=qMMs+13$s1SI3h>r<0p>,qB!:QFQJ,~> +iVshmoBbVtg==Nk`4idhYH+OpSsu(2OcPK[MM_=g)2X60Nf]HjR@KqGW3*A7]YUVldb*C9lL=\% +s,I'XgAlis[K4b8rS&9o=+T>)8=TXX9pXho0QmEq!&V.Ms'8XbhapE;s&JLjY4K(%n$lGMiM&l4 +s*l#Jn%_SOs/oLpiKQp0_G=gmenlB,s+2fRlWH4Irr>%=qh>[^mf7M%\cD'rrr>@BmCYoDaB*62 +gN+!Ws/oLpiKQp0r;Qio`m"5X!Vb@JrrJQ>JcC<$JcC<$^&S)d!J:@FgB +iVshmoBbVtg==Nk`4idhYH+OpSsu(2OcPK[MM_=g)2X60Nf]HjR@KqGW3*A7]YUMidb*C9lL=\% +s,I'Xec9d_XT?T$rRMpi+%Ss/]7kifm'2_+nUjenQ$%s+DiPmohaOrr>(Bs+Cs`mf7G']`@O$rr>CBm_)&Ea]<01 +hK'6Xs/]7kifm'2r;Qiqaj'V\!VbIMrrJW=JcC<$JcC<$^&S)_!H\;7ec_3;kEJSh~> +ZMspcJcCH(rn[X.o=Y1Uk5F-VIr!d/]KHZ/?Ps0W^DrrBJ,s7ZN^s8UpUn,<8emf93Ys7cPT +CYJg[_9N"0?Jb_MDh%Y2GPD.*Dg-h?df9?uXT8AE[K$9iSH&Th^&S,h!<<)Mp]&eEhZ*TU^&S-# +Dh!j]f)Pd$ +i;XYgna#;og=4Hj`4rmkZ)t"%Tq7aAQB[SqrK%kYP*;/tR[p+HVlR#-\@oc"YL1tXiooh+JcC]/ +rn@F)o&TGH#;sKs0`pIrrBM-s7ZZ^s8UsVn,<8emf]BZs7?8OCYJg[^s)h. +?f(hMD1DG2GP2")Dg-nDdf9?sWW<&A[f?BjRfEBf^An5f!<;rJp]&\Bg].9R^An6#D1@[^f)Pd$ +=+C*qGP2")Dg1sDrr__8(s@L1JcC<$JcEF`rn@I*s7Y1MRK2ZB9n3~> +i;XYgna#;og=4Hj`4rmkZ)t"%Tq7aAQB[SqrK%kYP*;/tR[p+HVlR#-\@oc"X3oPTiooh+JcC]/ +rmh'jo;r&9hYl"FHY_@8o>/]KHZ/?Ps0W^DrrBJ,s7ZN^s8UpUn,<8emf93Ys7cPTCYJg[_9N"0 +?Jb_MDh%Y2GPD.*Dg-h?df9?uXT8AE[K$9iSH&Th^&S,h!<<)Mp]&eEhZ*TU^&S-#Dh!j]f)Pd$ + +ZMspbJcCH(rn[X.o=Y1Uk5F-aM!8dbUhYR9Qf)5OR]h/hr[Jt_llMUYf +IrFcTmVdUCDu9V7CC'5*s6A/8rrCXIru_ +i;XYnp@.A.hq?N*b/M0/\$N9>W265]St)7;rKnChR[]k?TqnTdY-GCG^;J"8[+F*mkO%tos,6pV +gAlis[K4b8rS%BjC[:s!%e"=arVuoL!<<'!g].<2qZ$WJr;R&:9b9:is*nhMr;R%PD=.AjD1D"m +quH_3#j_Njldl.:!8%,J+9:0rs4[PRs/In0<<1eRrrCgRs6XZQIr"BMs6FHMmr/+(!Gh#irr\Q( +D +i;XYnp@.A.hq?N*b/M0/\$N9>W265]St)7;rKnChR[]k?TqnTdY-GCG^;J"8Yh.[ikO%tos,6pV +ec9d_XT?T$rRV!a!:'O_!q'uVrr38T!<<'!hZ*W4qZ$WIr;R&98e="js*nhLr;R%PDsmYnDh%1n +quH_3#jVHilIGt8!7q&I+9:9us5!bUs/e"1;?5SRrrCpUs6afTIr"?Ls6=BLmVi"'!Gguhrr\K& +C[ZiSJcC<$JcEC_rmh*ks7Y"HPQ9m28q6~> +ZMspbJcCH(rn[X.o=Y1Uk5F-UHY2"3o;T.pHZ/?Ps0W^DrrCpUs7ZNfs8Vi=r;Tdk`rH(H!:PPP +gAgcD[Jt_pmf2]nJ,=S>qu?QQk)3^"s8UZP!<3nDs8V7ts8N)Us8UpUs6fs5s8N)Us8N)Us8VM* +Iu@des8UYNJ,=S>qu?QQk3r +hu=Mkp$_2,hqHW-bJqE5]!erKXK&1pUS=I[TG=/qUo()kY-G@D]YMJ-c-j,Wiof_$JcCW-rn@F) +or;Tdka8c1F!:PPQgAgcE[f:homJlQj +J,+A8q>^?PkDa!$s8UTK!<3nEs8V8!s8N)Rs8UgRs6g*6s8N)Rs8N)Rs8VJ'Iu@gfs8UYOJ,+A8 +q>^?PkO8EPAN]"(s+13$s+13]s8LaRK)bQ!"I]>VRSA;~> +hu=Mkp$_2,hqHW-bJqE5]!erKXK&1pUS=I[TG=/qUo()kY-G@D]YMJ-c-iuSiof_$JcCW-rmh'j +o;r&9hYl"EG@oS/o;T.pHZ/?Ps0W^DrrCpUs7ZNfs8Vi=r;Tdk`rH(H!:PPPgAgcD[Jt_pmf2]n +J,=S>qu?QQk)3^"s8UZP!<3nDs8V7ts8N)Us8UpUs6fs5s8N)Us8N)Us8VM*Iu@des8UYNJ,=S> +qu?QQk3r +ZMspabPr%&63mipkPtS563mipkPY>\`W5`4s+gUR*W)rr$phH(s7`0<$phH(s6>Od":.oos8VOc +#S;(Vrr@EE!!76ASGN:&hZ*>P2%2d]s8V)&'GPWFs8Vhd/IXqUp\t4)rVlkEr;Zh=rr3@Z&-u2. +s8VOc#S;(VrtY:$/IXqUs8VAC,mZ)moDdfo'GPWFqu6X-rVm#p@gEWeo`+pI!/0sW!S6E3h@cbb +KAr[uSH#BWR-3<\F8u7?hZ!O_;Hi8H=ulW_SG3'X#efu7!;Em!KAr[uSH"(Wmf2]nJ,cHJT]_t> +SA@s4dai=[!<7*pf'cd&s8N)Us8UpUs6fs5s+CC's8N)Us8VM*J&+luhVNGjJ,cHJT]_t>SG`Bg +qoR+XrrUaO`r4J>s+13$s+13`s8LjUK`Cc&"IoJ\S5+S~> +hZ"Dip$_2-i7li1c,mo?]t1YZZ*1:1WMlcpV\Z51WiWA-ZF.3S^Ve+8cd^U\iof_#rm:ZSf0orR +mMuqlDU_6G!3soq3oG5dpXkl\m"l#Z^3js6EYj&97UIs*=PC +!d4O7qZ%Jfs7`TO$pqQ*s5S;=(3A@4s7N$;$p_9"q>UKm'`J%3H2R^CFoMCdJeSJjoDejV<)eT(E!BG8:s8hYmHUrVI<-rri()#S;.Ns8LaQK(HDOd,Y)Z&u\]\iM&l4 +s/>j4c81s8N)Rrr6A+=l\[N4TGGMoDeCf^@S?-o<_DJiM&l4s*nnQs6XZQs/K.lje>G>_G=gm +eo)Z2s)g$IlWH4IrrCgRs4[PRlN#9.L&f^&rrCgRs6XZQ_GP7+h00Q_s/K.lje>G>r;Qio`m"5X +!n..irRZW#JcC<$JcEF`rn@I*s7Y1MRK2ZB9n3~> +hZ"Dip$_2-i7li1c,mo?]t1YZZ*1:1WMlcpV\Z51WiWA-ZF.3S^Ve+8cd^UXiof_#rm:ZSej9WJ +;Wn)[ej9WJ;WmuX!QG-5s8RlRruM!=2%2d]s8Vhd/IXqUs8VAC+Tr'Kn,NFV;@!hTrr2tFr;Zn? +s.&rd(Z,2dXYgMQ\c;]hG8(a4hZ*WMTIgR<\bH+(&H2V/Hi3pEF8l1IKFeDep](9^;@!hTrr3`- +TIgR<\c;]pN$S`]SFcd?G8(a4hY[p'S$.] +ZMsp`hZ!S*q#CDEq#:dHF6DC`6N@'/F6DC`6N-ock843prri)AHR41@s!"jGLXpZQaoA#+SDJm) +`W(iVT]4g#9)noX;WlUcN;roUF8j_ln,N.\ef&m%s8Qk0LXpZQao@8o\\1ans8S`aSDJm)`Vf`= +pWfm5!<)ou;KMig0CSr@N%roH6>QW$6;m!t0oX*OTLta2K3lOsO@lY7S5,aWMb^gr:5AlLpWe+X +!<)p$CGF\k+dD["rn[X.o=Y1Uk5F-Lm^51`+cu-lpRj)D#]'27rrCpTs'Y;!#RF&cs8N)Us8ST+ +!$SKEs0Xuq+cu-lIrFcTmVdUTpS9ML.@B`,_+kCUCS_%2s(`/G6JhbB!8dbUh>mTUHO(1A64!Vk +!8dbUmVdUT`DR*]CJoZ^pS9ML.@B`)rrVV,J,'$DSGN;;s+13$s+13`s8LjUK`Cc&"IoJ\S5+S~> +h>\5fp$_2-iS<&6ccaAI_7mOk[^t-&8cSfZ +,MpWe+W!<)p$D)C"n+I;^# +rn@F)oVRSA;~> +h>\5fp$_2-iS<&6ccaAI_7mOk[^jGT`9pF\\1ans8Vi=HV+;:rrm0mhTd:/p&G$B! +-@b6!Qs9tee78U4U<3mmf3$S/I2Vtp](6nhZ!O_mofu&9'?6S!8dbUS/;5LXoJF/4U<3mmf.cTm +f2]nJ,f8=0aK4qoDcX%$lEcV!<<(7&-,&Rs8N)Us8UpUs8REO&<&p]s8N)Us8VM*J,dJc$lEbXJ +,f8=0aK4qoDJUimVdUMrrASd!.k0$s+13$s1SI3ecBjlp=9A2!9]S=J,~> +ZMsp_h>dM)rVunIq>Uunh>mm5s8V9%s6fsVs8U(fPkTBGp]'5sPjWI:qu>qsJ+I`.qu;.eoDe*G?lu5rs.8slMpn +h#A)ep$h;1iniD?da$%W`PTF)]XkY`rO;g:'"PWf^VRk.aj&2^f\GEHaR8d$i;`h,rVunIq>Uuo +hZ3ENs3guJhZ3ENs3guIrr`-aD#jD3"R2F`/:[[!,DTpgs2no@pNLcQs5A0el"P;5s8UCJs6fsR +s8Ttfs8OXQqt^72gE>h^rt&i"s2no@o5f9UrI&\UpNLcQs5A0er;QoaS7Ph'rVlpMY5A.s/0Mk1 +s6%H&mf;\Rs2-CQpNLcQs5A0emr*RMs646Io5f9UrI&\Us66%\=TJF#"VAEJs->M7s8LaQK(HDO +d,Y)Z!:?9U#M?oXs8UeeNIh+\s+13$s.02hgAup+p=f_=!:-(JJ,~> +h#A)ep$h;1iniD?da$%W`PTF)]XkY`rO;g:'"PWf^VRk.aj&2^f\GEH_s[6ti;`h,rVunIq>Uun +h>mm5s8V9% +s6fsVs8U(fPkTBGp]'5sPjWI:qu>qsJ+I`.qu;.eoDe*G?!)rs.8slMpn +ZMsp_h#ID(s8W+KpAYRFK_OqF3WK+&K_OqF3WB$\HW&]#p\u&tDh%feo5f9Us5n*Lo5f-Ms5IO< +qs*VKrt1bMs5sCNs8U(=s8N?Z!!#pcr;R-bXoJFa!.4VCk%fVJrsR7]MuWBiGPD-s@t4=S!T!hT +s!A"+!!#pcs6frH&-u2&s5sCNs8U(=J+I`*p]'5_J,bU.^%83uSGiHhdU%k1rrVWF!<3!&k5b8V +s3:oCs8LjTK_)kXf&lqf!:H?V#JF#blKX""\Ujd3s+13$s.02hh>r<0p>,qB!:QFQJ,~> +g]%rdp[[_9jP\hHe^Ddfb/_K=_SO%c^Ce8t_o0R9bKeJafA#0AkNgCgh>dM)s8W+Kq#:m'q]LL^ +mpS?Yq]LL^mpS?Xrr[ODgAq!J([Z'hs8V\3H27L%CPDA$GPD-tAV'aCmJd+tROnL%!:Tsf`ruGB +#W)Ma]PlL&V)\rQno.rUY)Sp]'8bJbf +g]%rdp[[_9jP\hHe^Ddfb/_K=_SO%c^Ce8t_o0R9bKeJafA#0AkNg4bh>dM)s8W+KpAYRFK_OqF +3WK+&K_OqF3WB$\HW&]#p\u&tDh%feo5f9Us5n*Lo5f-Ms5IO +ZMsp]g].5%p&>$*rW!',hZ*VhrW!30hZ*V`+nsgnp\trqDh%femVdUTs6afTs-Q6JhM3)#rr3], +[i'*s6afTrr3CV!*7\Ns-Q6JhM3)#q>UKP!<3!G +mkL[=hDnL6h>gI1hEkEGs"R^?ei<_[s-Q6JhM3)#s5%&^!!IB2rr3(M!*7\LrrVWF!<3!!h>mQT +!T!hLs8LjTK_)kXf&lqf!:HKn!m.'~> +g&EGroC)#.j5A_Gf$i$mc-+8Na2c3>a2c9Cb08/Xdad")hVmS\mIJoas8@H?rrBY0!!ES^s8T\0 +!!ikbs8TMQo@j9>rtOj;J,fQ;Dh%fem;7@QQU[_,LE(gCrtP47Go4a)s"IaAf/Nd1lSPL9h`Op; +rrJiRrr3el!.Y%Km;7@Qs8@?GrrLjSrr4V:1oC0-4B;F=!*.q66!=6f0O +g&EGroC)#.j5A_Gf$i$mc-+8Na2c3>a2c9Cb08/Xdad")hVmS\mIJ`\s8@H?rrBb3!!ESas8Te3 +!!ikes8TJMpYGoErtOm>J,fQ:Dh%femVdUTQpm\)LE(gCrtP48HPk'.s"R^?eirrLsVrr4A70r=p,3E#n6!)h\16m6Krmh'jo;r&9hYl"*lC._W +\NpK07CE#Ds+13$s+13@s8LRMF8tsb"I&oLPY-H~> +ZMsp]gAh2&o`#gHHhZu43WK*rHhZu43WJ7R`qS$ap]$``!#Y\7)t%Wos8Vh;Hi*j*CP2ZTOmQT#4DQds8U@M +p&G$J!/0sW!S6E3h>ltps+13$s+13$s0)J%h>r<0p>,qB!:QFQJ,~> +f`)Q^p[[b;kMtLVgXt*,e'ZOgci23%cd:(fe(*((gu%,Qkj7g5rnH0,s*sbB*<#aSIIlc14TG9u +IIlc14TFRY_t2=WoD>*Z!#Y\7)sqKls8Vb7H27L'D2&#YOsCT78Vd?Gb#^oHli-nfhaIYfJe7hZ +!QPKFrrW'?])M^7lMu5+s6"6QrVm8R=b6JMmueq-:J$?;rrLjSq>VYZ#liobn,NF2#ljq^70FT^ +&;U;SOsCT78Vd8&9XsPe9V).^"4C>'qu6TsmFqX?rs%choDej:%/'Z"gAlis[K4b8rS%>>JcC<$ +JcC<$JcDqRrn@I*s7Y1MRK2ZB9n3~> +f`)Q^p[[b;kMtLVgXt*,e'ZOgci23%cd:(fe(*((gu%,Qkj7g5rmog's*s\@)AmhqmohaOrtp+W +mohaOk7GZ-ec>IES,`O+rVmi)Dh%fepNLu]s5n*Ls6bsl$r0EMs8U2A6#5rgrrq7G!#UA#qu6]: +#lXc(+hdmT#O_Y>qu>eoKDkfXdT1kps6bsl$r0EMq>UKP!;ZX=`X)V-!:Tsfc3XIMhEh2ZKFiq= +mZ8S#:.g +ZMsp]c2SCIk5b8Vs4.2Mk5b8Vs4.2M^&%d0N;qu=oZT`;8lORrDM +#i>=Us-uFUcCFn6!QG]MrrK5Fr;RNDCZ>B=Asi=_V#UIEF7]G"lMLV+DkQq+!T!hUrrLE8rr4If +&HDb9kPtS%&HDdep](8`6D4AHlMLV+DkQ]CHi*j.COc)KV#UIEF7]D2!q+nFrVm$4`rH(/1%kSG +h>i6#]`H^HrS@PBJcC<$JcC<$JcDqRrn[[/s7Y:PS,i#J:4N~> +f)H6Xp@7S9kN1^]hV?iUN`D1DQb'8^M`s3>>Hs6o:Ns3>VYs0>I^nbiCmhtR0NSji\*T)S`j`t\OQ!NmRCrtZ;a +iW%2;RJ!#CrV_.anc/(Dqu=r]UA=figAq6Q!RIA:s!l&'s8NMjs8U+Ns8V!Ns8SZZUAs]Jqu=r] +U@E5Mqu>nrJb>J+rV_.anc&OimFqX>rrj+Ss8SZIp&G$G!.sgN!RTm*gApVls+13$s+13$s0)J% +gAup+p=f_=!:-(JJ,~> +f)H6Xp@7S9kN1^]hV?ia5!;HNnec>`` +qZ$VTq>UNaDh%cd'8LA_s35/Cs6f1Ls35JTs05=\oDJUohY7'MS3m8$SGrNh`Xr.L!NdOCrtZ/] +ir@88R.m,Hs8RRioDe7Dqu=oZT_\Tgh>mQT!R@57s!l#"s8NAfs8U(Ms8UsMs8STUT`=HFqu=oZ +T_!5Oqu>qsJ+oJ/s8RRioD\akmbRsBrritRs8STDp&G$B!-@b6!Qs9tec=uds+13$s+13$s0)J% +ecBjlp=9A2!9]S=J,~> +ZMsp]bPr"gF6DCX6N@'&F6DCX6N$iah>m+G&;?HN5;:9!)kB%lNcA'Y&(s8NNg +A+.\[[K"eXPjd0`F8l1?;KMd9)KT)Pmtb;ls+aLg]i'dgs-ui`hK*;_s38[_rVloT!<3!Nec:s' +ei@gOs'n[XhDoBOs#FTXc5 +eGfsSp%%SF(a.iBk,KffDkj[4D!j%*4#Ur"IAN.3-G6#!E!k7ru-#aRdGl; +b5I#k^qLmn(0OAs8QY)hqL,)s8P2mlcU9Rf`.L=W7uB+ +f_p^tXQ,aBT`'RA^qrrmO2oB.Nfp&G$G!.sgN!RTm*gApVls+13$s+13$s0)J% +gAup+p=f_=!:-(JJ,~> +eGfsSp%%Sors^tun'2cXs4.17n('L?rr3!sF8Z%VF'>+&LgJ4' +LJkt%;N(STS5-'?F(X/+c,ok]rrLsVrr4V%!0ls'3HP6#@nPV73G\Zp3FhO'(q&)tS5-'?F(X/+ +Maac-QqF%OLJkt%;N(MR!q+nFrVm%B6MKXlPkG(Uec9d_XT?T$rRLu6JcC<$JcC<$JcDqRrmh*k +s7Y"HPQ9m28q6~> +ZMsp]bPr%&63%9hkPtS563%9hkPY>\h>mmNS"LTZY&:=EV% +/3p2#]'27s5#a9(nCU*(%-u"$sLpUs6>Od">-/8s4V6"'R/R8rrVWF!<)p#pL=I7F7fM3h>i6#] +`H^HrS@PBJcC<$JcC<$JcDqRrn[[/s7Y:PS,i#J:4N~> +df0[Qq=F.FmHa'$kNDd*$0UO:lKms.o_JI_rrCfsrsdRK":S/js8U\>":S/jr;QfP!;HKpm;7@P +rt,(/2@Mj^s8VPI+:qc$s3+47!<<'%h`M#]Rf<>JcC<$JcC<$JcDqRrn@I*s7Y1MRK2ZB +9n3~> +df0[Qq=F.FmHa'$kNDd*$0UO:lKms.o_JI_rrCWnrsdOG!!lKcs8UY:!!lKcr;QfS!;HKpmVdUS +rt,(02$c@Us8VMF*"6#qs34=9!<<'%hDkQQSGrNk[4)(eHi0[*IlAQkh/Y)G4 +g1q66GN/T$!q+nFrVm#p@gE?]o`+pA!-@b6!Qs9tec=uds+13$s+13$s0)J%ecBjlp=9A2!9]S= +J,~> +ZMsp]JcCH(rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjUK`Cc&"IoJ\S5+S~> +ci4[Wq=X@LnF,f5mHs?0n*ol>JcC<$JcC<$JcDqRrn@I* +s7Y1MRK2ZB9n3~> +ci4[Wq=X@LnF,f5mHs?0n*ol +ZMsp]JcCH(rn[X.o=Y1Uk5F-:m]c]lmXKffJcC<$JcC?%rn[[/s7Y:PS,i#J:4N~> +bl@_B$N9o"qY9m_qu$Elo`"pGJcCH(rn@F)o +bl@_B$N9o"qY9m_qu$Elo`"pBJcCH(rmh'jo;r&9hYl"*lEL9hmXKffJcC<$JcC?%rmh*ks7Y"H +PQ9m28q6~> +ZMsp]JcCH(rn[X.o=Y1Uk5F-:mbn*GL[NXk"m.iFs+g%/s8U"9rsnP1M2Ae2s8V`1P(T(Fo7?pm +s+13$s0)J%h>r<0p>,qB!:QFQJ,~> +ZMspZJcCH(rn@F)oVRSA;~> +ZMspUJcCH(rmh'jo;r&9hYl"*lJV[CL[NXk"m.iFs+g%/s8U"9rsnP1M2Ae2s8V`1P(T(Fo7?pm +s+13$s0)J%ecBjlp=9A2!9]S=J,~> +ZMsp]JcCH(rn[X.o=Y1Uk5F-:mbn*G#i=&1"o#S-rs7KMrri5]]iKdbrslMIB5(bZs8TcJBm!s/ +P_&jcs+13$s0)J%h>r<0p>,qB!:QFQJ,~> +ZMspZJcCH(rn@F)o +ZMspUJcCH(rmh'jo;r&9hYl"*lJV[C#i=&1"o#S-rs7KMrri5]]iKdbrslMIB5(bZs8TcJBm!s/ +P_&jcs+13$s0)J%ecBjlp=9A2!9]S=J,~> +ZMsp]JcCH(rn[X.o=Y1Uk5F-UmbX[jON7(pqp1R\LUI$Zs*rU]`W,h?LP)Q"r;RUl!-d/sJ(Cs+ +iomY&KrF#2p:%!'Kp9p\q#;*-f!1!_W;$>GL\(Q*L[OS[s,-l$gALsOmXP!2(@\_K.=_Btq0ur/ +hI_33p4$Q$nAAKSJcC<$JcC<$YQ+UY!JLLHh?9>Kn!m.'~> +ZMspZJcCH(rn@F)o +ZMspUJcCH(rmh'jo;r&9hYl"ElJA7fON7(pqp1R\LUI$Zs*rU]`W,h?LP)Q"r;RUl!-d/sJ(Cs+ +iomY&KrF#2p:%!'Kp9p\q#;*-f!1!_W;$>GL\(Q*L[OS[s,-l$gALsOmXP!2(@\_K.=_Btq0ur/ +hI_33p4$Q$nAAKSJcC<$JcC<$YQ+UQ!H\;7ec_3;kEJSh~> +ZMsp]JcCH(rn[X.o=Y1Uk5F-Uc\Z+R@Vj$`cZ`iBG>>RGrr?tQ^&P$7HZMrPr;RUl!-d/s!,l_= +g8%JQF',%2mVb+0F'*M0q#;)XJQdDA:7V@<4dc+f>415Is,-l$gALsOl;%*R(A,kK:4NKn!m.'~> +ZMspZJcCH(rn@F)oOU;Hs,@#$f_kaMk=t^N(A,kK9n33Cq743: +k&,VAp3pW-prH\]JcC<$JcC<$YQ+UV!J:@FgB +ZMspUJcCH(rmh'jo;r&9hYl"EbDB\N@Vj$`cZ`iBG>>RGrr?tQ^&P$7HZMrPr;RUl!-d/s!,l_= +g8%JQF',%2mVb+0F'*M0q#;)XJQdDA:7V@<4dc+f>415Is,-l$gALsOl;%*R(A,kK:4N +ZMsp]JcCH(rn[X.o=Y1Uk5F-VQsd#bbD!>%P$Z&tmW1hsrr@,op\QP8oBqhhp\b%.!8dbU!->%$ +pT]h4iad-$mVbS.m($5]rVHO/rKLoeoBqhhp]%KUPWdIph>mTU!8dVQ!W1.7rrVV,J,Kr<0p>,qB!:QFQJ,~> +ZMspZJcCH(rn@F)o +ZMspUJcCH(rmh'jo;r&9hYl"FP[LT^bD!>%P$Z&tmW1hsrr@,op\QP8oBqhhp\b%.!8dbU!->%$ +pT]h4iad-$mVbS.m($5]rVHO/rKLoeoBqhhp]%KUPWdIph>mTU!8dVQ!W1.7rrVV,J,K +ZMsp]JcCH(rn[X.o=Y1Uk5F-VGAH4EHrrC1@s69T.It)A:ir&fn!8dbU!6O=% +s59oCQ:c)`mVd7?s7CGVo_SS&oQ<6>It)A:irAmZ(mt@'h>mTU!8d,C!q'uVr;R2?Bi_84s0Pa1 +GuS+.JcC<$JcC<$YQ+UY!JLLHh?9>Kn!m.'~> +ZMspZJcCH(rn@F)oIt)A:j8]!^(mt@'gAq9R!8Ho@!psiSrVm?+^1gZLrVj0' +>^'.SMh1nZs+13$s0)J%gAup+p=f_=!:-(JJ,~> +ZMspUJcCH(rmh'jo;r&9hYl"FF)0e8q;CE-Fa*QZs2>EHrrC1@s69T.It)A:ir&fn!8dbU!6O=% +s59oCQ:c)`mVd7?s7CGVo_SS&oQ<6>It)A:irAmZ(mt@'h>mTU!8d,C!q'uVr;R2?Bi_84s0Pa1 +GuS+.JcC<$JcC<$YQ+UQ!H\;7ec_3;kEJSh~> +ZMsp]JcCH(rn[X.o=Y1Uk5F-VF_Ke7q:+-jFa*QZs2>EHrrCpUs69T2It)fXlMUZ!!8dbU!8d/D +qke.eTN#jtmVd7?s7CGVo_SS&oQ<mTU!8d,C'^fmhs8Vu@dJrEGH2%,a +@bUXu=r`+nJcC<$JcDqRrn[[/s7Y:PS,i#J:4N~> +ZMspZJcCH(rn@F)od/N3DH1pu] +AD-^s>TeUtJcC<$JcDqRrn@I*s7Y1MRK2ZB9n3~> +ZMspUJcCH(rmh'jo;r&9hYl"FEG4A3q:+-jFa*QZs2>EHrrCpUs69T2It)fXlMUZ!!8dbU!8d/D +qke.eTN#jtmVd7?s7CGVo_SS&oQ<mTU!8d,C'^fmhs8Vu@dJrEGH2%,a +@bUXu=r`+nJcC<$JcDqRrmh*ks7Y"HPQ9m28q6~> +ZMsp]JcCH(rn[X.o=Y1Uk5F-tO^,'YbB^&bOBf]pmW1hsrrCpUs8+7DoCJo5s8U@@s8N)Qs8N)U +mf3$*BBJ,[D=$\/7_S6%=H`XZrtYKqBW1OoW.Y-MS405.`W+cps8N)Qqu6]oc1V)NmVdUTs8.t+ +i8/-sq>#MJeF::X+bBZEs+13$s0)J%h>r<0p>,qB!:QFQJ,~> +ZMspZJcCH(rn@F)o +ZMspUJcCH(rmh'jo;r&9hYl"dNEiXUbB^&bOBf]pmW1hsrrCpUs8+7DoCJo5s8U@@s8N)Qs8N)U +mf3$*BBJ,[D=$\/7_S6%=H`XZrtYKqBW1OoW.Y-MS405.`W+cps8N)Qqu6]oc1V)NmVdUTs8.t+ +i8/-sq>#MJeF::X+bBZEs+13$s0)J%ecBjlp=9A2!9]S=J,~> +ZMsp]JcCH(rn[X.o=Y1Uk5F-LbCa2F@Vj$`c#mK>F&'.CrrCpTs!+1?GBZrHs8PjZs8O6XgA_00 +mf30Q<-a6u8@S#*20!bDF3jX](!h;3GBZrHs2d:/s*IB'h>mTU+`#g0rrMC[p&>*]Dh%cd%^98R +Bk4^Qs1;rWF(03TJcC<$JcC<$YQ+UY!JLLHh?9>Kn!m.'~> +ZMspZJcCH(rn@F)o`nD1DTcrif^B +Bk=aPrjuoYF_#QXJcC<$JcC<$YQ+UV!J:@FgB +ZMspUJcCH(rmh'jo;r&9hYl"F&'.CrrCpTs!+1?GBZrHs8PjZs8O6XgA_00 +mf30Q<-a6u8@S#*20!bDF3jX](!h;3GBZrHs2d:/s*IB'h>mTU+`#g0rrMC[p&>*]Dh%cd%^98R +Bk4^Qs1;rWF(03TJcC<$JcC<$YQ+UQ!H\;7ec_3;kEJSh~> +ZMsp]JcCH(rn[X.o=Y1Uk5F-Lmb47bON7(pqp1R\K=1UVs*sJ9s!.9>K7fuks8RT:s8SaAgAcZj +pAb0LTS98LP/6R)A>k3JchmM;(&QC[K7fuks2gN&s5nt!mXP9:TRY\nrrMP;p&>*e\%hqB%d^TY +M7`9Ws7F9lJX4RPJcC<$JcC<$YQ+UY!JLLHh?9>Kn!m.'~> +ZMspZJcCH(rn@F)oKnH8os8RT9s8SaCf`-Hg +pAb0LTSBDPPJZ^(A>k3Jd/3V<(&QC[KnH8os2pQ&s6#%!m=509TRkbnrrMM:p&>*e[_MhA%d^TY +MS/KZs7OBoK9jdRJcC<$JcC<$YQ+UV!J:@FgB +ZMspUJcCH(rmh'jo;r&9hYl"K7fuks8RT:s8SaAgAcZj +pAb0LTS98LP/6R)A>k3JchmM;(&QC[K7fuks2gN&s5nt!mXP9:TRY\nrrMP;p&>*e\%hqB%d^TY +M7`9Ws7F9lJX4RPJcC<$JcC<$YQ+UQ!H\;7ec_3;kEJSh~> +ZMsp]JcCH(rn[X.o=Y1Uk5F-:ma1t8mVdT+s+13$s+13$s5X.Yh>r<0p>,qB!:QFQJ,~> +ZMspZJcCH(rn@F)o +ZMspUJcCH(rmh'jo;r&9hYl"*lHoP4mVdT+s+13$s+13$s5X.YecBjlp=9A2!9]S=J,~> +ZMsp]JcCH(rn[X.o=Y1Uk5F-:ma1t8nr/t7rrMJfqgSWt_1DW;s+13$s+14?s8LjUK`Cc&"IoJ\ +S5+S~> +ZMspZJcCH(rn@F)oV +RSA;~> +ZMspUJcCH(rmh'jo;r&9hYl"*lHoP4nr/t7rrMJfqgSWt_1DW;s+13$s+14?s8LRMF8tsb"I&oL +PY-H~> +ZMsp]JcCH(rn[X.o=Y1Uk5F-:ma1t8qpa9srrMJfqgSWt_1DW;s+13$s+14?s8LjUK`Cc&"IoJ\ +S5+S~> +ZMspZJcCH(rn@F)oV +RSA;~> +ZMspUJcCH(rmh'jo;r&9hYl"*lHoP4qpa9srrMJfqgSWt_1DW;s+13$s+14?s8LRMF8tsb"I&oL +PY-H~> +ZMsp]JcCH(rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjUK`Cc&"IoJ\S5+S~> +ZMspZJcCH(rn@F)oVRSA;~> +ZMspUJcCH(rmh'jo;r&9hYl"*l@Jtds+13$s+13Rs8LRMF8tsb"I&oLPY-H~> +ZMsp]cMmmZpj[kp_Z0Vl!/0sW!S6E3h>ltps+13$s+13$s0)J%h>r<0p>,qB!:QFQJ,~> +ZMspZcMmmZpj[kp_Z0Vi!.sgN!RTm*gApVls+13$s+13$s0)J%gAup+p=f_=!:-(JJ,~> +ZMspUcMmmZpj[kp_Z0Vd!-@b6!Qs9tec=uds+13$s+13$s0)J%ecBjlp=9A2!9]S=J,~> +ZMsp]cMms\561V2!<@WErtW5&J"Z60mXNZCJ#L]jp:%g:s7Wq:JcF^/rn[X.o=Y1Uk5F-:mXbCh +s+13$s+13Rs8LjUK`Cc&"IoJ\S5+S~> +ZMspZcMms\561V2!<@WFrtbRGM1MDgs6Y@; +JcC<$JcC<$YQ+UV!J:@FgB +ZMspUcMms\561V2!<@WErtW5&J"Z60mXNZCJ#L]jp:%g:s7Wq:JcF^/rmh'jo;r&9hYl"*l@Jtd +s+13$s+13Rs8LRMF8tsb"I&oLPY-H~> +ZMsp]c2Rj[It.Its*shD(;WmiG>=(rh>hVDCIN +ZMspZc2Rj[It.Its*shD(;<[hG>F(qgAlACD+&Bhm;7@Qs6XZQJcF^/rn@F)oVRSA;~> +ZMspUc2Rj[It.Its*shD(;WmiG>=(rh>hVDCIN +ZMsp]bQ%Tl!rmb"p\u",DsI(iCB*,jN:,td=H`#hJ,fQ:Dh!?luUrrdi> +s*sJ%rrVo'^OcE9s+13$s-3Q_h>r<0p>,qB!:QFQJ,~> +ZMspZbQ%Tl!rmb"p\u"+EU3:jC&cofNpl:f=-;fcJ,fQ9D1@-:iW&o3!.sgN!RTm*gApWQrrdi= +s*sG$rrVo&^OcE9s+13$s-3Q_gAup+p=f_=!:-(JJ,~> +ZMspUbQ%Tl!rmb"p\u",DsI(iCB*,jN:,td=H`#hJ,fQ:Dh!?!Irrdi> +s*sJ%rrVo'^OcE9s+13$s-3Q_ecBjlp=9A2!9]S=J,~> +ZMsp]b5_HjpAYm>`W,u0c2ZW#lMpnLF`lb`J,fQ:Dh!?luUrr`>YrrCp@ +rs&%USH$L=p@/(_hOa[q#eC!8lC`WZW:tlCJcC<$JcE7[rn[[/s7Y:PS,i#J:4N~> +ZMspZb5_HjpAYm>`rH)1bl?Dtl2UeHF*6M[J,fQ9D1@-:iW&o3!.sgN!RTm*gApWQrr`>VrrCg= +rs&"RSH$L=p[J1`h4=Lo#eL'9l_&f]WqCrCJcC<$JcE7[rn@I*s7Y1MRK2ZB9n3~> +ZMspUb5_HjpAYm>`W,u0c2ZW#lMpnLF`lb`J,fQ:Dh!?!Irr`>YrrCp@ +rs&%USH$L=p@/(_hOa[q#eC!8lC`WZW:tlCJcC<$JcE7[rmh*ks7Y"HPQ9m28q6~> +ZMsp]ao;?jp&>d9`W,u0[K#(`lMpnLF`lb`J,fQ2Aq,C3iW&o6!/0sW!S6E3h>luUrr`>YrrCp@ +rs&AQh#E"JmdU5XZ;=HuCC%3Fs*%4ZF&&6Vs+13$s+13[s8LjUK`Cc&"IoJ\S5+S~> +ZMspZao;?jp&>d9`rH)1[f>(^l2UeJFEHMZJ,fQ1Aq,C3iW&o3!.sgN!RTm*gApWQrr`>VrrCg= +rs&>Oh#E"ImdU5XZVOKuCC%0ErHV1]F&/6Us+13$s+13[s8LaRK)bQ!"I]>VRSA;~> +ZMspUao;?jp&>d9`W,u0[K#(`lMpnLF`lb`J,fQ2Aq,C3iW&o.!-@b6!Qs9tec>!Irr`>YrrCp@ +rs&AQh#E"JmdU5XZ;=HuCC%3Fs*%4ZF&&6Vs+13$s+13[s8LRMF8tsb"I&oLPY-H~> +ZMsp]]`/a9F6`Lm;ZGSRN:-+h=H`/pC$>8t4b&%_iW&o6!/0sW!S6E3hAbn>U4\QJpAb$9Q@jt# +qu?\Ger[lNqltp/W;$2h+juI%Mp;:WYg`XPa0+RgZ%n%7k5X;4K94.Is6-_CKrF#1rr`>YrrCpN +rtYF9LP)Q"s4W8"s5Jh%m^KsM76@I7rVlrm\%h_Kn!m.'~> +ZMspZ]`/a:F6iLl;ZGJON:6.j=ci&kB]o,t4b&%_iW&o3!.sgN!RTm*gDfM7U4n`MpAb$:Q@jt" +qu?\Ge;qQJqm)!0WqZDj+juL&NQhFWZ./gRaKXgjZA=7;jo4&0KosFLs6$VAKrEu0rr`>VrrCgK +rtYF:LP)W$s4W>$s5Sn'm^BmM7QRI5rVlrm[_MV;%\IFIlKdd/s5slfoC%VbJcC<$JcC<$\GuQ_ +!J:@FgB +ZMspU]`/a9F6`Lm;ZGSRN:-+h=H`/pC$>8t4b&%_iW&o.!-@b6!Qs9tef3o2U4\QJpAb$9Q@jt# +qu?\Ger[lNqltp/W;$2h+juI%Mp;:WYg`XPa0+RgZ%n%7k5X;4K94.Is6-_CKrF#1rr`>YrrCpN +rtYF9LP)Q"s4W8"s5Jh%m^KsM76@I7rVlrm\%h_ +ZMsp]]`/bX:31VSMuVW6CNjhgF3j`T:i1i53IcV[iW&o6!/0sW!S6E3hAadt@Wc-r`W+8.@X!#k +[K$7,Dem&dW*4Oh:7V7^.echRHY-%Ib@[";n9]*3Ug.nZ>,'f/@Wc-r`VZH+@WPYCqu?ZrhZ!T4 +q#;)->'kr<0p>,qB!:QFQJ,~> +ZMspZ]`/bW:31VTMuVN3Bm4PfFO0fT;/V)93.HMZiW&o3!.sgN!RTm*gDe@mA9MI!`;e/-@Wcfe +[f?@-DelraWa0mk:Rq@_.ecqWH"g"Ib%?n:nU,94VI+=_>GBi-A9MI!`;??)@W>J?qZ$Qqg]%9. +q#;)/?$UKeSH%;,Req53n)\D9`^W"eli$hbo9uQOrsjuVGCPmXp]'>lp&G'!(kM^ +ZMspU]`/bX:31VSMuVW6CNjhgF3j`T:i1i53IcV[iW&o.!-@b6!Qs9tef2eh@Wc-r`W+8.@X!#k +[K$7,Dem&dW*4Oh:7V7^.echRHY-%Ib@[";n9]*3Ug.nZ>,'f/@Wc-r`VZH+@WPYCqu?ZrhZ!T4 +q#;)->'k +ZMsp]]DiVRLOYubs5!atLP)POqu>ZQK8[4=^OcFDs8LjTK_)kXf&lqf*11-8nB6NmrK:L)oBpZg +s8N(?g@tIrT_%T9-27B<.dmA:pY'ugmX90?b@Hq3G>urQWk"p5XRODe[/7+if]$F_o)J^ihZ!T4 +q>V3,/YM_'PU6)(Yr.b/df8`RJ,b":mem(drT*,:rt#!`6ZmTV7A0\Xmf3$p>'ki#JcC<$JcE7[ +rn[[/s7Y:PS,i#J:4N~> +ZMspZ]`/cG[YKF.n,M,BaG5DJd/3k&W.g5?WkX,biW&o3!.sgN!RTm*gDlMfY40Se[/?"k_=?o6 +8H8\iFkZNc0VIt)PU-#%s!mI?W:ekip[3EMkf[P&oQaK_kGl%f@BT+BKXCT[OOrB"EUj)/!8IMR +g\Ljaq_4]5n!#*ls0+JH5go]"D1DS8D=.9$!rV'>q>V!%M+)Q2>tC^iqsFFX]NKT4s+13$s+13[ +s8LaRK)bQ!"I]>VRSA;~> +ZMspU]DiVRLOYubs5!atLP)POqu>ZQK8[4=^OcFDs8LRLF7ZL7b23*J*0aj4nB6NmrK:L)oBpZg +s8N(?g@tIrT_%T9-27B<.dmA:pY'ugmX90?b@Hq3G>urQWk"p5XRODe[/7+if]$F_o)J^ihZ!T4 +q>V3,/YM_'PU6)(Yr.b/df8`RJ,b":mem(drT*,:rt#!`6ZmTV7A0\Xmf3$p>'ki#JcC<$JcE7[ +rmh*ks7Y"HPQ9m28q6~> +ZMsp][Jp9a!.k1!s8LjTK_)kXf&lqf*-Z2Xs7tU8oQ>BOs8U%Hs8N)@s8V?aGCP*\!93tW.ed7n +K:LNms.j2?Sn,h;Wb[$D\a"rcirAi4k5XA>QBk-]mf3:ehZ!T4q>V2p!-`pBDZJesqcXi(s8VM* +J,b":md^;^qo?,.p:#N'rr3,CK6)\-JcC<$JcC<$\GuQb!JLLHh?9>Kn!m.'~> +ZMspZ[Jp9^!.k1!s8LaQK(HDOd,Y)Z*-H&Vs7tR6oQ56Ks8U(Ks8N)?s8VBdFanmZ!9=%X.ed@q +K:CV2q!d0!BDZJhtqct&+s8VJ' +J,b"8mIC2]qT$&-p:,Z+rr3,DK6)V*JcC<$JcC<$\GuQ_!J:@FgB +ZMspU[Jp9a!.k1!s8LRLF7ZL7b23*J*-5oTs7tU8oQ>BOs8U%Hs8N)@s8V?aGCP*\!93tW.ed7n +K:LNms.j2?Sn,h;Wb[$D\a"rcirAi4k5XA>QBk-]mf3:ehZ!T4q>V2p!-`pBDZJesqcXi(s8VM* +J,b":md^;^qo?,.p:#N'rr3,CK6)\-JcC<$JcC<$\GuQZ!H\;7ec_3;kEJSh~> +ZMsp][Jp9i0nKARs8LjTK_)kXf&lqf*-GrSs7t0uoQ>BOs8U%Hs8N)Us8V?aH[gNlBBoHq.f7&X +LNcqrqp/l*GC/cVhQrMtBW-JqirAi(gA@a_MkF$fmf3:ehZ!T4q>V2p!.03FHY;XDpJrSqs8VM* +J,b":md^;eo%N7+s5M$,s8T?9J$o$%s+13$s+13Zs8LjUK`Cc&"IoJ\S5+S~> +ZMspZ[Jp9f0S08Qs8LaQK(HDOd,Y)Z*-5iRs7t0uoQ56Ks8U(Is8N)Rs8V<`H%1V2o!-s'DHY;[EoiEJqs8VJ' +J,b"8mIC2do@i@,s5V-0s8TB=ICAg"s+13$s+13Zs8LaRK)bQ!"I]>VRSA;~> +ZMspU[Jp9i0nKARs8LRLF7ZL7b23*J*-#ZOs7t0uoQ>BOs8U%Hs8N)Us8V?aH[gNlBBoHq.f7&X +LNcqrqp/l*GC/cVhQrMtBW-JqirAi(gA@a_MkF$fmf3:ehZ!T4q>V2p!.03FHY;XDpJrSqs8VM* +J,b":md^;eo%N7+s5M$,s8T?9J$o$%s+13$s+13Zs8LRMF8tsb"I&oLPY-H~> +ZMsp][Jp:%Z%;r(s8LjTK_)kXf&lqf99EJ^nB6*UrK(:%oBpZgs8N)Us8VsFW:TVZKE(trqu?D' +hYXPXoD.s*3EE$Wqlca\el[3f>d!S>H)UI]BBJ,[D=%<&!8d_UhYI0dq^JK8o;IUleOGi&d[=:LBl7meFrp#(7JcC<$JcE7[rn[[/s7Y:PS,i#J:4N~> +ZMspZ[Jp:#Y^ui's8LaQK(HDOd,Y)Z99^,# +hYF;UnbD^)3``-XqQ?UZdo^mb>d*Y>H)UF\BBA#ZC[;$#!8IMRg\Ljaq'r93o;[NEs-c?O1;s4[ +D1DS8C[:ou!rV'>q>UlfP)\Di[=:UEkq[hGrp,.8JcC<$JcE7[rn@I*s7Y1MRK2ZB9n3~> +ZMspU[Jp:%Z%;r(s8LRLF7ZL7b23*J99!2ZnB6*UrK(:%oBpZgs8N)Us8VsFW:TVZKE(trqu?D' +hYXPXoD.s*3EE$Wqlca\el[3f>d!S>H)UI]BBJ,[D=%<&!8d_UhYI0dq^JK8o;IUleOGi&d[=:LBl7meFrp#(7JcC<$JcE7[rmh*ks7Y"HPQ9m28q6~> +ZMsp]JcCH(rn[X.o=Y1Uk5F-LbCa2F@Vj$`c#mK>F&'.CrrCpTs#$HQGBZrHs8PjZs8,]/HY6CV +rVr`F>1NZOVI"=`?DuS1@!,pp`VZ*'H&#?i\,ZI.hZ!T4q#;)%>'G0gOT3E_[JtSGlJul5s,]^E +m/?qcoUMfSrsSIn>\eIo>.O[D!+YtCJcC<$JcC<$\GuQb!JLLHh?9>Kn!m.'~> +ZMspZJcCH(rn@F)oh8rQVI+@_?`;V0@Cl/HQ0s,TXD +li$hbo9uQPrsSLp?>Odt=LeC>!+YtCJcC<$JcC<$\GuQ_!J:@FgB +ZMspUJcCH(rmh'jo;r&9hYl"F&'.CrrCpTs#$HQGBZrHs8PjZs8,]/HY6CV +rVr`F>1NZOVI"=`?DuS1@!,pp`VZ*'H&#?i\,ZI.hZ!T4q#;)%>'G0gOT3E_[JtSGlJul5s,]^E +m/?qcoUMfSrsSIn>\eIo>.O[D!+YtCJcC<$JcC<$\GuQZ!H\;7ec_3;kEJSh~> +ZMsp]JcCH(rn[X.o=Y1Uk5F-Lmb47bON7(pqp1R\K=1UVs*sJ9rt+q+K7fuks8RT:s8U?%Iur7\ +ruo3TL%bQHk,a8lZg.SESqE-FpAa!BKr22>_>jObmf.e)q#;*'XFl/$oDcpsqu>f2lKl!ps3He. +m/?qcp:%g3rs7u#M1_P9s8U@@It@WNs+13$s1&+.h>r<0p>,qB!:QFQJ,~> +ZMspZJcCH(rn@F)onZg%JBSqN6Ip]'*CL8_JC_Z0XcmJh\'q#;*'XG);(oDcssqu>i3l0Gdms3He/ +li$hbp9qa2rs7r"MM._:rVt+=It@WNs+13$s1&+.gAup+p=f_=!:-(JJ,~> +ZMspUJcCH(rmh'jo;r&9hYl"_>jObmf.e)q#;*'XFl/$oDcpsqu>f2lKl!ps3He. +m/?qcp:%g3rs7u#M1_P9s8U@@It@WNs+13$s1&+.ecBjlp=9A2!9]S=J,~> +ZMsp]JcCH(rn[X.o=Y1Uk5F-:mb%O@`F>?gs+13$s+13$s4dSQh>r<0p>,qB!:QFQJ,~> +ZMspZJcCH(rn@F)o +ZMspUJcCH(rmh'jo;r&9hYl"*lIc+<`F>?gs+13$s+13$s4dSQecBjlp=9A2!9]S=J,~> +ZMsp]JcCH(rn[X.o=Y1Uk5F-:mb.UBo7UW%JcC<$JcC<$JcFL)rn[[/s7Y:PS,i#J:4N~> +ZMspZJcCH(rn@F)oVRSA;~> +ZMspUJcCH(rmh'jo;r&9hYl"*lIl1>o7UW%JcC<$JcC<$JcFL)rmh*ks7Y"HPQ9m28q6~> +ZMsp]JcCH(rn[X.o=Y1Uk5F-:mb7[CiaXF9ir9"dq>^L$JcC<$JcC<$K)bi,!JLLHh?9>Kn!m.'~> +ZMspZJcCH(rn@F)o +ZMspUJcCH(rmh'jo;r&9hYl"*lIu7?iaXF9ir9"dq>^L$JcC<$JcC<$K)bi$!H\;7ec_3;kEJSh~> +ZMsp]JcCH(rn[X.o=Y@Zk3i9;hX5spJcC<$JcC<$YQ+UY!JLLHh?9>Kn!m.'~> +ZMspZJcCH(rn@F)o +ZMspUJcCH(rmh'jo;r5>hWk.+f'7hdJcC<$JcC<$YQ+UQ!H\;7ec_3;kEJSh~> +ZMsp]JcCH(rn[X.o=Y@Zk3hYUDsi2VJcC<$JcC<$YQ+UY!JLLHh?9>Kn!m.'~> +ZMspZJcCH(rn@F)o +ZMspUJcCH(rmh'jo;r5>hWjBAC[-KNJcC<$JcC<$YQ+UQ!H\;7ec_3;kEJSh~> +ZMsp]JcCH(rn[X.o=Y@Zk2("VDsi2VJcC<$JcC<$YQ+UY!JLLHh?9>Kn!m.'~> +ZMspZJcCH(rn@F)o +ZMspUJcCH(rmh'jo;r5>hUZHBC[-KNJcC<$JcC<$YQ+UQ!H\;7ec_3;kEJSh~> +ZMsp]JcCH(rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjUK`Cc&"IoJ\S5+S~> +ZMspZJcCH(rn@F)oVRSA;~> +ZMspUJcCH(rmh'jo;r&9hYl"*l@Jtds+13$s+13Rs8LRMF8tsb"I&oLPY-H~> +ZMsp]JcCH(rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjUK`Cc&"IoJ\S5+S~> +ZMspZJcCH(rn@F)oVRSA;~> +ZMspUJcCH(rmh'jo;r&9hYl"*l@Jtds+13$s+13Rs8LRMF8tsb"I&oLPY-H~> +ZMsp]JcCH(rn[X.o=Y@Zk3i9;hX9A&!T!h2rrLsVJcC<$JcC<$JcGQGrn[[/s7Y:PS,i#J:4N~> +ZMspZJcCH(rn@F)o +ZMspUJcCH(rmh'jo;r5>hWk.+f';5o!T!h2rrLsVJcC<$JcC<$JcGQGrmh*ks7Y"HPQ9m28q6~> +ZMsp]WrE.tTVUcN!V]4=rrW(jUsK&EIrFb)Dsm5s!cnXTnGiLE!/0sW#1hr)mVd%3iVrrIF5-]u +mrSO/&DlmU"i_u*'Q`,^s+13$s+138s8LjUK`Cc&"IoJ\S5+S~> +ZMspZWrE.rTVgoP!V9%`s+13$s+138s8LaRK)bQ!"I]>VRSA;~> +ZMspUWrE.tTVUcN!V]4=rrW(jUsK&EIrFb)Dsm5s!cnXTnGiL=!-@b6#0Pffl>(2#iVrrIF5-]u +mrSO/&DlmU"i_u*'Q`,^s+13$s+138s8LRMF8tsb"I&oLPY-H~> +ZMsp]WrE7NTZr79mcsfTV4@PshV8&3d\+k2rrmmjmXK0CoD\gk`p`s,h>i6#]a!'MhJWC4m^<&q +!8d2E"W2p']hX?6s+13$s+138s8LjUK`Cc&"IoJ\S5+S~> +ZMspZWrE7NTZi18n*9oUVOIMrhqS/4d\+h1rrmmhm=0!?oD\go_sdX)gAlis[Kb+=gMHq-mBurp +!8HuB"rr6)^/9[aJcC<$JcC<$QN-s=!J:@FgB +ZMspUWrE7NTZr79mcsfTV4@PshV8&3d\+k2rrmmjmXK0CoD\gk`p`s,ec9d_XTlr)enY8$lF$Wm +!8d2E"W2p']hX?6s+13$s+138s8LRMF8tsb"I&oLPY-H~> +ZMsp]^&Rj-p\tDqT_EqSmcsfT;XaVchV8&3W1`n2rrmmjmXK0CoD\gkhXCLDh>i6#]`H^HrSAL] +]gW2MORE/CTIgE\C\Rl/!3IE's7^"7#ZC-hrt!rds8N)$+]cS$;@FCS:4N'=(&P*n$n\.Hg1#F# +s2GW,h>mQm!"cR9rrLsVq#:T^!;HBjIrk44s+13$s+139s8LjUK`Cc&"IoJ\S5+S~> +ZMspZ^&Rj-p\tDsT_3eOmHX]S^( +s2P].gAq6n!"cI6rrLjSq#:T^!;HBhI<"e.s+13$s+139s8LaRK)bQ!"I]>VRSA;~> +ZMspU^&Rj-p\tDqT_EqSmcsfT;XaVchV8&3W1`n2rrmmjmXK0CoD\gkhXCLDec9d_XT?T$rRMqQ +]gW2MORE/CTIgE\C\Rl/!3IE's7^"7#ZC-hrt!rds8N)$+]cS$;@FCS:4N'=(&P*n$n\.Hg1#F# +s2GW,h>mQm!"cR9rrLsVq#:T^!;HBjIrk44s+13$s+139s8LRMF8tsb"I&oLPY-H~> +ZMsp]]`.s3r;Q`sp\ulCTVRJS#\)M;8lc?\K2N+@\c;)9;I0b\;XaYcpIbH&!"cR0[*ETVVU;8lc?\K2N*`F8l1BV',gUN;ih`!3IE's.pk= +(l\Io*!!)q(_C?2s8/oY$n\.Hs*ntTIrFcLTIgR8Z2=Fu'HmGf[K#\n#S:eGs8VOc#S:eGs8W)T +!/0sW!S6E3hA`_#J)[7qSH#N_R-3<\F8u7?0t$KM>#OP?8kT$]&)IKn!m.'~> +ZMspZ]`.s3r;Q`sp]"7jTVRPW#[uA88QQHcKiJUF\Gtr5;dU"a;="8^pIYB)!"cI9!3.0!s.UV9 +(lJ@nqfrkY7GI50qlVsg/u&+?dUa%$>Km-ETVVU;8QQHcKiJTgEW5t@U*'FQMZ3V^!3.0!s.UV9 +(lJ=m*!!,u*"li4s8/r[$ne:Ks*nnQIr4TIU+Qj:Yl"=t'HdAeZN'An#S:_Es8VOf#S:_Es8W)Q +!.sgN!RTm*gDd:sJ`3=oS,]E]QK-^RFoVIA1q)`M>ufqB8P/j[&).*arr=mad(E:idai7FJ,'$X +Y$%:RPY%\NZs0:!,c:Ur!<<'!g\_!Oo:#@P"T?\SM>`#*JcC<$JcD&9rn@I*s7Y1MRK2ZB9n3~> +ZMspU]`.s3r;Q`sp\ulCTVRJS#\)M;8lc?\K2N+@\c;)9;I0b\;XaYcpIbH&!"cR0[*ETVVU;8lc?\K2N*`F8l1BV',gUN;ih`!3IE's.pk= +(l\Io*!!)q(_C?2s8/oY$n\.Hs*ntTIrFcLTIgR8Z2=Fu'HmGf[K#\n#S:eGs8VOc#S:eGs8W)L +!-@b6!Qs9tef1_lJ)[7qSH#N_R-3<\F8u7?0t$KM>#OP?8kT$]&)I +ZMsp]]Dhj2rr2rupA]!\TZR4`DsmXT/Z@6(/Z@6(F7\g<+ctE53E#nWXd&kc!8dbU!&:nMmf7G' +]`@O$KIPL@=ui,QXB(kNPY.bGG?3/C>'K_lTZR4`/Z@6(/Z@7.!9aCN!-@nJ!:Tsf!&:nMmf7G' +]`@O$ru;#*hX8'GL]=GVSDoc*V#Pp_mXK0CW(r`>LJk\Zrt>Chs8PthkM@q7s8PthkM@q7s8W)T +!/0sW,1boEs6e`nCYJg[_9Mk(DsmZ*[2ArD!4Dk,pAj[^s5!bErVmDb!<<'![K$8gA((DWelI:r +rtYEXGPD.*Dg1sQN%$Jbs8UpUs8N)UmJd9 +ZMspZ]Dhj2rr2rupA]!\TZI._D=.@Q/umK,/?%-(F7\d9,*L]93)T_UY*/hb!8IPR!&V.Mmf7M% +\cD'rJh5O>=ZDoNY$%:RPY%\FH!/MD>BfhmTZI._/umK,/?%..!9aCN!-RnE!:0[b!&V.Mmf7M% +\cD'rru;#)hsJ'GL]=M[SDf](V#Pp]m=0!?W(iT8Ki5MYrt>Ces8Pqfj4c81s8Pqfj4c81s8W)Q +!.sgN,1,B;s6\QhCYJg[^s)V"D=.B'[N,>I!4Mq-pB9g^s5*hFrVmD_!<<'![f?AgA((DWe5_"o +rtYEZGP2")Dg1sQN@HVfs8UgRs8N)Rmf*FeI/m9sJcC<$JcC<$Q2gj +ZMspU]Dhj2rr2rupA]!\TZR4`DsmXT/Z@6(/Z@6(F7\g<+ctE53E#nWXd&kc!8dbU!&:nMmf7G' +]`@O$KIPL@=ui,QXB(kNPY.bGG?3/C>'K_lTZR4`/Z@6(/Z@7.!9aCN!-@nJ!:Tsf!&:nMmf7G' +]`@O$ru;#*hX8'GL]=GVSDoc*V#Pp_mXK0CW(r`>LJk\Zrt>Chs8PthkM@q7s8PthkM@q7s8W)L +!-@b6,0Jd-s6A0^CYJg[_9Mk(DsmZ*[2ArD!4Dk,pAj[^s5!bErVmDb!<<'![K$8gA((DWelI:r +rtYEXGPD.*Dg1sQN%$Jbs8UpUs8N)UmJd9 +ZMsp]])Mg3rrD`l-ENm3s*ntTs*nhLs*nhLs*ntTmVcn+s6A/8p]"F[s8N)Us8N),rr3kep]&eE +hZ"b,s8V8Pc22j3p]'Z"GOK`RcMm\[s/_mbs*nhLs*nhLs8N)Us8U[Es4.2,s8N),rr47pp]&eE +hZ*TU^&S-#Dh%Y2GPD.*Dg1r*Dsi*nl?dI;s4V(CqYq3/hZ*i6# +]`ZjJmdC'm!Gh!!rrVV,J,]HRec>aM!8dbUhYR9Qf)>UZh>mTU!8dbUIr"?Ls6=BLq>UK`Du9SF +C[1rA8c[HBs5!bUrrCpBrr^\MJ,=fsJcC<$JcD&9rn[[/s7Y:PS,i#J:4N~> +ZMspZ])Mg3rrD`lENGc%s*nnQs*nbIs*nbIs*nnQm;6Y)s67u4p]"CVs8N)Rs8N)-s8VuJp]&\B +g]&D)s8V8Nbklg4p&FK!GOKfUbkq53s/_g^s*nbIs*nbIs8N)Rs81FBs3gu&s8N)-s8VuJp]&\B +g].9R^An3Mm;7@MI=!8IPR[N,>Irn@F) +oVRSA;~> +ZMspU])Mg3rrD`l-ENm3s*ntTs*nhLs*nhLs*ntTmVcn+s6A/8p]"F[s8N)Us8N),rr3kep]&eE +hZ"b,s8V8Pc22j3p]'Z"GOK`RcMm\[s/_mbs*nhLs*nhLs8N)Us8U[Es4.2,s8N),rr47pp]&eE +hZ*TU^&S-#Dh%Y2GPD.*Dg1r*Dsi*nl?dI;s4V(CqYq3/hZ*(nFrrMP+qu@4B +lMnsu!7q2Mh>mTU!8d)B"4C5"qgncus+13$s-E]aecBjlp=9A2!9]S=J,~> +ZMsp]\c2X0p&>g)S=oo^IrFcTIrFcTIrFcTIrFcCDh%`c!s%3P6N6ug!8dbU!8d_T#d#g9;?5SR +!7q)J!Uan(!$c:EhLdC*GM;qaT`>%ADsmXTDsmZ*!8dbUXYDP.!8dbU!8d_T&?RZA;?5SRrrCpU +s6afTmVi"')JeVfDsi*nhJ[oDs6=BLbt\6=)#sU9hZ)F4rr3,P!<:mUrr3)O!<;fmh>i6#]ckth +hJWC4HY2"3o;T.pHZ/?Ps0W^DrrCpUs7ZNfs8Vi=r;R;a!<<'!hZ*V6@b1SZelI:rrtYEXHi*j? +io^:8K/#tYs8UpUs8N)UnG`OX9)ehlIrk44s+13$s+139s8LjUK`Cc&"IoJ\S5+S~> +ZMspZ\c2X0p&?*1Stc8bIr4TQIr4TQIr4TQIr4T?D12B_rVHTPpI5)]"T[V3*H$T5c +qr?l>omnkm4TGH9!<<'!g[P4JlUh!PrI&\UJcC<$JcC<$QN-s=!J:@FgB +ZMspU\c2X0p&>g)S=oo^IrFcTIrFcTIrFcTIrFcCDh%`c!s%3P6N6ug!8dbU!8d_T#d#g9;?5SR +!7q)J!Uan(!$c:EhLdC*GM;qaT`>%ADsmXTDsmZ*!8dbUXYDP.!8dbU!8d_T&?RZA;?5SRrrCpU +s6afTmVi"')JeVfDsi*nhJ[oDs6=BLbt\6=)#sU9hZ)F4rr3,P!<:mUrr3)O!<;fmec9d_XWbjD +enY8$G@oS/o;T.pHZ/?Ps0W^DrrCpUs7ZNfs8Vi=r;R;a!<<'!hZ*V6@b1SZelI:rrtYEXHi*j? +io^:8K/#tYs8UpUs8N)UnG`OX9)ehlIrk44s+13$s+139s8LRMF8tsb"I&oLPY-H~> +ZMsp]XT'?qTZ-s2IrFcTIrFcTIrFcTIrFcCDenYb+bB`R#irrCpUs6fs5s8N)Urtr0D +s6"1*qg3\es81gqu?QQk5TpLmXK0?HZ/?Pg1H9>rt>Chs7ZNMs8TJDs7ZNMs8TJDs0_j_!/0sW!S6E3 +h@cbbKAr[uSH#BWR-3<\F8u7?hZ!Nn;Hi8H=ulW_SG3'?!<<'!hZ*Vh8S6E%C(,UertMD4T]_t> +SH#Bk\c4+;s5!bUrrCXIrrMkap\tC.Pek%2Mh1nZs+13$s-r<0p>,qB!:QFQJ,~> +ZMspZXT'?oT#C[/Ir4TQIr4TQIr4TQIr4T?D/8J;ldl..ZASY]B`QofrrCgRs6g*6s8N)Rrtr3E +s6"7-q0@8]s81j>qqpH6fNEFFT#C[/Ir4TQIr4TQrrCgRmf^i6rrCgRrrCgRs6g*6s8N)Rs8N)R +s8VJ'J,+A8q>^?PkPp$Km=0!9H#;sKg1Q??rt>Ces766Fs8TMEs766Fs8TMEs0DXY!.sgN!RTm* +gCgG_KAiOqS,]?YQK-^RFoVIAg]%3ks//eRe-R6!<<'!g].;f8nZT'D%;'jrtMJ7T&l\? +Sc>Kk[ehJ1s4[PRrrCOFrrM_[q#:Tr&VRSA;~> +ZMspUXT'?qTZ-s2IrFcTIrFcTIrFcTIrFcCDenYb+bB`R#irrCpUs6fs5s8N)Urtr0D +s6"1*qg3\es81gqu?QQk5TpLmXK0?HZ/?Pg1H9>rt>Chs7ZNMs8TJDs7ZNMs8TJDs0_jW!-@b6!Qs9t +ee4cVKAr[uSH#BWR-3<\F8u7?hZ!Nn;Hi8H=ulW_SG3'?!<<'!hZ*Vh8S6E%C(,UertMD4T]_t> +SH#Bk\c4+;s5!bUrrCXIrrMkap\tC.Pek%2Mh1nZs+13$s- +ZMsp]XT'?CSF6FWIr"?LIrFcTIrFcTIrFcCD]t5!M`F]8>2K8YB`Q`arrCpUs6fs5s+CC's)TpG +moh`As/8tkkFbD:qgW)/dT1^nSF6FWIrFcTIrFcTrrCpUmf:Z5KE0U'rrCpUs6fs5s+CC's8N)U +s8VM*J,cHJT]_t>SH"(WmXK0CUe7$6LJk\Zrt>Chs8P\`kM@q7s8P\`kM@q7s'#EY!/0sW!S6E3 +h@f7a4U<3mmf3$S/I2Vtp](6nhZ!Ndmofu&9'?6S!8db4!<<'!hZ!NZ]h&VH?:o_BrtY:(0aK4q +o>t=#qu=qIf%pE,s$?^nr;QfS!;HKs[36(qGN+2RJcC<$JcD#8rn[[/s7Y:PS,i#J:4N~> +ZMspZXT'?ET'u^ZIqe0IIr4TQIr4TQIr4T?D'4qrM`al:?/PY]B`QW^rrCgRs6B[2qh>+"s)g$I +lWH3:s/K.lje>G>q0cc*c<#@kT'u^ZIr4TQIr4TQrrCgRlN#9.L&f^&rrCgRs6B[2qh>+"s8N)R +s8VJ'J,cNMT&l\?Sc=1Vm=0!?VG!64Ki5MYrt>Ces8Pebj4c81s8Pebj4c81s'>WY!.sgN!RTm* +gCihZ4piQun,N'P/-lYup](6ng]%3kmo^&):$;QV!8IP.!<<'!g].nDrtY:(1(#M" +o?(F$qu=tJe(Xm&s$6dnr;QfP!;HKs[NH+sH/jJUJcC<$JcD#8rn@I*s7Y1MRK2ZB9n3~> +ZMspUXT'?CSF6FWIr"?LIrFcTIrFcTIrFcCD]t5!M`F]8>2K8YB`Q`arrCpUs6fs5s+CC's)TpG +moh`As/8tkkFbD:qgW)/dT1^nSF6FWIrFcTIrFcTrrCpUmf:Z5KE0U'rrCpUs6fs5s+CC's8N)U +s8VM*J,cHJT]_t>SH"(WmXK0CUe7$6LJk\Zrt>Chs8P\`kM@q7s8P\`kM@q7s'#EQ!-@b6!Qs9t +ee78U4U<3mmf3$S/I2Vtp](6nhZ!Ndmofu&9'?6S!8db4!<<'!hZ!NZ]h&VH?:o_BrtY:(0aK4q +o>t=#qu=qIf%pE,s$?^nr;QfS!;HKs[36(qGN+2RJcC<$JcD#8rmh*ks7Y"HPQ9m28q6~> +ZMsp]XT/ +ZMspZXoAJ$StQ)_(n`'WfRIc*s*nnQs*nnQm;5El$nbQKqa(2\"[N9srrCgQruQRo'TPTjs8-XE +"@_5:s7`Ces8VC^#RkGAs8VC^#RkGAqa(59!.sgN#12Dt +s6\S.eGfU;D1@-:JcC<$JcC<$huE]1!J:@FgB +ZMspUXT/hWk.+ +f':cb!q'uVJcC<$JcC<$JcFX-rmh*ks7Y"HPQ9m28q6~> +ZMsp]R/[6UDh!?Kn!m.'~> +ZMspZR/[6TD1@-:nc/UC!.sgN#12Dtm;6_-eGfU;D1@-:JcC<$JcC<$huE]1!J:@FgB +ZMspUR/[6UDh!?!-@b6#0Pffl>(2#eGfU +ZMsp]R/[6UDh!? +ZMspZR/[6TD1@-:nc/UC!.sgN#12DbD#eG?eGfXDVMSIQ!!*:XJcC<$JcC<$JcFs6rn@I*s7Y1M +RK2ZB9n3~> +ZMspUR/[6UDh!?!-@b6#0PfRCB//8eGfXFWJk$X!!*4TJcC<$JcC<$JcFs6rmh*ks7Y"H +PQ9m28q6~> +ZMsp]R/[6^WNh$Snc/UF!/0sW!S6E3h>ltps+13$s+13$s0)J%h>r<0p>,qB!:QFQJ,~> +ZMspZR/[6\VQbXOnc/UC!.sgN!RTm*gApVls+13$s+13$s0)J%gAup+p=f_=!:-(JJ,~> +ZMspUR/[6^WNh$Snc/U>!-@b6!Qs9tec=uds+13$s+13$s0)J%ecBjlp=9A2!9]S=J,~> +ZMsp]JcCH(rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjUK`Cc&"IoJ\S5+S~> +ZMspZJcCH(rn@F)oVRSA;~> +ZMspUJcCH(rmh'jo;r&9hYl"*l@Jtds+13$s+13Rs8LRMF8tsb"I&oLPY-H~> +ZMsp]JcCH(rn[X.o=Y@Zk3i9;hX5spJcC<$JcC<$YQ+UY!JLLHh?9>Kn!m.'~> +ZMspZJcCH(rn@F)o +ZMspUJcCH(rmh'jo;r5>hWk.+f'7hdJcC<$JcC<$YQ+UQ!H\;7ec_3;kEJSh~> +ZMsp]JcCH(rn[X.o=Y@Zk3hYUDsi2VJcC<$JcC<$YQ+UY!JLLHh?9>Kn!m.'~> +ZMspZJcCH(rn@F)o +ZMspUJcCH(rmh'jo;r5>hWjBAC[-KNJcC<$JcC<$YQ+UQ!H\;7ec_3;kEJSh~> +ZMsp]JcCH(rn[X.o=Y@Zk2("VDsi2VJcC<$JcC<$YQ+UY!JLLHh?9>Kn!m.'~> +ZMspZJcCH(rn@F)o +ZMspUJcCH(rmh'jo;r5>hUZHBC[-KNJcC<$JcC<$YQ+UQ!H\;7ec_3;kEJSh~> +ZMsp]JcCH(rn[X.o=Y1Uk5F-:ma;%:\NO9QpAY3^Dh!?Kn!m.'~> +ZMspZJcCH(rn@F)o +ZMspUJcCH(rmh'jo;r&9hYl"*lI#V6\NO9QpAY3^Dh!?!H\;7ec_3;kEJSh~> +ZMsp]JcCH(rn[X.o=Y1Uk5F-:ma;%=Iq.L +ZMspZJcCH(rn@F)o +ZMspUJcCH(rmh'jo;r&9hYl"*lI#V9Iq.L +ZMsp]JcCH(rn[X.o=Y@Zk3i9;hX8ek#(LRZs*ntTfDc&6?A%+! +ZMspZJcCH(rn@F)o +ZMspUJcCH(rmh'jo;r5>hWk.+f':Z_#(LRZs*ntTfDc&6?A%+! +ZMsp]JcCH(rn[X.o=Zr<0p>,qB!:QFQJ,~> +ZMspZJcCH(rn@F)o7b^]Ni!UYC2rruP +ZMspUJcCH(rmh'jo;s1YhWja5T^(C.K94.Is81-.K86l3s8RT"ON%7sZ%mt1p\Xt+dZAs:c2[h@ +c^'9Vf_ri]4/fGR4/hNBrt"h4^]2&uer[lN`J]55c%#Wj!UbI3rruM +ZMsp]JcCH(rn[X.o=Z'k +ZMspZJcCH(rn@F)oVRSA;~> +ZMspUJcCH(rmh'jo;s1YhVbCkCWhQDF^f1+s3GmuGBZfis8N(;CVBbH>'k +ZMsp]mf*=ejamH#s8LjTK_)l$f&khdDl/n0XRODe[/H.o_t3;;7K+np\BtY_sjm8UAo^]mf.cTmeQknmVdUTIfOQDpJtVSpYK?Zqu6]oc1q;Djo@>]F^B:>JcC<$ +JcC<$QiI'A!JLLHh?9>Kn!m.'~> +ZMspZmf*=ejamH#s8LaQK(HDpd,WlVD5!J:@FgB +ZMspUmf*=ejamH#s8LRLF7ZLXb21^BCS6o"XRODe[/H.o_t3;;7K+np\BtY_sjm8UAo^]mf.cTmeQknmVdUTIfOQDpJtVSpYK?Zqu6]oc1q;Djo@>]F^B:>JcC<$ +JcC<$QiI'9!H\;7ec_3;kEJSh~> +ZMsp]nc&aka'pCGJcD2=rn[X.o=Y1Uk5F-VGAH4EHrrC1@s69T.It)A:ir/lm +pKi&rSC[`4Fa*QZib4&GIrFcTIrFcNrsJ14J,auuaoDAP^&J$4_#X!)#N>_[a6pQER">9gs+13$ +s-Ncbh>r<0p>,qB!:QFQJ,~> +ZMspZnc&aka'pCGJcD2=rn@F)o +ZMspUnc&aka'pCGJcD2=rmh'jo;r&9hYl"FF)0e8q;CE-Fa*QZs2>EHrrC1@s69T.It)A:ir/lm +pKi&rSC[`4Fa*QZib4&GIrFcTIrFcNrsJ14J,auuaoDAP^&J$4_#X!)#N>_[a6pQER">9gs+13$ +s-NcbecBjlp=9A2!9]S=J,~> +ZMsp]o`#-pa'oo!"gXHo!UbGls8LjTK_)l$f&l,_k2(5oirAi(g@TN3pAb03%0->-hZ*W@!.03F +HY;XBrtbLJUka.tp[E +ZMspZo`#-pa'oo!"gXHo!UYAks8LaQK(HDpd,X3SjP+`iirAi(g@TK0p&G'3%0->-g].<mJhZQmJ6bhm;7@QIfS7'rsZ^BrrKn8nG`a`#'fh@jD'C" +s+13$s+13:s8LaRK)bQ!"I]>VRSA;~> +ZMspUo`#-pa'oo!"gXHo!UbGls8LRLF7ZLXb22(Ai7;m]irAi(g@TN3pAb03%0->-hZ*W@!.03F +HY;XBrtbLJUka.tp[E +ZMsp]p\tNua'oo"!!!O]^]4>Kg&D*0!:Tph_n5W3s8LjTK_)l?f&lDoY,7=dY40V[W;Vf__t3;; +7Kf6'8>b_7JDsmXTD=%)u&+4@cs*k%*s8PZ"g@sH5 +!;lcsqp"m8"uU&2m^HMoJcC<$JcC<$QiI'A!JLLHh?9>Kn!m.'~> +ZMspZp\tNua'oo"!!!O]^]4>Kg&D*-!:Tph_n5Z4s8LaQK(HE6d,XNeXe_%`YOK\[W;M]]_=?o6 +7fWJgg].f#m5>b_7JD=.@QC[:fr&++4`s*k%'s8P]"g@sH6 +!;lcsq98U5"up>8m^?GmJcC<$JcC<$QiI'>!J:@FgB +ZMspUp\tNua'oo"!!!O]^]4>Kg&D*0!:Tph_n5W3s8LRLF7ZLsb22FUWh>>VY40V[W;Vf__t3;; +7Kf6'8>b_7JDsmXTD=%)u&+4@cs*k%*s8PZ"g@sH5 +!;lcsqp"m8"uU&2m^HMoJcC<$JcC<$QiI'9!H\;7ec_3;kEJSh~> +ZMsp]q>UQ<@MojX!!*@'^Ae7a5JPqP!T!hFrrRgom[O7Uh>i6#]bo>_k,pX]bCa2F@Vj$`c#mK> +F&'.CrrCpTs!"+>GBZrHs8PjZs8,]/HY6CVrQTIoF^TdSs*ntTs,]^Em/$_mmVdUTIfS@*s1M!9 +GB6dbqu6]^:A=bmXaaf$@Y*0_s+13$s+13:s8LjUK`Cc&"IoJ\S5+S~> +ZMspZq>UQ<@MojX!!*@'^Ae7a5JPqP!S[VCrrRgnm[O7UgAlis[M[BOjK1=Wab+#E@r93bc#dB: +F%j%BrrCgQs!"1?GB[&Ls8PgVs7oT/H"U4Sr69=lFC9[Rs*nnQs,TXDlh^Vlm;7@QIfS7's1Cs: +GB6mequ6][:&"YlXFF`$@Y!']s+13$s+13:s8LaRK)bQ!"I]>VRSA;~> +ZMspUq>UQ<@MojX!!*@'^Ae7a5JPqP!T!hFrrRgom[O7Uec9d_XVf4;i2SYMa+IcB@Vj$`c#mK> +F&'.CrrCpTs!"+>GBZrHs8PjZs8,]/HY6CVrQTIoF^TdSs*ntTs,]^Em/$_mmVdUTIfS@*s1M!9 +GB6dbqu6]^:A=bmXaaf$@Y*0_s+13$s+13:s8LRMF8tsb"I&oLPY-H~> +ZMsp]qYpo=DClV?$NL/-0)<6]"+NOb^\%O4^SIl`SAD.XT"4k#(&+YZLUI$Zs1sVCPdpeos80'V +K;eP>ruo?M4/g9*e,THgY26XdY(`+dqu?WGS:uTop](9AOFN25\c;Zc!/0sW(tRj+VJi@"g7EqY +a7fQ1c^'39[JU"(J+!=9,580kK;A,=s*sJ:s36IOOOjI3qp1R\SCmf?^UNq:c]G6pqYpcn\%hsX +J+!=9"hfhDPdpekrrMP;p\tHpe!PcXf_pC$JcC<$JcD):rn[[/s7Y:PS,i#J:4N~> +ZMspZqYpo=DClV?$NL/-0)<6]"+NOb^\%O4^SIocS\hC]S[e\!(&+_\LU?sYs2'bFPI:Jks80*W +K<"\@ruoBO3iC'(eGoQiYhcaeYD/:eqZ$NFS:uTqp](9AOaiA7\c;Z`!.sgN(sq^UEk9c]G9pqYq*"[_MjW +J*m:9rP"2>PILVirrMM:p\tHnd[,WWfDU:#JcC<$JcD):rn@I*s7Y1MRK2ZB9n3~> +ZMspUqYpo=DClV?$NL/-0)<6]"+NOb^\%O4^SIl`SAD.XT"4k#(&+YZLUI$Zs1sVCPdpeos80'V +K;eP>ruo?M4/g9*e,THgY26XdY(`+dqu?WGS:uTop](9AOFN25\c;Z[!-@b6(s:^dTPL4cg7EqY +a7fQ1c^'39[JU"(J+!=9,580kK;A,=s*sJ:s36IOOOjI3qp1R\SCmf?^UNq:c]G6pqYpcn\%hsX +J+!=9"hfhDPdpekrrMP;p\tHpe!PcXf_pC$JcC<$JcD):rmh*ks7Y"HPQ9m28q6~> +ZMsp]p\tHsIMDh_)sY-:s*k*bJ%t[V,(D\(ECs>]ED/Cas8S`eGBZfis1qHBGB6dbs8T$$HZMrP +q#;PW76@H]AsE8`HZ.AO3HoOHF3jm8JSTIN:7V@Y;J1>g:A;@"rn[X.o=Y1Uk5F-:m^2uph>i-, +JcC<$JcC<$rr;uS!JLLHh?9>Kn!m.'~> +ZMspZp\tHsIMDh_)sY-:s*k*bJ%t[V,(D\)F%]YbE_AC`s8S`fF`gBds2%QGGB6gcs8T*)H#lcO +q#;PX7QRHZAsE8aHuIDL3d>aLER+U6K55XO:RqIZ +ZMspUp\tHsIMDh_)sY-:s*k*bJ%t[V,(D\(ECs>]ED/Cas8S`eGBZfis1qHBGB6dbs8T$$HZMrP +q#;PW76@H]AsE8`HZ.AO3HoOHF3jm8JSTIN:7V@Y;J1>g:A;@"rmh'jo;r&9hYl"*lEpQlh>i-, +JcC<$JcC<$rr;uK!H\;7ec_3;kEJSh~> +ZMsp]q#:QU6[V9%'[s_ps*k.N^OO#ls!*"eam*_:am*^CirAm)Pk4 +df0=Hir9#5q#:QU6[V9%'[s_ps*k.N^OO#ls!*"eam*_9aQdUBiW&^%P4@p\8H2Oog@sH6!<;pM +T^hK8,kq*6+b+@rnpGNBb@Qt(D+D>-\6"giQ"\8.n!#*lmf]T^g3NG(s8LaQK(HDOd,Y)Z!:@u0 +!UHiaIfY/VRSA;~> +df0=Hir9#0q#:QU6[V9%'[s_ps*k.N^OO#ls!*"eam*_:am*^CirAm)Pk4 +ZMsp]q>UYh29Gk=&?`YFs*k.N^OO#lrtU$#m/MQPm/MP8hZ)j@mf3=+%0$aLrr3>A!<;>HpKi&rSCY7Nrn[X.o=Y1Uk5F-:m`YV2m&^)6 +!J/)ZrrMl!JcC<$JcC<$JcG`Lrn[[/s7Y:PS,i#J:4N~> +gA_?Ur;6?dq"sdgq"adarp'L`g\LjRS2JFg*>#Bcs8RQNs1a&upAYkWC[;"MC[;"M!8IP^]=E#!d0!BDZJhmrucf.mJito^RSlTm;6t;s7:;SlZP@9It)A:j7cu+K:C<"s8LaQ +K(HDOd,Y)Z!:@u0!UHiaIfY/VRSA;~> +gA_?Ur;6?dq"sdgq"adarp'L`f(o=MS2JFg*>#Bcs8RQNs1a&upAYkWD=%:PD=%:P!8db@!:Kme +`=2bT^&J$=_#XN#!-`pBDZJelrucf0mf0(n^ReuTmVd7?s7CGVl?,18It)A:iqHc'K:LN's8LRL +F7ZL7b23*J!:%c-!UQobIfY,:o`"sdZ%;q+s+13$s+14Ls8LRMF8tsb"I&oLPY-H~> +ZMsp]qu6oa9lB\)p&]]Ss8RQM^OO#krtU$%mf.cTmf.b:hZ)j@mf3=+%0$aPrr3>A!<;Kn!m.'~> +h>[c[qt^!ZoCDG@r9s[T#4_9Vp@nF^mJd1@qu6oa9lB\)p&]]Ss8RQM^OO#krtU$#mJhZQmJhY9 +g]-L@!<;9`H%1F?Fr#IsYhoqnf4c +F]nIMs8LaQK(HDOd,Y)Z!:>@;JcC<$JcC<$YQ+UV!J:@FgB +h>[c[qt^!ZoCDG@r9s[T#4_9Vp@nF^mJd1;qu6oa9lB\)p&]]Ss8RQM^OO#krtU$%mf.cTmf.b: +hZ)j@mf3=+%0$aPrr3>A!<; +ZMsp]r;Qhu1;3\e!Q\ESrr[a8J%tXU,(FP!s*ntTs*k%*s8++,oBpZgs%[%WpYKN_s8+7DoCJo5 +p\uCdD=%;nI5t?*s6aep^$`L7P5CDUc1CP*KE([YhYXPXo=Fu$h>i6#]`H^HrS@PBJcC<$JcC<$ +JcDqRrn[[/s7Y:PS,i#J:4N~> +hu=)^q=aINn*TK,l0.@0k6pM@;JcC<$JcC<$YQ+UV!J:@FgB +hu=)^q=aINn*TK,l0.@0k6pM +ZMsp]rr3)g>%$n!rrLF?\c2_\5JQgi,(FP!s*ntTs*k%*s8STaGB6Nes1qHBGB[Nss8S`qGBZrH +p\uCu7\]8LVCPj#s6ae_EHPN-ci;=>BQnYuOT52UC3sSoqmuh,h>i6#]`H^HrS@PBJcC<$JcC<$ +JcDqRrn[[/s7Y:PS,i#J:4N~> +ir9MfqtTjTn*K?'k2bR]i8FRk%H-4)j5oFelg=35pA4dZrrCgQrr_[m@.F3q!RCJNrrR[7^[qIL +Ir4TQIr4TQIfS7's-uf+F%j%B_G+ZeG@LXQs.WY?G?'e"rumFmU?hiV/6pd+m;4\!FBs"7s3Q1- +GB[&Ls7oT/H"U4S])Vca!.sgN!RTm*gApVls+13$s+13$s0)J%gAup+p=f_=!:-(JJ,~> +ir9MfqtTjTn*K?'k2bR]i8FRk%H-4)j5oFelg=35pA4dZrrCXLrr_[m@.F3q!RCJNrrR[7^[qIL +IrFcTIrFcTIfS@*s-ui.F&'.C_+nTdG@LXQs.EP>G>aOsrumInU?qoW.pCL'mVat&F^0"6s3H+, +GBZrHs8,]/HY6CV])Vc\!-@b6!Qs9tec=uds+13$s+13$s0)J%ecBjlp=9A2!9]S=J,~> +ZMt*bs8TS`Yd+6.^[hCK^UNq:^UNq:^OP\Os7`IEK=1UVs1sVCR*pKas7`UIK;A,5rufXLXR,u' +:keWCmVcX7KpL'^s8132K7fuks8U?%Iur6ls8LjTK_)kXf&lqf!:GFKn!m.'~> +j8TYeq"4+ElKI?hi838Dg=b-XfDjJ5g"P3:hr3VXkj.[-p%n[[rrq0Ws0sefZ2aj=o)BoK[eBa9 +[eBa9J*m:9or*Ls[/9n'^l*]/dXV;spT0($VXsQ^+O?.Oli4dtLA1`8D.KT!QI,O/qpCdaK;S8? +s3?OPOjprArn@F)oVRSA;~> +j8TYeq"4+ElKI?hi838Dg=b-XfDjJ5g"P3:hr3VXkj.[-p%n[[rrq!Rs0sefZ2aj=o)BoK\+]j: +\+]j:J+!@:pS`^s[JU"(_2Ef1dXV;spT0""V"=?\+O?.Nm/OmuL%bQ7De,f#QI#I.qpCd`K;A,= +s36IOOOguBrmh'jo;r&9hYl"*l@Jtds+13$s+13Rs8LRMF8tsb"I&oLPY-H~> +ZMt'aoOq.fJcG0<#l19*\,ZKrDh"V`rn[X.o=Y1Uk5F-:mc"0HmXO'm!IsP1rrMP;JcC<$JcC<$ +L&_//!JLLHh?9>Kn!m.'~> +jo6\+q=X:FlK7-bhV6`8e^Msod*U+bd*U1geCE1(gtq#NkNhU-pAFU`"P`Q^;X/i5n,E^npnhT2 +s8VJ'J#32EgAlis[K4b8rS%>>iVrrGJ)1,*J*lq/!UYAfs+13$s+13(s8LaRK)bQ!"I]>VRSA;~> +jo6\+q=X:FlK7-bhV6`8e^Msod*U+bd*U1geCE1(gtq#NkNhU-pAFU`"P33Y;X/i5mf*RgUggh5 +s6afTV>pPG!-@b6!Qs9tec>!orrMP;h>[M*me-S\mXKffJcC<$JcCH(rmh*ks7Y"HPQ9m28q6~> +ZMt!+1STI#qYpQLqL8Ns_>aH@n>fTDkl:\RN0pIArn[X.o=Y1Uk5F-:mc"0Hh>lC3!lrrq\\ItO]oJcC<$JcC<$\,ZHa!JLLHh?9>Kn!m.'~> +k5Q%jp[dhpPL!.sgN!RTm*gApX"rrLjSh>[KUg\CdOL[Y9'3n=66l_&f]WqCrCJcC<$ +JcE4Zrn@I*s7Y1MRK2ZB9n3~> +k5Q%jp[dhpPG!-@b6!Qs9tec>!orrLsVh>[KUhY@*RL[P3)3S"-5lC`WZW:tlCJcC<$ +JcE4Zrmh*ks7Y"HPQ9m28q6~> +[/U7(G\ec,es:31JOS:U]ks+13$s0r%-h>r<0p>,qB!:QFQJ,~> +kPl4lp$qD4jP\hHe^Ddfb/hT@_nj1f^_=Q$_o0R9bK\D_f%Ss=jlu4(q#:-i"8R.'j+77+rrC^J +IfY/>iVrr5!8[YU!8I;K":3i`ot&`S#5rgCF`U?I +JcC<$JcC<$\,ZH^!J:@FgB +kPl4lp$qD4jP\hHe^Ddfb/hT@_nj1f^_=Q$_o0R9bK\D_f%Ss=jlu4(q#:-i"8R.&j+77+rrCdL +IfY,:rr3)iZ'<`:rrW)IfVSWIec9d_XT?T$rRLu6iVrr8!8[YU!8dMN":3lapUSiS"a<^_F&&6V +s+13$s+13Zs8LRMF8tsb"I&oLPY-H~> +[Jp?U2jE-Ls+ULPh>i6$h?':Gk5F-UmbX[jON7(pqp1R\LUI$Zs*rU]`W,h?LP)Q"r;S/$!<<)8 +ORDi@esqG[chmaWWjA_b^T+c$LU6:GrRRKmQI#I.rrCpOrt((gGM;J7s8RSjMgr:ZLP*/:rs\kW +S;!9Tqu=F2Z`<$`rrVo'^\Ig5l3sK[mYiHCs+13$s+13Zs8LjUK`Cc&"IoJ\S5+S~> +l2MLtq=O.Ak2P4Me^DadaMl$3^:_(h\c'#P\[oDd^VRh-air)[f%T$AkNqa4rVZZtco73CJcCH( +rn@F)o\'A8UZ;=-)sl@SKosFLs810/K86i2s8RSuO2V(qZA4(4p\b%=gAq9R_N40[rRRKmQI,O/ +^T+T>iP2G'NJOn)s8CN=KpL*_s8N)Rq>V"c!-Qr]J,fOuaGkb^b)(b\rr3E*esqG[d/3jXWj8Y` +rVlrm[_MV;#3QJloC%VbJcC<$JcC<$\,ZH^!J:@FgB +l2MLtq=O.Ak2P4Me^DadaMl$3^:_(h\c'#P\[oDd^VRh-air)[f%T$AkNqa4rVZZtco73>JcCH( +rmh'jo[X)0T&0Fs)sZ:SK94.Is81-.K86l3s8RT"ON%7sZ%mt1p\b%=h>mTU_3"*YrRRKmQI#I. +^T+W?ikMM&MheY(s8CN=KpL'^s8N)Uq>V"a!-d/bJ,fOuabtYZabPMZrr3E*esqG[chmaWWjA_b +rVlrm\%h_<#3lVmoBqP`JcC<$JcC<$\,ZHY!H\;7ec_3;kEJSh~> +\,QX0Kg+5jhLY]\s8LjUK`Cc6"5[X6k5F-Uc\Z+R@Vj$`cZ`iBG>>RGrr?tQ^&P$7HZMrPr;S/$ +!<9l5K^SQ_JSTI;Fj9r92f4f9Ili.4Alc)7dt):Y@!0`orrCpOrud4"GM:mRs8N(CLKZ]^K3gS0 +q>\opCNj0/c2W8PD3WoUrrVh_VYL/qk&:"os0WPls+13$s+13Zs8LjUK`Cc&"IoJ\S5+S~> +lMhY!q=O+>jl"nFe'H4X`501"\[SuQZEaD5s03QL['dBR]YD>&aNW#\f\PNKlgXTGs8W"N52-%k +JcCH(rn@I*s7Yg^gh(]hrS&:8Lh([=CrHA"G@G];9Xb!$!,lYas/K@MF&/>(s!QP$s1hWcn+Y=a +CNa-/c2W8PD3E\(4+MusL+H6ADf\6E+rr@8">rm1`?T@ZQs3u[:G%#O< +s*ltEP2-$9!qVkXq#:Q[D>!r/Zl"AoJcC<$JcE4Zrn@I*s7Y1MRK2ZB9n3~> +lMhY!q=O+>jl"nFe'H4X`501"\[SuQZEaD5s03QL['dBR]YD>&aNW#\f\PNKlgXTGs8W"N52-%f +JcCH(rmh*ks7Y^[g159]rRMq2M.1U:CrQG#G@Gc?:::-%!,lYds/9+JF&&8's!QY's1qTdme5+\ +CNj0/c2W8PD3Wn,4+Dlp2i0rr@7u=ugeZ?9.WQs3lO7G@5R< +s*ltEPhuB=!q`"[q#:Q\Dtj;3[1n2lJcC<$JcE4Zrmh*ks7Y"HPQ9m28q6~> +\Glce5)&q7s5&>,L&_//!JLLHh?1GdGNSk\*11-8nB6NmrK:L)oBpZgs8N(?g@tIrT_%T9-27B< +-f+j:6?i&$rKLp9nA/@iqgUW'kkfio\+\?UKDh$Hamd,ONq`PRhY@*m!8db4!<<'!R/>S;R/>S; +ipco2B$'PY>`S]23Mu$;rVlrtjQ?7;#5digpUsaWJcC<$JcC<$\,ZHa!JLLHh?9>Kn!m.'~> +li0!Eq=F%=jPS\Ad*0SL^qI:e['?g:XK&8!W2Q\qX/rJ.ZF%*P^;7e1c-k1ti8s7mp&3T.L&CuN +gO]BYs8LaRK)bQ!"1h3Lj8I^PQ=6rcb(R/"OBo]mm;khurr@2qp%pG9naD\fp\k+>gAnlPGOb_3 +Q"\8._-DusIlaLjqL:`=p=*8urKV';nA/@hp\t6Jq#;E9g]-".s8N(dqq;%9qq;&/nGSVcaRI#N +NVe2dVs!pTrrW,XjnSi[qsFFX]NKT4s+13$s+13Zs8LaRK)bQ!"I]>VRSA;~> +li0!Eq=F%=jPS\Ad*0SL^qI:e['?g:XK&8!W2Q\qX/rJ.ZF%*P^;7e1c-k1ti8s7mp&3T.L&CuN +eq*jTs8LRMF8tsb"1:aBhYl"FP[LT^bD!>%P$Z&tmW1hsrr@,op\QP8oBqhhp\k+>h>k5UGOtk5 +P\/&,_-E#tIlXCiqgUi>p=3?!rKLp9nA/@ip\t6Mq#;E9hZ)F4s8N(dqq;%;qq;&0nGSS`amd,O +Nr+;dVWdpUrrW/[k4nr\qsOLY]ioc6s+13$s+13Zs8LRMF8tsb"I&oLPY-H~> +])Mm3OuLbdrrCo,s+ULPh>r<0p>,nb:3Yk!hAg`TirAi4k4Ee?pAb03%0->-aoDD+!-`pBDZJeq +s!QY'=s8k)s7CFqH[g5uEU\HgjT#70B'0-]Dh%D%4*uI$2f[jW!8dMN*<=srh>mTU!8@JQ!8@JQ +!8d/9FZXr"G;jO2Iq7X-rrgGrF+`V+s+13$s+13Zs8LjUK`Cc&"IoJ\S5+S~> +li.dso^:r'hV$E)b/V92\[JfIXK&1qUna[_TbsN!V5C2lXg#.@]>)8(bg=nohrX.lNAo5_rrCf) +s+ULPgAup+p=f\\9m,LpgDk?OirAi3jn*Y;p&G'3%fcP/aT);+!d0!BDZJhrs!QP$=sAk(s7LOs +H@L,tEUeNhj8]./B'''[D1D5%4*lC#2f[mX!8I;K*<=jogAq9R!8%8N!8%8N!8Ho6Fut#"G;jO3 +Iq7U,rrgJsF+NG(s+13$s+13Zs8LaRK)bQ!"I]>VRSA;~> +li.dso^:r'hV$E)b/V92\[JfIXK&1qUna[_TbsN!V5C2lXg#.@]>)8(bg=nohrX.lNAo5_rrCW$ +s+ULPecBjlp=9>R8orkfef8aHirAi4k4Ee?pAb03%0->-aoDD+!-`pBDZJeqs!QY'=s8k)s7CFq +H[g5uEU\HgjT#70B'0-]Dh%D%4*uI$2f[jW!8dMN*<=srh>mTU!8@JQ!8@JQ!8d/9FZXr"G;jO2 +Iq7X-rrgGrF+`V+s+13$s+13Zs8LRMF8tsb"I&oLPY-H~> +]Dhum7X+\+rrCo,s+ULPh>r<0p>,nb:3Yk!hAgZOirAi(g@TN3pAb03%0->-hZ*W@!.03FHY;XB +s!QY'R$9&Js7CFsI=HZbW:YRXmf3<:DsmYnDh%D%4ahg.EN&1&!8dMN*<=srh>mTU!8dbU!8dbU +!8d/9FZk/&I;u4XIrFcCrrfT=J$o$%s+13$s+13Ys8LjUK`Cc&"IoJ\S5+S~> +m/Iq!o^:u(hUp<&ai(s+\$N9=W268^St):=rL"IjS"61FUSb#lYdCpQ_8XRCe_8p(6#cAlrrCf) +s+ULPgAup+p=f\\9m,LpgDk9KirAi(g@TK0p&G'3%0->-g].<VRSA;~> +m/Iq!o^:u(hUp<&ai(s+\$N9=W268^St):=rL"IjS"61FUSb#lYdCpQ_8XRCe_8p(6#cAlrrCW$ +s+ULPecBjlp=9>R8orkfef8[CirAi(g@TN3pAb03%0->-hZ*W@!.03FHY;XBs!QY'R$9&Js7CFs +I=HZbW:YRXmf3<:DsmYnDh%D%4ahg.EN&1&!8dMN*<=srh>mTU!8dbU!8dbU!8d/9FZk/&I;u4X +IrFcCrrfT=J$o$%s+13$s+13Ys8LRMF8tsb"I&oLPY-H~> +]`/&,1WB4j!8`;,L&_//!JLLHh?1GdGNSk\8LQqt!Qdp\Btcc1B2G_>f"'mf3<:DsmYnDh%_KB[-.nQGNV$!8dMN*<=gnh>mTU!8dbU!8dbU +!8d/BP%`,3c'H3GIrFcPrrW/[k4nrYl7meFrp#(7JcC<$JcE4Zrn[[/s7Y:PS,i#J:4N~> +mJf*Ap@%;-hUp<%aMYa&[BQa2V4jKMR?s2&P*1riP*;,qQ^XG:U8FolZF7B\`6-EUMCl+!nbE(_ +!8E))L&_/,!J:@FgB5#]FlWGT8<@)[nB-$Tr/Y+!na1Bes8N)Rs8VmEVXa8XL&_1sq>]1LqsdNh +p\9kabOWrF_Z,+&mJm39D=.AjD1DJGBZotlQbre&!8I;K*<=^kgAq9R!8IPR!8IPR!8Ho=O_Dr/ +cBlEJIr4TMrrW,XjnSiXkq[hGrp,.8JcC<$JcE4Zrn@I*s7Y1MRK2ZB9n3~> +mJf*Ap@%;-hUp<%aMYa&[BQa2V4jKMR?s2&P*1riP*;,qQ^XG:U8FolZF7B\`6-EUMCl+!nbE(_ +!7l`$L&_/'!H\;7ecWLQqt!Qd +p\Btcc1B2G_>f"'mf3<:DsmYnDh%_KB[-.nQGNV$!8dMN*<=gnh>mTU!8dbU!8dbU!8d/BP%`,3 +c'H3GIrFcPrrW/[k4nrYl7meFrp#(7JcC<$JcE4Zrmh*ks7Y"HPQ9m28q6~> +^Ae<#;J5XtrrCo,s+ULPh>r<0p>,nb:3Yk!h@e"a@!,pp`W+2*@Wulg[K$7,hZ!O1TNZP`;NUqY +:?ql9!<<)@,c&i6I;!h8EQA*/DsmZ*IrFcTmVdUTc[BJNA9#ZirrCpNruE7ug>2i0rrCpUrrCpU +rrCpDs3H+,G@Y^6s*ntTqu6`iS>Q)]!kA;@rG-uaJcC<$JcE4Zrn[[/s7Y:PS,i#J:4N~> +mf,9Fp[RS2hq?K'aMY^$Z`^=*U7Rg?P`_#dN/ELLM2I4MNf]EhR%'_CVlR&0]"c1o6W_^pkO%m= +rr2uQJcCH(rn@I*s7Y1L\kR>-rS%t,K4/n4D8lP"FCB<59Xb!$!8IMQ187oqG?'e*s%V\VgAq9R +asO"6d!]SOAol&nIr4TQs*nnQs6XZQs3Q1-G@kp;s8N)Rq#;?WCY7AAs8N)Rs8N)Rs8N)RmJkO] +BQn'0aT$a,mJHnbo9uQOrrTV/A,U1os+13$s+13Zs8LaRK)bQ!"I]>VRSA;~> +mf,9Fp[RS2hq?K'aMY^$Z`^=*U7Rg?P`_#dN/ELLM2I4MNf]EhR%'_CVlR&0]"c1o6W_^pkO%m= +rr2uLJcCH(rmh*ks7Y"G[7YMsrRMV%K4&h2CrQG!F^fQ9:::-%!8d_T18%fpG>aP&s%_kZh>mTU +aX!\0c[BJNA9#ZiIrFcTs*ntTs6afTs3H+,G@Y^6s8N)Uq#;?XCYIVFs8N)Us8N)Us8N)Umf1U] +BQn!,a8^X-med"coUMfRrrT_2A,U1os+13$s+13Zs8LRMF8tsb"I&oLPY-H~> +^]+A>0t?uX!8`;,L&_//!JLLHh?1GdGNSk\'(4JFK94.Is81-.K7gT/s8RT:rr3E$XFl/$oDei? +mf2^)rr414XO[3qR"LXHq>\4=pAb0-\+]jq\%ht?d?oQNbPD2 +mf+4$oBk\ug=4Eh_nERcXf/%fS!T>#NJ`OHKS05T)2!TsLl7:UPaIu7V5g`+\V);md+7"3kjJ-C +rrCf)s+ULPgAup+p=f\\9m,LpgCil+SqN6Ip](*8Q%Odtqu?\GmJd+opT0($VXsifJ*m:'J,]Hl +S$(?>d?oTObk_:R[eBb$^UEk9p9qa9qpCdaPg&t's*sG2ruIb`f^41gs*sG9s*sG9s*sG0s8132 +KTs^Vs1c#9qu6`k[_MV;!6k=iJcC<$JcC<$\,ZH^!J:@FgB +mf+4$oBk\ug=4Eh_nERcXf/%fS!T>#NJ`OHKS05T)2!TsLl7:UPaIu7V5g`+\V);md+7"3kjJ-C +rrCW$s+ULPecBjlp=9>R8orkfee79%SqE-FpAb$9Q@jgtqu?\Gmf*4ppT0""V"=WdJ+!@)J,]Hl +S?19 +_>aW-?sr<0p>,nb:3Yk!h>ltps+13$s+13$s0)J%h>r<0p>,qB!:QFQJ,~> +n,F@)p$_,(gt'fn_nNXdXf/"dR?`noM2$Y5IXHM@H6%?\It<9.MN3jcS"HLUUdRPT`QQZ\hW3tl +qZ$TMJcCH(rn@I*s7Y1L\kR>-rS%>>JcC<$JcC<$JcDqRrn@I*s7Y1MRK2ZB9n3~> +n,F@)p$_,(gt'fn_nNXdXf/"dR?`noM2$Y5IXHM@H6%?\It<9.MN3jcS"HLUUdRPT`QQZ\hW3tl +qZ$THJcCH(rmh*ks7Y"G[7YMsrRLu6JcC<$JcC<$JcDqRrmh*ks7Y"HPQ9m28q6~> +_>aPBX7ZDlhLY]\s8LjUK`Cc&"2.HRk5F-:m^iDugA6:!K7X&Rs+13$s+14Ms8LjUK`Cc&"IoJ\ +S5+S~> +n,F@%o'>Anf[7m]^U^\QVkT`LPE(NTJq&/nGBJ!+EZT:DFa&.ZJ:iW8OHc0*Uj$sg]"lA2e(N[A +mI^DXgO]BYs8LaRK)bQ!"1h3Lj8I^4mCN;tf_U'tKn98Ts+13$s+14Ms8LaRK)bQ!"I]>VRSA;~> +n,F@%o'>Anf[7m]^U^\QVkT`LPE(NTJq&/nGBJ!+EZT:DFa&.ZJ:iW8OHc0*Uj$sg]"lA2e(N[A +mI^DXeq*jTs8LRMF8tsb"1:aBhYl"*lFQuqgA6:!K7X&Rs+13$s+14Ms8LRMF8tsb"I&oLPY-H~> +ZMsp]JcCH(rn[[/s7Y:O]hWe6rS@PB])McaqL8KtJcC<$JcC<$JcGcMrn[[/s7Y:PS,i#J:4N~> +nGb]Lp$V#%g=+ +nGb]Lp$V#%g=+ +ZMsp]JcCH(rn[[/s7Y:O]hWe6rS@PBJcC<$JcC<$JcDqRrn[[/s7Y:PS,i#J:4N~> +nGb]Io'>Amf$MOV]XG#CURdd8NJE+6H$++DC1q0h@q&nWA7fLjDK9oHItWZ=PaS/?X0K1K`66T] +hra=urnE#(L&_/,!J:@FgB5#]FlWGT!:>@;YQ#"0r;-3^p%7hGnaZ,JnH\XVo_/%Tr;M9IJcC<$ +R/d0?!J:@FgB +nGb]Io'>Amf$MOV]XG#CURdd8NJE+6H$++DC1q0h@q&nWA7fLjDK9oHItWZ=PaS/?X0K1K`66T] +hra=urmlZ#L&_/'!H\;7ecW +ZMsp]JcGWI!>++Is8LjUK`Cc&"2.HRk5F-:mXbChs+13$s+13Rs8LjUK`Cc&"IoJ\S5+S~> +nc(iOp$V#%g=+9b^UUSMV4X0>NJE+5GB7Y:An,4T>[(B9>?tTEA7o[rFEr=fM3!mjTr5$"]"uG4 +e_K3MnbV!ks82fs&DrKIrn@I*s7Y1L\kR>-rS%>>JcE"T$iKhoo^_M>mHa'%l2KoHkmH_Am-a?4 +oChtWrdk*#s+13>s8LaRK)bQ!"I]>VRSA;~> +nc(iOp$V#%g=+9b^UUSMV4X0>NJE+5GB7Y:An,4T>[(B9>?tTEA7o[rFEr=fM3!mjTr5$"]"uG4 +e_K3MnbUgfs82fs&DiEHrmh*ks7Y"G[7YMsrRLu6JcE"T$iKhoo^_M>mHa'%l2KoHkmH_Am-a?4 +oChtWrdk*#s+13>s8LRMF8tsb"I&oLPY-H~> +ZMsp]XoABNq#CDEg&D'QhMqR7h>r<0p>,nb:3Yk!h>ltps+13$s+13$s0)J%h>r<0p>,qB!:QFQ +J,~> +nc'X+oBbSqf?hXV]3'f[_9W& +db3RAme5D;rr@QE!!%N%rrE,SNW9"4!J:@FgB5#]FlWGT!:>@;[/UU7qY0XPn*K?'k2bR^iV_UC +iSrnYjlbmomdTiAqYl'GJcC<$ScA]D!J:@FgB +nc'X+oBbSqf?hXV]3'f[_9W& +db3RAme556rr@QE!!%N%rrE,VNW9"/!H\;7ecW +ZMsp]XT/R0nfT$@h8oDn,NCfXYBf!XoB4<&@MP7+TMpSc'A>. +h>r<0p>,nb:3Yk!h>ltps+13$s+13$s0)J%h>r<0p>,qB!:QFQJ,~> +o)BX-p[IG,gXOKf^UUPKUn*g5MM$>$EboAt?+p(6*Tg0sBPqm:It`iER%L7WZb!uocdpq5 +lgic2s8RTIs8RTDrsAS*3XdX8pS_A3rr3rlLB%!Z&-u;)s8N)%1^"r$ru;"MZN$dI!"N5WQ2gj< +!J:@FgB5#]FlWGT!:>@;[f7WPqY'OMm-3]oio&\KgY1?5f[na-f\,!6h;@2Ok32.!o(DkYJcC<$ +JcDABrn@I*s7Y1MRK2ZB9n3~> +o)BX-p[IG,gXOKf^UUPKUn*g5MM$>$EboAt?+p(6*Tg0sBPqm:It`iER%L7WZb!uocdpq5 +lgiT-s8RTIs8RTDrsAS)3=IL4pS_D6rr3rmK`CdV&-u2&s8N)$0`WB!rtk_I[K!'H!"<&UQ2gj7 +!H\;7ecW +ZMsp]X8i3Ls8W+Kp\u_E +o)B^.p$V##f[7jZ]X=o?TUD"&L4=JiD.d9^=1eM1qtV8k8PDr[=C#EMDKL5VLQ7XiU8bB+^;\=F +g>_Adf;nrOJ,fQKJ+rsmY$%:RPY%\%@2/V=f)Y"6>ufqB8P/s^!(#N>I1ZGY!8IPB"Kq@G>Bfh- +s8LaRK)bQ!"1h3Lj8I^4m=G;Hrt5/&oC)&0jl51Qg=Y!+da?FgrQc&%dF-Lof@ep8iT01emdg&I +JcC<$JcDDCrn@I*s7Y1MRK2ZB9n3~> +o)B^.p$V##f[7jZ]X=o?TUD"&L4=JiD.d9^=1eM1qtV8k8PDr[=C#EMDKL5VLQ7XiU8bB+^;\=F +g>_Add]#OP?8kT-`!(,QBHP$5W!8dbE!3Q"H>'K_, +s8LRMF8tsb"1:aBhYl"*l@JuErt5/&oC)&0jl51Qg=Y!+da?FgrQc&%dF-Lof@ep8iT01emdg&I +JcC<$JcDDCrmh*ks7Y"HPQ9m28q6~> +ZMsp^WrN$Ip\uWm$E!58F4h>mTU!8dbE!2]Ghqj%3\h>r<0 +p>,nb:3Yk!h>ltps+13$s+13$s0)J%h>r<0p>,qB!:QFQJ,~> +o)CuOoBbPof$DCP\[&93SX,=nJpVWXBOY4I;7QY75s@Cm4?Ynp77g6Q=C,NQE-H_`MijC!VQI5; +_TL9YhrjFVWrN$Ip\u`?I$-rS%>>JcE:\')hXunEfB"iSE5>eC)^hbf\&Kr5TGkai_fOcdC4mg"bKFkN_L, +p\f[CJcC<$UAt5I!J:@FgB +o)CuOoBbPof$DCP\[&93SX,=nJpVWXBOY4I;7QY75s@Cm4?Ynp77g6Q=C,NQE-H_`MijC!VQI5; +_TL9YhrjFQWrN$Ip\uWm$E!58F4h>mTU!8dbE!2]Ghqj%3\ +ecBjlp=9>R8orkfec=uds1/.BrV?*Tm-*Nhh:gK1da6:`b/qa&`tHJ8b0/&UdF6\"gu%/TlL+9< +rIP!"s+13Es8LRMF8tsb"I&oLPY-H~> +ZMsp^WW3!JpAY0]Du9SAC[/07A,dCRs5*VQ'A!0`hZ*W4!<<'!hZ*Vh8e<7lg6M^;h>r<0p>,nb +:3Yk!h>m!%rrE\fdJj4YhVeG6L]7;Wicc(N.D>`\"kH!G#ZBi:s+13$s+13\s8LjUK`Cc&"IoJ\ +S5+S~> +o)CuNn`o/heBPtH\$2g*RZi\aIX#jIA6i869Xau-3B&fP1c73Q5!qk8;d!I>CiaoQLQ7XjUoUf3 +^rOdPh<""MWW3!JpAY0^Du9SAC[89;AcEaYs53\R'A*6ag].<.!<<'!g].;f9b8RmfTlL9gAup+ +p=f\\9m,LpgApX!rrE\gdJj4Yhr4S:rJ-7/rrqG,()A=4rr3/R70j<%kJ[B7qtK[Kl/gm[g"+Wu +c-",H`59@+^](tI^Cn?!_o9[VRSA;~> +o)CuNn`o/heBPtH\$2g*RZi\aIX#jIA6i869Xau-3B&fP1c73Q5!qk8;d!I>CiaoQLQ7XjUoUf3 +^rOdPh<""HWW3!JpAY0]Du9SAC[/07A,dCRs5*VQ'A!0`hZ*W4!<<'!hZ*Vh8e<7lg6M^;ecBjl +p=9>R8orkfec>!nrrE\fdJj4YhVeG6L]7;Wicc(N.D>`\"kH!G#ZBj(rtPA)o'P]'iSE2 +ZMsp^SGt,:H[GYiqr6cmTU!8dbM`rH(m>'K_,s8LjUK`Cc& +"2.HRk5F-:mbn*G!8c-'!r<0 +p>,qB!:QFQJ,~> +o)D#MnEAibd`]P@[B63) +^;eFIguI\Grgj'>q0@8]s81j>s5Cj,^)[1Qo)SC^s7ak>rrCgRs4[PRrrCgRpW*=6fNEFmQN-s= +!J:@FgB5#]FlWGT!:A_E!^qI@k\@8sG[M63\\\#Pi_8OC:c-b%ngu7A[mdp7*s+13$s.o\ogAup+p=f_=!:-(J +J,~> +o)D#MnEAibd`]P@[B63) +^;eFIguI\Crgj'>qg3\es81g!g4.(&IUmlf[9a +g=F]tbK.Z>^qI@k\@8sG[M63\\\#Pi_8OC:c-b%ngu7A[mdp7*s+13$s.o\oecBjlp=9A2!9]S= +J,~> +ZMsp^S,W\k3G'P3WK*ZhZ*W4!<<'!f)PIM^%]rUHi'-/rn[[/s7Y:O +]hWe6rSAL]]gW2MORE/CTIgE\C\Rl/!3IE's7^"7#ZC-hrtlRQ&@MNm +oD_/Vp?q,%f[7gX]!JK7SX,=lJ9c0NA6i538OB`_Xt2%K,U=]c/M]:L6qUG>rr3$>,^o[l)HBr#k?U4MrrCgRs4[PRrrCOJo*E)%c<#AgQN-s= +!J:@FgB5#]FlWGT)sb()$m^r%s7`6?#S:_Es8N)!+H$3nAIo,In,32&)upPUrrB'1*orqZ3TlK-sZf@/'ha2>^+]"#/SZEUO8Y-"k0YHY:<[C<]Z^V\"4 +c-b(qhW*heo([e9JcC<$V>pPL!J:@FgB +oD_/Vp?q,%f[7gX]!JK7SX,=lJ9c0NA6i538OB`_Xt2%K,U=]c/M]:L6qU#ZC-krrB/U!$SKE(]Y,TrrB/EN,qnrrrO@6c2IYDmVdUMrtami?7g[, +s7:;Yqu?ZrkPsB=p](9E!6>*orqZ3TlK-sZf@/'ha2>^+]"#/SZEUO8Y-"k0YHY:<[C<]Z^V\"4 +c-b(qhW*heo([e9JcC<$V>pPG!H\;7ec_3;kEJSh~> +ZMsp^S,W]eUbN-(R.L=W!daq1rr3l)=pPC+kPtP^hZ*W4!<<'c!4Dis(]Y+[es6:7h>r<0opc(Y +GNSk\)mNFnihT/9s/8\[kM@q7s8N'R[I=-!;Wln/9)JW)!8dbU!&8QCmrpe"g0T!tmVa81g4r+q +q#;J<<`8,;8kT-`!(,QBHP$5W!8dbU!&:nMc3X1E`X)>9rVlrraj'PZ!q'uVqu6`GV&943"X/Qn +mpeA/s+13$s+13\s8LjUK`Cc&"IoJ\S5+S~> +oD_/Up$Lnuf?_OS\[&61S[qMhH%:^3QCaqS +[(F5udb3UCcMX44#5=oK$7_M9rr3%F!9=(Y)=L]:#YjL^rrCgRs4[PRs$6dns.1&-&nf^Zs8LaR +K)bM4!_mgtrS&9o=+T>)8=TXX9pXho0QmEq!&V.Ms'8XbhapE:rtb[is8N'UFjTN"3jdIDFnP-= +,*L]97&]n\+KejIj53F"s8N'bhtqg5s8N)Rs8N'U[d2*Bp]&8>oD/@b!r9j[pAY3]D1DK`%/nPM +)#O@1+G04R@6DX21]I4Amcifig==QoaMbm,\[AcJXfJG"Vl$>fV5C/gWN3/)Z*_!P^;@n4cdUP& +iof\!plGITrW<&qqY^-lqYU3hrdk*#s/5nrgAup+p=f_=!:-(JJ,~> +oD_/Up$Lnuf?_OS\[&61S[qMhH%:^3QCaqS +[(F5udb3UCao%\/#5=oJ#Uu/4rr3%H!9a@])=L`7#YO:[rrCpUs5!bUs$?^ns-so+&80RZs8LRM +F8tot!_ROjrRMpiX/W%qVP^2dVPg>kX0&S1[(!Z]_T'aEe(EL9 +kNqd7O8o7Z!r`#mr:pKlqu$ElJcC<$VuQbI!H\;7ec_3;kEJSh~> +ZMsp_JcCH(rn[X.o)Jf8k5F-VIr!d/]KHZ/?Ps0W^DrrBJ,s7ZN^s8UpUn,32&!8dbU!4CP\ +s6AbDl>(>+mVcn+s6=BHqtg=/qg3PUs8UpUn,EC$s8UpUs8N)Us8N),rr3*EXoE0?mJd7UDh%]b +"+L:NXoA>#^&A!4#i:.4JcC<$JcE:\rn[[/s7Y:PS,i#J:4N~> +oD]d-o^1bsf$DCP\?W$-RZi\`I?W^/s69DCIru^c:+t50'4[_tB>%),aGCP@-Q(=_OZb"#q +dFdC?c2*"kLB%8-!.seB!HmH$gDkWWgAgc[bkZO$mJm3p*<6$=[f?C%"RH*fhZ3-Crtkajs8N)- +gAh-9kPOLXD=-_-C[;#bBm]aRrtkQ\GO,;/hZ3-F!5AL5gAq9R!8IPR!4Mn,");*c-k5!iTBLspltddrr)]gp\"1Mo'u8>rp^6aoCV_Mq>C5Cs+13Ns8LaRK)bQ!"I]>VRSA;~> +oD]d-o^1bsf$DCP\?W$-RZi\`I?W^/s69DCIru^c:+t50'4[_tB>%),aGCP@-Q(=_OZb"#q +dFdC?aSLJfLB%8(!-@`3!H[,ref9$PgAgc\c22j+mf3m$Brtkams8N), +gAh3sTrtkQZGOPS3h>m$E!58F4h>mTU!8dbU!4Dh+"qYd:gN +^Vn4 +ZMsp_JcCH(rn[X.o=Y1Uk5F-aM!8dbUhYR9Qf)5O\!8dbU!8d/Ds42*C +5q-a?mVdUSrrR[gmeZqamVht&&(^a\hZ*W4!<<'!hZ*TUhZ!NXF&N#L^$Yh$mVdUPrsR8O#l"B! +(jl#G=uh06JcC<$JcE:\rn[[/s7Y:PS,i#J:4N~> +oD_/So^(Ype]u1L\$2g)RZ`S]Hus4:?<0im5WP*s;D9%AH1:k-6mXB@4$lP:=^Yl\G(,.)PanML +ZF[lod+I7=bkZhiLB%8-!.sgN!RTm*gB)5-li$hnmr*XQs8U[Ns8N)Rs8V!R!!(XKrtkajs8N)R +mJm49D_aX/D=-_-IJs3EI;eTNrrMS,qZ%/YrrCgRs4[PRrrCgRrrCgQrrdIEq[gL/rrVS)J,B6R +d\[HIs8*daoBT[Eao<4So^:r'h:U0#aMYd([^*'9Vk]oVS!fY0r08+bQ^O>6T;&0]XK](A]u%e4 +dFR(2k3V[7Qi@Hkq=jOPnF#]0lKRR3kR6Y?lg4$-nalGKqu20HJcDnQrn@I*s7Y1MRK2ZB9n3~> +oD_/So^(Ype]u1L\$2g)RZ`S]Hus4:?<0im5WP*s;D9%AH1:k-6mXB@4$lP:=^Yl\G(,.)PanML +ZF[lod+I7=a8(;dLB%8(!-@b6!Qs9tf)KLclM^_amVdUSrs70Us8N)Us8UsQ!!(UJrt5=gs8N)U +mf3=6T;&0]XK](A]u%e4 +dFR(2k3V[7Qi@Hkq=jOPnF#]0lKRR3kR6Y?lg4$-nalGKqu20HJcDnQrmh*ks7Y"HPQ9m28q6~> +ZMsp_JcCH(rn[X.o=Y1Uk5F-UHY2"3o;T.pHZ/?Ps0W^DrrCpUs7ZNfs8Vi=qu7K5hZ*TUhX:F< +GA$((bDqYqB/H[GMes7ah=rrCpUs5!bUrrCpUrrCpTrr^OPXWd'2#OZM[s8Vh` +V#LB#!9aCF!;HNnec::$JcC<$JcE:\rn[[/s7Y:PS,i#J:4N~> +oD_/SoBbPoe]u1L\$2g)R?EG[HZX(8>uj]j5<(>G+W:t$"YT?F\Ki[\3^H>7=C5]YG(#('PaeGJ +ZFRfmd+@1;bkQbhLB%8-!.sgN!RTm*gDbENgAgcE[eY2cmJm3p)#sU9g].2D/d+6t1kO%m +oD_/SoBbPoe]u1L\$2g)R?EG[HZX(8>uj]j5<(>G+W:t$"YT?F\Ki[\3^H>7=C5]YG(#('PaeGJ +ZFRfmd+@1;a7t5cLB%8(!-@b6!Qs9tef/gFgAgcD[JP;hmf3"/lMp-bHi*[>(]/Q+p](9fcN!nEhZ*W4!<<'!hZ*TUhZ!NWc3UNqm/I=Y +Dh%fepRJ&Yrs8\gs5sCVs8UXMb5WCWp$_/*h:U0#a25O"Z`gF-US"'EQBRGmNfF$s)NBc2D/d+6t1kO%m +ZMsp_JcCH(rn[X.o=Y1Uk5F-LW)A9-Ue.*VUe7$;hD'*OrrCpTs!(EFkN?#/s8SV`s8N)Ms8N)U +mf3$*BBI#sC[1>++ctQ=7B$"](8t8;kN?#/s8N)Us8UpUs8N)Ms8N)UrVlpB&:=9R!rL*apAY3^ +Dh%cd"c$!l]o3g-rrj6\kMB*'JcC<$JcC<$\c;Zc!JLLHh?9>Kn!m.'~> +oD_/SoBbPoe]u1L\$2g)R?EG[HZX(8>uaWi5<(>G+W:t$"U9tdp*M:F3^H>6=C5]YG(#('PaeGJ +ZFRfmd+@1;bkZhiLB%8-!.sgN!RTm*gCgG_KAiOqS,]?YQK-^RFoVIAg]%3ts//eRe-RZ +!7UuJ!8Ho@olXp/^dDi#m;3u-h2=b$q#;,0='=SH>s//e!8IPRgAq9R!7UuJ!8IJP![JJ.qYpWm +`m"/V'^]aes8W%O;8Bi%)#O@1*-(0!VRSA;~> +oD_/SoBbPoe]u1L\$2g)R?EG[HZX(8>uaWi5<(>G+W:t$"U9tdp*M:F3^H>6=C5]YG(#('PaeGJ +ZFRfmd+@1;a8(;dLB%8(!-@b6!Qs9tee4cVKAr[uSH#BWR-3<\F8u7?hZ!O";Hi8H=ulW_SG3'` +!7q2M!8d/DpNL94_*Vr&mVa81hMXh$q#;,.mTU!7q2M!8d\S![/,%qYpWo +aj'PZ!q'uVrr3.W;Sg&*)#jO=(j"ls;F8S,)u]O#lK$gSd`ohL]XP2KW2-)VR$<_nN/<=EL&HZC +KnkMCNKB?jR\$7OX0B"C^r=LDf%f9Kme$LMs!7U@p[dnAlK@9gi838Dg=b-2f@S[-g"P3:hr3VW +kj.[,p%\N;s+13Us8LRMF8tsb"I&oLPY-H~> +ZMsp_JcCH(rn[X.o=Y1Uk5F-Lm^51`+cu-lpRj)D#]'27rrCpTs!-u9#RF&cs8N)Us8PCc[Jp6` +mf3=#5n$M$-%kW(8l?'LT_J6](AFV.#RF&cs8N)Us8UpUs8PCc[Jp6`rVlqG!8dSP!q'uVpAY3^ +Dh%cd"km`''Ih'$rrpt?!!lKcJcC<$JcC<$\c;Zc!JLLHh?9>Kn!m.'~> +oD_/ToBbPoe]u1L\$2g)RZ`S]Hus4:?<0im5WLPK,9.F/$k-/9s$&%k4$lP:=C>c[G(,.)PanML +ZF[lod+I7=bkcnjLB%8-!.sgN!RTm*gCihZ4piQun,N'P/-lYup](6ng]%3tmo^&):$;QV!8IPR +5m4Ln!8Ho@s1Uf0;/%G4m;5El$ne:Kq#;-(Ufe*3:$;QV!8IPRgAq9R5m4Ln!8IJP!dOe'qYpWa +D1D9Z!psiSrr3/UCCh74a8Z)Bf0ofN;3PKRnW%I!g9g +I!pKpK8#,?O->p#TV\Zl[(=&lbL+qtj6H..TDoN#p[mtAl/gp^h:gN4eC2gld/D9%d*^:je^rL/ +hVdGVlL"-7qLSZts0Mb)gAup+p=f_=!:-(JJ,~> +oD_/ToBbPoe]u1L\$2g)RZ`S]Hus4:?<0im5WLPK,9.F/$k-/9s$&%k4$lP:=C>c[G(,.)PanML +ZF[lod+I7=a81AeLB%8(!-@b6!Qs9tee78U4U<3mmf3$S/I2Vtp](6nhZ!O"mofu&9'?6S!8dbU +63+Fn!8d/Ds1L],:1kl*mVb]q$n\.Hq#;-(Ufn$09'?6S!8dbUh>mTU63+Fn!8d\S!daq(qYpWb +Dh%K\!q'uVrr3/UCCh1/`r>uAej9?B;Wl7'3r/@5k24k@c,[]6[]up3U7I^;Oc>3PKRnW%I!g9g +I!pKpK8#,?O->p#TV\Zl[(=&lbL+qtj6H..TDoN#p[mtAl/gp^h:gN4eC2gld/D9%d*^:je^rL/ +hVdGVlL"-7qLSZts0Mb)ecBjlp=9A2!9]S=J,~> +ZMsp_JcCH(rn[X.o=Y1Uk5F-:ma1t8mVdU-rrJ'=JcC<$JcC<$NrT+8!JLLHh?9>Kn!m.'~> +oD_/To^(\qf$;=N\?W$-RZiY_I?W^,r69@"U-6O3B',4RKs$SLu4[_qA>%))`GCP@-Q(=_O +Zb"#qdFdC?c23(lLB%8-!.sgN!RTm*gApWfrrVS)J(Xc%N]a$8*;fEski(:HcH*l8\$@WNW\?^rFUHg#(oXo),6^')q_"na,K$iSN>Ae^Dgi +bf\&Kr5\iY&B`2@cdC4mg"bKEk3DC*pAKRBJcE.Xrn@I*s7Y1MRK2ZB9n3~> +oD_/To^(\qf$;=N\?W$-RZiY_I?W^,r69@"U-6O3B',4RKs$SLu4[_qA>%))`GCP@-Q(=_O +Zb"#qdFdC?aSUPgLB%8(!-@b6!Qs9tec>!^rrVV,J(Xc%N&dU3*;fEski(:HcH*l8\$@WNW\?^rFUHg#(oXo),6^')q_"na,K$iSN>Ae^Dgi +bf\&Kr5\iY&B`2@cdC4mg"bKEk3DC*pAKRBJcE.Xrmh*ks7Y"HPQ9m28q6~> +ZMsp_JcCH(rn[X.o=Y1Uk5F-:ma1t8mVdU.rrV@,V1JYts+13$s,R-Yh>r<0p>,qB!:QFQJ,~> +oD_/Up$Lnuf?_OR\[&61S!8ndIWo^D@9QT'76WXb.jZ;Y)]Oe:qD'ie5Y"RL>[qMgH%:[2QCaqS +[(F5udb3RBc2<.mLB%8-!.sgN!RTm*gApWfrrVS)J(jo)qr]6!UAl85p[RP0h:L#r`4`XbXJVY[ +QB@,]K78/kFE)59D#J,hD/O<6G'SLeKo1qUR@^4RYI2!YaNi;iiTT_&rhTQ.r:fgNlK.!\g=Fa" +c-",I`59@+^](tI^Cn>u_o0R:bKeMbfA#3CkNhX0qLSZts0ht,gAup+p=f_=!:-(JJ,~> +oD_/Up$Lnuf?_OR\[&61S!8ndIWo^D@9QT'76WXb.jZ;Y)]Oe:qD'ie5Y"RL>[qMgH%:[2QCaqS +[(F5udb3RBaS^VhLB%8(!-@b6!Qs9tec>!^rrVV,J(ai'k96[rruM+5n`o2kf?h[Y]sk8JVP'EE +Oc5'JIX?9\EGo]1rG*BiDK'Z?H$t6tMN=!iStr?h[Ca8rcICV-kjJ-CV#M/,p[dh +ZMsp^JV8`1rn[X.o=Y1Uk5F-:ma1t8pSkFrrrLhWqZ$X"KBW=:`W/nfs+13$s+130s8LjUK`Cc& +"IoJ\S5+S~> +oD_/Vp?q,$f[.aW]!JH5SX,:kJ9c-L@pE#084#Bq0InLr, +oD_/Vp?q,$f[.aW]!JH5SX,:kJ9c-L@pE#084#Bq0InLr,!^rrVqoZ2O\'g04@R! +ZMsp^JV8`1rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjUK`C_8!s%e[:4N~> +o)D#Mn*&``d`]P@[B60( +^;\@HgZ.SFI"?^#IK%t$K(HDOd,Y)Z!:>@;`;^nXq!mY0gt'fm_Rm4YW1ofJOG\aAH?F4DBOtX[ +?!I;3*EucT@:X"dDfg5PK8>PPR\6OZZFR`ibgY;)kO/&@s"XBCnE]5qgt:*%bK.W<^:Un`['Hp> +Y-+n0Y-5(7ZF%'N]YD>&aj&5`g"t`Om.'iLJcC<$\c;Z`!J:@ERKEQURSA;~> +o)D#Mn*&``d`]P@[B60( +^;\@HgZ.SBEe/:dF8j_jF7ZL7b23*J!:#.8`;^nXq!mY0gt'fm_Rm4YW1ofJOG\aAH?F4DBOtX[ +?!I;3*EucT@:X"dDfg5PK8>PPR\6OZZFR`ibgY;)kO/&@s"XBCnE]5qgt:*%bK.W<^:Un`['Hp> +Y-+n0Y-5(7ZF%'N]YD>&aj&5`g"t`Om.'iLJcC<$\c;Z[!H\;6PQLpKPY-H~> +ZMsp^JV8`1rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjTK_#0Fn!m.'~> +o)BU&n`o,geBGnG\$)a(RZi\aIWoaG@pN,4MuN_WqtC$h*r(1F4[MY5;HR:;CiXfOLQ7XiUT1T0 +^rOaOh;mqMJUN6#rn@F)o[V)UD/slLK8GYSS"Zd`[Ca;td+7%6lgk"Qs"jNDn*8ukg=FZraMbm-\[JiKY,nY% +Vl$>fV5C,fWN*&'Z*_!O^;@n4cI:D#iTBIrpATXCJcE=]rn@F)o)JjXRSA;~> +o)BU&n`o,geBGnG\$)a(RZi\aIWoaG@pN,4MuN_WqtC$h*r(1F4[MY5;HR:;CiXfOLQ7XiUT1T0 +^rOaOh;mqHJTHNdrmh'jo;r&9hYl"*l@JuRruV(1mcWN]d`]VD\$;s/SX5IsKn"DjDe`m3P`h,h +NrG"ZNfT9/>[V)UD/slLK8GYSS"Zd`[Ca;td+7%6lgk"Qs"jNDn*8ukg=FZraMbm-\[JiKY,nY% +Vl$>fV5C,fWN*&'Z*_!O^;@n4cI:D#iTBIrpATXCJcE=]rmh'jo)JjTPY-H~> +ZMsp]JV8`1rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjTK_)kYn!m.'~> +o)B^,oBYJnf$;=O\[&93SX,=mJU;KVB45"FB +HrttV)n*/oig!n?k`kf@![Bcs9W2?AbTV%hSSJRli +Tq\B]WN<;0\%9>la3;r^gYh5[nb7V7JcE=]rn@F)o +o)B^,oBYJnf$;=O\[&93SX,=mJU;KVB45"FB +HrttV)n*/oig!n?k`kf@![Bcs9W2?AbTV%hSSJRli +Tq\B]WN<;0\%9>la3;r^gYh5[nb7V7JcE=]rmh'jo;r)WPY-H~> +ZMsp]JV8`1rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjTK_)kYn!m.'~> +o)CuRp$Lo!f[7jY]fCh@'Z<`2Xa7mo^26:41492AJf?".JbFF&ImNg-$-WNWbE +`QZfbiTTdaJUN6#rn@F)o[_8^F*W7iN0B^'VlmG?_ogBZhra>!WrEt6p$_/+hV$B'ai(s+[^*'9Vkg#X +S=5h3QMm'cQC"#.SY)XQW3!2/\@fYtbL"eni9'@qpjrHrs1A=1gAlis[K>c`9n3~> +o)CuRp$Lo!f[7jY]fCh@'Z<`2Xa7mo^26:41492AJf?".JbFF&ImNg-$-WNWbE +`QZfbiTTd\JTHNdrmh'jo;r&9hYl"*l@JuSru_77nEAibe',eF\$2j,S<].kJpVWYBk(IPJZYuP +W2KWl*r,c\8kr8c>[_8^F*W7iN0B^'VlmG?_ogBZhra>!WrEt6p$_/+hV$B'ai(s+[^*'9Vkg#X +S=5h3QMm'cQC"#.SY)XQW3!2/\@fYtbL"eni9'@qpjrHrs1A=1ec9d_XTI[M8q6~> +ZMsp]JV8`1rn[X.o=Y1Uk5F-:mXbChs+13$s+13Rs8LjTK_)kYn!m.'~> +nc(iJnEJoce',eF\$;s.SX,=nK7.r`CM%![=B&-m9M7uM92/2[*+KgAlis[K4b8rS%>>JcE^h6N$NDk24h=b/:m!Xf%h[P)G!BGB%A.?[hAaFaJ^sO-Z?4X0T:Oa3N5kjQlC8XT(9Xp@.A.hV$B&aMYa&['6U/US+-F +QB[PoNfB$WN/`jYOHPlqR[p.KW3*>5]>)>-cdgb-k3V[7JcC<$]`7uc!.sgN!pc:LJ,~> +nc(iJnEJoce',eF\$;s.SX,=nK7.r`CM%![=B&-m9M7uM92/2[[hAaFaJ^sO-Z?4X0T:Oa3N5kjQlC8XT(9Xp@.A.hV$B&aMYa&['6U/US+-F +QB[PoNfB$WN/`jYOHPlqR[p.KW3*>5]>)>-cdgb-k3V[7JcC<$]`7u^!-@b6!p>e?J,~> +ZMsp]JV8`1rn[X.o=Y1Uk5F-:m^rK%!8d_UhLY]Xs+13$s+14Hs8LjTK_)kYn!m.'~> +nc'X+o'>Amf$DFR][V)UD/slLK8GYSS>*!d[_9W% +dFd@=mJ,FeI>*+KgAlis[K4b8rS%>>]Di!6g]%9.OT.ZDoBbPof$DFQ\[&93SX,=mJU2BSAmeb? +:.R`93ArZJ0Xh#F3]o\t92SbrA86+/It`iFR\?[`\%]i*e(`mHnGKEg)ufX&lfI$We'?%O]stDO +WMQ8XR$EhqN/EFGL&HZCKnkJANK99hR\$7NX08n@^Vn:@e_B'GmIU:-s+13`s8LaQK(HDPl^COu~> +nc'X+o'>Amf$DFR][V)UD/slLK8GYSS>*!d[_9W% +dFd@=mJ,7`F+n]7ec9d_XT?T$rRLu6]Di!6hZ!T4OT.ZDoBbPof$DFQ\[&93SX,=mJU2BSAmeb? +:.R`93ArZJ0Xh#F3]o\t92SbrA86+/It`iFR\?[`\%]i*e(`mHnGKEg)ufX&lfI$We'?%O]stDO +WMQ8XR$EhqN/EFGL&HZCKnkJANK99hR\$7NX08n@^Vn:@e_B'GmIU:-s+13`s8LRLF7ZL8kEJSh~> +ZMsp]JV8`1rn[X.o=Y1Uk5F-:m^rK%!8d_UhWXt@LNi1Hrr@EE!!mZGs40L:#ZC,Bs+13$s+13e +s8LjTK_)kYn!m.'~> +nc(iPp$V#$g!\*`^::GKUn3s:N.un2GB.P7AR]"P>?Y04>$P?@@qKIoF*N+cLlR^gTVeit]"lA3 +eD'!InGV*mI>*+KgAlis[K4b8rS%>>]Di!6g]%9.kl1^>GOOSl!."MC#^-1If0KNF/L.k*RUs,o)p4$Z80;d!L@DKU>ZMNO9uVQI8=_ogE\ +i90S)XoCHYoBk`!g=4Eh_S*FaXf%qcR['"pMMHk:J:;ooH[C-gIXm$(Ll7=XQ^aYDWNNV=^Vn:A +f%f9Ln+M>4JcEF`rn@F)o +nc(iPp$V#$g!\*`^::GKUn3s:N.un2GB.P7AR]"P>?Y04>$P?@@qKIoF*N+cLlR^gTVeit]"lA3 +eD'!InGUphF+n]7ec9d_XT?T$rRLu6]Di!6hZ!T4kl1^>GOFMk!.4YE#]p%Gej9?B;X`9<6M^0: +j4r//`kJpdWM5lHN.l_)E+rfe/L.k*RUs,o)p4$Z80;d!L@DKU>ZMNO9uVQI8=_ogE\ +i90S)XoCHYoBk`!g=4Eh_S*FaXf%qcR['"pMMHk:J:;ooH[C-gIXm$(Ll7=XQ^aYDWNNV=^Vn:A +f%f9Ln+M>4JcEF`rmh'jo;r)WPY-H~> +ZMsp]JV +kMAg'JcC<$JcC<$_Z0Vl!/0sW!q2XSJ,~> +nGb]In`o/jf$DIT]X=rBURda6N.un3G][n@BkLsd@UW\S@qB:fD/j]DIY3H9PF8&>Wj0%H`66Q\ +hrX4ts4`*Ql[8iG_M.c5!kQ7&WdXm$!.sgN!RTm*gApWQrr`>VrrCg;rrR[emGn3I$Y5]L>p@%8*gt'fn_nEObXJ_e`R$3PfL4au'H?XOSrc9'$F`r"UI=R!+ +N0'9lSti6e[(3rjbL+u!jQl@5JcC<$^An2e!.sgN!pc:LJ,~> +nGb]In`o/jf$DIT]X=rBURda6N.un3G][n@BkLsd@UW\S@qB:fD/j]DIY3H9PF8&>Wj0%H`66Q\ +hrX4ts42aBlZ3-8]mKNq!k#^gWcS0j!-@b6!Qs9tec>!Irr`>YrrCp>rrR[gmc4$Y5]L>p@%8*gt'fn_nEObXJ_e`R$3PfL4au'H?XOSrc9'$F`r"UI=R!+ +N0'9lSti6e[(3rjbL+u!jQl@5JcC<$^An2`!-@b6!p>e?J,~> +ZMsp]JVi6#]`RYm +:4N~> +nGaL,o^:o#g=+9c^q$eRVkT]JOc5'JIX63ZE,KK,rb3EgCMe!0FEi1`KSb_QR%C(PYI2!ZaNi>j +ip#t1s4`*Ql[8iK`eF29!ku[.WdXm$!.sgN!RTm*gDfIi7LUQ(mJlpT/dN#&p](6nWZ[3!pLOmA +VrrCgKru^uH&-u;)s8N)% +1^"r$e;XqD@G$'0AQ=.gWrN*]@F"iO!psiSp\t:GY5A1t#1ESuj`l5rpeR-QsHLk5VP*,qC]05"/.E>%22bGCP@,Q(4VLZFRclcdpt7mIm^_*;K*j +jkeY;bJh3*ZE0stS^rO^Kg>M/]o_sFA +JcELbrn@F)o +nGaL,o^:o#g=+9c^q$eRVkT]JOc5'JIX63ZE,KK,rb3EgCMe!0FEi1`KSb_QR%C(PYI2!ZaNi>j +ip#t1s42aBlZ3-<_0bru!kH-oWcS0j!-@b6!Qs9tef3nf70k&umf3$S/I2o'p](6nXW`Z'pL=a? +;XaM_+f-=^(nCW:@]9>,OC$2[@h9&Pc2YIG#U,;ts3bBg$rY(Drr`>YrrCpNru^uF&-u2&s8N)$ +0`WB!er'qB@bQ<4B3'LkW;lmZ?HrHK!q'uVp\t7CXo/2'ec>1=s3:Vss$HJKkht.Bb/:m!Xeq_W +OGSO7F)5Am<_l7P3\r?8+WMKas/d@h.ks"I78-Za@VKe*ItirJS>3*i\\Q;4f&#NUp9+E8q"!b4 +hUp6!`P/jfXJ_b^QB@,^K7A5mFE2;:D#J,hCi435G'SIcKo(hRR%C(PY-bdUa3E)ei90M"rdk*# +s1eU5ec9d_XTI[M8q6~> +ZMsp]MM)7ocM.@iN.g'5re)'M]nD?ip]&g'KS71mb4GMYR(`Cod[9la#/0p2d_4)2re)*V[?U]n +p]'*/KS5<0p]&g'KS6Jrs7`qBK`9g.K_)kXf&lqf)mNFnihT/9s/8\[kM@q7s8N'R[I=-!;Wln/ +9)S]=mf9Ka[/f[PCOc6@B7KMsi6# +]`RYm:4N~> +n,F@%n`o2kf?qaZ^::JMVkKWIP)Yb^chI=cL4.[srd>RF\p]1Vp\WEoI=8iX`q/rKPIUGccB7sO#.XL+cF2'!rd>UOY)`O^ +oD@-oI=6^np\WEoI=8-as7gDi7QH"_Rm.U +V4O!6LOaYiC1CII9h%?+0I\1c(17+A8/`Tu/i>aY8lAf"B5_sAKoD4bU8bE-^rOaOh;mqpYQ$fa +p$V#%g=4Bf^q-nTVkT]IOc+sFI<]jQD/*]pA7K(X@q91bCMn-7H@CL&NKT[#UoCQ+]Y_e:f%f +n,F@%n`o2kf?qaZ^::JMVkKWIP)YgDi7QH"_Rm.U +V4O!6LOaYiC1CII9h%?+0I\1c(17+A8/`Tu/i>aY8lAf"B5_sAKoD4bU8bE-^rOaOh;mqpYQ$fa +p$V#%g=4Bf^q-nTVkT]IOc+sFI<]jQD/*]pA7K(X@q91bCMn-7H@CL&NKT[#UoCQ+]Y_e:f%f +ZMsp]MM2#\s-3Q?)hb?fKS5lqP_@W=Pa(%]KS725]o8fE]nF5DN;/&L_9%Xtc,GL#SH"P4s5'!) +kLP<>f%-(MhVNpEs/h6^s,>'Qrn[X.o=Y1Uk5F-VIr!d/]KHZ/?Ps0W^DrrBJ,s7ZN^s8UpU +n,<7umf93Ys7cQnOD!.P@]](>+s8N)UrrCpOru^t8n,NFE!:Tpf +^&S,h!.Y#uDsmXT>2'#VOD!.P@]])!#4F:Cs5#b6rVlugHPk%Zs+13$s+13es8LjTK_)kYn!m.'~> +n,F@*p$V&&gXXWl_nEObXJ_e`R$<\kLkUG1I=$;=Go_3YIXm'*M2dX_R\$:QY-Y[R`6-KZh;dei +rr<#QML?Yodu'1?gO5snI=7:YMLEh"MN6HGI=8iu[YUU1\p_<4L%p'>^;bkbb.`@`Req\ss4`Qs +j4A^-cdRc3gY7.2s/(OFs+\XDrn@F)o&TGH#;sKs0`pIrrBM-s7ZZ^s8UsV +n,<89mf]BZs7?9jO([%NA$5QO;p>.7q2]=Fs7=%MrTWMBl"P#%s8N)RrrCgLru^tMB,WO([%NA$5>$#4"+CqqjM9rVlugGo4`Xs$QeZmcWK[dE08:Z`KsnQB$`M +G]@G,>#S*`4Z4o?+;kg.s3;,j*@*0l3^QD8=C5]YFa\q%PFA5FYdhHfcILb3lgq=[*Vo9ljPAG7 +ahtd"YGn:fQ][2\J9uHZD/!Qj?X6q9=rggP?=78UC2S*;I=d66PF8&>X0K1K`QQ]_i9'G!JcC<$ +^]4;f!.sgN!pc:LJ,~> +n,F@*p$V&&gXXWl_nEObXJ_e`R$<\kLkUG1I=$;=Go_3YIXm'*M2dX_R\$:QY-Y[R`6-KZh;dei +rr<#LMK9rdeqA_-eoR\^F*!/]KHZ/?Ps0W^DrrBJ,s7ZN^s8UpU +n,<7umf93Ys7cQnOD!.P@]](>+s8N)UrrCpOru^t8n,NFE!:Tpf +^&S,h!.Y#uDsmXT>2'#VOD!.P@]])!#4F:Cs5#b6rVlugHPk&]s$QeZmcWK[dE08:Z`KsnQB$`M +G]@G,>#S*`4Z4o?+;kg.s3;,j*@*0l3^QD8=C5]YFa\q%PFA5FYdhHfcILb3lgq=[*Vo9ljPAG7 +ahtd"YGn:fQ][2\J9uHZD/!Qj?X6q9=rggP?=78UC2S*;I=d66PF8&>X0K1K`QQ]_i9'G!JcC<$ +^]4;a!-@b6!p>e?J,~> +ZMsp]MhE:5s,;VaPlH]$s.tCghLpjfN.ckKs,;cMdZfEDah`YHac-"J+Ml0YKY>g$WQs(JPlI8, +KUi($N;niiKTuLqN;p;EKT+!*rn[X.o=Y1Uk5F-aM!8dbUhYR9Qf)5O` +]h/hr[K$9;@]5&ohZ)Gb/L5PoIr"?IrsdOt0gR7,mf3:ehZ!T4q>UHOqZ%JarrCpUs5!a*s*ntT +s*ntTs8TW=W/#!Ali."*#lai+PSe3)JcC<$JcC<$_>jMk!/0sW!q2XSJ,~> +mf,9Co'GMsg!e3e_S!@`XJhndR[0+tN/<=DK7\Z)JUrE*KnkPFOHc*$TVSQiZad`faj8MliTT_) +rr2uQMgZe$s,)5QNrOicpS*)Rf6r8NMLBo5s+Z*=cAd7-aM*):`eF29+MPjLIC@:\Us%)4NrP8j +I?snbK)^^VI?+>^LB"E.I=fOgrn@F)oUHPqZ%JbrrCgRs4[O's*nnQ +s*nnQs8TZAW.em@li."+#lai+Q5FK)f`+>pH@Tl](6p3F\-Qa0c +s8Q[@'cePM1HIio:fq(:DKUA\Mj'U(WN`nI`m3,jjQlEAs$6PVn*&`ae',hH\?`03SsY\"L4FSm +E,0&p?<_BC]>;(OCfFG<>[LuSCiOZHJr#GOR\6R\[(=)pcdgh2lLFe's+13cs8LaQK(HDPl^COu~> +mf,9Co'GMsg!e3e_S!@`XJhndR[0+tN/<=DK7\Z)JUrE*KnkPFOHc*$TVSQiZad`faj8MliTT_) +rr2uLMfU(hs*Jg.K`?C?pRH<aM!8dbUhYR9Qf)5O` +]h/hr[K$9;@]5&ohZ)Gb/L5PoIr"?IrsdOt0gR7,mf3:ehZ!T4q>UHOqZ%JarrCpUs5!a*s*ntT +s*ntTs8TW=W/#!Ali."*#lai+PSe3)f`+>pH@Tl](6p3F\-Qa0c +s8Q[@'cePM1HIio:fq(:DKUA\Mj'U(WN`nI`m3,jjQlEAs$6PVn*&`ae',hH\?`03SsY\"L4FSm +E,0&p?<_BC]>;(OCfFG<>[LuSCiOZHJr#GOR\6R\[(=)pcdgh2lLFe's+13cs8LRLF7ZL8kEJSh~> +ZMsp]MhE:9s2Dgjao@T]k.-0%`Is?Q`PKC4s-/JYc&dU;ah`YHac-"J+Ml0YKX&muZf8ZOPlHhu +KTQ4qOT3;9_9)_ALsjD][@GEHrrCpUs69T2It)fX +lM^`4qn](`G?Xb +mf,9Gp[IJ/hUp9$a25KuZEC1'Tq.U;PE:f`Mi!:HLl%"INK93dQ^XM?VQ-i,\\>u'cICS+k3V^= +rr2uQMgZe*s2;Xc`rD!Ij1'Nl]R>V9`4it*s,;WFbDL_&a1cu9`eF29+MPjLIB1M^Yi3$=NrOl_ +I>n2^M#YE-^;g23J^23HYa!4Wrn@F)o?"9b4uY,C+W@!Qm1g3M*[NBp4$lP:=C>cZG(#%&PFJ;GYdhHgcILb3m.@O^)uB0mjkeV9 +ahkZtY,@t^P`:HLHZa7AARJ_I^A@jM]i7j.=C#?ICiOZIK8GYTS>3'f\%]i)db3RAme;;4JcEOc +rn@F)o +mf,9Gp[IJ/hUp9$a25KuZEC1'Tq.U;PE:f`Mi!:HLl%"INK93dQ^XM?VQ-i,\\>u'cICS+k3V^= +rr2uLMfU(ls1PtR_>f./ijEjY\T<9#]t(\is+>[/a+8;[_msj!_0bru+M#=8F/6jBW8=e#K`?OC +F+=4>J,d3m\\\;uGKIP/W/J`EHrrCpUs69T2It)fX +lM^`4qn](`G?Xb?"9b4uY,C+W@!Qm1g3M*[NBp4$lP:=C>cZG(#%&PFJ;GYdhHgcILb3m.@O^)uB0mjkeV9 +ahkZtY,@t^P`:HLHZa7AARJ_I^A@jM]i7j.=C#?ICiOZIK8GYTS>3'f\%]i)db3RAme;;4JcEOc +rmh'jo;r)WPY-H~> +ZMsp]MhE:9s2Dgj`QaKt\`/kJR"US$`PKC0c&dh!ac(n3ah`YHac-"J+i29ZKUU0%a7kgOPlH\q +KTQ4qOT3;9_8XZ\Lo%Aib1;BjK`9g.K_)kXf&lqf99EJ^nB6*UrK(:%oBpZgs8N)Us8VsFW:TVZ +KE(trqu?D'hYXPXoD.s*3EE$Wqlca\el[3f>d!S>H)UI]BBJ,[D=%<&!8d_UhYI0mq^JK8o;I +mJe(&p$_/*h:U0#a25O"['-O.UnF9IR$Nu"OoCIBOVa4GQC+/5Tr"]hZ*h0X`6$,\TID3^TpjF="`eAi*ahG^bNI?ieLOFT. +s+lL2_S3b"b(kJ:XLc?uJ]W?>gAlis[K4b8rS'u,>d*Y>H)UY%>f#stF%630!8IPRq'r93o;[NE +s31<=ok3.WXoe.go6r`$VZ6LqU[-B2H(/'BnB-$Tp3102hdLmjm;]Fs$?GM +lJgRJc,RN.Z)a[kQB6rUI!0IDA7&J>_>jMBF'isqrr9`*9i>"r@:j=rG^b@)P*qr>XgG^WajAYr +jm;UjMh!.sgN!pc:LJ,~> +mJe(&p$_/*h:U0#a25O"['-O.UnF9IR$Nu"OoCIBOVa4GQC+/5Tr"]hZ*h0X`6$d!S>H)U\'>f61$F%--/!8dbUq^JK8o;Imjm;]Fs$?GM +lJgRJc,RN.Z)a[kQB6rUI!0IDA7&J>_>jMBF'isqrr9`*9i>"r@:j=rG^b@)P*qr>XgG^WajAYr +jm;UjMc!-@b6!p>e?J,~> +ZMsp]MhE:1lEQ;,Z,)*HPkW>WKS50HZ&FXH_1[KXac(n3ah`YHbDu@N+i2?^KSm\.f\V:7PlJ:Q +N5=e8M!D7(N3U3PN7*rHXoF@%K`9g.K_4+&Su1ZKh@e"a@!,pp`W+2*@Wulg[K$7,hZ!O4TNZP` +;NUqY:?qlV;JLPkaP& +rrCpUs5!a*s,]^Em!isArr3(t.pCL#rrVh_VYL/ul<7Mi6# +]`RYm:4N~> +m/Iq#o^:r&h:L*"aMYd([^*'9Vkg&ZSXZ(9rKnChR[ftBU8=fhYHt^M^r=IBeCi^>lL4ZKrrCf3 +I3O1,XFl/*\p]!dou,L2I=LRaK7g28I=8iLKmeZp^4!`:L\Q9@^;u(XK>6BnepVt,s1!c<]'Fiq +l`>_kWk*V3b)LnLs+\XDrn@F)o\'A8UZ;=-'$5c7F^o:-s35XoF`U6bs8N)Rrr4jM=a,'iOoPGU +kPt?VCO'Poq>C8i/6pd+r2*hAE*oQrK4/n4D8l@>h8fP!qVkXq#:]e>@D_u=LeC>!+YtCf`+;4nEAibd`]SA[B?F"R$!5W +HZX+9?<9ur6Td7[.Y@^!3ZK4d-nR595Y"RL>[qMgG^tR0Q(=_OZF[lnd+@.:mJ*sd+8tp%kMY%A +bJ_*&YGe.`PDk3FG]IS2?Wg?8rr8r]48q5^VZ6Wp7nZZY>@D/]FF&LoNg6-0Wj0(K`m*#fj6H14 +JcC<$_>jMh!.sgN!pc:LJ,~> +m/Iq#o^:r&h:L*"aMYd([^*'9Vkg&ZSXZ(9rKnChR[ftBU8=fhYHt^M^r=IBeCi^>lL4ZKrrCW. +F!>hnUjIHa[<$DAo>0"!F*HoFH[DQrF*"n.HZOPM\T>=!IJ@k,\\ii:H+W(aeoPncs0[3'[H_s[ +lD/cRUpY8jab+f1s*Mk/rmh'jo[X)0T&0Fs'$#W4F^f1+s35[qGB6Nes8N)Urr4jK>'G0gOT5>U +lMp`ZC3sSoqu-Pl.pCL'rMEnBEF,TtK4&h2CrQ:?<-a6u8@SX!!8d_UhY@*dTNZP`;NUqY!8dbU +h>i'*O@Gu2IrFcSrr],K>1NNM!q`"[q#:]d>$cDo>.O[D!+YtCf`+;4nEAibd`]SA[B?F"R$!5W +HZX+9?<9ur6Td7[.Y@^!3ZK4d-nR595Y"RL>[qMgG^tR0Q(=_OZF[lnd+@.:mJ*sd+8tp%kMY%A +bJ_*&YGe.`PDk3FG]IS2?Wg?8rr8r]48q5^VZ6Wp7nZZY>@D/]FF&LoNg6-0Wj0(K`m*#fj6H14 +JcC<$_>jMc!-@b6!p>e?J,~> +ZMsp]MM)/_>jObmf.e)q#;-(XFl/$ +oDei?mf3=TJ%u$0OK@f`\+]h$"2RffpA=jkp:%g2rs.8BItt`;s3:FjJcC<$JcC<$_Z0Vl!/0sW +!q2XSJ,~> +li.duoBkc$h:U3%ai2*/\[A]FX/W"nUS=I\TGF5qUSXlfXKSq<]"Z&$bKn\khrO%jqYU9mgPUC_ +Y1(8)e[5=(!o*CFrd>H_e&&uP[!dA:`eAhp`kHl0gocG.I3H&SY(qG+l-H5TI?+?M_T(E7I=89I +_SXj1I=]nh^s(;ZaaO0C!J:@Fm/u-UFlWGT'(";CKTX@Ms8($+K8$]0s8RT9rr3Q(XG);(oDei? +mJm43Mgpl=rr4.X:kn`Es5pG;LpQ@Ffq*kZaS5_kTSBDPPJ[A"J*m8cmJ-\spT0($VXsifJ*m:9 +m=2nNc]G9p^UEk8rr^)6LA1TF!qs(;p\tKNQ%+Y'rVt+=J(Xcfq!mV.gXOHd^:(5DTph1'KRJ#^ +BOP(D9h.H02)*ne^Eb*Q.P*[:f^h2BlJ9FKSu"]TVns#]u8+Bg#;/`q18Qs +s2"a7gAlis[K>c`9n3~> +li.duoBkc$h:U3%ai2*/\[A]FX/W"nUS=I\TGF5qUSXlfXKSq<]"Z&$bKn\khrO%jqYU9mer"MP +W6W5qd]rdm!nR+=rc8aOcG@?FZ#aus_0^6M_7=Wkg89;oF!7dAVLrr4.X:keWCs5pD:LU6:Gg7EqYa7fPiTS98LP/71uJ+!>dmeHetpT0""V"=WdJ+!@: +mXN"Oc]G6p^UNq9rr^)6L%bED!qs+*[:f^h2BlJ9FKSu"]TVns#]u8+Bg#;/`q18Qs +s2"a7ec9d_XTI[M8q6~> +ZMsp]MM).hXhhiYN;eJ3V88,iKG00D_83C8KS6(sLkLS]U4euGXcE41+I]jCNeF.\^Pmi!PlIDi +_6&NHKSZbH_6JrPKUf`l\X:Hkrn[[/s7Y:O]hWe6rS@PBg&D0MUgggKrr`(m@^gpgJcC<$JcDDC +rn[X.o=Y4oS5+S~> +lMiR6oBkc%hV$E*bK%N8]XYAUYcb(-WMlbnVl6PnX0&S1ZaREX^r4==dFR%/jQZ..qYpQMML>YZ +W4]dCKDp9#TY?-ZI1q11]Y(7sI=7Z[JUN*CSUHd3Vhb(r+I0C5LOG]C\qPQ\NrPNY]W-O2I=\?0 +]W-U4I?h=V[Zed\rn@I*s7Y1L\kR>-rS%>>gA_aZ;pnhT2[f8\io'>Ale]u4M\?W'/ +S<]+iJ9c0NA6r>791;*-D#aNL/1iJ/2*!if8P`>k@VKb(I>!NAR@pI\[_9W&db<[DnGKQk6Mg9= +jPAA3a2#3iWhZ)LNJ;q-EGB#i<_uCqs8Qdd.k)kp@K6@&3^?,-;HR:jMh!.sgN!pc:LJ,~> +lMiR6oBkc%hV$E*bK%N8]XYAUYcb(-WMlbnVl6PnX0&S1ZaREX^r4==dFR%/jQZ..qYpQHMK8rF +TXhV/HiA'fR(7SCEt`bj\[eM_F*!Y9GB7tsQ?/%iTRHWW+HAm +e]u4N\?W'/S<](gIs>sJA6i548mhVuBeLtc-n&O(s%YU?78-T]?Y4.sH\.*9Q_(%TZb+)rd+I7< +me;;4JcERdrmh'jo;r)WPY-H~> +ZMsp]JV;d2",O51WW%POPlJ+Drn[[/s7Y:O]hWe6rS@PBgA_#J!/0sW!q2XSJ,~> +l2MLooBtl(hqHZ0cH=/E^qI=h[C!6EYl:j+YS4:J['mKV^;7b.bKnYhgu.;Zn+c_Y!8E'QgjK +l2MLooBtl(hqHZ0cH=/E^qI=h[C!6EYl:j+YS4:J['mKV^;7b.bKnYhgu.;Zn+c_Y!7l^BgN*I@ +VTG;+ErgsoZuc5t!H\;7ecWZO]p7R=rlj#/bP2`Wuc6qL-Q>$trZF*`@lNg6-0Wj0(K`m*#gj6H16 +Z2[/dn`o/heBPtH\$)a(RZiY_I[qMgG^kL/Q(4VM +ZFRclcdpt7m.Gr0JcERdrmh'jo;r)WPY-H~> +ZMsp]JV<9@!KO;Y_#U5/KER7;f"[=GKE71s["SG8!JLLHh?1GdGNSk\!:JS@"7WR[e)LB-gA6:! +KBW=;n?J5,JcC<$JcC<$TE"oI!/0sW!q2XSJ,~> +kl2@noBto*iS<)8dEThS`504%]=PP_rO3HM\[oGf^r"(3bKePeg"t`NlgXfHrrCf)IHc.\LSt$` +!PFp$I0>;)e%LYc`9n3~> +kl2@noBto*iS<)8dEThS`504%]=PP_rO3HM\[oGf^r"(3bKePeg"t`NlgXfHrrCW$F6R`HIA$JJ +!OnCN4QIKStt[T;J]r]"uJ7 +fA>WUos"H_q!dP-g=+6`]sY#@TU:k!JpVTVAR8G78O>L/c2ZahI[/pmau1_'2a'Q%;H[FADg$S` +N0Ba*WNitJa3N5kjQlDgs+13ds8LRLF7ZL8kEJSh~> +ZMsp]JV<9@!KO;Y_#U5/KEI16\![,I!Jm_^K`9g2K`BocVYb98VV]!]GNSk\!:GF +kPl4np$h;1j58VDeBuRbaiDB<_8*kb^Cn>u_Sa@5air)Ze_/a9jQQ"%rV-[MisO&W3EbF`m*&hj6Q:8JcC<$ +_Z0Vi!.sgN!pc:LJ,~> +kPl4np$h;1j58VDeBuRbaiDB<_8*kb^Cn>u_Sa@5air)Ze_/a9jQQ"%rV-_+H_.BurGr!`TX8e>ec^'of%-MkCB^B^[7YMsrRLu6JcE[g*r,9jj4r21a2#6kX/2DSOc"g@ +G]IV4@9cl4:eXJOr^Ir!8P;fV*!e[_9W&db3RAmea6g*V]'eiS)`'_n +ZMsp]JV8`1rn[g3s5)V02uN^\2l>HF:3Yk!h>ltps+13$s+13$s0)J%h>i6#]`RYm:4N~> +k5Q%mp[[b;k2P7Pf[\HtcHOJRaSa*Ya:HG:c-Oedf%Jj9j6,aspAXdc!8E'QKmisT"bQd/gS?b+ +!!Y?WgU^+%j8I^4m=G;UruV(/m,d-WdE9A>[]cX(S<].lK7.raChI6a>$"[#;#X32:Jk%k=^>HJ +C2e[&p3sQ]HrRH#dY0>Z=Ee4u[3Ds4!!G +LXZ:k>U(aS3C-23='fHTFF8^uP*r#BYID6bc.(P/lLOq*s+13es8LaQK(HDPl^COu~> +k5Q%mp[[b;k2P7Pf[\HtcHOJRaSa*Ya:HG:c-Oedf%Jj9j6,aspAXdc!7l^BKld7E"`s^pet=o! +!!Y[]cX(S<].lK7.raChI6a>$"[#;#X32:Jk%k=^>HJ +C2e[&p3sQ]HrRH#dY0>Z=Ee4u[3Ds4!!G +LXZ:k>U(aS3C-23='fHTFF8^uP*r#BYID6bc.(P/lLOq*s+13es8LRLF7ZL8kEJSh~> +ZMsp]JV8`1rn[[/s8Lj_VDeJT2l>Hg]hWe6rS@PBJcC<$JcC<$JcDqRrn[X.o=Y4oS5+S~> +jSpM&o^M53jl51Rg=Y!+da?Ffcd'h^cd:(geCN:,h;I>UlL+6>o`"pGJUN6#rn@I*s8La\Uc&2Q +2Pf*^\kR>-rS%>>JcE[g5Q:BFki1@Hc,[W1Z`L$sRZrhgJphi`DJA8#e! +G'eaoN00HuUoLZ.]u8+Af\biYpTXZcrqGmFi7ZQ$_n3:XV4O$7Lk'bkC1CII9gq6?pA^[$&deaH +R/ckC.Pa"L85E8mASlO9K8Ph[TW#'&^;\@HgZ.Sirdk*#s2+g8gAlis[K>c`9n3~> +jSpM&o^M53jl51Rg=Y!+da?Ffcd'h^cd:(geCN:,h;I>UlL+6>o`"pBJTHNdrmh*ks8LRWTJZ]L +25&OO[7YMsrRLu6JcE[g5Q:BFki1@Hc,[W1Z`L$sRZrhgJphi`DJA8#e! +G'eaoN00HuUoLZ.]u8+Af\biYpTXZcrqGmFi7ZQ$_n3:XV4O$7Lk'bkC1CII9gq6?pA^[$&deaH +R/ckC.Pa"L85E8mASlO9K8Ph[TW#'&^;\@HgZ.Sirdk*#s2+g8ec9d_XTI[M8q6~> +ZMsp]JV8`1rn[[/s8CdVVDgH2rn[aj:3Yk!h>ltps+13$s+13$s0)J%h>i6#]`RYm:4N~> +j8TYhqXa4Dl0%-dhqd&@g"=pUf)O>1f\,!6hVdDTkN_I*qXXXdgOXd,IK%t%K)bf("/Ih5V#K*N +\kR>-rS%>>JcEXf*Vo9mjkeV:b/D!&Z)XXmR?NVdK78)fE,96#@prdD?QjtF*E"_L5V1\ +S>)sb[Ca8rcIL_0l1"VSs$HJKki(4Cb/:m!Xeq\VOGJI5Ebf/ic`9n3~> +j8TYhqXa4Dl0%-dhqd&@g"=pUf)O>1f\,!6hVdDTkN_I*qXXXdeq%mrF8j_kF8u3i"/%M/T`3LE +[7YMsrRLu6JcEXf*Vo9mjkeV:b/D!&Z)XXmR?NVdK78)fE,96#@prdD?QjtF*E"_L5V1\ +S>)sb[Ca8rcIL_0l1"VSs$HJKki(4Cb/:m!Xeq\VOGJI5Ebf/i +ZMsp]J\d&Vrn[[/s8:aRVZ#?R]hWe6rS@PBi;X+oKA,aCs8TjCp4<6ps+13$s+14(s8LjTK_)kY +n!m.'~> +iVsAdq=O4FlfmToj5T"Shu2C?hr*JQjQ5Rim-jNArpg!ggO[(m^AcjfK)bc's.oVJ"1h3Lj8I^4 +mGS!N\qAZ&J,fP`[e>:Pmf+=,p@%5)gXXTj_Rm4ZWMH,QPE(KQJ9uK^EGfQ,B`;WaBPD7!E-$2K +IY*?6Od;N3Vld;9^W+LGg#(rZp9+E^p[IG+g="0_]X=o?T9t^sJpMKSAR/>483sogfHq[Y()%Au +eGm$22*=5u;HR=>DK^G]N0B^)WN`kH`m3,ijQlDgs+13ds8LaQK(HDPl^COu~> +iVsAdq=O4FlfmToj5T"Shu2C?hr*JQjQ5Rim-jNArpg!geq(Ac\c1.\F8u0hs.K>A"1:aBhYl"* +lJV[K\V&W(J,fP`\+YCQmf+=,p@%5)gXXTj_Rm4ZWMH,QPE(KQJ9uK^EGfQ,B`;WaBPD7!E-$2K +IY*?6Od;N3Vld;9^W+LGg#(rZp9+E^p[IG+g="0_]X=o?T9t^sJpMKSAR/>483sogfHq[Y()%Au +eGm$22*=5u;HR=>DK^G]N0B^)WN`kH`m3,ijQlDgs+13ds8LRLF7ZL8kEJSh~> +ZMsp]JcCH(rn[[/s7Y:O]hWe6rS@PBi;X*DBA2K.s8RRemb@aBL[Ngps24^5!/H.*JcC<$JcE.X +rn[X.o=Y4oS5+S~> +hu=&_r:fgPmd09(ki_m+s5s[OlKdj+nb;q[n,ECBJcCH(rn@I*s7Y1L\kR>-rS%>>i;X*GA_?$( +s8RRcmG%XAL[Wpr!rg*Uqu6]qM9,]ZqXa(9hq?K&`k]0mY,S4gR?WbjL4Xo%G]n1LE,]`6E,frA +GC"[gKo(hQQ^jeJXKo@M`6-HYh;deiriZ8fq!mV.gXFBc]sb,BTUCt$K7%fZB4"b=916/engc"0 +*?6%?nGf[I3Bou-<*NgGE-HbcNKfs.Wj0(La3N8ljm;Yks+13ds8LaQK(HDPl^COu~> +hu=&_r:fgPmd09(ki_m+s5s[OlKdj+nb;q[n,EC=JcCH(rmh*ks7Y"G[7YMsrRLu6i;X*DBA2K. +s8RRemb@aBL[Ngps24^5!/JYq4Sn[9k24k@bf7H0['$C(Ssbh(MhZh5H['^UEc>u9DfB];Fa&.[ +JVAr@P*_]5Vl[26^;S1@fA5KPnG]Tj6MU*8inMr+`P&^`VkBH@Mh?D"DJ*+BlJ9FL5hFfUT1T/^rOaOh<""rJcC<$_>jMc!-@b6!p>e?J,~> +ZMsp]JcCH(rn[[/s7Y:O]hWe6rS@PBi;X)`df9'Ps8RRemb@aB#i=>9"T.>pi6#]`RYm:4N~> +g]%KUq=O7JnF-8B"n1sNo_A@MrrCf)s+ULPgAup+p=f\\9m,LpgApX!rs8bQs7Nu's*nnQg]%9[ +h:qr4qrYOlK`1oQpXZ&Ga8[+QnET)jf?qd\^UgeTWMH/TQBI5bL4b#*I!U):GT(jSI=Hj&Ll@F[ +R@U(MXKo=K_o^6TgYqAaq5sZ`q= +ZMsp]JcCH(rn[[/s7X,.XA4!%rSAL]hP,Xaa7fQ1c^'3=[JU"(J(B_#s80'VK;ePBrt1=mGM;J7 +s8RRemet_,KpL'^q#:W`Z%n%7k1m2irr4(%c2Z4RIuDSOMZ@tTs80'VK;ePEs*r.KJ% +f`2!N$N9o!qY9m`r;HWokl1Y;JcCH(rn@I*s7X#+W_IWrrS&:XgnKLba7fQ1d$B<>[/9n'J(0Ot +s80*WK<"\Drt1:lFkH)2s8RRcmJYV+KpL*_q#:W`ZA=7;jkI&irr4(&cMu=TIuV\PN<"+Rs80*W +K<"\Gs*r+LJ\&^uL7R)rrrVo&^\Ig3n?m*]J,TBLiJCD3a8[+Wp$V&'gt'ip`P9!kYH"InSX>\) +Nf/^JKS"dSJ/WujK8#)M,[oW8'Zqt'19h:Bon_7?kPUn*g4Lk0kn +Ch@$V;G9X4s8KHL@@D2_FaSh!OdMc +f`2!N$N9o!qY9m`r;HWokl1Y6JcCH(rmh*ks7Wi&Ue5^brRMqQhP,Xaa7fQ1c^'3=[JU"(J(B_# +s80'VK;ePBrt1=mGM;J7s8RRemet_,KpL'^q#:W`Z%n%7k1m2irr4(%c2Z4RIuDSOMZ@tTs80'V +K;ePEs*r.KJ%\) +Nf/^JKS"dSJ/WujK8#)M,[oW8'Zqt'19h:Bon_7?kPUn*g4Lk0kn +Ch@$V;G9X4s8KHL@@D2_FaSh!OdMc +ZMsp]JcCH(rn[[/J+ZP<-[4_OhAadt@Wc-r`W+8.@X!#k[K$7,Dem&dW*4Oh:7V7^&Wd!:g>2i0 +s*ntTdt):Y@!0`is"4,D>]FaeU<@/5qu>XianJ\GHY-%IMZ@tTs/9+JF&&8*rr@7u=uf]5ED2\o +r;QijS>Q)]"SBs9:4N6B"7!.]!29DnddD\dJcC<$QiI'A!/0sW!q2XSJ,~> +ZMspZJcCH(rn@I*J+ZM;-?\AIgDe@mA9MI!`;e/-@Wcfe[f?@-DelraWa0mk:Rq@_&WZp7f\6E+ +s*nnQe:VO[@[laS/\KH"g"IN<"+Rs/K@MF&/>+rr@8">rl,;E_D\n +r;QiiR\ol["SBs99n3-A"6m+[!65$uqXa+;iS2o0b/M-,[BZg3UnF6FQ'.5hN/EIKLl$tGMiEd[ +P^JRaU8Fro[(*ifaNi8fhrX1orN-#>rUoUAhq6?!_n<@YVkBH@MhHM%E+rff=&DXi^&@m/+9&f@ +5=A%;;d!I=CiXfNL5hFeU8bB+^;\=Fg>_AdrIP!"s2"a7gAlis[K>c`9n3~> +ZMspUJcCH(rmh*kJ+ZD8-$.rAef2eh@Wc-r`W+8.@X!#k[K$7,Dem&dW*4Oh:7V7^&Wd!:g>2i0 +s*ntTdt):Y@!0`is"4,D>]FaeU<@/5qu>XianJ\GHY-%IMZ@tTs/9+JF&&8*rr@7u=uf]5ED2\o +r;QijS>Q)]"SBs9:4N6B"7!.]!65$uqXa+;iS2o0b/M-,[BZg3UnF6FQ'.5hN/EIKLl$tGMiEd[ +P^JRaU8Fro[(*ifaNi8fhrX1orN-#>rUoUAhq6?!_n<@YVkBH@MhHM%E+rff=&DXi^&@m/+9&f@ +5=A%;;d!I=CiXfNL5hFeU8bB+^;\=Fg>_AdrIP!"s2"a7ec9d_XTI[M8q6~> +ZMsp]JcCH(rn[Xco615Ak5F-VQsd#bbD!>%P$Z&tmW1hsrr@,op\QP8oBqhhp\b%'!8db4!<<(K +DsW^#amd,ONr/\Q/baHgd-.L?mX90)s-PP +ZMspZJcCH(rn@F]o5t)GU7@L/LOjbnDJ3Ka=&Maa>)AuPTnZN36Uj[D<*c`9n3~> +ZMspUJcCH(rmh(Oo5ar7hYl"FP[LT^bD!>%P$Z&tmW1hsrr@,op\QP8oBqhhp\b%'!8db4!<<(K +DsW^#amd,ONr/\Q/baHgd-.L?mX90)s-PPGU7@L/LOjbnDJ3Ka=&Maa>)AuPTnZN36Uj[D<* +ZMsp]JaS6[rnd2-!93t6*-Z2Xs7tU8oQ>BOs8U%Hs8N)@s8V?aGCP*\!93qV&HL\fh>mTUIrFCj +4*uI$2f\?`s"4,JCOM8SlMmYjJ,_Zis7]iDK:LNmrrCpUl2Z$XIr>>HrrCdQrr?p&s*ntTmf*@V +Dh%Za!T!gMrr`4*2t;J!JcC<$RK*9C!/0sW!q2XSJ,~> +ZMspZJaJ0YrnHu'!8mb0*-H&Vs7tR6oQ56Ks8U(Ks8N)?s8VBdFanmZ!9="W&HLScgAq9RIr44h +4*lC#2f\Bas"4,LCjhARkl7GjJc.]js7]rGK:C>IrrC[Nrr?j#s*nnQmf*@U +D1DH_!S[UmrttP$lfR-[f$V^^_S3RgZEC4*V4sWSS=>uHR2DB^S=Q:GUnp4#VR3kG_8XRCeCi^> +l0nMKruV%-lf?mQccEr6Z`L$rR$*A]IsH*PB4>.K<)?:]8H)("7n?9J;,pe,A8,t)H\$s4PaeAF +YI2$\b0el!k3_qps+13cs8LaQK(HDPl^COu~> +ZMspUJa.sSrmpVr!8@D&*-5oTs7tU8oQ>BOs8U%Hs8N)@s8V?aGCP*\!93qV&HL\fh>mTUIrFCj +4*uI$2f\?`s"4,JCOM8SlMmYjJ,_Zis7]iDK:LNmrrCpUl2Z$XIr>>HrrCdQrr?p&s*ntTmf*@V +Dh%Za!T!gprttP$lfR-[f$V^^_S3RgZEC4*V4sWSS=>uHR2DB^S=Q:GUnp4#VR3kG_8XRCeCi^> +l0nMKruV%-lf?mQccEr6Z`L$rR$*A]IsH*PB4>.K<)?:]8H)("7n?9J;,pe,A8,t)H\$s4PaeAF +YI2$\b0el!k3_qps+13cs8LRLF7ZL8kEJSh~> +ZN#L4J_obM*-GrSs7t0uoQ>BOs8U%Hs8N)Us8V?aH[gNlBBoEp&HL\fh>mTUIrFCj4ahg.EN&O+ +s"43+Z^-)SW;FnQ/_1cqs80X.LNcqrrrCpUl2Z0\IsYhnrrCpUrr@!*s*ntTmf*@VDh%Za!T!gL +rr`1%3V%b$JcC<$RfEBD!/0sW!q2XSJ,~> +ZN#L1J_TPG*-5iRs7t0uoQ56Ks8U(Is8N)Rs8V<`H%1 +ZN#L,J_'2=*-#ZOs7t0uoQ>BOs8U%Hs8N)Us8V?aH[gNlBBoEp&HL\fh>mTUIrFCj4ahg.EN&O+ +s"43+Z^-)SW;FnQ/_1cqs80X.LNcqrrrCpUl2Z0\IsYhnrrCpUrr@!*s*ntTmf*@VDh%Za!T!gp +rtk\0na,Dsgt1!!ai2'/\[A`HXK&4rUndj^(8I\%Vl?`"Y!+8=^Ve+9d+-k-jQc41W;f-]p$V#$ +g!\'^]sk5FUR[X3MM-G'Ec#K"?X$T2;G^.a:/=\be?J,~> +ZN#L4J_obM16GhEnB6*UrK(:%oBpZgs8N)Us8VsFW:TVZKE(trqu?ZrhZ)F4s8RRem#l&,o?ET; +q>W&DYG1C7=F'`5I2$!_s8Vh+hYXPXoD\gEs8+7DoCJo5s8N)Us8N(;mf.cTmem(drT*,7rrVV, +J,B6Hh>j2J"8l.]oR[$ns+13>s8LjTK_)kYn!m.'~> +ZN#L1J_TPG16>bEnB-$Tr/Y+!na1Bes8N)Rs8VmEVXa8XL&_1sq>^Hpg]-".s8RRclB,c'o$3T= +q>W&CY+t=3=F']5IMQ9cs8Vb)hYF;Unc&U@s7n.Anaic7s8N)Rs8N(9mJhZQmJQtcr8Qi3rrVS) +J,B6HgAo.k(B4'um-!Bag!nBmaMbm-]!o&PYcb(.r2LL4XKA\2ZaRET5:sRkcI1>"i8s7mqPO9V +p[IG-gss]k_7I"UVkKTFO,8L$+j,<``C,?!q/TCN+EBIt`fCQ^snNYI;*]b0\bs +jm;Vjs+13bs8LaQK(HDPl^COu~> +ZN#L,J_'2=16#PAnB6*UrK(:%oBpZgs8N)Us8VsFW:TVZKE(trqu?ZrhZ)F4s8RRem#l&,o?ET; +q>W&DYG1C7=F'`5I2$!_s8Vh+hYXPXoD\gEs8+7DoCJo5s8N)Us8N(;mf.cTmem(drT*,7rrVV, +J,B6Hh>kIn(B4'um-!Bag!nBmaMbm-]!o&PYcb(.r2LL4XKA\2ZaRET5:sRkcI1>"i8s7mqPO9V +p[IG-gss]k_7I"UVkKTFO,8L$+j,<``C,?!q/TCN+EBIt`fCQ^snNYI;*]b0\bs +jm;Vjs+13bs8LRLF7ZL8kEJSh~> +ZMsp]J['s7!9WP&'$Go8F^f1+s35[qGB6Nes8N)Urr3t2>'G0gOT5>UlMpkahZ)F4s8RRema$;p +G@Y^6q>W&FVI"=`?E!i&VG7Dus8VsoC3sSoqu7gTgAdgLGBZrHs8N)Us8N(;mf.cTmem(doUMfP +rrVV,J,B6Hh>j/I"8bt\on!-os+13?s8LjTK_)kYn!m.'~> +ZMspZJZaa1!9<=u'$5c7F^o:-s35XoF`U6bs8N)Rrr3t4=a,'iOoPGUkPtP^g]-".s8RRcmEg8p +G@kp;q>W&EVI+@_?`VRQf`.[KGB[&Ls8N)Rs8N(9mJhZQmJQtco9uQM +rrVS)J,B6HgAo(i(&7@ekiCXUf@/'haMbp0]XkV][C!:>ZN%6E['mHS]=u(u_E]lte_/g=kNh[8 +V>hS5o^1eug!\'_^::JLV4X3@NerC;H?F4EC1_!c@/XFP@:Ee\CMn0:H[pg-OHuB0Vld>;^rO^L +gYqAbqgncus1eU5gAlis[K>c`9n3~> +ZMspUJY\$r!8ctk'$#W4F^f1+s35[qGB6Nes8N)Urr3t2>'G0gOT5>UlMpkahZ)F4s8RRema$;p +G@Y^6q>W&FVI"=`?E!i&VG7Dus8VsoC3sSoqu7gTgAdgLGBZrHs8N)Us8N(;mf.cTmem(doUMfP +rrVV,J,B6Hh>kCl(&7@ekiCXUf@/'haMbp0]XkV][C!:>ZN%6E['mHS]=u(u_E]lte_/g=kNh[8 +V>hS5o^1eug!\'_^::JLV4X3@NerC;H?F4EC1_!c@/XFP@:Ee\CMn0:H[pg-OHuB0Vld>;^rO^L +gYqAbqgncus1eU5ec9d_XTI[M8q6~> +ZMss^K_3(\6A3/U6?/`moYCPpn*/[Amb47bON7(pqp1R\K=1UVs*sJ9ruCd7K7fuks8RT:s8RT: +s6bC:s1c&:qpCd`PfrmtrsRtjKnm#"s8Tof*:Eh+*R0LlOOjI3TRY\qpT0""V"=WdJ+!@:J%,.O +^UNq7rrVo'^\7[/p:%g6rrMP;Sc8fg:I=c+s+13$s.02hh>i6$h?.*QS5+S~> +ZMss[K(QhY6%m#S6#NEeoY:Jnlfm.:m+It`Oi[:sqTb@YKs^aWs*sG8ruCd7KnH8os8RT9s8RT9 +s6Y=9s1c#9qpCdaPg&sursRtkKo!)"s8Tof*q0+.*R9RmOjsF2TRkbqpT0($VXsifJ*m:9J%#(N +^UEk6rrVo&^\7[/p9qa5rrMM:_#Gt_o^D)-iniD?da-.Z`l,a0^:h1k]=PVd]Y2%o_8F:6bKe.nEJugf$DIU]XG&EV4X0?O,AXAI!BaQDJX'$rb!3aBkhL'Ecu_VJqf2FQCFSF +XKo@M`QQZ]hW=+rJcC<$^An2e!.sjs"'b)[9n3~> +ZMssVF7d!C3J=pD3FnkGoXOu_kNUP1lIqh^ON7(pqp1R\K=1UVs*sJ9ruCd7K7fuks8RT:s8RT: +s6bC:s1c&:qpCd`PfrmtrsRtjKnm#"s8Tof*:Eh+*R0LlOOjI3TRY\qpT0""V"=WdJ+!@:J%,.O +^UNq7rrVo'^\7[/p:%g6rrMP;_#Gt_o^D)-iniD?da-.Z`l,a0^:h1k]=PVd]Y2%o_8F:6bKe.nEJugf$DIU]XG&EV4X0?O,AXAI!BaQDJX'$rb!3aBkhL'Ecu_VJqf2FQCFSF +XKo@M`QQZ]hW=+rJcC<$^An2`!-@e`"'4TL8q6~> +ZMt!_K`Cc6!mL`FL@,,f!+)D5!<;c\!mL`6l.uF.^]+E3UfMDbs4mVVq+F3OJcC<$JcD>Arn[[/ +s7Yj`blI4&:4N~> +ZMt!\K)bQ3!mCZEL@#&d!*c2+!<;c[!mCZ1l.Z4*_#FQ:pnh24JcGcM')hRpmHN`kh:gK1dE]tY +aN)9i7c]*a2,BqYH"CjR[&tmLP()(H$4:MEH#i7 +E,frAGC"[fKSYVNQCOYGXKf7J_o^6Ugu@Ser.4m!s1\O4gAup+p?`",!:-(JJ,~> +ZMt!WF8tt!!m(HBL?\i^!*5hr!<;cX!m(H*l.,k"^]+E3UfMDbs8W*3rV,jLl/gm[g=Oj%cHOGP +a2Q!p_\'i+`lQ9Fc-Xqjg"&GQhs9e-U&R7Qo^1i"g=4Bf_7R+YWhc8UQ&poZK7A8oG'%bEE,TZ6 +EH?8HH@: +ZMt!_K`Cc&!h98jL>E!%!+)D5!<;cL!h98Zl.uF.d/O.5XS[JJJ\h6`"RrHm3RWKYg]%BN84iT- +s+13$s.B>jh>r<0p>,qB!:QFQJ,~> +ZMt!\K)bQ!!h',hL>)cu!*c2+!<;cI!h',Tl.Z4*d/O.4XS[JJK#7Eb"RijPW?mmJNUV)u9$ijkeYVRSA;~> +ZMt!WF8tsb!gE]bL=QEj!*5hr!<;cD!gE]Jl.,k"d/O.5XS[JJJ\h6`"RrHm3RWKYrVmK+o^M52 +jl,(Ng"+[$d*L"^bl5fcbR_tDd*^=mf\50>jPW?mmJNUV)u9$ijkeY +ZMt!_K`Cr+!,qhm!h98jL>E!%!+)D5!<<#S!,qbk!h98Zl.uF.d/O.5XS[JJJ\h6`"7WR[W.G!% +rr`!a9_j8>JcC<$T`>#J!JLLHh?9>Kn!m.'~> +ZMt!\K)b`&!,_\h!h',hL>)cu!*c2+!<<#P!,_Vf!h',Tl.Z4*d/O.4XS[JJK#7Eb"RrXZW;V;I +r;R<,qXa4Cl/q$bhV?i;f@JLMeGn&/f@\g3h;I;Skj.['6qmEKru:t2n*/lgf?qd\^q-qXX/;V^ +R?`qqMhm+@JqluT;/?eZF@K`a3E)dhrX1prdk*#s1SI3gAup+p=f_=!:-(J +J,~> +ZMt!WF8u-g!,MPa!gE]bL=QEj!*5hr!<<#K!,MJ_!gE]Jl.,k"d/O.5XS[JJJ\h6`"7WR[W.G!G +rt#,*oC2,2k2YFWgtLE4f%']Js4%V2f\5*9i8Wk^lgF+-9_k=\)ufR"l/UUOdEBME]!\cCVP9WL +Q'./cLk^S7Jc:0=JV&N.LP^tNPEqZ0USt<#\%KSuc-tA'jm2L:JcC<$^&S)_!H\;7ec_3;kEJSh~> +ZMt!_K`Cu,!H.u:h?'2Is+]A3S,cli]`A*2h>mUpr8%LF!:T1/!:GF +ZMt!\K)bc'!Gqi8gB*fDs+]80RK-Qd[K-@+gAq:kr7_:A!:/n(!:>@;[/UX8qXj=GlfmQmio/eO +h;/%b&`)=$i8WeYkj%R)pAFsa6;RHMs#BoJmc`Zcf$V[\^q7%[Xf/%gS=,\,OH,9XMM[.GM2I4N +O-,Z]KV"m7Wii\<]u%h7db*F;lL=\%s+13_s8LaRK)bQ!"I]>VRSA;~> +ZMt!WF8u0h!G_]6ecM'9s+])+PQ4aYXT8D"ec>bdr71q6!9`Ut!:#.8[/UX8qXj=GlfmQmio/eO +h;/%b&`)=$i8WeYkj%R)pAFsa6;RHMs#BoJmc`Zcf$V[\^q7%[Xf/%gS=,\,OH,9XMM[.GM2I4N +O-,Z]KV"m7Wii\<]u%h7db*F;lL=\%s+13_s8LRMF8tsb"I&oLPY-H~> +ZMt!_K`D#-!cJ&=rS@UG!<7f1!h96sU:gR,rSIPS!,qhm!h98Zl.uF.JcCo5"8+lWqgneErrSK, +hLY]Xs/Z2!h?/H2hVS*Is5!mK!:QFQJ,~> +ZMt!\K)bf(!c7o;rS%CB!<7f.!h'*nU:((%rS.>P!,_\h!h',Tl.Z4*JcE"T)Z9C%nF#Z.l0.9l +jlPRajlPXfkj%O&nFZPTrVlum5?%HOru(\)mHQB[SrrK.q[PEhK& +S6Q\fW3!52\\>u&c-k8#j6?%/JcC<$]Dqlb"+pR-g\PdegB +ZMt!WF8u3i!c%c9rRM%7!<7f)!gE[cU9+FqrRUuK!,MPa!gE]Jl.,k"JcE"T)Z9C%nF#Z.l0.9l +jlPRajlPXfkj%O&nFZPTrVlum5?%HOru(\)mHQB[SrrK.q[PEhK& +S6Q\fW3!52\\>u&c-k8#j6?%/JcC<$]Dql]"*=Lnf(s1^ec_3;kEJSh~> +ZMt!_K`D&.!,qi:rS@UG!<7f1!h96sU:gR,rSIMR!,qkn!h98Zl.uF.JcCl4"7nWUr.4nFrrRco +i.:oZs/c8"h?8N3hVN2K!!dH!hOFT7S5+S~> +ZMt!\K)bi)!,_]8rS%CB!<7f.!h'*nU:((%rS.;O!,__i!h',Tl.Z4*JcDnQ#Q=MmoC;>=melJP +mKN(LoD&7_q>UQg4BDBPs"sQBm-!?_f?qg`_nN^jZEC7,V5']TSXZ(:R@0M4S=Q:GUS_'+R^9K9 +^r4@?e(EO;l0e@us+13]s8LaUK)aT[D>XA=D;3g6!:-(JJ,~> +ZMt!WF8u6j!,MQ6rRM%7!<7f)!gE[cU9+FqrRUrJ!,MSb!gE]Jl.,k"JcDnQ#Q=MmoC;>=melJP +mKN(LoD&7_q>UQg4BDBPs"sQBm-!?_f?qg`_nN^jZEC7,V5']TSXZ(:R@0M4S=Q:GUS_'+R^9K9 +^r4@?e(EO;l0e@us+13]s8LRPF8shBC]"/;CY%($!9]S=J,~> +ZMt!_K`D&.r;cfQ!h98jL>E!%!+)D5!<<#Sr;ciR!h98Zl.uF.JcCi3"7eHUrIP"GrrRWmidq,\ +s/l>#h>r<0rndPQrn[dJ!:QFQJ,~> +ZMt!\K)bi)r;cfN!h',hL>)cu!*c2+!<<#Pr;ciO!h',Tl.Z4*JcDbM%0$5'qtg-`q>1$frr2Qi +"7eHUrLa**q"!e7in`89cH=,B]t(SXYH=h'VPU'cU)'N"V5L;nY-;"EV7sgebL"blhW*kgpjrHr +s1/1/gAup+rnI>Nrn@RE!:-(JJ,~> +ZMt!WF8u6jr;cfI!gE]bL=QEj!*5hr!<<#Kr;ciJ!gE]Jl.,k"JcDbM%0$5'qtg-`q>1$frr2Qi +"7eHUrLa**q"!e7in`89cH=,B]t(SXYH=h'VPU'cU)'N"V5L;nY-;"EV7sgebL"blhW*kgpjrHr +s1/1/ecBjlrmpuIrmh4:!9]S=J,~> +ZMt!_K`D#-rW)oR!h98jL>E!%!+)D5!<<#SrW)oR!h98Zl.uF.JcCf2"7\9UrIP"GrrRKljFR>^ +s/uD$h>r<0rSIMRrS@[I!:QFQJ,~> +ZMt!\K)bf(rW)oO!h',hL>)cu!*c2+!<<#PrW)oO!h',Tl.Z4*JcCf2"7\9UrLa**q"!h9j5/J> +d*0SL_7dFh[Bd$>XfJN$WYMS9Xfo"9[CEhP)mRN1dam12jQZ..JcC<$\GuQ_!J:@Mg\q3OgB +ZMt!WF8u3irW)oJ!gE]bL=QEj!*5hr!<<#KrW)oJ!gE]Jl.,k"JcCf2"7\9UrLa**q"!h9j5/J> +d*0SL_7dFh[Bd$>XfJN$WYMS9Xfo"9[CEhP)mRN1dam12jQZ..JcC<$\GuQZ!H\;>f)>[Jec_3; +kEJSh~> +ZMt!_K`Cu,rrE#S!h98jL>E!%!+)D5!<<#SrrDuR!h98Zl.uF.JcCc1"7J$Srdk+HrrRr<0r8%DRr8%RH!:QFQJ,~> +ZMt!\K)bc'rrE#P!h',hL>)cu!*c2+!<<#PrrDuO!h',Tl.Z4*JcCc1"7J$Srh'3*qXa.>jP\eE +e'H7Z`PTC&]"#2V['Tb;s0EBG[C<]Y]thP*>TUQ.g"kWLlgXb%s+13Zs8LaRK)bc'!!)rO"I]>V +RSA;~> +ZMt!WF8u0hrrE#K!gE]bL=QEj!*5hr!<<#KrrDuJ!gE]Jl.,k"JcCc1"7J$Srh'3*qXa.>jP\eE +e'H7Z`PTC&]"#2V['Tb;s0EBG[C<]Y]thP*>TUQ.g"kWLlgXb%s+13Zs8LRMF8u0h!!)rJ"I&oL +PY-H~> +ZMt!_K`Cr+!!)uS!h98jL>E!%!+)D5!<<#S!!)oQ!h98Zl.uF.Rf +ZMt!\K)b`&!!)uP!h',hL>)cu!*c2+!<<#P!!)oN!h',Tl.Z4*RfSe]Y;.s_o9^>c-]f?aPc.CmIU:-s+13Ys8LaRK)bQ!"I]>V +RSA;~> +ZMt!WF8u-g!!)uK!gE]bL=QEj!*5hr!<<#K!!)oI!gE]Jl.,k"RfSe]Y;.s_o9^>c-]f?aPc.CmIU:-s+13Ys8LRMF8tsb"I&oL +PY-H~> +ZMt!_K`Cc&!h98jL>E!%!+)D5!<;cL!h98Zl.uF.S,WT^IgggXs7ZHoln3"'s8)`sD(Fn&s+13T +s8LjUK`Cc&"IoJ\S5+S~> +ZMt!\K)bQ!!h',hL>)cu!*c2+!<;cI!h',Tl.Z4*S,WT^IgggXs7ZHoln3"DrtGD+o'P]'iSN;? +eBuUdb/hWB`5MVl&]DZ.a2uNLd*gIsgg;DWlgOS!s+13Ws8LaRK)bQ!"I]>VRSA;~> +ZMt!WF8tsb!gE]bL=QEj!*5hr!<;cD!gE]Jl.,k"S,WT^IgggXs7ZHoln3"DrtGD+o'P]'iSN;? +eBuUdb/hWB`5MVl&]DZ.a2uNLd*gIsgg;DWlgOS!s+13Ws8LRMF8tsb"I&oLPY-H~> +ZMt!_K`Cc&!h98jL>E!%!+)D5!<;cL!h98Zl/!BI]gW2MORE/CTIgE\C\Rl/!3IE's7^"7#ZC-h +s"W@1V&021n+Zj3Kn!m.'~> +ZMt!\K)bQ!!h',hL>)cu!*c2+!<;cI!h',Tl.[0C\jd)ROR<)BU+Q]_C%qZ-!3.0!s7^(9#Z^?k +s"W7.VAKG5lh1:-VRSA;~> +ZMt!WF8tsb!gE]bL=QEj!*5hr!<;cD!gE]Jl.-g=]gW2MORE/CTIgE\C\Rl/!3IE's7^"7#ZC-h +s"W@1V&021n+Zj3 +ZMt!_K`C_8! +ZMt!\K)bM4!^fQ*GU.CdRY$%:RPY%\Nmf]fb[K,db"Kq@)!:Tjc!V;cRrrfuG +Q>M,&s7H +ZMt!WF8tot! +ZMss^KRj-r!!$?"^%24(n*/[KIr!d/]KHZ/?Ps0W^DrrBJ,s7ZN^s8UpUn,<8@h>l.,s4.0s +qgVf's8..]mf2!QMi_`.s7=(Oqg3P]s6afLn*^7Qp](!fmf93Ys7c!^"kmaNl>M0's7?6lk9b>& +s82furaI\PJcC<$[/^-^!/0qD!q2XSJ,~> +ZMss[Jq3pp!!$5t[dsJ!lfm.DI;@R:o>&TGH#;sKs0`pIrrBM-s7ZZ^s8UsVn,<8@gAok*s3gsp +qgVi(s7pqWmJkmSNK7l/s7=%MqgE\]s6jlMn*^CRp]'jbmf]BZs7>^Z"kmgOkA>^"s7?6lk9b>@ +rsekuna>`-kN([^i83;GrS/"@hV[8Mj5oIhm-jTDs8HI`mt(Lis0Vh*gAlis!!;JT9n3~> +ZMssVF+F>a!!$&oXn)MmkNUP;HY_@8o>/]KHZ/?Ps0W^DrrBJ,s7ZN^s8UpUn,<8@h>l.,s4.0s +qgVf's8..]mf2!QMi_`.s7=(Oqg3P]s6afLn*^7Qp](!fmf93Ys7c!^"kmaNl>M0's7?6lk9b>@ +rsekuna>`-kN([^i83;GrS/"@hV[8Mj5oIhm-jTDs8HI`mt(Lis0Vh*ec9d_!!;>J8q6~> +ZMsp]Ja\<]l.uJ[C[1lu!q'uVrr38T!<<'!hZ*W4qZ$WIrVloT!<)p0!,q:TDsmYnDh%fel>(m! +C[1lu!Uan(!"Neqs1M#93OSne]h/hr[IF2$o6Y90k%f>@JcG?A!p(\QJcGWI"8uO`n:CUjs0_n+ +hXU%8S5+S~> +ZMspZJa8$Ul.Z8VC[:s!%e"=arVuoL!<<'!g].<2qZ$WJrVm\g!;lfrqZ(R'Ir4TQmr*XQs6FHM +Ir"BKrrMS,qu@4BfDiQZ&3-uqs1V2=33r&R#kNI[gZ*!np\f[Co`#!S,^GjY)ZBL(naGl2l0. +ZMspUJ`haMl.5lM!:'O_!q'uVrr38T!<<'!hZ*W4qZ$WIrVloT!<)p0!,q:TDsmYnDh%fel>(m! +C[1lu!Uan(!"Neqs1M#93OSne]h/hr[IF2$o6Y90k%f>@JcG?A!p(\QRf=<)q=O4GmHa$#kN:mf +jQ,Fak3(snlg=05q#10k"8uO`n:CUjs0_n+f(%o(PY-H~> +ZN#L4J_kt7J_oqR!lZ0rQJVStpYc&.pYH#-:,(+IhLXOYh>s-AJ,~> +ZN#L1J_Pb1J_T_L!l>snR+hl"f@80mcHXT2bQ#cic-F\cf@p&T"5-C&c[ji%gS +ZN#L,J_#D'J_'AB!koXfR+;Mmda-+ZaiMO#`rF'_aN;WOdae0E"4TsrbCS5qet^dsPY-H~> +ZN#L4J_kt7J_onQ!lGssJ_p:\"5?I)dt-A,hPB!/S5+S~> +ZN#L1J_Pb1J_T\K!l5goQ.l`$g"=p-e^W.#f@em3o\0K#8i"bEgO\+TgB!a;J,~> +ZN#L,J_#D'J_'>A!k]FgQ.?)geC2jnrm1eqdaZgto[X,n8MSD>eq)DJecD"-J,~> +ZMsp]^&J'4iW&q-f`1u$g]%6R_#FB7\GuSYao;BkDh6d7h?(M2FSjnpfn/&_g.4*OJ_kt7V;;6C +:4N~> +ZMspZ^&J'4iW&q-f`1u$g]%6R_#FB7\GuSYao;BkD1UI2gB,)+F8O\leq2WYf1.^HJ_Pb1V:u$> +9n3~> +ZMspU^&J'4iW&q-f`1u$g]%6R_#FB7\GuSYao;BkCOt(+ecNE"E;S2dd=TpOdR?";J_#D'V:G[3 +8q6~> +ZMss^s1dClS,iTLh?1GdJ*u%I"2.HZmbQ_$S,iT+h?'2Is17%h]hX(FbME<6:2b^?n(n,\(O5l7 +!J'!Xh?2G"37`Z;J_m9\!Luo8~> +ZMss[s1d:iRK3BJgB5#]J*ktE"1h3VmG6LuRK3B)gB*fDs16qe\kR\AbM**09l5I9n(RoV(O#`2 +!IrgTgB6"p2q*?4J_R'V!Lc`5~> +ZMssVs1d+dPQ:aDecW +ZMss^s1dClS,iTLh?1GdJ*u%I"2.HZmbQ_$S,iT+h?'2Is17%h]hX(FbME<6:2b^?mbS#Y(46#: +!E"u1h?2Ft44f&?J_m<]!Luo8~> +ZMss[s1d:iRK3BJgB5#]J*ktE"1h3VmG6LuRK3B)gB*fDs16qe\kR\AbM**09l5I9mb7fT(4#l5 +!Dni.gB6"m3n/`8J_R*W!Lc`5~> +ZMssVs1d+dPQ:aDecW +ZMss^s1dClS,iTLh?1GdJ*u%I"2.HZmbQ_$S,iT+h?'2Is17%h]hX(FbME<6:2b^?mG7oW'S$,= +!_F*YJ_gL]5s-AJ,~> +ZMss[s1d:iRK3BJgB5#]J*ktE"1h3VmG6LuRK3B)gB*fDs16qe\kR\AbM**09l5I9mFq]Q'Rfu8 +!_=!TJ_L:W5!@DHgO\+XgB!a;J,~> +ZMssVs1d+dPQ:aDecW +ZMss^s1dClS,iTLh?1GdJ*u%I"2.HZmbQ_$S,iT+h?'2Is17%h]hX(FbME<6:2b^?m+quX'S>2u +70%[ch?;]e4@.VNhLXO_h>s-AJ,~> +ZMss[s1d:iRK3BJgB5#]J*ktE"1h3VmG6LuRK3B)gB*fDs16qe\kR\AbM**09l5I9m+VcR'7eil +6i_L`gB?9\4@%GJgO\+YgB!a;J,~> +ZMssVs1d+dPQ:aDecW +ZMss^s1dClS,iTLh?1GdJ*u%I"2.HZmbQ_$S,iT+h?'2Is17%h]hX(FbME<6:2b^?leVlU&r,70 +!"7/)h?;]d3^_SOhLXO`h>s-AJ,~> +ZMss[s1d:iRK3BJgB5#]J*ktE"1h3VmG6LuRK3B)gB*fDs16qe\kR\AbM**09l5I9le;ZP&qo"* +!"7)'gB?9[3C;;JgO\+ZgB!a;J,~> +ZMssVs1d+dPQ:aDecW +ZMss^s1dClS,iTLh?1GdJ*u%I"2.HZmbQ_$S,iT+h?'2Is17%h]hX(FbME<6:2b^?lJ;`Q&W&aQ +#@rDBs5!k.2b2SQhLXOah>s-AJ,~> +ZMss[s1d:iRK3BJgB5#]J*ktE"1h3VmG6LuRK3B)gB*fDs16qe\kR\AbM**09l5I9lIuNK&ViRN +#@`8=s4[Y(2Fc;LgO\+[gB!a;J,~> +ZMssVs1d+dPQ:aDecW +ZMss^s1dClS,iTLh?1GdJ*u%I"2.HZmbQ_$S,iT+h?'2Is17%h]hX(FbME<6:2b^?l.uWM&1.AW +#/u/Ms5!k-2+lVShLXObh>s-AJ,~> +ZMss[s1d:iRK3BJgB5#]J*ktE"1h3VmG6LuRK3B)gB*fDs16qe\kR\AbM**09l5I9l.ZEH&1%;V +#/YrGs4[Y'1eH>NgO\+\gB!a;J,~> +ZMssVs1d+dPQ:aDecWs4.:r1IooFeq)DRecD"-J,~> +ZMss^s1dClS,iTLh?1GdJ*u%I"2.HZmbQ_$S,iT+h?'2Is17%h]hX(FbME<6:2b^?n(nK.LaX/[ +*?Q1#!!!0sJ_pC_"4K#"gO\44hQ>W8S5+S~> +ZMss[s1d:iRK3BJgB5#]J*ktE"1h3VmG6LuRK3B)gB*fDs16qe\kR\AbM**09l5I9n(S9(L+!oX +*?H+"!!!0rJ_U1Y"4/brfR_e.gTB32RSA;~> +ZMssVs1d+dPQ:aDecW +ZMss^s8:aO!!,*nbME;j!<;uRqu@!_^!5DP?Hq>FrW!$'-%s_Fh?1GdJ+!9l!,qc8"$+'OhY[?Y +3PF;+!577Gr;Zm)6H8r["2.HZmekoJdYKI\$q7,OhJ`>m"rr!7hVJ7GrS@Rg!<(aUS,iTehY[?W +6I+H4?>oSp!cMDihqeF)!<;uRqu?mDLt9gWDti)iDdL]?h?1GdJ+!OJ_kt7Y202L:4N~> +ZMss[s8:XM!!39_]$&h0RK3Bcg\h*Y"?=!8gMLMgMQoi#6Grlb1kYN^AZde^&\,d!h',h +r7h/M"p@B +ZMssVs8:IG!!,*fbLQ`Z!<;uJqu@!_[Dg^4?HLc6rW!$'-%F&8ecWab)#F$q6uCenb3a"rqj+f%'i7rRM"_!<(IMPQ:a]f),LO +6H7Tu?>K;d!c),ahpqjn!<;uJqu?mDK[.\GC\QB]CL5-7ecW +ZMss^s8:^X!3OPl]g=0kh?'2Is8:^g!3OPl]g=0#hJZ`FhJZH.b.2jNDq=pE"2.HZmetu`DcXQs +cFlOnhVJ77cHa*i.Gin]^#%TGXj4qg8eB;Hh?1GdJ+!9l("-kTLV9Z^4f$qQk7f +h>t:irS@UG!<;uR$373RcF#AnhU\3mqq_O$CXV'X?HqBa!h98jr8%b\?&=#'Xk2 +hJZ`FfA6VC:4M^2h@/,dcFm7kcHb"tDcXRNc2tC.6N,aC3>`%Qrn\%&&6cK]N"Bd]h>j\Uc2cGp +h?(AcDh6d2h?'W"$N'l)!]GVdrS@Y'/Q:L'h>l2ZhLXOkh>s-AJ,~> +ZMss[s8:UU!34)[[RDUfgB*fDs8:Ud!34)[[RDTqgML%BBWlN/_9b>ADgB5#]J*m3h("$kVKt=3Y4JLSI +ZMssVs8:FP!2[]T[6c=cecM'9s8:F_!2[]T[6cDQ.G!&M^"1a7V8gNW8eB#@ecW?HM*U!gE]br722T;RONTD`Nd%PaqX%#'4Cu +en\U6f@C&38q6.*edU!L`jJ]S`l?`WE7s3W7M20c12ArmhIj&5oXDN"BdUec;QE`W4<` +ecN6OCOt(&ecMQh$N'l)!]5JZrRM(m.o=doec='Jeq)D[ecD"-J,~> +ZMss^s8:^S!58Bg!mUekbh`Dk!<;uR!k7f +h>t:irS@UG!<;uR!s_YpYH*m?Hq>F?HqBa!h98jr8%YY?Hq?``E$pPh?In)c/*AFc.CeS +]hX(Fqq_D!?>of!!cMDirS@RW!<1gX`W2h8PlBP>;Z5GUN!S:s!580a!kh?IJ_o\K"5ShE#lO`( +#Gcq-rS@Y&.TbI)h>rA[J_kt7[b_%T:4N~> +ZMss[s8:UP!5AHe!m:SabhE2f!<;uO!uMbKZ?gB5#]J*m3h!c;;g +rn@V"!6i;RSA;~> +ZMssVs8:FK!58B_!lb5[bgli[!<;uJ!KMj!c),arRM"O!<1OP]`=Q&PlB57;G-o="cN` +ZMss^s8:^S!58Bg/C"78h>icWHf!%?#RE/Pg>8Vg#TS39hV+>O#Ut_`hRQf$$r4(a\QTOE(khV> +!h98jr8%GS^&Hjqc2_A&DdL]FDdL]fh?(5kJ,\:,]hX(FrS@To?HrK+"jR+fh>k7gh?hI-`ng^/ +hVJ7GrS@R>+oU7"]hX(FrS@Ts?HN0&s3_>4DdL]FhUZ=LdbXu;rS@Rg!<(aUS,iTeh>mVirn\Eu +#in\=8leMT$j]nIDaI>5$s'q9hAcN[.0(3LhVOJ?!#UCGhUYj9#YND!hJ3Z.*.,s0h?1Gd +J+!3j!mP%irS@To?HrH*!OW44h?80BhEjR.h>mnir8%RrhVJ7Gptc(c:2b^?khZEjBa+b?!!+;" +J_p@^"3r;th>Z73E,:*^J_mil!Luo8~> +ZMss[s8:UP!5AHe/B\%2gAmBRFkb)2#RiJUg"W5`$6FQK$Nq&)#4RgMLi2jD-tNcgB"G_rn@Qu*8H@t!5A6_!kM*CJ_TDC"41Zb"o\K& +,2.V&rS%Ft-s54&gAu2-J_Pb1[bChO9n3~> +ZMssVs8:FK!58B_/B.\'ec:pOF4S>t#RE#DdajpK#TS35f$]X3#UtSTf!/*]#YM5MYu1u1&;9c. +!gE]br71lK^&HRi`W0>eCL5-6CL5-^ecN6[GQ-.q[7Yr6rRM$c?HN2t"i^PUec/C0gs2b]#CL5-6f$7c3ajpI*rRM"_!<(IMPQ:a]ec>carmhje +#hI+(CY!8n<^fFc#ZA'G6;e@'J,-8leAL$j]b=CI1o1$s'e5ef4[S.0(3Lf%,cs!"ah7f$7/!#YN+femf6s*.,g,ecW

        ;!!+7n +J_'eN"3)]hec+,#ChSCNJ_%9\!L-3,~> +ZMss^s8:_/!4CD/`_%E2h>f:#cJClJ`k?F_TVtV,`Jcs8hO%&o`d^",en7U_\P?qgB1fS_S,h.7 +h?'2Is8:^a!4CD/`_%D(hJZ`FhJZ`Frn[_$?>oi""2.HZmetuZDd(-2f#C<*hVJ7?f%.i5+l;&U +^#%TG^&?dg]`A#f"2.HZmf)&FdUR(HqVDTu>/f(k7fh>t:irS@UG!<;uR)Z[:sf#B^R +hT]AFhJX6C[FT8&c/*@UHd\iUF8ar1en7U_\XWZNhGQtc`]6'9F"+fJ8hp;$!h98jr8&@m>/f(< +In?0T?>n\=(hU2,DdL]FD]!iLW',5!hAcMU`nkJ:hTc+cesl`khGQtc`]6'9F"+fAN'2sqh?1Gd +J+!3j!mP%irS@To>/gKl!kgp(rn[r1CYk!Mf&"CHf(n/'!4DaU!8@G/!kh?IJ_oPG#/`k6!< +ZMss[s8:V,!4()&_bDE2gAidrbM,HJ_n'hWTVbD&_Mg[4gQkQg^jS>'eRqO^[S:D\B1oV\RK1\. +gB*fDs8:U^!4()&_bDD+gML%BB^AZde[KQQ`"1h3VmJbiBcXCP>qV)Bp=i/Y2LcBn=gAntdgB#"grS%CB!<;uO)Z[1me&+:R +gWX#AgMI^;[*rl!bhHtOH-`?OF8ai.eRqO^[[I3BgJLP[^-"O4E@AEA9ecP#!h',hr7`.j=i/Y2 +InZBV?Z"P7(1jl%D-tNAD&@QEVET(ugDg2M_q/`6gWokce!^9_gJLP[^-"O4E@AE8N';spgB5#] +J*m-f!mG"grS%Bj=i10e!kLKsrn@`-D;0pNceH>;e+q`!!4)RP!S,m&gB+r\D1UI(gBPJ^"p"]1 +Ne,s_gB,JC>Q3@YC^.q2gO\+ggB!a;J,~> +ZMssVs8:G'!3OPl]h0I!ec7.h`n!I:]sVcJS=i>e]neh(es&p^]mDbpdUu1W[8(A[B1fGSS,gk/ +ecM'9s8:FY!3OPl]h0Gpen\U6en\U6rmh.i>&4,g"1:aNlM]9NCKA-ocFlgnf%'i'cHa*q+kG3E +^"1a7^&?L_[/g0V"1:aNlMf?:a^/W.qUQ$iJ,-'On>mCL5-6CD_9;UciYnef4ZE^"-s)f$48[cC=m[el#,R]fA+(CE^+!LcpCiecW

        iAJ_%<]!L-3,~> +ZMss^s8:aO!$X/2hVJ7&hVQf`^#%VZ>'kU"^#%>>Z.`u+f&"hbB?k-ZZ2WG]en7IWf&#!Oh?'2I +s8:aO!"(Hoc/*AFc/*AFc2QQ$c!G>fh?1GdJ+!9l!,qc8"#7LGhY[?[0tlH#!577G!58?f!PJL3 +h?1GdJ+!k7fh>t:irS@UG!<;uRqZ%A5hT]AFhJZ0&hVN0Wc/*A: +_;:E;ec="PhSoQ@(nBI>c@l/Q!h98jr8.;N'ir;u?>n\=:;,N@DdL]FDc4F. +c!G>chAcNlhVPpGhTc+[f&#!7ec="PhSoQ@(nBI>eu>qRh?1GdJ+!3j!mP%irS@NmqZ%*3[GKcR +!577?!0#dS!7pu&qZ$XKSGq@H]hWKshW3MsXs-AJ,~> +ZMss[s8:XL!$X,.gY2_"gY:0W]%buN=a>9q]%bW0Yh*T#e(`)RB?t9^Y5[#WeS.OVe(`@FgB*fD +s8:XL!"(Ekb1gc?bhHuAbl6>uaB```gB5#]J*m3h!,_W6"#.=@g\_$X0Y6#n!5@4B!5AEd!O`.0 +gB5#]J*m6i"4(CifD=5%D>aG=(j4!UgAntdgB#"grS%CB!<;uOqu@K8Y1q0-D;/o']\D1X?d.8A +;81f-?#BPp("I"gY1MFWgX?/GgY9FBcPW,*gW,'IgB*fDs8:XL!#>W=`6;S*gMKa!gY6RPbhHu3 +^>$e9D>W'-!2A6&\cCL2"H([_e(`=ObM1dc!7)LNgY:,QYl +ZMssVs8:IG!$Wl"f%'hkf%/+H[FWp:;KI%_[FWX"XjUilcIU*DA&`"JXo?`Qc=9>GcIU;7ecM'9 +s8:IG!"(0_`R\g2ajt66ao9im`EI'VecWGdal^pc2bl8f"D('(mNV&`e=J,-::\s,CL5-6CJMFk +`EI'Sef4[[f%.5/f#@]KcIU:lc2bl8f"D('(mNV&cD@ZBecW +ZMss^s8:^S!586c"TZ6mhSoQph?D/1Df8t,rS@S'A,H<:?Hp<-.3N69hSF7^&2B^Kh?'2Is8:^S +!586c#&dhmhJZ`Frn[^e>(?P."2.HZmetuDDdL]bh>mViqVDAU^#%TG^&?dgPTg"^"2.HZmetuD +F'?Q^h?M\`CW13uc2-8t!58?f!PJL3h?'2Is8:^S!58Bg(4-VLc!G>FDdL]FhJZ`FhJZ`FhT]AF +r8%[pF"&4dSDM9_!!9h6^%pLdS,iTehAZH$c/.F?Hq?O?>o_t%KO3! +hRrpGhPEOM0q@+?qZ$]2!583b"2.HZmebiBc!G>eh?%V%c2QQ'elj6RhJ<#h"?G>q#d+'^"kG-) +h>k7ah?(AcDh6d*h?;Yp&HI-.hYZ.7aWY3OCB+ROJ_kt7\D@7V:4N~> +ZMss[s8:UP!5ACHM*"1h3VmJYc@D-tN`gAq;gqV)/R^>%BB^AZdeQ6?.\"1h3VmJbiB +fl(*5rn@Y/Iqc]8?d/B$!`,n^2YPD0Ugb6qZ$]/!4r!\!h',hr7`.j?d.9XVF#@H?Z"P7?-:pVD-tNAD-b<=b[55agC=4p +gY9FBgY8Gg&29R-_tsE8\cB>;gB5#]J*m-f!mG"grS%Bj?d/N("j^ +ZMssVs8:FK!586["TYs]f"D(_ecj#rCMR+mrRM"k?Mjd5>/e$j.3N*-f"#]J&2BFCecM'9s8:FK +!586[#&@Peen\U6rmh.YcaqUPfM^"1a7^&?L_PSjAM"1:aNlM]98 +Dd(!VecsQLB>&(eanjQh!4DdV!OVq+ecM'9s8:FK!58B_(3:&/f(7?>KGh%KNof +f"D(7eso&<0pL8&qZ$]*!4DXR"1:aNlMK-6a^/c]ecKVnao9ipc<;CBemnUX"?G&a#d+'V"jSQn +ec +ZMss^s8:^S!586c.0.'mViqVDAU^#%TG^&?dg3Fi$E"2.HZ +metuDPY.2>h?LAWB@_!-c2-8t&>f?^!Ls`"h?'2Is8:^S!58Bg(=iK*_-V':DdL]FhJZ`FhJZ`F +hT]AFrSA:C_8Y9ES,gjc!7p#`f!X?GSDN,JA,Y6cS,iTehAZH$c/.=2CLb1eDr,D=c/.;q?Hq>F +?Hq?O?>o_t*!!\/hRrpGeudfcS,gjc!7p#`f!X?GSDN,RR%XH8"2.HZmebiBc!G>eh?%V%c2HK& +]`?pG(i0-%"cNH,hF^E=h?AiB8hLXOnh>s-A +J,~> +ZMss[s8:UP!5A%BB^AZde4C.p?"1h3V +mJYc@PYIDAgBP#TA^bL'bkg&p&>K-X!La`$gB*fDs8:UP!5AHe(=N9$^gCs5D-tNAgMLb[55cgB)5!bl-9" +[K,(:(iB9$"c<<#gJ(E@gBET;bM*$2ptGk]9l5I9iS+I_CDb?Jg\]_0`#iW*!!>?7gO\+hgB!a; +J,~> +ZMssVs8:FK!586[.0.'4f$49'f%.B4F)tBmf$9AEdV!4@f$_7Jd^d'B[7XrO\\6SFPQ8_tecM'9 +s8:FK!586[#&@Peen\U6rmh.+8XTM."1:aNlM]98CL5-Zec>caqUPfM^"1a7^&?L_3ElC4"1:aN +lM]98O@kW6ecrBKA'SjranjQh&=i^M!L+/oecM'9s8:FK!58B_(L*CL5-6en\U6en\U6 +f#_66rRM_3\\6S%PQ8_K!7'0HcE5Y/SCZ92>5d"RPQ:a]ef+Tqak#%kB3r8XCY!9-ak#$Y?HLc6 +?HLd??>KGh*!!\'f"D(7cDB+CPQ8_K!7'0HcE5Y/SCZ9:OI6$u"1:aNlMK-6a^/c]ecKVnao0co +[/ee/(i0,r"bZlqek/R5ech!4`mt.'psoMS8o'"/iRS+UBGejBf)+#&^`R*!!!><6eq)D^ecD"- +J,~> +ZMss^s8:^S!586c!mViqVDS[^#%TGXj4qo8eB;Hh?1Gd +J+!9l%+8oGK>"6V25&fE?Hr?'#BL`H`l&DkZ05W> +?Hq?O?>of!(#!S,]sX()f%s"k`m.I:hJ4RRc:Thih?'2Is8:^m!*6eYb*4o1c!G>FDdL]FhJZT> +hJZ`FhT]AFqq`:m^#%VZ!8c94V:Lk2f%s"k`m.I:hJ4RR`Jcs8rn[aj:4M^/h?(r.DuJMoDdL]e +h?CRmhQ6dtrn\1:#^bFJ&5opUN!NqMh>j\Uc2cGph?(AcDh6d&h>t['J_p7[#/qW=!!!5'J_kt7 +\_[@W:4N~> +ZMss[s8:UP!5A%BBWlN/_9b>ADgB5#] +J*m3h%*rWAJ%2FO1n<<;?d/B$#Ak<<_nM!BFoC%rRK3BcgD^-ma2c)/'\gi=D;/o7bhLlh>KGQ9 +?d.9K?Z#br("I)"]!@J%ceD#c_oGV2gLVeEb=s\hgB*fDs8:Uj!*-PL`KWK/b[55AD-tNAgML09 +gML%DV!8Gg*U=58.ceD#c_oGV2gLVeE_Mg[4rn@Od9n2R-gB,T+D>i2jD-tNc +gBG:kgSOtbrn@t4#^tIF&5fUJNXT4JgAn8O`rONdgB+r\D1UI!gB#6sJ_U%U#/VE:!!!5%J_Pb1 +\_@.R9n3~> +ZMssVs8:FK!586[!caqUQ#S^"1a7V8gNW8eB#@ecW

        ]%&D"s0CY!9-ak#$Y>/f'. +?HLd??>KMj("-kq[B5AfcIP +ZMss^s8:^S!586c!8J_#TS?BhV+&?#UP/PhR-)a$s'pq[8$u-(mOaN!h98j +r8%GS^&$RgDdL]FDuK_="=8QBrS@Xi:4M^1h?%V%c2-8t!586c!s$$khYdEU#XZ6&h?1GdJ+!6k +$dF/I":R_thVN0Wc2$3"KIHm$.?+2]!h98jr8.>O(+HBBc!G>FDdL]FhMW@K]l.6%hT]AFr8&-q +:'_7FPht9J8dG\h`noQ^&-+`1rS@UG!<;uRqu@KHCW4P)Dr,D=c/.<8*)=1c?Hq?O?>o_t*!!\/ +hRrpGhM`!G(mNn6ek-2R6H7m=@h8ncJ(">Q"2.HZmebiBc!G>eh?%V%c2HK%c2c^)1&Tl78ho2Z +]g;u)(j#]5q>^_8g>:]":2b^?J_o/<#F[:b)BBn?!r`0"."U6+hRVJDS5+S~> +ZMss[s8:UP!5AH)*0U$M!h',h +r7_5P^A?ReD-tNAD>jM;"=A]DrS%Fc9n2R/gB)5!bkg&p!5Ampu[IF%oI"1h3VmJGW>b[55cgB)5!bl-9!cN)^&0`9Z39eb>V +^-rG1*-(u4q>^_6fA#&k9l5I9J_Sr6#FI+_)BBk>!r`0"."U-(gUZ&>RSA;~> +ZMssVs8:FK!586[!!gE]b +r71lK^&$:_CL5-6C]4;9"=8Q>rRM(Y8q6.)ecKVnanjQh!586[!s$$cf)5RM#XZ6&ecW7WM!gE]br7:cG(+HB:a^/c6CL5-6eq4f3[;0*jf#_66r72Ra +8dGhBN7QS*8dG\h^"1b<#QQlurRM%7!<;uJqu@KHCVe,!CY!9-ak#$q(eV2O?HLd??>KGh*!!\' +f"D(7eq=S7(l[%sc:S?J6G:t$=pP6NHcc9<"1:aNlMK-6a^/c]ecKVnao0cn`W4Rf1&TT/8h&?B +]g;u)(i0-%q>^_4dam!W8o'"/J_&T,#F-kZ)B9e +ZMss^s8:^SDpA.8!H66^hBUU9F*!,Kgtof(F+(4DhV>SmF+LUKhT;()FdgM1ad.!dHb&k,!h98j +r8%Hmc2-9"VOl>pVZ(K9EIk:CrS@Xi:4M^1h?'RGeb\,'DpA.8")lH@hYh?oF-+ZEh?1GdJ+!6k +$e;D2EHRD]hVP-$ebS&*Z%Ht5K=1X6!h98jr8.?i(5'T_eXiYpVOl>phR%]sc).)_hUS3pr8&.0 +Q?.1q\_c2sP]Lo-db`iRGAiDfrS@UG!<;uRr,3?rUs[_aVV\2Ae_]/iI?r-TT%2]JSu2DA*,j*Y +hT]uqhR.QGHb&"ig6?Z"OOE"BTQbI*Y1Wa1"2.HZmebiBeXiZ:h?'RGec">-eo0E8L]$!8P_\,P +c&d!bH`6r#qJQS6gtpo$:2b^?J_o,;"3OOB!W)it#aU%uJ_mup!Luo8~> +ZMss[s8:UPD9Vk2!H$'[gEY1/EH?lFg=j,nEI=krS%Fc9n2R/gB+1Ae,%f"D9Vk2")Z9:g\ksjEKJEBgB5#]J*m0g +$e)80E-.2WgY8Npe+q`%Y(:M0Ke"*<5gB+1Ae,A#(eSX'0L&B[3P_IlH +b`Hm_H`$esqJ?G2g"Y8m9l5I9J_So5"3=@?!W)it#aBnpJ_Rcj!Lc`5~> +ZMssVs8:FKCWZG(!GfgVeg&J!Df:99eCM*]D0`)+f$psSDgeY3f"mGdEKnAl_2rSNGHpkm!gE]b +r71maanjQkTUO3XT`/^/D1/G1rRM(Y8q6.)ecMM5chc2nCWZG(")H$0f)9@cDiDg9ecW2!qKDa:,OFPj4 +ac(.RGG+rdqJ-;,eCN3Y8o'"/J_&Q+"2dq8!W)it#a'\hJ_%E`!L-3,~> +ZMss^s1dClS,iTLh?1GdJ*u%I"2.HZmbQ_$S,iT+h?'2Is17%h]hX(FbME<6:2b^?J_o&9!k'lj +qZ$X6g4A+3hRhVFS5+S~> +ZMss[s1d:iRK3BJgB5#]J*ktE"1h3VmG6LuRK3B)gB*fDs16qe\kR\AbM**09l5I9J_Si3!jj]g +qZ$X6f7D\-gUl2@RSA;~> +ZMssVs1d+dPQ:aDecW +ZMss^s1dClS,iTLh?1GdJ*u%I"2.HZmbQ_$S,iT+h?'2Is17%h]hX(FbME<6:2b^?J_nu7!h'u5 +r;Zj)J:ZR.hRhVFS5+S~> +ZMss[s1d:iRK3BJgB5#]J*ktE"1h3VmG6LuRK3B)gB*fDs16qe\kR\AbM**09l5I9J_Sc1!gjf2 +r;Zj)It?@*gUl2@RSA;~> +ZMssVs1d+dPQ:aDecW +ZMss^s1dClS,iTLh?1GdJ*u%I"2.HZmbQ_$S,iT+h?'2Is17%h]hX(FbME<6:2b^?J_nr6"5/&. +#Q=](%F]mbJ_n&r!Luo8~> +ZMss[s1d:iRK3BJgB5#]J*ktE"1h3VmG6LuRK3B)gB*fDs16qe\kR\AbM**09l5I9J_S`0"4hc) +#Q=](%+'R[J_Ril!Lc`5~> +ZMssVs1d+dPQ:aDecW +ZMss^s1dClS,iTLh?1GdJ*u%I"2.HZmbQ_$S,iT+h?'2Is17%h]hX(FbME<6:2b^?J_nl4#0pBU +!rrUBJ_kt7]\W[Z:4N~> +ZMss[s1d:iRK3BJgB5#]J*ktE"1h3VmG6LuRK3B)gB*fDs16qe\kR\AbM**09l5I9J_SZ.#0U-Q +!rrU@J_Pb1]\ +ZMssVs1d+dPQ:aDecWJ_#D'][d+J8q6~> +ZMss^s1dClS,iTLh?1GdJ*u%I"2.HZmbQ_$S,iT+h?'2Is17%h]hX(FbME<6:2b^?J_nf2"hQc! +!!^PrhLXOsh>s-AJ,~> +ZMss[s1d:iRK3BJgB5#]J*ktE"1h3VmG6LuRK3B)gB*fDs16qe\kR\AbM**09l5I9J_ST,"h6Mr +!!^GogO\+mgB!a;J,~> +ZMssVs1d+dPQ:aDecW +ZMss^s1dClS,iTLh?1GdJ*u%I"2.HZmbQ_$S,iT+h?'2Is17%h]hX(FbME<6:2b^?J_n`0"/-b9 +;h +ZMss[s1d:iRK3BJgB5#]J*ktE"1h3VmG6LuRK3B)gB*fDs16qe\kR\AbM**09l5I9J_SN*".gP6 +;M!SRgV)>BRSA;~> +ZMssVs1d+dPQ:aDecW +ZMss^s1c5KS,iTL]`Y?2J)8nm"0>7IhVGjHS,iT+]`O](s15lGXA49%bImtY:2b^?J_n]/"5AGB +]7JgihS.hIS5+S~> +ZMss[s1c,HRK3BJ\c\s,J)&bh"0,(FgtfOCRK3B)\cS<#s15cDW_IuubIRbT9l5I9J_SK)"5&/= +\:NCcgV2DCRSA;~> +ZMssVs1brCPQ:aD[0*3sJ(iVa"/JP=g>0. +ZMss^J%eYY!.X!'![g3EfhMSr5A&\G!<@Vc:&t?E\P<2R5A&&5!@Mf:hLXP-h>u$&J_kt7^>8m\ +:4N~> +ZMss[J%eVX!.X!&![^-DfhDMp5A&\F!<@Vc9`Y6D\P3,P5A&&4!@DZ7gO\,'gB#TsJ_Pb1^=r[W +9n3~> +ZMssVJ%eMU!.X!#![U'Afh);l5@iPA!<@Vc8c\pA\OloL5@ho/!@;N4eq)DrecEpfJ_#D'^=E=L +8q6~> +ZMsp]JU`6#Jq!d'J_kt7J_kt7LY`'%:4N~> +ZMspZJU`6#Jq!d&J_Pb1J_Pb1LYDiu9n3~> +ZMspUJU`6#Jq!d#J_#D'J_#D'LXlKj8q6~> +ZMsp]JcC<$K)Yi=rS@P!J\?WJJ\?WJS\5$[o!&8YS5+S~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRLtfJ[L':J[L':S[AIKntZ?DPY-H~> +ZMsp]JcC<$K)Yi=rS@RgFb*;=:4Z/k:7k8_c/+-G_#m$mS5+S~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"WDh1N38qBTc8tS]WajtP*\H=kTPY-H~> +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMDu\[Is7Yj`blI4&:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fH5Cifc!7CiMC]Dt,s7Y^\aT1Lg8q6~> +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!.b-$!2';pf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs8:^RDuAGpS,i#J:4N~> +ZMspZJcC<$K)Yi`,kRK2ZB9n3~> +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp]_uBc*It@WNs2+d9metuC]hX+YJH4'ts$)&7JH4*u"Q'18K`D#-!cJ'WrS@[I!:QFQJ,~> +ZMspZ_uBc)It@WNs2+d9mJYc?\kR_UJH4'ts$)&7JH4*u"P`t2K)bf(!c7pSrS%ID!:-(JJ,~> +ZMspU_uBc*It@WNs2+d9lM]97[7YuMJH4'ts$)&7JH4*u"P3V(F8u3i!c%dOrRM+9!9]S=J,~> +ZMsp]_uBbo!;HKrqltd/\`NhprRREkLUl4?"o[#@KqR5Yrri5(K87"Urrr;)RY@Ei^&J97es_;c +gsZ3&metuC]hX+YJH16$JH2>C"Q'18K`D&.!,qi:!,qkn"IoJ\S5+S~> +ZMspZ_uBbl!;HKrqlk^.]&iqqrRIKqR5Yrri5'K87%Vrrr;(R>%C"P`t2K)bi)!,_]8!,__i"I]>VRSA;~> +ZMspU_uBbo!;HKrqltd/\`NhprRREkLUl4?"o[#@KqR5Yrri5(K87"Urrr;)RY@Ei^&J97es_;c +gsZ3&lM]97[7YuMJH16$JH2>C"P3V(F8u6j!,MQ6!,MSb"I&oLPY-H~> +ZMsp]_uBbo!;HKsYtB(U9$-5q#2"?1EGnIkh>[]3I9pnuM<*Up"gL[@DbA-jrs#`#??'5,jM(Wu +fR7(G>`%OjrrDKch>t;bJH16$d/a22JH16$dJsO/s5&G/hVN2K!!dH!hOFT7S5+S~> +ZMspZ_uBbl!;HKsZ:]+T9$6;r#2"B2E,S@kh>[]3IU6u!MWE^q"gUa?DbA0krs#c$?>s2-jM(Wu +fR@.G?&I^lrrDHbgB"l[JH16$d/a22JH16$dJsO,s4`/*gY6TC!!dAqgR8-.RSA;~> +ZMspU_uBbo!;HKsYtB(U9$-5q#2"?1EGnIkh>[]3I9pnuM<*Up"gL[@DbA-jrs#`#??'5,jM(Wu +fR7(G>`%OjrrD?_ecE0NJH16$d/a22JH16$dJsO's428kf%+X7!!d;jes$0tPY-H~> +ZMsp]rVlodJ,]I+mXP9:J(C!:OOjI3J(C!FLU6:GkconsW8djW^SIl`SAD.XT%sAGrRRKmK;ePE +s1sVCPdpegrrkQ0mcI\^k5PXW>-dGU>%6=f#+f#(lC_Ifi;WsDGOFTs@`A?O4a43sTO+k:rs#&u +ZKe)hYg`UPmetuC]hX+YJH4'ts$)&7JH4*u#2]C:K`BocqJZDj"IoJ\S5+S~> +ZMspZrVlocJ,]I+m=509J(C$=O4=40J(C$ILpH:FkHK_qW8[dV^SIocS\hC]S_O2ErRRKmK<"\G +s2'bFPI:JcrrkT3mH7\bk5PXW>I3VX>@lXj#+f&*lChUki;WsEH0sd!A]=ZR5'aL"TjY1?rs#'! +Zg+5lZIAgRmJYc?\kR_UJH4'ts$)&7JH4*u#2B14K)aT[qJH8e"I]>VRSA;~> +ZMspUrVlodJ,]I+mXP9:J(C!:OOjI3J(C!FLU6:GkconsW8djW^SIl`SAD.XT%sAGrRRKmK;ePE +s1sVCPdpegrrkQ0mcI\^k5PXW>-dGU>%6=f#+f#(lC_Ifi;WsDGOFTs@`A?O4a43sTO+k:rs#&u +ZKe)hYg`UPlM]97[7YuMJH4'ts$)&7JH4*u#1ih*F8shBqJ6,^"I&oLPY-H~> +ZMsp]rVloT!<3!Vh>mTU!-a3J:1!u#!-a3NAlc)3UeYB?=J#EiIli."8m$L]9U>MSdt):YF&&8* +_+nTdF&i;9rs&'#gAh2X*9[>,q0d8Ls8T>DhZ!iVI<"WRfOg-%rs&'#gAh2X*6/!`o*F(:s4CqC +^]+Q7I<"WRfOg,lrrDKch>t;bJH16$JH16$TE#,Os5&G/p>,qB!:QFQJ,~> +ZMspZrVloQ!<3!VgAq9R!-a9L:0mo"!-a9QB2u,3UeP<<=.T3fIli1%93H^a99o>Qe:VO[F&/>+ +_G=liF&rA:rs&'#f`1uU+Qrb0q0d5Js8T;GhZ!iVI;nNPf4^6(rs&'#f`1uU+NFEdo*F"7s4:qF +^]+Q7I;nNPf4^5orrDHbgB"l[JH16$JH16$TE#,Ls4`/*p=f_=!:-(JJ,~> +ZMspUrVloT!<3!Vh>mTU!-a3J:1!u#!-a3NAlc)3UeYB?=J#EiIli."8m$L]9U>MSdt):YF&&8* +_+nTdF&i;9rs&'#gAh2X*9[>,q0d8Ls8T>DhZ!iVI<"WRfOg-%rs&'#gAh2X*6/!`o*F(:s4CqC +^]+Q7I<"WRfOg,lrrD?_ecE0NJH16$JH16$TE#,Gs428kp=9A2!9]S=J,~> +ZMsp]rVloT!<3!Vh>mTU!.XG:X9ek+!.XG1eQRV&Z&mVtA97>AIn[uLD+r(:D/o(DP\/&,mZ]$l +:0uZKhI6K[rs%H_qu?]I"R#din8WmTs8UXQhZ!iMEIIfcl>;+2rs%H_qu?]I"NLHHiW/lSs6=HP +^]+Q.EIIfcl>;+$rrDKch>t;bJH16$d/a22JH16$dJsF,s5&G/p>,qB!:QFQJ,~> +ZMspZrVloQ!<3!VgAq9R!.XJ9X9ek+!.XJ2e67M$Y`RJo@r_&=In[uLD+ht8D/f"CQ"\8.n!#*l +;.&&OhI?Q\rs%K`q>^KD#3Z!knSrsSs8UOPhZ!iNEI@]al"kt1rs%K`q>^KD#0-ZJirJoQs64?O +^]+Q/EI@]al"kt#rrDHbgB"l[JH16$d/a22JH16$dJsF)s4`/*p=f_=!:-(JJ,~> +ZMspUrVloT!<3!Vh>mTU!.XG:X9ek+!.XG1eQRV&Z&mVtA97>AIn[uLD+r(:D/o(DP\/&,mZ]$l +:0uZKhI6K[rs%H_qu?]I"R#din8WmTs8UXQhZ!iMEIIfcl>;+2rs%H_qu?]I"NLHHiW/lSs6=HP +^]+Q.EIIfcl>;+$rrD?_ecE0NJH16$d/a22JH16$dJsF$s428kp=9A2!9]S=J,~> +ZMsp]rVloT!<3!Qh>mTU!6tQDh>mTU!6tQDmVdUTi2W*/8oO.tIr4QPIr4QPIrFcIFZXr"Ir>>H% +))Z?!Pnd2rs%Tcqu?]I"R#dinoK6Xs8UXQhZ!iOF++#el>;+2rs%Tcqu?]I"NLHHjoG;Ws6=HP^ +]+Q0F++#el>;+$rrDKch>t;bJH16$d/a22JH16$dJsF,s5&G/p>,qB!:QFQJ,~> +ZMspZrVloQ!<3!QgAq9R!6kKCgAq9R!6kKCm;7@PhQ)s-8o>I% +_hrB!Pe^1rs%Qbq>^KF#3Z!knoB-Us8UURhZ!iOEd[fbl>;.3rs%Qbq>^KF#0-ZJjT,,Ss6=HQ^ +]+Q0Ed[fbl>;.%rrDHbgB"l[JH16$d/a22JH16$dJsF)s4`/*p=f_=!:-(JJ,~> +ZMspUrVloT!<3!Qh>mTU!6tQDh>mTU!6tQDmVdUTi2W*/8oO.tIr4QPIr4QPIrFcIFZXr"Ir>>H% +))Z?!Pnd2rs%Tcqu?]I"R#dinoK6Xs8UXQhZ!iOF++#el>;+2rs%Tcqu?]I"NLHHjoG;Ws6=HP^ +]+Q0F++#el>;+$rrD?_ecE0NJH16$d/a22JH16$dJsF$s428kp=9A2!9]S=J,~> +ZMsp]rVloT!<3!QblIeD!8dbUh>mTU!8dbUmVdUPVJD*_<,_4)IrFcTIrFcTIrFcIFZk/&IsYhn% +)MrC!Pnd2rs&3'hZ*V`*9[>,qgW\Ts8TJHhZ!iXIrk&Xg1ZK)rs&3'hZ*V`*6/!`pB]XDs4V.G^ +]+Q9Irk&Xg1ZJprrDKch?(Ac*.^,$3Is2A3MAG9hZ)Gcs7X,/S,i#J:4N~> +ZMspZrVloQ!<3!QbQ.\C!8IPRgAq9R!8IPRm;7@MVe_3`C3h\S;g]-#[s7X#,RK2ZB9n3~> +ZMspUrVloT!<3!QblIeD!8dbUh>mTU!8dbUmVdUPVJD*_<,_4)IrFcTIrFcTIrFcIFZk/&IsYhn% +)MrC!Pnd2rs&3'hZ*V`*9[>,qgW\Ts8TJHhZ!iXIrk&Xg1ZK)rs&3'hZ*V`*6/!`pB]XDs4V.G^ +]+Q9Irk&Xg1ZJprrD?_ecE0NJH16$JH16$TE#,Gs428kp9b$f!9]S=J,~> +ZMsp]rVntA!7Lo8HN=*G!8dbUh>mTU!8dbUmVdULGA$'r?u>91IrFcTIrFcTIrFcRP%`,3o;I +ZMspZrVnt>!7:c7HN=*G!8IPRgAq9R!8IPRm;7@HGA$$p@;G3/Ir4TQIr4TQIr4TNO_Dr/o;[NE +;.82QhIQ]]rrkfBq=A*-k5PXZA%hU-@qFKr#,,P?p8_`0i;WsKK),"JA]=ZR7"W,GX(8NMrs#0, +^%SL;ZIAgRmJYc@\kP5:Fb+\`FoReZFb+\`FTgJ!gOaqJ9`kC]RSA;~> +ZMspUrVntA!7Lo8HN=*G!8dbUh>mTU!8dbUmVdULGA$'r?u>91IrFcTIrFcTIrFcRP%`,3o;I +ZMsp]rVntR7Tt:sErc7?!8dbUh>mTU!8dbUmVdUPRT+Qf<(OLuIrFcTIrFcTIrFcTc[BJNG>aP& +_+nTdG@LXIrrof.Is4Z0k5PY>K5#[V:l#24]DHX_ooi;Wtg;L`mcai48a[8L^c@Z0<]rs%20 +BmX +ZMspZrVntP7pCP$E<-%=!8IPRgAq9R!8IPRm;7@LR8nNg<_9e#Ir4TQIr4TQIr4TQd!]SOG?'e* +_G+ZeG@LXIrroi.IsFf3k5PY>K4oXX: +ZMspUrVntR7Tt:sErc7?!8dbUh>mTU!8dbUmVdUPRT+Qf<(OLuIrFcTIrFcTIrFcTc[BJNG>aP& +_+nTdG@LXIrrof.Is4Z0k5PY>K5#[V:l#24]DHX_ooi;Wtg;L`mcai48a[8L^c@Z0<]rs%20 +BmX +ZMsp]r;SgsLOY]BJ,fOumf3=TJ,fOumf3=\\%ht"TS98LP/715\+]j:\+]j:\+]k!d?oQ=V"=Wd +_2Ef1dXV#k"T,HVK=U+D"o[#@K7g_irrrAPRY.3ehu`;^# +ZMspZr;SgtM1M)EJ,fOumJm4RJ,fOumJm4[[_Mk!TSBDPPJ[@7[eBa9[eBa9[eBaud?oT>VXsif +^l*]/dXV#k"T,EWKt?CG"oZu?Kn[+nrrrAORY@Bhhu!P2!V!.b-$!.b-C!!M'VgOfJ!m0(W5l^COu~> +ZMspUr;SgsLOY]BJ,fOumf3=TJ,fOumf3=\\%ht"TS98LP/715\+]j:\+]j:\+]k!d?oQ=V"=Wd +_2Ef1dXV#k"T,HVK=U+D"o[#@K7g_irrrAPRY.3ehu`;^# +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fHIt7TN!7CiMhZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fHIt7TN!7CiMf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!.b-$!2';pf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!.b-$!2';pf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fHIt7TN!7CiMhZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fHIt7TN!7CiMf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!.b-$!2';pf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!.b-$!2';pf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fHIt7TN!7CiMhZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fHIt7TN!7CiMf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!.b-$!2';pf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!.b-$!2';pf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fHIt7TN!7CiMhZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fHIt7TN!7CiMf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!.b-$!2';pf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!.b-$!2';pf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fHIt7TN!7CiMhZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fHIt7TN!7CiMf)O +ZMsp]_uBbo!.k0$s2+d9metuC]hX+YJH16$JH2>C"Q'18K`Cc&"IoJ\S5+S~> +ZMspZ_uBbl!.k0$s2+d9mJYc?\kR_UJH16$JH2>C"P`t2K)bQ!"I]>VRSA;~> +ZMspU_uBbo!.k0$s2+d9lM]97[7YuMJH16$JH2>C"P3V(F8tsb"I&oLPY-H~> +ZMsp]_uBbo!;?F$KFeDep](9jXYgMQ\buI/ia[K3rrr;%2%1%;h#@QTXYgMQ\_mDipL=I?F2\(^ +pL?&h6+O]/"oGDZ$phGPrrDKch>t;bJH16$JH16$TE#,Os5&G/p>,qB!:QFQJ,~> +ZMspZ_uBbl!;?F$JeSJjoDejfX>C>P]);R0iF.60rrr;$1^jqC>P]&3MjpL+==Fi=:` +pL,le6Fsl1"oGAX$pqPRrrDHbgB"l[JH16$JH16$TE#,Ls4`/*p=f_=!:-(JJ,~> +ZMspU_uBbo!;?F$KFeDep](9jXYgMQ\buI/ia[K3rrr;%2%1%;h#@QTXYgMQ\_mDipL=I?F2\(^ +pL?&h6+O]/"oGDZ$phGPrrD?_ecE0NJH16$JH16$TE#,Gs428kp=9A2!9]S=J,~> +ZMsp]_uBbo!;HL'N%roH6>QW$[8)M.G?W,frr_kB%"J!N#.73]f$5G'h>[\e:5@DE>05_&"`#jL +c68Fkrs!6nLXpZQahIcZ[8)M.G?W,;rrDKch>t;bJH16$d/a2rJH16$dJsF,s5&G/p>,qB!:QFQ +J,~> +ZMspZ_uBbl!;HL'N\K)F6#HZ%[SVb0G?`5hrr_kB%"J!N#.@?`e^#A(h>[\f:l!SF>KYn("`6*N +cQJOmrs! +ZMspU_uBbo!;HL'N%roH6>QW$[8)M.G?W,frr_kB%"J!N#.73]f$5G'h>[\e:5@DE>05_&"`#jL +c68Fkrs!6nLXpZQahIcZ[8)M.G?W,;rrD?_ecE0NJH16$d/a2rJH16$dJsF$s428kp=9A2!9]S= +J,~> +ZMsp]rVloT!<3!Vh>mTU!3IsU+g(eE!3Ism&80SodUNgs;T8\9InE`M3J13<4h:UmqlMje#ZC-k +s+D3h.?ji,rsg3ls8V9%s8RjdhZ(],R/[*fTN3q"pAY>PB&C"Q'18K`Cc&"IoJ\S5+S~> +ZMspZrVloQ!<3!VgAq9R!3S-]+KGJA!3S-u&nTSmcs[Io;8iM7In<]Q4G?cF4LkFkqlVsg#Z^?n +s+VKn.$=T)rtQZos8V9&s8RjehZ(`0RK*PBAW[gQ8IFt#)?jJs2no@i;Wrl^An5! +1r]J!&AI$$b%7!Trs"6khZ(`0RFD09mJYc?\kR_UJH16$JH2>C"P`t2K)bQ!"I]>VRSA;~> +ZMspUrVloT!<3!Vh>mTU!3IsU+g(eE!3Ism&80SodUNgs;T8\9InE`M3J13<4h:UmqlMje#ZC-k +s+D3h.?ji,rsg3ls8V9%s8RjdhZ(],R/[*fTN3q"pAY>PB&C"P3V(F8tsb"I&oLPY-H~> +ZMsp]rVloT!<3!Vh>mTU!(,QBHP$5W!(,Q1]g[_NG<*(b??c/DIk@VM=tQ](>''<8XB(kNhF^E? +KG_,c]bgbErtb&'N"?7mn,MQlHi*j*CP2Ze]k3XMJ+rsJo5f9Us8U@MhZ!iPF*mfak%fV.rs%Wd +p](9=#fclLk5b8Rs5n*L^]+Q1F*mfak%fUurrDKch>t;bJH16$JH16$TE#,Os5&G/p>,qB!:QFQ +J,~> +ZMspZrVloQ!<3!VgAq9R!(#N>I1ZGY!(#N0]LI\NG<3+^?$5i?IkIbP>:c`(>BBE9Y$%:RhapE> +L)@Af^)R.Jrtb&'MA-=rn,MQlH27L%CPDfg]OdCGJ+rsJo5f3Qs8U7NhZ!iPF*[T]j_KS/rs%Wd +oDej6%*&;Pk5b,Ls5e$M^]+Q1F*[T]j_KS!rrDHbgB"l[JH16$JH16$TE#,Ls4`/*p=f_=!:-(J +J,~> +ZMspUrVloT!<3!Vh>mTU!(,QBHP$5W!(,Q1]g[_NG<*(b??c/DIk@VM=tQ](>''<8XB(kNhF^E? +KG_,c]bgbErtb&'N"?7mn,MQlHi*j*CP2Ze]k3XMJ+rsJo5f9Us8U@MhZ!iPF*mfak%fV.rs%Wd +p](9=#fclLk5b8Rs5n*L^]+Q1F*mfak%fUurrD?_ecE0NJH16$JH16$TE#,Gs428kp=9A2!9]S= +J,~> +ZMsp]rVloT!<3!Qh>mTU!58F4h>mTU!58F4mVdUTlK8!/CN&Y@Ir"?LIr"?LIrFcPH[#5as5!bE +(o@63!Nc@srtaGk;Vp7s>6!GtJ,fQ:Dh%f@CQ.OsJ+rsEmVdUSrrLsVhZ!iKDh%femVdU6rrLsV +rr3#U!65$>h>mQT!q'uV^]+Q,Dh%femVdU(rrDKch>t;bJH16$d/a2rJH16$dJsF,s5&G/p>,qB +!:QFQJ,~> +ZMspZrVloQ!<3!QgAq9R!5AL5gAq9R!5AL5m;7@Ol0%s.Bl38;Iqe0IIqe0IIr4TMID2[RoJ+rsEmr*^TrrLjShZ!iLDh%fem;7@3rrM!W +rr3#R!65$>hZ3ZU!psiS^]+Q-Dh%fem;7@%rrDHbgB"l[JH16$d/a2rJH16$dJsF)s4`/*p=f_= +!:-(JJ,~> +ZMspUrVloT!<3!Qh>mTU!58F4h>mTU!58F4mVdUTlK8!/CN&Y@Ir"?LIr"?LIrFcPH[#5as5!bE +(o@63!Nc@srtaGk;Vp7s>6!GtJ,fQ:Dh%f@CQ.OsJ+rsEmVdUSrrLsVhZ!iKDh%femVdU6rrLsV +rr3#U!65$>h>mQT!q'uV^]+Q,Dh%femVdU(rrD?_ecE0NJH16$d/a2rJH16$dJsF$s428kp=9A2 +!9]S=J,~> +ZMsp]rVloT!<3!Hh>mTU!8dbUh>mTU!8dbUmVdUTeo*9/.<"YSIrFcTIrFcTIrFcCDu0M9ec=;# +rrL[Nq#;/n!:Tsfc3XI=GC05ek%fV@HY_L#CO>sRrs/:BHi*jCc3W;,#PWFhqu>eoKAlh7mf<+^ +s3:nirs.ump\Y!QCP0D%#PWFhqu>eoK@Bi#metuC]hX+YJH16$JH2>C"Q'18K`Cc&"IoJ\S5+S~> +ZMspZrVloQ!<3!HgAq9R!8IPRgAq9R!8IPRm;7@Qe8I*/.W+SQIr4TQIr4TQIr4TADu0M9f)XG% +rrL^Oq#;2k!:Tsfb6\.6FaqEI;@TtBmKIOq>U]mFaH27L'D2$sK#3u9\ +s8UCR`r?;0!;$$dkA>pfrs/4>H27L'D2$I=!:Ba>!P2!V!.b-$!.b-C!!M'VgOfJ!gB +ZMspUrVloT!<3!Hh>mTU!8dbUh>mTU!8dbUmVdUTeo*9/.<"YSIrFcTIrFcTIrFcCDu0M9ec=;# +rrL[Nq#;/n!:Tsfc3XI=GC05ek%fV@HY_L#CO>sRrs/:BHi*jCc3W;,#PWFhqu>eoKAlh7mf<+^ +s3:nirs.ump\Y!QCP0D%#PWFhqu>eoK@Bi#lM]97[7YuMJH16$JH2>C"P3V(F8tsb"I&oLPY-H~> +ZMsp]rVloT!<3!Q]`A*4!8dbUh>mTU!8dbUmVdULGA$(BoS2&k"#)@!Rs35/Ci;Wrl`rH(/ +0ua.s&B<`0c"Kn!m.'~> +ZMspZrVloQ!<3!Q^&\35!8IPRgAq9R!8IPRm;7@IH"Z7=Ir4TQIr4TQIr4TQIr4TKH$T5cs7ak> +*30#>Hi;Wrp_uKb. +1r]J!'Z8o0c=r]\rs"C"P`t2K)bQ!"I]>VRSA;~> +ZMspUrVloT!<3!Q]`A*4!8dbUh>mTU!8dbUmVdULGA$(BoS2&k"#)@!Rs35/Ci;Wrl`rH(/ +0ua.s&B<`0c"C"P3V(F8tsb"I&oLPY-H~> +ZMsp]rVntJ!2]_P3<9*Z!8dbUh>mTU!8dbUmVdULGA$'S6>PldIrFcTIrFcTIrFcTW)fSSk?9nG +KG_,c]cdCMrsiO2hVL8.s8TW5SF;8?ao25@mVdUKrs$#;SF>ViPhl?D\QYNlLgJ3]rrmI1pZEui +`W$-`6CdM#?HKq]#.[p-mtb;le,KF9rS@Uh:,.<0JRd, +ZMspZrVntF!2BMN2us!Y!8IPRgAq9R!8IPRm;7@GF_BgO6YYfbIr4TQIr4TQIr4TQW`PeRk?U4M +L)@Af^*!FMrsiU7hqL,)s8TZ7RdGl;b5M>Am;7@Hrs$&=RdK8dQJMQF\m(WjLL83^rrmO2oB.Nf +`W$-b6(71r?cp+_#.e$-m>#&ke,KF8rS%@a9n<:q!7:fHIt7TN!7CiMg]-#[s7Y1MRK2ZB9n3~> +ZMspUrVntJ!2]_P3<9*Z!8dbUh>mTU!8dbUmVdULGA$'S6>PldIrFcTIrFcTIrFcTW)fSSk?9nG +KG_,c]cdCMrsiO2hVL8.s8TW5SF;8?ao25@mVdUKrs$#;SF>ViPhl?D\QYNlLgJ3]rrmI1pZEui +`W$-`6CdM#?HKq]#.[p-mtb;le,KF5rRM"W8q?tn!7:fHIt7TN!7CiMf)O +ZMsp]r;Sg:&-+Ge!<<'!hZ*W4!<<'!hZ*WDDh%f#5n$M$-%l5IDsmXTDsmXTDsmZ"UbN,\9'?6S +KGX\DV#^8i%JO$3#]'27s8/oU#X,`rrrVV,J+imGqlM^]#]nf$"oGDZ#X,`Urri(+#RGLirrr., +2$c@U^&J95XYgAI\^LKXmetuC]hX+YJH16$JH2>C"Q'18K`Cc&"IoJ\S5+S~> +ZMspZr;Sg<'Eg7m!<<'!g].<.!<<'!g].<@D1DT"64Qk,.#%\ND=.@QD=.@QD=.AtUbW8_:$;QV +Jf"JAV?$Aj%J*^.$u,J9s8/lU$p_E%rrVS)J+imGqlD[a%!CA*"oGAZ$p_D]rri()$k.9srrr.* +2@Mj^^&J95X>UJP]$gTYmJYc?\kR_UJH16$JH2>C"P`t2K)bQ!"I]>VRSA;~> +ZMspUr;Sg:&-+Ge!<<'!hZ*W4!<<'!hZ*WDDh%f#5n$M$-%l5IDsmXTDsmXTDsmZ"UbN,\9'?6S +KGX\DV#^8i%JO$3#]'27s8/oU#X,`rrrVV,J+imGqlM^]#]nf$"oGDZ#X,`Urri(+#RGLirrr., +2$c@U^&J95XYgAI\^LKXlM]97[7YuMJH16$JH2>C"P3V(F8tsb"I&oLPY-H~> +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!.b-$!2';pf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fHIt7TN!7CiMhZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fHIt7TN!7CiMf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!.b-$!2';pf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!.b-$!2';pf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fHIt7TN!7CiMhZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fHIt7TN!7CiMf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!.b-$!2';pf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!.b-$!2';pf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!.b-$!2';pf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!.b-$!2';pf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp]_uBc*It@WNs2+d9metuC]hX+YJH4'ts$)&7JH4*u"Q'18K`Cc&"IoJ\S5+S~> +ZMspZ_uBc)It@WNs2+d9mJYc?\kR_UJH4'ts$)&7JH4*u"P`t2K)bQ!"I]>VRSA;~> +ZMspU_uBc*It@WNs2+d9lM]97[7YuMJH4'ts$)&7JH4*u"P3V(F8tsb"I&oLPY-H~> +ZMsp]_uBbo!;QQoc22tjmf!1c_uBZ>rRREkT\T8@"o[#@Knm4orrrAPRY@EihugqE^krRREkT\Ruq!:KgB!PM6Z!.b-$!.b-C!!M0YhLtq&h?9>Kn!m.'~> +ZMspZ_uBbl!;QQocMN(kmJd+erkl\RrrrAOR>%% +ZMspU_uBbo!;QQoc22tjmf!1c_uBZ>rRREkT\T8@"o[#@Knm4orrrAPRY@EihugqE^krRREkT\Ruq!:'O6!OYON!.b-$!.b-C!!LmQeoUlbec_3;kEJSh~> +ZMsp]_uBbo!;QQoao,u\4X'7nqrYRmKDtlTfR7(G>`%P9rs%,(??'mUakct$fR7(G>`%P"rroZ" +F)O[m`W$.I8mZLBM<)DN#2"?1EEn@]e,KF9rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J +:4N~> +ZMspZ_uBbl!;QQoaSfl[4sgTb2*(%fR@.G?&I_$rro]# +EGnIl`W$.J8mZIBMWDMO#2"B2E*\@^e,KF8rS%@a9n<:q!7:fH5Cifc!7CiMg]-#[s7Y1MRK2ZB +9n3~> +ZMspU_uBbo!;QQoao,u\4X'7nqrYRmKDtlTfR7(G>`%P9rs%,(??'mUakct$fR7(G>`%P"rroZ" +F)O[m`W$.I8mZLBM<)DN#2"?1EEn@]e,KF5rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp]rVlodJ,]I+mXP9:J(C!:OOjI3J(C!FLU6:GkconsW8djW^SIl`SAD.XT%sAGrRRKmK;ePE +s1sVCPdpehrrDrql3Z0>R/d3U[;@@Brr31o>-dFd?Ej2(#+f#(lK28Zh>[\N>-dFd?EiAf"[.(t +k#u65rrtW1ZKe)hYeL,ASm&GbTO+kOrrDKch>t;bJH16$d/a22JH16$dJsF,s5&G/p>,qB!:QFQ +J,~> +ZMspZrVlocJ,]I+m=509J(C$=O4=40J(C$ILpH:FkHK_qW8[dV^SIocS\hC]S_O2ErRRKmK<"\G +s2'bFPI:JdrrDlolNu6I3Ug@']P,#+f&*lK;A_h>[\N>I3Ug@'\_j"[75! +k?DN:rrtZ4Zg+5lZG->CSm/PdTjY1TrrDHbgB"l[JH16$d/a22JH16$dJsF)s4`/*p=f_=!:-(J +J,~> +ZMspUrVlodJ,]I+mXP9:J(C!:OOjI3J(C!FLU6:GkconsW8djW^SIl`SAD.XT%sAGrRRKmK;ePE +s1sVCPdpehrrDrql3Z0>R/d3U[;@@Brr31o>-dFd?Ej2(#+f#(lK28Zh>[\N>-dFd?EiAf"[.(t +k#u65rrtW1ZKe)hYeL,ASm&GbTO+kOrrD?_ecE0NJH16$d/a22JH16$dJsF$s428kp=9A2!9]S= +J,~> +ZMsp]rVloT!<3!Vh>mTU!-a3J:1!u#!-a3NAlc)3UeYB?=J#EiIli."8m$L]9U>MSdt):YF&&8* +_+nTdF&i;5rtC<'p](9cc)+6cs8VnCEU<_XArZTc#Pidfm/R*j*8^]#q0d8Ls4CqCiVs,R#ho=Y +YoLd`#P/5Tm/P^MNPGJuq0d8Ls4CqCe,KF9rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +ZMspZrVloQ!<3!VgAq9R!-a9L:0mo"!-a9QB2u,3UeP<<=.T3fIli1%93H^a99o>Qe:VO[F&/>+ +_G=liF&rA6rtC<%p&G'ac)+3bs8VnCE9mPUBTMrg#Pideli7!h+Q!,'q0d5Js4:qFiVs,R#h]1W +YTUsc#P/5Rli5RMO2(]"q0d5Js4:qFe,KF8rS%@a9n<:q!.b-$!2';pg]-#[s7Y1MRK2ZB9n3~> +ZMspUrVloT!<3!Vh>mTU!-a3J:1!u#!-a3NAlc)3UeYB?=J#EiIli."8m$L]9U>MSdt):YF&&8* +_+nTdF&i;5rtC<'p](9cc)+6cs8VnCEU<_XArZTc#Pidfm/R*j*8^]#q0d8Ls4CqCiVs,R#ho=Y +YoLd`#P/5Tm/P^MNPGJuq0d8Ls4CqCe,KF5rRM"W8q?tn!.b-$!2';pf)O +ZMsp]rVloT!<3!Vh>mTU!.XG:X9ek+!.XG1eQRV&Z&mVtA97>AIn[uLD+r(:D/o(DP\/&,mZ]$l +:0uZKhI6KXrrUV/f)5OVmVdUTs6t#VrVu/"Jb]6Mn8WmTs8UXQhZ!iMEIIfcl>;+2rs%H_qu?]I +"NLHHiW/lSs6=HP^]+Q.EIIfcl>;+$rrDKch>t;bJH16$d/a22JH16$dJsF,s5&G/p>,qB!:QFQ +J,~> +ZMspZrVloQ!<3!VgAq9R!.XJ9X9ek+!.XJ2e67M$Y`RJo@r_&=In[uLD+ht8D/f"CQ"\8.n!#*l +;.&&OhI?QYrrUP.eGT=Tm;7@Qs7()Vr;Z"tK)#?NnSrsSs8UOPhZ!iNEI@]al"kt1rs%K`q>^KD +#0-ZJirJoQs64?O^]+Q/EI@]al"kt#rrDHbgB"l[JH16$d/a22JH16$dJsF)s4`/*p=f_=!:-(J +J,~> +ZMspUrVloT!<3!Vh>mTU!.XG:X9ek+!.XG1eQRV&Z&mVtA97>AIn[uLD+r(:D/o(DP\/&,mZ]$l +:0uZKhI6KXrrUV/f)5OVmVdUTs6t#VrVu/"Jb]6Mn8WmTs8UXQhZ!iMEIIfcl>;+2rs%H_qu?]I +"NLHHiW/lSs6=HP^]+Q.EIIfcl>;+$rrD?_ecE0NJH16$d/a22JH16$dJsF$s428kp=9A2!9]S= +J,~> +ZMsp]rVloT!<3!Qh>mTU!6tQDh>mTU!6tQDmVdUTi2W*/8oO.tIr4QPIr4QPIrFcIFZXr"Ir>>H% +))Z?!Pnd/rrHVVqu7)mDh%fenoK6Xs6=HPq#:TiF++#es4.>0rs/+9IJs32D1U[G#3Gp_s8UXQ` +r?;+!;l`pl>;*ers/+9IJs32D1U19#4CXAk2*dHJH16$d/a22JH16$dJsF,s5&G/p>,qB!:QFQJ +,~> +ZMspZrVloQ!<3!QgAq9R!6kKCgAq9R!6kKCm;7@PhQ)s-8o>I% +_hrB!Pe^/rrW.-H2IUMm;7@Qs712Xr;Z&!K)#?NnoB-Us8UURhZ!iOEd[fbl>;.3rs%Qbq>^KF# +0-ZJjT,,Ss6=HQ^]+Q0Ed[fbl>;.%rs%lOmH!$59n<:q!7:fH5Cifc!7CiMg]-#[s7Y1MRK2ZB9 +n3~> +ZMspUrVloT!<3!Qh>mTU!6tQDh>mTU!6tQDmVdUTi2W*/8oO.tIr4QPIr4QPIrFcIFZXr"Ir>>H% +))Z?!Pnd/rrHVVqu7)mDh%fenoK6Xs6=HPq#:TiF++#es4.>0rs/+9IJs32D1U[G#3Gp_s8UXQ` +r?;+!;l`pl>;*ers/+9IJs32D1U19#3t.3i7>;.JH16$d/a22JH16$dJsF$s428kp=9A2!9]S=J +,~> +ZMsp]rVloT!<3!QblIeD!8dbUh>mTU!8dbUmVdUPVJD*_<,_4)IrFcTIrFcTIrFcIFZk/&IsYhn% +)MrC!Pnd0rrUaphY[<]mVdUTs8.:emf2!SNVNMYqgW\Ts8TJHhZ!iXIrk&Xg1ZK)rs&3'hZ*V`* +6/!`pB]XDs4V.G^]+Q9Irk&Xg1ZJps8VKjp9(:+:)JO:JMGA'JH16$ec5j0s5&G/p>,qB!:QFQJ +,~> +ZMspZrVloQ!<3!QbQ.\C!8IPRgAq9R!8IPRm;7@MVe_3` +ZMspUrVloT!<3!QblIeD!8dbUh>mTU!8dbUmVdUPVJD*_<,_4)IrFcTIrFcTIrFcIFZk/&IsYhn% +)MrC!Pnd0rrUaphY[<]mVdUTs8.:emf2!SNVNMYqgW\Ts8TJHhZ!iXIrk&Xg1ZK)rs&3'hZ*V`* +6/!`pB]XDs4V.G^]+Q9Irk&Xg1ZJps8V?eoW"Rl8q?tn!6Y@nJ*_Xb5MQ85J+WdA5PBR,5OJOHJ ++!@;5NmUp!!LmQeoUlbec_3;kEJSh~> +ZMsp]rVntA!7Lo8HN=*G!8dbUh>mTU!8dbUmVdULGA$'r?u>91IrFcTIrFcTIrFcRP%`,3o;I +ZMspZrVnt>!7:c7HN=*G!8IPRgAq9R!8IPRm;7@HGA$$p@;G3/Ir4TQIr4TQIr4TNO_Dr/o;[NE +;.82QhIQ]\rrIU?qYpWaD1DQb#,,P?p8_`0p\tGkA%hU-@qFKr#,,P?p8_`0i;WsKK),"JA]=ZR +7"W,GX(8NMrs#0,^%SL;ZIAgXmGtB-D7WGm!.b-t!<5:^!.ga.ScI'ks7`,XrrC@ +ZMspUrVntA!7Lo8HN=*G!8dbUh>mTU!8dbUmVdULGA$'r?u>91IrFcTIrFcTIrFcRP%`,3o;I +ZMsp]rVntR7Tt:sErc7?!8dbUh>mTU!8dbUmVdUPRT+Qf<(OLuIrFcTIrFcTIrFcTc[BJNG>aP& +_+nTdG@LXHrrF^9qYpWbDh%cd#24]DHX_oop\tHOK5#[V:l#24]DHX_ooi;Wtg;L`mcai48a +[8L^c@Z0<]rs%20BmX +ZMspZrVntP7pCP$E<-%=!8IPRgAq9R!8IPRm;7@LR8nNg<_9e#Ir4TQIr4TQIr4TQd!]SOG?'e* +_G+ZeG@LXIrrW-MOo#(Zm;7@Prs%20BRF?LjS/ZYg4O'dGtuNVRSA;~> +ZMspUrVntR7Tt:sErc7?!8dbUh>mTU!8dbUmVdUPRT+Qf<(OLuIrFcTIrFcTIrFcTc[BJNG>aP& +_+nTdG@LXHrrF^9qYpWbDh%cd#24]DHX_oop\tHOK5#[V:l#24]DHX_ooi;Wtg;L`mcai48a +[8L^c@Z0<]rs%20BmX +ZMsp]r;SgsLOY]BJ,fOumf3=TJ,fOumf3=\\%ht"TS98LP/715\+]j:\+]j:\+]k!d?oQ=V"=Wd +_2Ef1dXUuj!JB,&rrVo'^]+6:rRREiT%s&>"o[#@K7g_irrrAPRY.3ehu`;^#Kn!m.'~> +ZMspZr;SgtM1M)EJ,fOumJm4RJ,fOumJm4[[_Mk!TSBDPPJ[@7[eBa9[eBa9[eBaud?oT>VXsif +^l*]/dXV#k!r[c(qYpWj[_MhA"oZu?KqI/orrrAORY?I,h#@QVeXD2agu&,7qlkd.]#=UPqllNC +TA7-["oZu?KqI/KrrDHbgB"l[JH16$JH16$TE#,Ls4`/*p=f_=!:-(JJ,~> +ZMspUr;SgsLOY]BJ,fOumf3=TJ,fOumf3=\\%ht"TS98LP/715\+]j:\+]j:\+]k!d?oQ=V"=Wd +_2Ef1dXUuj!JB,&rrVo'^]+6:rRREiT%s&>"o[#@K7g_irrrAPRY.3ehu`;^# +ZMsp]JcC<$K)Z&Ck3hBg]hX+YJH4'ts$)&7JH4*u"Q'18K`Cc&"IoJ\S5+S~> +ZMspZJcC<$K)Z&BjR)$_\kR_UJH4'ts$)&7JH4*u"P`t2K)bQ!"I]>VRSA;~> +ZMspUJcC<$K)Z&?i9K7O[7YuMJH4'ts$)&7JH4*u"P3V(F8tsb"I&oLPY-H~> +ZMsp]JcC<$K)bl="ST!q]hX+YJH4'ts$)&7JH4*u"Q'18K`Cc&"IoJ\S5+S~> +ZMspZJcC<$K)bl<"SSsn\kR_UJH4'ts$)&7JH4*u"P`t2K)bQ!"I]>VRSA;~> +ZMspUJcC<$K)bl9"SA^e[7YuMJH4'ts$)&7JH4*u"P3V(F8tsb"I&oLPY-H~> +ZMsp]JcC<$K)Z&Ck,pX]]hX+YJH16$JH2>C"Q'18K`Cc&"IoJ\S5+S~> +ZMspZJcC<$K)Z&BjK1=W\kR_UJH16$JH2>C"P`t2K)bQ!"I]>VRSA;~> +ZMspUJcC<$K)Z&?i2SYM[7YuMJH16$JH2>C"P3V(F8tsb"I&oLPY-H~> +ZMsp]JcC<$K)Z&ChPP4g]hX+YJH4'ts$)&7JH4*u"Q'18K`Cc&"IoJ\S5+S~> +ZMspZJcC<$K)Z&BgSA\^\kR_UJH4'ts$)&7JH4*u"P`t2K)bQ!"I]>VRSA;~> +ZMspUJcC<$K)Z&?et?fO[7YuMJH4'ts$)&7JH4*u"P3V(F8tsb"I&oLPY-H~> +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp]JcC<$K)Z&Ck3hBg]hX+YJH16$JH2>C"Q'18K`Cc&"IoJ\S5+S~> +ZMspZJcC<$K)Z&BjR)$_\kR_UJH16$JH2>C"P`t2K)bQ!"I]>VRSA;~> +ZMspUJcC<$K)Z&?i9K7O[7YuMJH16$JH2>C"P3V(F8tsb"I&oLPY-H~> +ZMsp]JcC<$K)bl="ST!q]hX+YJH4'ts$)&7JH4*u"Q'18K`Cc&"IoJ\S5+S~> +ZMspZJcC<$K)bl<"SSsn\kR_UJH4'ts$)&7JH4*u"P`t2K)bQ!"I]>VRSA;~> +ZMspUJcC<$K)bl9"SA^e[7YuMJH4'ts$)&7JH4*u"P3V(F8tsb"I&oLPY-H~> +ZMsp]JcC<$K)Z&Ck,pX]]hX+YJH4'ts$)&7JH4*u"Q'18K`Cc&"IoJ\S5+S~> +ZMspZJcC<$K)Z&BjK1=W\kR_UJH4'ts$)&7JH4*u"P`t2K)bQ!"I]>VRSA;~> +ZMspUJcC<$K)Z&?i2SYM[7YuMJH4'ts$)&7JH4*u"P3V(F8tsb"I&oLPY-H~> +ZMsp]JcC<$K)Z&ChPP4g]hX+YJH16$JH2>C"Q'18K`Cc&"IoJ\S5+S~> +ZMspZJcC<$K)Z&BgSA\^\kR_UJH16$JH2>C"P`t2K)bQ!"I]>VRSA;~> +ZMspUJcC<$K)Z&?et?fO[7YuMJH16$JH2>C"P3V(F8tsb"I&oLPY-H~> +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp]JcC<$K)Z&Ck3hBg]hX+YJH4'ts$)&7JH4*u"Q'18K`Cc&"IoJ\S5+S~> +ZMspZJcC<$K)Z&BjR)$_\kR_UJH4'ts$)&7JH4*u"P`t2K)bQ!"I]>VRSA;~> +ZMspUJcC<$K)Z&?i9K7O[7YuMJH4'ts$)&7JH4*u"P3V(F8tsb"I&oLPY-H~> +ZMsp]JcC<$K)bl="TRoH]hX+YJH16$JH2>C"Q'18K`Cc&"IoJ\S5+S~> +ZMspZJcC<$K)bl<"TRlD\kR_UJH16$JH2>C"P`t2K)bQ!"I]>VRSA;~> +ZMspUJcC<$K)bl9"TRc<[7YuMJH16$JH2>C"P3V(F8tsb"I&oLPY-H~> +ZMsp]JcC<$K)bi +ZMspZJcC<$K)bi;s(qeJ9n<:q!.b-$!2';pg]-#[s7Y1MRK2ZB9n3~> +ZMspUJcC<$K)bi8s(_YC8q?tn!.b-$!2';pf)O +ZMsp]JcC<$K)Z&ChJWC4]hX+YJH4'ts*oS"JH4*u"Q'18K`Cc&"IoJ\S5+S~> +ZMspZJcC<$K)Z&BgMHq-\kR_UJH4'ts*oS"JH4*u"P`t2K)bQ!"I]>VRSA;~> +ZMspUJcC<$K)Z&?enY8$[7YuMJH4'ts*oS"JH4*u"P3V(F8tsb"I&oLPY-H~> +ZMsp]_uBbo!.k0$s2+d9metuC]hX+YJH16$JH2>C"Q'18K`Cc&"IoJ\S5+S~> +ZMspZ_uBbl!.k0$s2+d9mJYc?\kR_UJH16$JH2>C"P`t2K)bQ!"I]>VRSA;~> +ZMspU_uBbo!.k0$s2+d9lM]97[7YuMJH16$JH2>C"P3V(F8tsb"I&oLPY-H~> +ZMsp]_uBbo!;HKsej9WJ;WmuXs+gLOs+g:I"oGDZ$kRL8rrr;%2%2d]huKn!m.'~> +ZMspZ_uBbl!;HKsf0orR\!r[n2r;QisLPb^("oGAX$kRR:rrr;$1^l^^hu!P2!V!.b-$!.b-C!!M'VgOfJ!gB +ZMspU_uBbo!;HKsej9WJ;WmuXs+gLOs+g:I"oGDZ$kRL8rrr;%2%2d]hu +ZMsp]_uBbo!;HL%+`li'>!i8hpWe+3J,]HNpWe+3J+`gG[8)M.c68G.rs#l+LXpZQal*1&CGF\k ++dBtG#&>rKeot<>^AeDF:5@DE>057ns6^$imbYWPJH16$d/a2rJH16$dJsF,s5&G/p>,qB!:QFQ +J,~> +ZMspZ_uBbl!;QR'q]L%EhcW8Fs7afc,_,gn"S^2g,_,Le#.@?`e^#A(h>[\f:l!SF>KYn("`6*N +cQJOmrs! +ZMspU_uBbo!;HL%+`li'>!i8hpWe+3J,]HNpWe+3J+`gG[8)M.c68G.rs#l+LXpZQal*1&CGF\k ++dBtG#&>rKeot<>^AeDF:5@DE>057ns69aelIN@8JH16$d/a2rJH16$dJsF$s428kp=9A2!9]S= +J,~> +ZMsp]rVloT!<3!Vh>mTU!3IsU+g(eE!3Ism&80SodUNgs;T8\9InE`M3J13<4h:UmqlMje#ZC-k +s+D3h.?ji-rsdfbp](9E!<<)aS6ddsrr3,dS6ddsp&>5OB&C"Q'18K`Cc&"IoJ\S5+S~> +ZMspZrVloQ!<3!VgAq9R!3S-]+KGJA!3S-u&nTSmcs[Io;8iM7In<]Q4G?cF4LkFkqlVsg#Z^?n +s+VKn.$=T*rsdicp](9B!<<)`S6mgsrr3,cS6mgsp&>5OBAW[gQ8IFt#)?jJs2no@i;Wrl^An5! +1r]J!&AI$$b%7!Trs"6khZ(`0RFD38mJh#(\kR_UJH16$JH2>C"P`t2K)bQ!"I]>VRSA;~> +ZMspUrVloT!<3!Vh>mTU!3IsU+g(eE!3Ism&80SodUNgs;T8\9InE`M3J13<4h:UmqlMje#ZC-k +s+D3h.?ji-rsdfbp](9E!<<)aS6ddsrr3,dS6ddsp&>5OB&C"P3V(F8tsb"I&oLPY-H~> +ZMsp]rVloT!<3!Vh>mTU!(,QBHP$5W!(,Q1]g[_NG<*(b??c/DIk@VM=tQ](>''<8XB(kNhF^E? +KG_,c]bgbDrrjR!p[;TPrVlreDh%]b!q'uVpAYBhF*mfas3:o,rs/.:Hi*j*CP1UG#3Q!\s8U@M +`r?;,!;HBjk%fUars/.:Hi*j*CP1+9#4C>d!,oPGJH16$JH16$TE#,Os5&G/p>,qB!:QFQJ,~> +ZMspZrVloQ!<3!VgAq9R!(#N>I1ZGY!(#N0]LI\NG<3+^?$5i?IkIbP>:c`(>BBE9Y$%:RhapE> +L)@Af^)R.Jrs&?CK(JG>4T59]m;7@NrrVS)J+imIo5f3Qs8U7NhZ!iPF*[T]j_KS/rs%WdoDej6% +*&;Pk5b,Ls5e$M^]+Q1F*[T]j_KS!rs%lFD#eFa9n<:q!.b-$!2';pg]-#[s7Y1MRK2ZB9n3~> +ZMspUrVloT!<3!Vh>mTU!(,QBHP$5W!(,Q1]g[_NG<*(b??c/DIk@VM=tQ](>''<8XB(kNhF^E? +KG_,c]bgbDrrjR!p[;TPrVlreDh%]b!q'uVpAYBhF*mfas3:o,rs/.:Hi*j*CP1UG#3Q!\s8U@M +`r?;,!;HBjk%fUars/.:Hi*j*CP1+9#3scT!,Ju7JH16$JH16$TE#,Gs428kp=9A2!9]S=J,~> +ZMsp]rVloT!<3!Qh>mTU!58F4h>mTU!58F4mVdUTlK8!/CN&Y@Ir"?LIr"?LIrFcPH[#5as5!bE +(o@63!Nc@rrrBb3!!*A^rVlreDh%]b!q'uVpAY3^Dh%cd!T!h5rs.t1J,fQ:Dh$aG!T!hUrrLsV +`r?(r!<3!"mVdTirs.t1J,fQ:Dh$79!:KgB!PM6Z!.b-t!<7P"!.b-u!!M0YhLtq&h?9>Kn!m.'~> +ZMspZrVloQ!<3!QgAq9R!5AL5gAq9R!5AL5m;7@Ol0%s.Bl38;Iqe0IIqe0IIr4TMI!P2!V!.b-t!<7P"!.b-u!!M'VgOfJ!gB +ZMspUrVloT!<3!Qh>mTU!58F4h>mTU!58F4mVdUTlK8!/CN&Y@Ir"?LIr"?LIrFcPH[#5as5!bE +(o@63!Nc@rrrBb3!!*A^rVlreDh%]b!q'uVpAY3^Dh%cd!T!h5rs.t1J,fQ:Dh$aG!T!hUrrLsV +`r?(r!<3!"mVdTirs.t1J,fQ:Dh$79!:'O6!OYON!.b-t!<7P"!.b-u!!LmQeoUlbec_3;kEJSh~> +ZMsp]rVloT!<3!Hh>mTU!8dbUh>mTU!8dbUmVdUTeo*9/.<"YSIrFcTIrFcTIrFcCDu0M9ec=;# +rrL[Np\tC6HhZu43W8sZmVdUQrrVV,J+imIpNLu]s8U@MhZ!iTGC05ek%fV.rs%olp](9=#fclL +mf<+Zs5n*L^]+Q5GC05ek%fUurrDKch>t;bJH16$JH16$TE#,Os5&G/p>,qB!:QFQJ,~> +ZMspZrVloQ!<3!HgAq9R!8IPRgAq9R!8IPRm;7@Qe8I*/.W+SQIr4TQIr4TQIr4TADu0M9f)XG% +rrL^Oq#:Qq*.@GE>s/)c!psiSr;QicD1D9Z#PE4bq>^K@%,V!holYQUs6"6QiVs,J!;$6jcO@hn +#O;B]q>]VpKtmWmolYQUs6"6Qe,KF8rS%@a9n<:q!.b-$!2';pg]-#[s7Y1MRK2ZB9n3~> +ZMspUrVloT!<3!Hh>mTU!8dbUh>mTU!8dbUmVdUTeo*9/.<"YSIrFcTIrFcTIrFcCDu0M9ec=;# +rrL[Np\tC6HhZu43W8sZmVdUQrrVV,J+imIpNLu]s8U@MhZ!iTGC05ek%fV.rs%olp](9=#fclL +mf<+Zs5n*L^]+Q5GC05ek%fUurrD?_ecE0NJH16$JH16$TE#,Gs428kp=9A2!9]S=J,~> +ZMsp]rVloT!<3!Q]`A*4!8dbUh>mTU!8dbUmVdULGA$(B=Ash'Is6^'jmbYWP0nEhc@tA-@A"dB8hZ)Gcs7Y:PS,i#J:4N~> +ZMspZrVloQ!<3!Q^&\35!8IPRgAq9R!8IPRm;7@IH"Z7=Ir4TQIr4TQIr4TQIr4TKH$T5cs7ak> +*30#>H^AeCoCZ5<=BpmHMs6TshmG#3HJH16$JH16$TE#,Ls4`/*p=f_=!:-(JJ,~> +ZMspUrVloT!<3!Q]`A*4!8dbUh>mTU!8dbUmVdULGA$(B=Ash'Is69aelIN@8JH16$JH16$TE#,Gs428kp=9A2!9]S=J,~> +ZMsp]rVntJ!2]_P3<9*Z!8dbUh>mTP!8dbUmVdULGA$'S6>PldIrFcTIrFcTIrFcTW)fSSk?9nG +KG_,c]cdCMrrj6\kMB*'rVlreDh%]b!q'uVp&>6,>+G'E.?rZg#.[p-mtb;li;Wt%6MKXlPfKn!m.'~> +ZMspZrVntF!2BMN2us!Y!8IPRgAq9M!8IPRm;7@GF_BgO6YYfbIr4TQIr4TQIr4TQW`PeRk?U4M +L)@Af^*!FNrs&??ETc4Z62gfbm;7@NrrVS)J+`gG\m(Wjk9uYPrs$&=RdGl;b2E:'F[>W^.$hjP +#'E5#m>#&k^AeDK>FOr7?cpjtrpB`&!P2!V!.b-t!<7P"!.b-u!!M'VgOfJ!gB +ZMspUrVntJ!2]_P3<9*Z!8dbUh>mTP!8dbUmVdULGA$'S6>PldIrFcTIrFcTIrFcTW)fSSk?9nG +KG_,c]cdCMrrj6\kMB*'rVlreDh%]b!q'uVp&>6,>+G'E.?rZg#.[p-mtb;li;Wt%6MKXlPf +ZMsp]r;Sg:&-+Ge!<<'!hZ*W/!)Q>hhZ*WDDh%f#5n$M$-%l5IDsmXTDsmXTDsmZ"UbN,\9'?6S +KGX\DV#^8i"kH!?#ZBjarrVV,J,K +ZMspZr;Sg<'Eg7m!<<'!g].<)!)Q>hg].<@D1DT"64Qk,.#%\ND=.@QD=.@QD=.AtUbW8_:$;QV +Jf"JAV?$Aj"kQ0G$rQ3drrVS)J,KUJP]&3MjpL+I=Fi=:`pL,rg +5e=Z/"oGAZ$p_DPrs%lFD#eFa9n<:q!.b-$!2';pg]-#[s7Y1MRK2ZB9n3~> +ZMspUr;Sg:&-+Ge!<<'!hZ*W/!)Q>hhZ*WDDh%f#5n$M$-%l5IDsmXTDsmXTDsmZ"UbN,\9'?6S +KGX\DV#^8i"kH!?#ZBjarrVV,J,KCB/.X8q?tn!.b-$!2';pf)O +ZMsp]oD]$k[ndeJ''b&JJcD):!:KgB!PM6Z!.b-$!.b-C!!M0YhLtq&h?9>Kn!m.'~> +ZMspZoD]$k[ndeJ''b&JJcD):!:Ba>!P2!V!.b-$!.b-C!!M'VgOfJ!gB +ZMspUoD]$k[ndeJ''b&JJcD):!:'O6!OYON!.b-$!.b-C!!LmQeoUlbec_3;kEJSh~> +ZMsp]pAYEp[ndeO!!!_]JcC<$QN$sQrS@Rg:4WCr!7:fHIt7TN!7CiMhZ)Gcs7Y:PS,i#J:4N~> +ZMspZpAYEp[ndeO!!!_]JcC<$QN$sPrS%@a9n<:q!7:fHIt7TN!7CiMg]-#[s7Y1MRK2ZB9n3~> +ZMspUpAYEp[ndeO!!!_]JcC<$QN$sMrRM"W8q?tn!7:fHIt7TN!7CiMf)O +ZMsp]q>UTo[ndhPr;Zj1Y(?V(s-Kn!m.'~> +ZMspZq>UTo[ndhPr;Zj1Y(?V(s- +ZMspUq>UTo[ndhPr;Zj1Y(?V(s- +ZMsp]qYpouRS4J3'*A47%Xip.JcCu7rpKf)!PM6Z!.b-$!.b-C!!M0YhLtq&h?9>Kn!m.'~> +ZMspZqYpouRS4J3'*A47%Xip.JcCu7rpB`&!P2!V!.b-$!.b-C!!M'VgOfJ!gB +ZMspUqYpouRS4J3'*A47%Xip.JcCu7rp'N!!OYON!.b-$!.b-C!!LmQeoUlbec_3;kEJSh~> +ZMsp]pAYKn!m.'~> +ZMspZpAY +ZMspUpAY +ZMsp]p\tH.0;]Nd/,4\qJcCr6!:KgB!PM6Z!.b-$!.b-C!!M0YhLtq&h?9>Kn!m.'~> +ZMspZp\tH.0;]Nd/,4\qJcCr6!:Ba>!P2!V!.b-$!.b-C!!M'VgOfJ!gB +ZMspUp\tH.0;]Nd/,4\qJcCr6!:'O6!OYON!.b-$!.b-C!!LmQeoUlbec_3;kEJSh~> +ZMsp]q>U]lBhdd<%3"G]s+135rrDKch>t;bJH16$JH16$TE#,Os5&G/p>,qB!:QFQJ,~> +g&D6Tr;6?dq#'jhq"adarTaC_g\LjSoP%%_d1%#XJcC<$P5bOLrS%@a9n<:q!.b-$!2';pg]-#[ +s7Y1MRK2ZB9n3~> +g&D6Tr;6?dq#'jhq"adarTaC_f(o=NoP%%_d1%#XJcC<$P5bOIrRM"W8q?tn!.b-$!2';pf)O +ZMsp]qYpf>0qA:DT*t;bJH16$d/a2rJH16$dJsF,s5&G/p>,qB!:QFQJ,~> +h#@ZZqtTmXoCDG@r9jp\nac;EpA"Rbmf*:AqYpf>0qA:DT* +h#@ZZqtTmXoCDG@r9jp\nac;EpA"Rbmf*:0qA:DT* +ZMsp]r;QlpG=1 +hu=,aqtTmVnaGl3lKRNrroa=F$0gaAmI0T:pA"RTrrCgOrr`(@6f@tD!H4uhs+133rrDHbgB"l[ +JH16$JH16$TE#,Ls4`/*p=f_=!:-(JJ,~> +hu=,aqtTmVnaGl3lKRNrroa=F$0gaAmI0T:pA"RTrrCXJrr`(@6f@tD!H4uhs+133rrD?_ecE0N +JH16$JH16$TE#,Gs428kp=9A2!9]S=J,~> +ZMsp]rVluG2N[q:rrBoes+132rrDKch>t;bJH16$JH16$TE#,Os5&G/p>,qB!:QFQJ,~> +iVt#!qY0XPmd03$jlGF[i8EMMi8ESQj5f@cl0Rj.o_8:SrrCgPrr^S*OT,4Y!5a +!P2!V!.b-$!.b-C!!M'VgOfJ!gB +iVt#!qY0XPmd03$jlGF[i8EMMi8ESQj5f@cl0Rj.o_8:SrrCXKrr^S*OT,4Y!5a +ZMt-cs8W"M44f&`JcCQ+!:KgB!PM6Z!.b-t!<7P"!.b-u!!M0YhLtq&h?9>Kn!m.'~> +j8TYhqtK^OmHWlqio&\KgY1?YfFHTggYCZDioK:fmI9`Ar:L!ng]. +j8TYhqtK^OmHWlqio&\KgY1?YfFHTggYCZDioK:fmI9`Ar:L!nf)PdJKKIe:s+13+rrD?_ecE0N +JH16$d/a2rJH16$dJsF$s428kp=9A2!9]S=J,~> +ZMt*bs4KPfr.4m!s+gUSmetuC]hX+YJH16$JH2>C"Q'18K`Cc&"IoJ\S5+S~> +jSoehq"4(Bl/q!`h:gN4e^Msor6Q#&daQatf\>6?j6#UmnFZMTpAYC"P`t2K)bQ!"I]>VRSA;~> +jSoehq"4(Bl/q!`h:gN4e^Msor6Q#&daQatf\>6?j6#UmnFZMTpAYC"P3V(F8tsb"I&oLPY-H~> +ZMt$_OuCU:s+13(rrDKch>t;bJH16$JH16$TE#,Os5&G/p>,qB!:QFQJ,~> +k5Q(nqY'IHl/q!_gXt*+dEg(\b/sS&&]r2=c-Oedf%Aa7io]Lmo(N"Urr^u<2U6mRJcCH(!:Ba> +!P2!V!.b-$!.b-C!!M'VgOfJ!gB +k5Q(nqY'IHl/q!_gXt*+dEg(\b/sS&&]r2=c-Oedf%Aa7io]Lmo(N"Urr^f72U6mRJcCH(!:'O6 +!OYON!.b-$!.b-C!!LmQeoUlbec_3;kEJSh~> +Zi:-d4EgLNs+13'rrDKch>t;bJH16$d/a2rJH16$dJsF,s5&G/p>,qB!:QFQJ,~> +kPl7pq=O.AkMtISf[S!P2!V!.b-t!<7P"!.b-u!!M'VgOfJ!gB +kPl7pq=O.AkMtISf[S +[/U3"19/7l!T!g-s+13errDKch>t;bJH16$JH16$TE#,Os5&G/p>,qB!:QFQJ,~> +kl2Cqq"*q:0nT/*@_rrLjS +JcC<$_Z'W'rS%@a9n<:q!.b-$!2';pg]-#[s7Y1MRK2ZB9n3~> +kl2Cqq"*q:0nT/*4[rrLsV +JcC<$_Z'W$rRM"W8q?tn!.b-$!2';pf)O +[f6Kp:hfRY_uBbo!;HKsej9WJ;WmuXs+gUR#3q-j">-;@p\tEoXYgLuF56d!qlM^a6+PnQ"SZC3 +&9Gei"nuLa$phG;rrr;%2%2d]df0=8rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +l2NF7p[[_8j5/M@dEThR_n`ss\@/cNZE^[=Z*CU@['mKU]thP*b0JDcg>:lRmIL#NrrhR_C%U/t +rrLjSp\tEK70j<%kPbD^rJ-7/rs%eJ+pKZmp%SIiqlDU_&9Ze."oGAX$pqP_rri()#S;-srrr.* +1^l^^^&J95X>C>P]$gTYmJYc?\kR_UJH16$JH2>C"P`t2K)bQ!"I]>VRSA;~> +l2NF7p[[_8j5/M@dEThR_n`ss\@/cNZE^[=Z*CU@['mKU]thP*b0JDcg>:lRmIL#NrrhR_C%Tuo +rrLsVp\tEJ63mipkPYA[L]7;XlAQkh/YMk4rrr;%2%1%;h#@QTXYgMQ\_mDipL=I?F2\(^pL?&h +6+O]/"oGDZ$phGPrrD?_ecE0NJH16$JH16$TE#,Gs428kp=9A2!9]S=J,~> +\,QW60Y.5`hS]?oh>mPF's.8p\tHMI9po39$,oh#2"?1EEn@] +i;Wtc8onoCai48aYtA>B>`%OUrs%,(??'5,jO=,/metuC]hX+YJH4'ts$)&7JH4*u"Q'18K`Cc& +"IoJ\S5+S~> +lMhXtp[[\6in`8:ccaAH^V%+cZa$^9X/c)s*3-'AYHbFB\\,\pa32fYf\PQMm.0rOXY@[]3IU6u!MWE^q"gUa?DbA0k +rs#c$?>s2-jM(WufR@.G?&I^lrrDHbgB"l[JH16$d/a22JH16$dJsF)s4`/*p=f_=!:-(JJ,~> +lMhXtp[[\6in`8:ccaAH^V%+cZa$^9X/c)s*3-'AYHbFB\\,\pa32fYf\PQMm.0rOXY@ +\c2d#>?pXrrrCpSrrMP;rr4nEJ,fOuesLrKf)Pc"esMANZg.SSZ%n%3irB%nUoK-=_5N'Zh#IEQ +esqGHW;$>l_2Ef-\H)F)([_jBoBqths8VSrD(0u7s7CY?bOE2IP5"t\Sm&Gbk#u6Mrs#&uZKe)h +Yi,Nb4a6>V>%5JN#!I19lC_If^AeD/>-dFd?EhoY!:KgB!PM6Z!.b-t!<597!.b-u!!M0YhLtq& +h?9>Kn!m.'~> +li.h#p[[\6iS<&5c,mo>]XYATY,nV#V50l\rh14(USO`aWN<;/[CElb`Q?ERfA,?Jm,M8Sn,E=e +g\q-Qm=5-82XQ"os*rUfJWJ=\s*rUfNJOn)s6$VAKrEu1s1bA1LRl;^L7R*!s8CN=KnHE"s8Trq +J!7;@q#;/t&@V3!Q6?5-n?m*]J,fQ?H +li.h#p[[\6iS<&5c,mo>]XYATY,nV#V50l\rh14(USO`aWN<;/[CElb`Q?ERfA,?Jm,M8Sn,E=e +f)>ULmXP692XZ(ps*rUeIur7]s*rUeMheY(s6-_CKrF#2s1bA0Kq,uXKq@*"s8CN=KnH>us8Ton +J!@JDq#;/u&@M,tQm)M0n?m*^J,fQ@HW\J\]i^Hlrs#&uZKgRU@bq%gSm&GbTO+k\rrkQ0mcI\^ +`W$-*GI$M,?Eh0D#+f#(lC_Ife,KF5rRM"W8q?tn!7:fH5Cifc!7CiMf)O +])MiB0s1BR!8d\S!T!hUs#/^6s8N(CLOW'*s8N(CLP'2>Y5>"i@WPYCqu;.-IrS(_I;qrHq>\op +CNjhpSH$E#Ap8;s!;QR1mL[)/pRE6's7FR5:4ND`r?;8#hnYFfOg,Xrs/@JEU<_XArY:>!:KgB!PM6Z!.b-$!.b-C!!M0YhLtq& +h?9>Kn!m.'~> +m/It&q"!h8iS<#3bf@W8]!eoIWi)\fTUq[CRf8]lR[]h=TVA9\X0/b:]"Z)&bgG%tiM\B)qYgEo +g\q-QgAq6Q2VS63rr@8"Inj;Lrr@8"KP+;fqkQqqEEA_-s*m+\F%]YbE_D\ns3u[:G&pHLs2%QG +GB6gcq#;/t&A7i5RNDM/oZa7-J,fQ>WNl="fk-0;rs/@JE9mQ(YTVg&#Pideli5RMO5^*Co*F"K +s0*Wcrs/-$f^&RhBTKq.#Pideli5RMO44+/mJYc?\kR_UJH16$JH2>C"P`t2K)bQ!"I]>VRSA;~> +m/It&q"!h8iS<#3bf@W8]!eoIWi)\fTUq[CRf8]lR[]h=TVA9\X0/b:]"Z)&bgG%tiM\B)qYgEo +f)>ULh>mQT2VnH6rr@7uInj>Mrr@7uK4\/eqkQtrF',%2s*m+[ECs>]ED2\os3lO7GB6NLs1qHB +GB6dbq#;/u&A@u8S/hS/oZa7.J,fQ>WjDU&g16'8rs/@JEU<`*YoMX##Pidfm/P^MNT'mAo*F(M +s03Q`rs/-$g?ejlArXS*#Pidfm/P^MNRRn-lM]97[7YuMJH16$JH2>C"P3V(F8tsb"I&oLPY-H~> +]`/*+Bi+'krrCpSrrLsVrr4n5!<<'!J+!?<#ljo)J+!$\>(c^WOP&Q'F7T?\9ZcR:9ZcR:EU`pk +B$'Q1PU6'!?J>/1@K?0&"`H_SG;Y +m/K!?oBkc$gt'ls`kf?uZ`gF-UnF9JR$O#$P*1rjP*;/sR%'Y>Uo18sZad]ca3;rV>=[32o_e^g +!8IJP!S[VRs#/U3s8N(Kn,9C9WCkBG%#Oue]r;Z"tK%'`"mJYc?\kR_UJH4'ts$)&7JH4*u"P`t2K)bQ!"I]>V +RSA;~> +m/K!?oBkc$gt'ls`kf?uZ`gF-UnF9JR$O#$P*1rjP*;/sR%'Y>Uo18sZad]ca3;rV>=[32o_e^g +!7q,K!T!hUs#/^6s8N(Kmf0?os8N(Kme517KDVp8f]$F_o)F25am*_:am*__nGSS`ame__-21/@ +g@sH5!;HKsDaJep22hG5%.8%`s8W&ipAa\rVu/"J\V3in8WmTs6=HPe,KF5rRM"W8q?tn!7:fH5Cifc!7CiMf)O +^&J/Q1SFRD!8d\S!T!hUs"W@1s8N)Ds8UpUs8N)Ds8VM*J,eH,QBk-]mf.cRm/MQPm/MQRmdtW` +H[gN`!9+F#rr3#8!;HKsC-?of0oQ#1!q'uVrVm&^VL*Zto(`4inoK6Xs8UXQhZ!iOF++#el>;+2 +rs%Tcqu?]I"NLHHjoG;Ws6=HP^]+Q0F++#el>;+$rrDKch>t;bJH16$d/a22JH16$dJsF,s5&G/ +p>,qB!:QFQJ,~> +mJe($p$_/*h:L&u`kf!k +g\q-QgAq6Q1"u^.rrC:Cs4[PRrrC:Cs6XZQrna]APtGo\s*nhMs*nhMs*nnQol`ECIt)A:isrg& +rrKn8q#:QsCHd#d15l,2!psiSrVm&^VgE`ro(`4inoB-Us8UURhZ!iOEd[fbl>;.3rs%Qbq>^KF +#0-ZJjT,,Ss6=HQ^]+Q0Ed[fbl>;.%rrDHbgB"l[JH16$d/a22JH16$dJsF)s4`/*p=f_=!:-(J +J,~> +mJe($p$_/*h:L&u`kf!k +f)>ULh>mQT1#;p1rrC=Ds5!bUrrC=Ds6afTs59oCQ:c)`s*nnPs*nnPs*ntToQ<6BIt)A:iXEO" +rrKq9p\tD34aVB-MuEYWmVdURrrqJ\H[E0kq#:TiF++#es4.>0rs/+9IJs32D1U[G#3Gp_s8UXQ +`r?;+!;l`pl>;*ers/+9IJs32D1U19!:'O6!OYON!.b-t!<597!.b-u!!LmQeoUlbec_3;kEJSh~> +^]+E3G"1KdrrCpSrrLsVrr4^t!<<'!hZ*W4!<<'!hZ*WDDh%Y]CQ"ibDsmXTDsmXTDsmXTDsm7? +4ahg0HY;UP_>aH8_#X91#4i86qtHHtrVlreDh%cd"k\`SRb%B1rs/FNF70).[2e''#Q'!lmf2!S +NT'mApB]XUs0Widrs/9(hX:EtBT9e,#Q'!lmf2!SNRRn-metuD]hTDq0nD'10nD'Q0a-A5hLtq& +h?9>Kn!m.'~> +mf+7*p[RP0hUp9#`kf9qZ)jjtSskt.O,SpNKnP*XJfTJsKnkMDO-5ftSti3bZ*q,F2mq)dhW4"m +q>^KLrVloQ!<3!QbQ.\C!8IPRgAq9R!8IPRm;7@MVe_3`Xs +8TMM`r?;:$JXtJg1ubars/CMEp`n\CQHsE!:Ba>!P2!V!.b-$!.b-C!!M'VgOfJ!gB +mf+7*p[RP0hUp9#`kf9qZ)jjtSskt.O,SpNKnP*XJfTJsKnkMDO-5ftSti3bZ*q,F2mq)dhW4"m +q>^KGrVloT!<3!QblIeD!8dbUh>mTU!8dbUmVdUPVJD*_<,_4)IrFcTIrFcTIrFcIFZk/&IsYhn% +)MrC!Pnd2rs&''\bl,g*<#m=mVdUSrrq&5F.1\Wp\tKqIrk&Xs0Wj'rs/FNF70(^BT;!N#58D[s +8TJH`r?;<#i>"Lg1ZJ\rs/FNF70(^BT:L@!:'O6!OYON!.b-$!.b-C!!LmQeoUlbec_3;kEJSh~> +_#FM_2O".7rrCpSs#]?Cdf8`^!<<'!hZ*W4!<<'!hZ*WDDh%M*BBJ,[D=%:PDsmXTDsmXTDsmRe +B[-/?W.Y+F@bUS5B)q`,#4i82oBqhdrVm;oDh%feoT!%WjQc%&p\tGj@__[/@Ue0n#,#G?pSq], +i;WsHK_t@M@`A?O6%m#IWa`3Hrs#-*^\=a;Yg`UPmetuD]hU;5@tA.Kn!m.'~> +mf+7&oBk\ug=+M,\ +oD\dErVnt>!7:c7HN=*G!8IPRgAq9R!8IPRm;7@HGA$$p@;G3/Ir4TQIr4TQIr4TNO_Dr/o;[NE +;.82QhIQ]^rs%p%[.EaN,5qNMm;7@Qs7D:SYN5$8p%SIjTjte0o4)@frs#0,^%SL;ZJb`d7"Y7+ +@qEXZ#"4'Vp8_`0^AeD2A%hT:A?s\a!:Ba>!P2!V!.b-t!<597!.b-u!!M'VgOfJ!gB +mf+7&oBk\ug=+M,\ +oD\d@rVntA!7Lo8HN=*G!8dbUh>mTU!8dbUmVdULGA$'r?u>91IrFcTIrFcTIrFcRP%`,3o;I]!:'O6!OYON!.b-t!<597!.b-u!!LmQeoUlbec_3;kEJSh~> +_#FFZg@bIGhYmI9pI8>9LN'"a_Pl:UfmVdUTs4gj6BDuZH[J0\*g4O*dG>?99rs%20BmXGJMD^1dP:c(JMD^1dP1m]s5&G/p>,qB +!:QFQJ,~> +n,FC*p@%8*gss`m_nEOaX/;PZQBI5aKn4]"G]n1Mrc0$#FEVnUIY!30NKTTsTr(YQ\%T]$cdpn2 +l0nEIg\q.6og`5;M/`OqrrCgRs4[PRrrCgRs6XZQqO(?/LfR*Vs*nnQs*nnQs*nnQs3Q1-GB[&L +s2%KCGB[Nsq#:QsH!>E6;j@7\#jlJYs8UeW0k^K$!4DS$#24]CI!JK0h>[]5K4oXCN9&ps"h%9X +GtuN$rs#o/BRF?LjM(Wug4O'dA;oTurrDHbgB"l[JH16$d/a22JH16$dJsF)s4`/*p=f_=!:-(J +J,~> +n,FC*p@%8*gss`m_nEOaX/;PZQBI5aKn4]"G]n1Mrc0$#FEVnUIY!30NKTTsTr(YQ\%T]$cdpn2 +l0nEIf)>V1pI8>9LN'"a_Pl:UfmVdUTs4gj6BDuZH[J0\*g4O*dG>?99rs%20BmX +ZMsp]r;SgsLOY]BJ,fOumf3=TJ,fOumf3=\\%ht"TS98LP/715\+]j:\+]j:\+]k!d?oQ=V"=Wd +_2Ef1dXV#k"mVb1K;eD?rs&2+^]4>rVYkoD^\@a3rRREiK=Te;"o[#@K:^lUrri5(K7g_Qrrr;) +RY.3e^&J97es_5_gsZ3&metuC]hX+YJH16$JH2>C"Q'18K`Cc&"IoJ\S5+S~> +n,GQHo'GJpf[7m\^::JMVP0KFOGemGIX63[EGo]0CMIX#D/XE9GC"^iLQ%@]S"Z^\ZFIWfbL+u" +jm;U;g\h(4`ef87e:7MuJ*m:9m=509J*m:9p9qa9hOoV%[$/B,^UEk9^UEk9^UEk9qpCdaK;S8? +s1jPBQdUBXrrq__JVC&orVm)q[_MkBjehs*!5J:."oZu?Kn[+nrrrAORY@Bhhu!P2!V!.b-$!.b-C!!M'VgOfJ!gB +n,GQHo'GJpf[7m\^::JMVP0KFOGemGIX63[EGo]0CMIX#D/XE9GC"^iLQ%@]S"Z^\ZFIWfbL+u" +jm;U;f)5P/`J8r1epm`"J+!@:mXP9:J+!@:p:%g:hOoS"Z]`0)^UNq:^UNq:^UNq:qpCd`K;A,= +s1sVCR*pKYrrq\\ItO]krVm)q\%htCk,/'+!5J:."o[#@K7g_irrrAPRY.3ehu`;^#< +Z'p<:gqE^krRREiT%qco!:'O6!OYON!.b-$!.b-C!!LmQeoUlbec_3;kEJSh~> +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J:4N~> +nGaO-p@%5(gXOKg^q-nTVkKWHOGejEI!B^OCh[NnAH$'ZA7]CgCiFK@I"@$1OHuE2Vld>;_8jjN +gZ%GcpY19!JcC?%!:Ba>!P2!V!.b-t!<597!.b-u!!M'VgOfJ!gB +nGaO-p@%5(gXOKg^q-nTVkKWHOGejEI!B^OCh[NnAH$'ZA7]CgCiFK@I"@$1OHuE2Vld>;_8jjN +gZ%GcpXXoqJcC?%!:'O6!OYON!.b-t!<597!.b-u!!LmQeoUlbec_3;kEJSh~> +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J:4N~> +nGaO*oBbSqf?qaY]XG#CURda6MhQ\.G&_A5AR]%Q>lIqJ>[CfIASH"$G'eaoMij?sUT(K+]u/"? +f\Y`Vo@ecqJcC?%!:Ba>!P2!V!.b-t!<597!.b-u!!M'VgOfJ!gB +nGaO*oBbSqf?qaY]XG#CURda6MhQ\.G&_A5AR]%Q>lIqJ>[CfIASH"$G'eaoMij?sUT(K+]u/"? +f\Y`Vo@8ElJcC?%!:'O6!OYON!.b-t!<597!.b-u!!LmQeoUlbec_3;kEJSh~> +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +nc(iQp[IG,gXOKf^q$bPV4X-@(cOCN+ECJ;0#GR@g=WZFRck +c.(M-l1!;Us+13%rrDHbgB"l[JH16$JH16$TE#,Ls4`/*p=f_=!:-(JJ,~> +nc(iQp[IG,gXOKf^q$bPV4X-@(cOCN+ECJ;0#GR@g=WZFRck +c.(M-l1!,Ps+13%rrD?_ecE0NJH16$JH16$TE#,Gs428kp=9A2!9]S=J,~> +ZMsp^JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J:4N~> +nc'[.p$Uu"f[7jZ]X=rATpq:,Lk0nqE,&rl>ZasDci3nC*9M-<=Bo6FC2e?CJV]>NS"cjb[_9T$ +dFdC?maQgfJcC?%!:Ba>!P2!V!.b-t!<597!.b-u!!M'VgOfJ!gB +nc'[.p$Uu"f[7jZ]X=rATpq:,Lk0nqE,&rl>ZasDci3nC*9M-<=Bo6FC2e?CJV]>NS"cjb[_9T$ +dFdC?ma$IaJcC?%!:'O6!OYON!.b-t!<597!.b-u!!LmQeoUlbec_3;kEJSh~> +ZMsp^JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J:4N~> +nc(lNo'>Amf$DCP\[&94SsPOrK7.r`CLpjV+$OdDZ9XL#LS +aNrGnjQk1lJcC<$KDtr=rS%@a9n<:q!7:fH5Cifc!7CiMg]-#[s7Y1MRK2ZB9n3~> +nc(lNo'>Amf$DCP\[&94SsPOrK7.r`CLpjV+$OdDZ9XL#LS +aNrGnjQk"gJcC<$KDtr:rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp^JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +nc'[*nEJree',eF\$2j+S!8neJ9c0OAmeeB:h08"`DQUs*^FaK7nZZY>$trYF*W:jNKfp,WN`kG +`QZidip"bdJcC<$KDtr=rS%@a9n<:q!.b-$!2';pg]-#[s7Y1MRK2ZB9n3~> +nc'[*nEJree',eF\$2j+S!8neJ9c0OAmeeB:h08"`DQUs*^FaK7nZZY>$trYF*W:jNKfp,WN`kG +`QZidip"V`JcC<$KDtr:rRM"W8q?tn!.b-$!2';pf)O +ZMsp_JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J:4N~> +o)Bg1p@%2&g!Rs[]X4f-:fU_/BQ&$@K8PeYT;J`t +]Yhn>g#2&VRSA;~> +o)Bg1p@%2&g!Rs[]X4f-:fU_/BQ&$@K8PeYT;J`t +]Yhn>g#2&7pjrHrs+C=OlM]97[7YuMJH4'ts$)&7JH4*u"P3V(F8tsb"I&oLPY-H~> +ZMsp_JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J:4N~> +o)D#Ro^1etf?hUT]!AB4SX#4jJ9c0NA6r>794@l#B/2+l/1iM12**rj92SbsA8?42J;9/NSYWZ5pOW?qs+C=OmJYc?\kR_UJH4'ts$)&7JH4*u"P`t2K)bQ!"I]>VRSA;~> +o)D#Ro^1etf?hUT]!AB4SX#4jJ9c0NA6r>794@l#B/2+l/1iM12**rj92SbsA8?42J;9/NSYWZ0pOW?qs+C=OlM]97[7YuMJH4'ts$)&7JH4*u"P3V(F8tsb"I&oLPY-H~> +ZMsp_JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +o)D#QoBbPoe]u4M\?Ms,S!/ebIWfXC@9QW)7[a;D0InLu,UFfg0/P^T7SQic@;'S'IYE`FS"cme +\A-)0f%oE0oR[$ns+C=OmJYc?\kR_UJH16$JH2>C"P`t2K)bQ!"I]>VRSA;~> +o)D#QoBbPoe]u4M\?Ms,S!/ebIWfXC@9QW)7[a;D0InLu,UFfg0/P^T7SQic@;'S'IYE`FS"cme +\A-)0f%oE,oR[$ns+C=OlM]97[7YuMJH16$JH2>C"P3V(F8tsb"I&oLPY-H~> +ZMsp_JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J:4N~> +o)D#Po'>>keBPtH[]cX&R?EG[Hus4;?WU)s6biZ71n!A%n]#Jn.PE\B6V:3X?Y4.sI"R<>RA$R_ +\%]l,eD0*+nq$gls+C=OmJYc?\kR_UJH4'ts$)&7JH4*u"P`t2K)bQ!"I]>VRSA;~> +o)D#Po'>>keBPtH[]cX&R?EG[Hus4;?WU)s6biZ71n!A%n]#Jn.PE\B6V:3X?Y4.sI"R<>RA$R_ +\%]l,eD0*'nq$gls+C=OlM]97[7YuMJH4'ts$)&7JH4*u"P3V(F8tsb"I&oLPY-H~> +ZMsp_JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J:4N~> +o)Bj.n`o,ge',bD[B?F"R#m/UH?3k4>ZFNh5h5nAh>dNBrr4%E-S$r45Y"RL?"@_lH@h!9R%L:Z +[_9Z(eD'$)o)=4?JcCE'!:Ba>!P2!V!.b-t!<597!.b-u!!M'VgOfJ!gB +o)Bj.n`o,ge',bD[B?F"R#m/UH?3k4>ZFNh5h5nAh>dNBrr4%E-S$r45Y"RL?"@_lH@h!9R%L:Z +[_9Z(eD'$%o)=4?JcCE'!:'O6!OYON!.b-t!<597!.b-u!!LmQeoUlbec_3;kEJSh~> +ZMsp_JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +o)D&On`f&ee'#\B[B6?"9b52lP@rfSK:+.rT5N$p><5"/1F>@VDgH%Cd5Q_1.W +[CjH$e(Wg&nbn%=JcCE'!:Ba>!P2!V!.b-$!.b-C!!M'VgOfJ!gB +o)D&On`f&ee'#\B[B6?"9b52lP@rfSK:+.rT5N$p><5"/1F>@VDgH%Cd5Q_1.W +[CjH$e(Wg!nbn%=JcCE'!:'O6!OYON!.b-$!.b-C!!LmQeoUlbec_3;kEJSh~> +ZMsp_JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J:4N~> +o)D&On`f#dd`]P@[&p3sQB-fOG]@G,>#S*_4lHA?V\]mN"UVX;n161L4[_tB>@M>eH%:^3Q_((V +[CjH$e(Wg&nbn%=JcCE'!:Ba>!P2!V!.b-t!<597!.b-u!!M'VgOfJ!gB +o)D&On`f#dd`]P@[&p3sQB-fOG]@G,>#S*_4lHA?V\]mN"UVX;n161L4[_tB>@M>eH%:^3Q_((V +[CjH$e(Wg!nbn%=JcCE'!:'O6!OYON!.b-t!<597!.b-u!!LmQeoUlbec_3;kEJSh~> +ZMsp__uBc*It@WNs2+d9metuC]hX+YJH4'ts$)&7JH4*u"Q'18K`Cc&"IoJ\S5+S~> +o)D&OnEJocd`]P@[&p0rQB-fOG]@G,>#S'^4P9]66l-C?!shR"s&q$44[_tB>%22cH%:^3Q_((V +[(O?#e(Wg&nbpH,!UYAfs+13errDHbgB"l[JH16$d/a22JH16$dJsF)s4`/*p=f_=!:-(JJ,~> +o)D&OnEJocd`]P@[&p0rQB-fOG]@G,>#S'^4P9]66l-C?!shR"s&q$44[_tB>%22cH%:^3Q_((V +[(O?#e(Wg!nbpH,!UbGgs+13errD?_ecE0NJH16$d/a22JH16$dJsF$s428kp=9A2!9]S=J,~> +ZMsp__uBbo!;HKslC`c^W:Tres24j9"n9-\M7N'JrrrAPRY?O-h#@QVes_;cgu&,7qltd/\]"LO +qluTDT\R6\"o[#@KqR5LrrDKch>t;bJH16$JH16$TE#,Os5&G/p>,qB!:QFQJ,~> +o)D&On`f&ee'#\B[B6?"9b4i76s,T.7)$P"Zds)p%R5"/.E>@VDgH%:^3Q_1.W +[CjH$e(Wg&nc$N-!S[VKrrq__K8$>srVlru_o2QS"nB3^MRi0KrrrAOR>$F-h#@QVeX;,agu&,7 +qlk^.]#=UPqllKBT\R6\"oZu>KqR5LrrDHbgB"l[JH16$JH16$TE#,Ls4`/*p=f_=!:-(JJ,~> +o)D&On`f&ee'#\B[B6?"9b4i76s,T.7)$P"Zds)p%R5"/.E>@VDgH%:^3Q_1.W +[CjH$e(Wg!nc$N-!T!hNrrq\\K7g,or;Zf7rr3/k]8;BTme6YarRREkLUl4?"o[#@KqR5Yrri5( +K87"Urrr;)RY@Ei^&J97es_;cgsZ3&lM]97[7YuMJH16$JH2>C"P3V(F8tsb"I&oLPY-H~> +ZMsp__uBbo!;HL%HWkW7[]3I9pnuM<*Up"gL[@ +DbA-jrs#`#??'5,jM(WufR7(G>`%OjrrDKch>t;bJH16$d/a22JH16$dJsF,s5&G/p>,qB!:QFQ +J,~> +o)D#On`o,ge',bD[B?F"R#m/UH?*e3>ZFNg5G8&R-QO$<',3)!s*-7X5=\IK>\%VkH@^p7R%L:Z +[CsN&e(`m'n\kG.gAq$K%K1WZGBHuOs8VuY]N0^brs#uCB51n^rqHEsfR@.GDbA1.rs%,)?>s2- +jP^%AZ:]+T9$5-Q#-pj-E*\@^^AeDiIU6u!MWE7d!:Ba>!P2!V!.b-t!<597!.b-u!!M'VgOfJ! +gB +o)D#On`o,ge',bD[B?F"R#m/UH?*e3>ZFNg5G8&R-QO$<',3)!s*-7X5=\IK>\%VkH@^p7R%L:Z +[CsN&e(`m#n\kG.h>m`%P" +rroZ"F)O[m`W$.I8mZLBM<)DN#2"?1EEn@]e,KF5rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp_rVlodJ,]I+mXP9:J(C!:OOjI3J(C!FLU6:GkconsW8djW^SIl`SAD.XT%sAGrRRKmK;ePE +s1sVCPdpehrtk)7[I`gR+ohT3[;@@Bs8VnEAD5mPIJNX;#+f#(lK28Zh>[\N>-dFd?EiAf"[.(t +k#u65rrtW1ZKe)hYeL,ASm&GbTO+kOrrDKch>t;bJH16$d/a22JH16$dJsF,s5&G/p>,qB!:QFQ +J,~> +o)D#Oo'58ieBPtH[]cU%R?cc&8g>Dt)]VfVs'moJ6:k!T?=dtqH\73=R@pL^ +[_B`*eD0*+oD\ajm=5-82XQ"os*rUfJWJ=\s*rUfNJOn)s6$VAKrEu1s1bA1LRl;^L7R*!s8CN= +KnHE"s8TrqJ!7;@q#;/t&@V3!Q6?5-n?m*]J,fQEJ7[EiB70IJrs#'!Zg-^XA_m@jSm/PdTjY1a +rrkT3mH7\b`W$-+H*ce0@'[NH#+f&*lChUke,KF8rS%@a9n<:q!7:fH5Cifc!7CiMg]-#[s7Y1M +RK2ZB9n3~> +o)D#Oo'58ieBPtH[]cU%R?cc&8g>Dt)]VfVs'moJ6:k!T?=dtqH\73=R@pL^ +[_B`*eD0*'oD\ajmXP692XZ(ps*rUeIur7]s*rUeMheY(s6-_CKrF#2s1bA0Kq,uXKq@*"s8CN= +KnH>us8TonJ!@JDq#;/u&@M,tQm)M0n?m*^J,fQEIq79iApsLLrs#&uZKgRU@bq%gSm&GbTO+k\ +rrkQ0mcI\^`W$-*GI$M,?Eh0D#+f#(lC_Ife,KF5rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp_rVloT!<3!Vh>mTU!-a3J:1!u#!-a3NAlc)3UeYB?=J#EiIli."8m$L]9U>MSdt):YF&&8* +_+nTdF&i;9rtk)7^&.Po*<6'2c)+6cs8Vo*^&@/5J,/m>#Pidfm/R*j*8^]#q0d8Ls4CqCiVs,R +#ho=YYoLd`#P/5Tm/P^MNPGJuq0d8Ls4CqCe,KF9rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J +:4N~> +o)Bg/oBYJne]u1L\$2j+RZi\`I*WDR\Hdc +\A-&/e_TC"P`t2K)bQ! +"I]>VRSA;~> +o)Bg/oBYJne]u1L\$2j+RZi\`I*WDR\Hdc +\A-&/e_T<+p&=slh>mQT2VnH6rr@7uInj>Mrr@7uK4\/eqkQtrF',%2s*m+[ECs>]ED2\os3lO7 +GB6NLs1qHBGB6dbq#;/u&A@u8S/hS/oZa7.J,fQE\\7t*Dh%TXrs/@JEU<`*YoMX##Pidfm/P^M +NT'mAo*F(Ms03Q`rs/-$g?ejlArXS*#Pidfm/P^MNRRn-lM]97[7YuMJH16$JH2>C"P3V(F8tsb +"I&oLPY-H~> +ZMsp_rVloT!<3!Vh>mTU!.XG:X9ek+!.XG1eQRV&Z&mVtA97>AIn[uLD+r(:D/o(DP\/&,mZ]$l +:0uZKhI6KZrrm=-HZq?*rVlreDh%]b"2APRYkS/&n8WmTs8UXQhZ!iMEIIfcl>;+2rs%H_qu?]I +"NLHHiW/lSs6=HP^]+Q.EIIfcl>;+$rrDKch>t;bJH16$d/a22JH16$dJsF,s5&G/p>,qB!:QFQ +J,~> +o)D#Ro^1bsf?_OR\[&61S<]+iIs>sJA6i548jnI7qi*<\0q/+APrV#r8l/PoA8?42J;9/MS><3k +\\ZA5fA>Z5p\t0ngAq6Q2VS63rr@Q;rN$;)rr@Q;p=*8uqQQ(5hd^Zrs*m_al>928l>:M0rKV'; +nF)SepJG;OpYKB[q#:QsD*`Gk1lM>4!psiSrVm'#]kCTQrqQKunSrsSs8UOPhZ!iNEI@]al"kt1 +rs%K`q>^KD#0-ZJirJoQs64?O^]+Q/EI@]al"kt#rrDHbgB"l[JH16$d/a22JH16$dJsF)s4`/* +p=f_=!:-(JJ,~> +o)D#Ro^1bsf?_OR\[&61S<]+iIs>sJA6i548jnI7qi*<\0q/+APrV#r8l/PoA8?42J;9/MS><3k +\\ZA5fA>Z0p\t0nh>mQT2VnH6rr@Q:s/ZM+rr@Q:p=3?!qlu78iad-$s*m_al>95:l>:P1rKLp9 +nEuMepJ,&KpYK?Zp\tD86@3o6MuEYWmVdUQrr^#M>-Rc$#Ol_]rVuoK"Q'.`n8WmTs6=HPiVs,A +!;lfreca"m#N>a\rVu/"J\V3in8WmTs6=HPe,KF5rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp_rVloT!<3!Qh>mTU!6tQDh>mTU!6tQDmVdUTi2W*/8oO.tIr4QPIr4QPIrFcIFZXr"Ir>>H% +))Z?!Pnd1rrm.#HZq3&rVlreDh%]b"M\YOWr;_m#P)qarVuoK"Q'.`noK6Xs6=HPiVs,E!;lfre +ca"m#Nc$`rVu/"J\V3inoK6Xs6=HPe,KF9rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J: +4N~> +o)D#Tp?q,%f[7jY]+:K1M+B5Vj>Jr,SVT;J`t +]>D_;.3 +rs%Qbq>^KF#0-ZJjT,,Ss6=HQ^]+Q0Ed[fbl>;.%rrDHbgB"l[JH16$d/a22JH16$dJsF)s4`/* +p=f_=!:-(JJ,~> +o)D#Tp?q,%f[7jY]+:K1M+B5Vj>Jr,SVT;J`t +]>D_mQT1#;p1rrC=Ds5!bUrrC=Ds6afTs59oCQ:c)`s*nnPs*nnPs*ntToQ<6B +It)A:iXEO"rrKq9p\tD34aVB-MuEYWmVdUQrrg)N +ZMsp^rVloT!<3!Hh>mTU!8dbUh>mTU!8dbUmVdUTeo*9/.<"YSIrFcTIrFcTIrFcCDu0M9ec=;# +rrL[Np\tC6HhZu43W8sZmVdUPrr^\MJ,B$@#PWFhqu?]A#i>RdpNLu]s5n*LiVs,N!;HNnc3VGi +#O_Zequ>eoK>7EkpNLu]s5n*Le,KF9rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +nc(lKnEJoce',bD[]cX'RZi\aIs?!LARAS>:.ds)dJNtBf7Z=R77p?T=^P`UEd3(gN0B^(W3s/)c!psiSqu6cH=b6JFrs/4>H27L?cOA\1#PE4bq>]VpL#N%9lN$PV +s3D+nrs.iioD/FLD2#b)#PE4bq>]VpL"$&%mJYc?\kR_UJH16$JH2>C"P`t2K)bQ!"I]>VRSA;~> +nc(lKnEJoce',bD[]cX'RZi\aIs?!LARAS>:.ds)dJNtBf7Z=R77p?T=^P`UEd3(gN0B^(W3mTU!8dbUh>mTU!8dbUmVdUTeo*9/.<"YSIrFcTIrFcTIrFcCDu0M9 +ec=;#rrL[Np\tC6HhZu43W8sZmVdUPrr^\MJ,B$@#PWFhqu?]A#i>RdpNLu]s5n*LiVs,N!;HNn +c3VGi#O_Zequ>eoK>7EkpNLu]s5n*Le,KF5rRM"W8q?tn!.b-$!2';pf)O +ZMsp^rVloT!<3!Q]`A*4!8dbUh>mTU!8dbUmVdULGA$(B=Ash'I!:KgB!PM6Z!.b-n!.eX2A(G,mhZ)Gcs7Y:PS,i#J:4N~> +nc'[+o'>Ale]u4N\[&93SX,@oJp_`\C1LXR<)?7Z7R[5B*CN@f9Me\k?=[eiG'nq!OI)N6X0T:O +a3N5kjQk1krrLjSrr4^e!<<'!g].<.!<<'!g].<@D1D;*BBB/CD=.@QD=.@QD=.@QD=..EH27L? +pWrjZWrE#!WW;ho#3,^Xs8UONrVm;nD1DTcopVWRrI&\Up\tGSCZ5>Hi;Wrp +_uKb.1r]J!'Z8o0c=r]\rs" +nc'[+o'>Ale]u4N\[&93SX,@oJp_`\C1LXR<)?7Z7R[5B*CN@f9Me\k?=[eiG'nq!OI)N6X0T:O +a3N5kjQk"frrLsVrr4^d!<<'!hZ*W4!<<'!hZ*WDDh%M*BBK5DDsmXTDsmXTDsmXTDsmLLHi*jC +pWidUXoA>$XT8.r#3Q!\s8UXMrVm;oDh%fepRJ&Zs*o+]p\tGQCZ>BoS2&k"#)@!Rs35/Ci;Wrl +`rH(/0ua.s&B<`0c" +ZMsp]rVntJ!2]_P3<9*Z!8dbUh>mTU!8dbUmVdULGA$'S6>PldIrFcTIrFcTIrFcTW)fSSk?9nG +KG_,c]cdCMrrj6\kMB*'rVlreDh%cd"c$!l]i'd^rs$#;SF>ViPhl?D\QYNlLgJ3]rrmI1pZEui +`W$-`6CdM#?HKq]#.[p-mtb;le,KF9rS@Rg:4WCr!7:fHJ,K@u@tA.@@g(ZghLtq&h?9>Kn!m.'~> +nc'[/o^1euf[7jZ]X=o?Tph1)LOa\mDeW`h>#nQu9hYIX*D9+';c[(/A8,t(H@Ud1Pa\;DY-kp[ +b0el!k3^^ts#]NHUAs+1!<<'!g].<.!<<'!g].<@D1D5$BB?orC$GYID=.@QD=.@QD=.A'=.\O: +>s/.A(q/nI+9;3:#5]B[kMK9*rVm;nD1DTcrIn+a]MXOap\tH/>FOsB.$i]h#.e$-m>#&ki;Wt' +61a7fQGrk.F[;J`LL83 +nc'[/o^1euf[7jZ]X=o?Tph1)LOa\mDeW`h>#nQu9hYIX*D9+';c[(/A8,t(H@Ud1Pa\;DY-kp[ +b0el!k3^Oos#]ZLV>oC4!<<'!hZ*W4!<<'!hZ*WDDh%M*BBI#sC[1qLDsmXTDsmXTDsmY([\i>+G&;?HM.*"`m$) +k:)Y7rs!O2SF;8?ahIcZ\QYNlLgJ3PrrD?_ecE0NqZ$VGp]'a`jSo40_Z0[dqu?Eko)H]0o)A]> +bl@`nqu6YGqZ$VGp&BRCJH47$"P3V(F8tsb"I&oLPY-H~> +ZMsp]r;Sg:&-+Ge!<<'!hZ*W4!<<'!hZ*WDDh%f#5n$M$-%l5IDsmXTDsmXTDsmZ"UbN,\9'?6S +KGX\DV#^8i"kH!?#ZBjarrVV,J,]HOg1q66GN/>r"oGDZ#RGM,rrr;%2$c@UhuKn!m.'~> +nGb]HnEJoceBQ"J\?W*1SsPRtKn"AiDe`il?!16.;c6Ij;cHh%?"%;ZDfg;TKo;(\Su&Kn\A-&. +eD'!InCda>3fXmW1o^QUrrCgRs4[PRrrCgRs6XZQs1Uf0;/%G4s*nnQs*nnQs*nnQs7`UIqFkm!#qlD[a5e>kQ"SZ=5%!BMg +"nuF`$p_D;rrr;$2@Mj^df0=7rS%@a9n<:q!.b-$!2';pg]-#[s7Y1MRK2ZB9n3~> +nGb]HnEJoceBQ"J\?W*1SsPRtKn"AiDe`il?!16.;c6Ij;cHh%?"%;ZDfg;TKo;(\Su&Kn\A-&. +eD'!InC7C93fFUM0s(HUrrCpUs5!bUrrCpUs6afTs1L],:1kl*s*ntTs*ntTs*ntTs7` +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +nGaO)o'>Anf?hXV]X=rAU7@O2MM-J*F`;,/A78eL>5hYF>$PBBA8#duFaAOjMNF-oU8Y9']Y_e; +fA>WUo\4rsJcC?%!:Ba>!P2!V!.b-$!.b-C!!M'VgOfJ!gB +nGaO)o'>Anf?hXV]X=rAU7@O2MM-J*F`;,/A78eL>5hYF>$PBBA8#duFaAOjMNF-oU8Y9']Y_e; +fA>WUo[\TnJcC?%!:'O6!OYON!.b-$!.b-C!!LmQeoUlbec_3;kEJSh~> +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fHIt7TN!7CiMhZ)Gcs7Y:PS,i#J:4N~> +nGaO-p$V&&g=+V8aqV-T$JcC?%!:Ba>!P2!V!.b-t!<7P"!.b-u!!M'VgOfJ!gB +nGaO-p$V&&g=+V8aqUU5tJcC?%!:'O6!OYON!.b-t!<7P"!.b-u!!LmQeoUlbec_3;kEJSh~> +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +n,FC'o'>Anf?h[Y]sk8IV4X3@O,AXBIC"P`t2K)bQ!"I]>VRSA;~> +n,FC'o'>Anf?h[Y]sk8IV4X3@O,AXBIC"P3V(F8tsb"I&oLPY-H~> +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +n,GQMp$_,'gXONi_S!=]Whl>VQ&pr[K7J>pGB@nGEH#oC"P`t2K)bQ!"I]>VRSA;~> +n,GQMp$_,'gXONi_S!=]Whl>VQ&pr[K7J>pGB@nGEH#oC"P3V(F8tsb"I&oLPY-H~> +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fHIt7TN!7CiMhZ)Gcs7Y:PS,i#J:4N~> +mf,EGo'GJqg!\*a^q7"XWhlAXQBI8cL4b#*I!U'`G^4R\I"$X!LPh+TQCFPDWirhB_8a^Jg#(rY +o`+sGJcC<$K)Yi +mf,EGo'GJqg!\*a^q7"XWhlAXQBI8cL4b#*I!U'`G^4R\I"$X!LPh+TQCFPDWirhB_8a^Jg#(rY +o`+sBJcC<$K)Yi9rRM"W8q?tn!7:fHIt7TN!7CiMf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +mf+7*p@.A-h:L&t`PB'mYc=RoSX>\)Nf&XIKS"dSJ/s2mKSG;@NffTpSY;p]YdM'X`QQZ\h;dei +rVuoPJcC<$K)Yi +mf+7*p@.A-h:L&t`PB'mYc=RoSX>\)Nf&XIKS"dSJ/s2mKSG;@NffTpSY;p]YdM'X`QQZ\h;dei +rVuoKJcC<$K)Yi9rRM"W8q?tn!.b-$!2';pf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +mJe+&o^:r>'ip`PB'mYcOatT:;45P)kT\MM_=g)2X6/NK96fR%'_CVl[/2]"l;.d+7"3kjJ6F +rrCf)s+13%rrDHbgB"l[JH16$JH16$TE#,Ls4`/*p=f_=!:-(JJ,~> +mJe+&o^:r>'ip`PB'mYcOatT:;45P)kT\MM_=g)2X6/NK96fR%'_CVl[/2]"l;.d+7"3kjJ6F +rrCW$s+13%rrD?_ecE0NJH16$JH16$TE#,Gs428kp=9A2!9]S=J,~> +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fHIt7TN!7CiMhZ)Gcs7Y:PS,i#J:4N~> +m/It"o'GPugXXZn`PB*oZEC1(U7[sDQ^*btOoCFZOckonQ^OA9U8FrnZF@H]`QQWYgYq>_p&4mj +gO]BUs+:7NmJYc?\kR_UJH4'ts*oS"JH4*u"P`t2K)bQ!"I]>VRSA;~> +m/It"o'GPugXXZn`PB*oZEC1(U7[sDQ^*btOoCFZOckonQ^OA9U8FrnZF@H]`QQWYgYq>_p&4mj +eq*jPs+:7NlM]97[7YuMJH4'ts*oS"JH4*u"P3V(F8tsb"I&oLPY-H~> +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +li.drna#>qgXXZo`kf?u['6X2VPBfUS=>rEQku0[S=ZFLVQ$]%[(*fc`ll`ZgYh5\o)AUf!8E)) +JcC?%!:Ba>!P2!V!.b-$!.b-C!!M'VgOfJ!gB +li.drna#>qgXXZo`kf?u['6X2VPBfUS=>rEQku0[S=ZFLVQ$]%[(*fc`ll`ZgYh5\o)AUf!7l`$ +JcC?%!:'O6!OYON!.b-$!.b-C!!LmQeoUlbec_3;kEJSh~> +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +li/d?p@7J1i7li1bf@W9]=5,NXfA=sUnXQUTV%mMTq\?[W2cu(Za[Q]_oU'Lf%]-Flgk#PrrCf) +s+13%rrDHbgB"l[JH16$JH16$TE#,Ls4`/*p=f_=!:-(JJ,~> +li/d?p@7J1i7li1bf@W9]=5,NXfA=sUnXQUTV%mMTq\?[W2cu(Za[Q]_oU'Lf%]-Flgk#PrrCW$ +s+13%rrD?_ecE0NJH16$JH16$TE#,Gs428kp=9A2!9]S=J,~> +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fHIt7TN!7CiMhZ)Gcs7Y:PS,i#J:4N~> +lMh\"p@.D1iS<&5cH4&A^:Le\Z*1:1Wi2lqVuN\0WiN8*ZF%*P^;@k3cI1>"iTBIqr;6KogO]BU +s+:7NmJYc?\kR_UJH4'ts*oS"JH4*u"P`t2K)bQ!"I]>VRSA;~> +lMh\"p@.D1iS<&5cH4&A^:Le\Z*1:1Wi2lqVuN\0WiN8*ZF%*P^;@k3cI1>"iTBIqr;6Koeq*jP +s+:7NlM]97[7YuMJH4'ts*oS"JH4*u"P3V(F8tsb"I&oLPY-H~> +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +l2MLsp@7M4in`8:d*0SM_7mOl\$WKHYl([=Yd(OB\%0,b_8XL>d+$b)iof[ur;-EngO]BUs+:7N +mJYc?\kR_UJH16$JH2>C"P`t2K)bQ!"I]>VRSA;~> +l2MLsp@7M4in`8:d*0SM_7mOl\$WKHYl([=Yd(OB\%0,b_8XL>d+$b)iof[ur;-Eneq*jPs+:7N +lM]97[7YuMJH16$JH2>C"P3V(F8tsb"I&oLPY-H~> +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +kl2@rp[RY7j58VCe'H7Z`P]L*]XkY`r3m?L\[oGf^r+15bg4bigY_&TmIL5OrrCf)s+13%rrDHb +gB"l[JH16$JH16$TE#,Ls4`/*p=f_=!:-(JJ,~> +kl2@rp[RY7j58VCe'H7Z`P]L*]XkY`r3m?L\[oGf^r+15bg4bigY_&TmIL5OrrCW$s+13%rrD?_ +ecE0NJH16$JH16$TE#,Gs428kp=9A2!9]S=J,~> +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fHIt7TN!7CiMhZ)Gcs7Y:PS,i#J:4N~> +k5Qq,oBto*iSE5=e'Q@^aMu08_8*h#^;%Fu_8=.1aNDcSe(37/iT9@mpAXje!8E))JcC?%!:Ba> +!P2!V!.b-t!<7P"!.b-u!!M'VgOfJ!gB +k5Qq,oBto*iSE5=e'Q@^aMu08_8*h#^;%Fu_8=.1aNDcSe(37/iT9@mpAXje!7l`$JcC?%!:'O6 +!OYON!.b-t!<7P"!.b-u!!LmQeoUlbec_3;kEJSh~> +ZMsp]_uBbo!.k0$s2+d9metuC]hX+YJH16$JH2>C"Q'18K`Cc&"IoJ\S5+S~> +jo5tjo^M52jP\kJf@83oc-+8Na2e2!&]_u7b0/&UdF?e%h;RGYm-siErrCfkrrLjSJcC<$_Z'W' +rS%@a9n<:q!.b-$!2';pg]-#[s7Y1MRK2ZB9n3~> +jo5tjo^M52jP\kJf@83oc-+8Na2e2!&]_u7b0/&UdF?e%h;RGYm-siErrCWfrrLsVJcC<$_Z'W$ +rRM"W8q?tn!.b-$!2';pf)O +ZMsp]_uBbo!;HKsej9WJ;WmuXs+gRQ"i3e>/Yr:t;bJH16$JH16$TE#,Os5&G/p>,qB!:QFQJ,~> +jSohkp[[e=kN(UYgY(3/e'ZOgci25icOS=Le(*('gYUoMkNhU0r:U'igVa$lgAq!J"kQ0K$ruKh +rrW.SM>dGX^e5.iV"scb"oGAX$kRR:rrr;$1^l^^hu!P2!V!.b-$!.b-C!!M'VgOfJ!gB +jSohkp[[e=kN(UYgY(3/e'ZOgci25icOS=Le(*('gYUoMkNhU0r:U'if#.Lgh>m +ZMsp]_uBbo!;HL%+`li'>!i8hpWe+3J,]HP`En2RPZG3drs#l+LXs`oPhl?D[8)M.G?W,Hrrm0m +hTd:/`W$-X0o+>(>04MY#.73]eot<>e,KF9rS@Rg:4WCr!7:fHIt7TN!7CiMhZ)Gcs7Y:PS,i#J +:4N~> +ir9Mep@@Y;kiLj_hV?l=f[p&P%G07cgYCZDj5oLjme$DJrrCfkrrLjSq#:g#+Ecl)=[E)fpWe+2 +J,]HP`a=ASP#o*drs#o.LXj]nQJMQF[SVb0G?`5Jrrm6rgWq".`W$-Z1l'V*>KX\[#.@?`eTY6? +e,KF8rS%@a9n<:q!7:fHIt7TN!7CiMg]-#[s7Y1MRK2ZB9n3~> +ir9Mep@@Y;kiLj_hV?l=f[p&P%G07cgYCZDj5oLjme$DJrrCWfrrLsVp\tXFF6DC`6N@)[c?gje +rr32A?A%+![\e:5@DE>05_&"`#jLc68Fkrs!6nLXpZQahIcZ[8)M.G?W,; +rrD?_ecE0NJH16$d/a2rJH16$dJsF$s428kp=9A2!9]S=J,~> +ZMsp]rVloT!<3!Vh>mTU!3IsU+g(eE!3Ism&80SodUNgs;T8\9InE`M3J13<4h:UmqlMje#ZC-k +s+D3h.?ji-rsdfbp](9E!<<)aS6ddsrr31aF5m5kf&HH(#)?gIs8S<[\7B&t;bJH16$JH16$TE#,Os5&G/p>,qB!:QFQJ,~> +i;X5ap@@\>lKIBkio/hQr8%h=i8N\Vk32*tn+6GHrrCgPrrLjSrr4n2!<<'!Xu$8(WrN)!Xu%+2 +A^pb,@1WiE`;fje8QQHcKiJUF\GuU+XuZssR?S$^+rs"O( +iW&N$j7iQXLM,KDs->Ltrs"6khZ(`0RGe)K&AJ2EQ8HS\"q\N*s2no@^AeCmBAW[2AXV$I!:Ba> +!P2!V!.b-$!.b-C!!M'VgOfJ!gB +i;X5ap@@\>lKIBkio/hQr8%h=i8N\Vk32*tn+6GHrrCXKrrLsVrr4n5!<<'!XYBf!XoJD$XYCY( +B@d+2@h9&H`W,sf8lc?\K2N+@\c;^,XZ6dq;XaYcKGX\ +ZMsp]rVloT!<3!Vh>mTU!(,QBHP$5W!(,Q1]g[_NG<*(b??c/DIk@VM=tQ](>''<8XB(kNhF^E? +KG_,c]bgbDrrjR!p[;TPrVm;oDh%fepNK)%$n\.Hq#:TjF*mfas3:o,rs/.:Hi*j*CP1UG#3Q!\ +s8U@M`r?;,!;HBjk%fUars/.:Hi*j*CP1+9!:KgB!PM6Z!.b-$!.b-C!!M0YhLtq&h?9>Kn!m.'~> +hZ"JlqXsCImHa'%kND!ijlY^gkNV9tmI0ZAr9sXcg\q-QgAq6Q2VS63rr>@BqgB%Urr>@BmCYoD +pNJ[*em3Fks*lZ$els](em!4es/oLpiSR3us+VB!n%KbAq#:Qq+G04R@6FMg%..n]s8Vh;78FCG +UA+H_#P3"^q>^K<%,V!ho5f3Qs5e$MiVs,F!;$6jb7)Dj#Nl*Yq>]PlKtmWmo5f3Qs5e$Me,KF8 +rS%@a9n<:q!.b-$!2';pg]-#[s7Y1MRK2ZB9n3~> +hZ"JlqXsCImHa'%kND!ijlY^gkNV9tmI0ZAr9sXcf)>ULh>mQT2VnH6rr>CBs*G=Wrr>CBm_)&E +pNJX*g0T!ts*lW!eljZ(elm.ds/]7kind:"s+D5sn%BP +ZMsp]rVloT!<3!Qh>mTU!58F4h>mTU!58F4mVdUTlK8!/CN&Y@Ir"?LIr"?LIrFcPH[#5as5!bE +(o@63!Nc@rrrBb3!!*A^rVm;oDh%femVaPAhN1L6q#:E`Dh%cd!T!h5rs.t1J,fQ:Dh$aG!T!hU +rrLsV`r?(r!<3!"mVdTirs.t1J,fQ:Dh$79!:KgB!PM6Z!.b-t!<7P"!.b-u!!M0YhLtq&h?9>K +n!m.'~> +g]%QYqtK^QnF,f5rpBaR"n1sOp%nXRrrCgPrrLjSrr4_-!<<'!^An5f!<<'!^An6#D1DNKkPOLX +D=.@QC$GYIC$GYID=.4KGP2"9hZ3*bVZ-SrWW;en!4r.0!=7HYrs\:3J,fQ;D^q45OA2hOrrVY- +J,]HKgAp+1#OcS\s8VJ'J)UD.hZ3ZU!S[UorrM!Wrr3&eD1B>##OcS\s8VJ'J(+DtmJYc?\kR_U +JH4'ts*oS"JH4*u"P`t2K)bQ!"I]>VRSA;~> +g]%QYqtK^QnF,f5rpBaR"n1sOp%nXRrrCXKrrLsVrr4_0!<<'!^&S,h!<<'!^&S-#Dh%fPk54FZ +DsmXTC[1qLC[1qLDsmLLGPD.;h>m!]V>gJqXT8+q!58@3!=7Q\rs\=6J,fQ:D^h(1OA;nPrrVV, +J,]HKh>lF4#OZM[s8VM*J)UD.h>mQT!T!grrrLsVrr3&fDh#P%#OZM[s8VM*J(+DtlM]97[7YuM +JH4'ts*oS"JH4*u"P3V(F8tsb"I&oLPY-H~> +ZMsp]rVloT!<3!Hh>mTU!8dbUh>mTU!8dbUmVdUTeo*9/.<"YSIrFcTIrFcTIrFcCDu0M9ec=;# +rrL[Np\tC6HhZu43W8sdmVdUTs7:;Up]'AkKD>HOpNLu]s8U@MhZ!iTGC05ek%fV.rs%olp](9= +#fclLmf<+Zs5n*L^]+Q5GC05ek%fUurrDKch>t;bJH16$JH16$TE#,Os5&G/p>,qB!:QFQJ,~> +fDc6XrVZQhq"X[^r;HWYrrCgPrrLjSrr4D$!<<'!g].<.!<<'!g].<@D1DT8D_aX/D=.@QD=.@Q +D=.@QD=-e1qZ$]L!8%5M!S7>Hrs&??IIlc14T59gm;7@Qs7()Qp]'>hKD>HOolYQUs8UCRhZ!iR +Faq3rs%choDej:%*&;PlN$PPs6"6Q^]+Q3Faq%rrDHbgB"l[JH16$JH16$TE#,L +s4`/*p=f_=!:-(JJ,~> +fDc6XrVZQhq"X[^r;HWYrrCXKrrLsVrr4D'!<<'!hZ*W4!<<'!hZ*WDDh%f +ZMsp]rVloT!<3!Q]`A*4!8dbUh>mTU!8dbUmVdULGA$(B=Ash'I!:KgB!kh>`JRa7@JRa7@Tjn5^s5&G/p>,qB!:QFQJ,~> +ZMspZrVloQ!<3!Q^&\35!8IPRgAq9R!8IPRm;7@IH"Z7=Ir4TQIr4TQIr4TQIr4TKH$T5cs7ak> +*30#>H^AeCoCZ5<=BpmHM!:Ba>!P2!V!.b-$!.b-C!!M'VgOfJ!gB +ZMspUrVloT!<3!Q]`A*4!8dbUh>mTU!8dbUmVdULGA$(B=Ash'I!:'O6!OYON!.b-$!.b-C!!LmQeoUlbec_3;kEJSh~> +ZMsp]rVntJ!2]_P3<9*Z!8dbUh>mTU!8dbUmVdULGA$'S6>PldIrFcTIrFcTIrFcTW)fSSk?9nG +KG_,c]cdCMrrj6\kMB*'rVlreDh%cd#-h3nhMY:>p\tH.>+G'E.?rZg#.[p-mtb;li;Wt%6MKXl +PfKn!m.'~> +ZMspZrVntF!2BMN2us!Y!8IPRgAq9R!8IPRm;7@GF_BgO6YYfbIr4TQIr4TQIr4TQW`PeRk?U4M +L)@Af^*!FNrs&??ETc4Z62gfbm;7@Prs#c3QJIBo[.jS)\m(Wjk9uYPrs$&=RdGl;b2E:'F[>W^ +.$hjP#'E5#m>#&k^AeDK>FOr7?cpjt!:Ba>!P2!V!.b-t!<7P"!.b-u!!M'VgOfJ!gB +ZMspUrVntJ!2]_P3<9*Z!8dbUh>mTU!8dbUmVdULGA$'S6>PldIrFcTIrFcTIrFcTW)fSSk?9nG +KG_,c]cdCMrrj6\kMB*'rVlreDh%cd#-h3nhMY:>p\tH.>+G'E.?rZg#.[p-mtb;li;Wt%6MKXl +Pf +ZMsp]r;Sg:&-+Ge!<<'!hZ*W4!<<'!hZ*WDDh%f#5n$M$-%l5IDsmXTDsmXTDsmZ"UbN,\9'?6S +KGX\DV#^8i"kH!?#ZBjarrVV,J,]HPqku4T3O/JUrrr;%2$aJ/h#@QTXYgAI\_mDipL=I7F2\(^ +pL?&d4h89+"oGDZ#X,`HrrDKch>t;bJH16$JH16$TE#,Os5&G/p>,qB!:QFQJ,~> +ZMspZr;Sg<'Eg7m!<<'!g].<.!<<'!g].<@D1DT"64Qk,.#%\ND=.@QD=.@QD=.AtUbW8_:$;QV +Jf"JAV?$Aj"kQ0G$rQ3drrVS)J,]HPq5,hQ3jAMUrrr;$2@L":h#@QTX>UJP]&3MjpL+I=Fi=:` +pL,rg5e=Z/"oGAZ$p_DPrrDHbgB"l[JH16$JH16$TE#,Ls4`/*p=f_=!:-(JJ,~> +ZMspUr;Sg:&-+Ge!<<'!hZ*W4!<<'!hZ*WDDh%f#5n$M$-%l5IDsmXTDsmXTDsmZ"UbN,\9'?6S +KGX\DV#^8i"kH!?#ZBjarrVV,J,]HPqku4T3O/JUrrr;%2$aJ/h#@QTXYgAI\_mDipL=I7F2\(^ +pL?&d4h89+"oGDZ#X,`HrrD?_ecE0NJH16$JH16$TE#,Gs428kp=9A2!9]S=J,~> +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!.b-$!2';pf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7X,/S,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)GcJ+ZP=!:QFQJ,~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!.b-$!2';pf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiLhZ)GcoR?rCn!m.'~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fH5Cifc!7CiLf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Yj`blI4&:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!.b-$!2';pf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';rhZ)Gcs5)W/Du\YsS,i#J:4N~> +ZMspZJcC<$K)YinRK2ZB9n3~> +ZMspUJcC<$K)Yi9rRM"W8q?tn!.b-$!2';rf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiPhZ)Gcs5)UNqu?t?hVOb&n!m.'~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fH5Cifc!7CiPf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs8LjTDuK_:DuSSrS,i#J:4N~> +ZMspZJcC<$K)YijM8D>r8mRK2ZB9n3~> +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs8CdUDZF_Sh?9>Kn!m.'~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!.b-$!2';pf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs8:^RDuAGpS,i#J:4N~> +ZMspZJcC<$K)Yi`,kRK2ZB9n3~> +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!.b-$!2';phZ)Gcs7Y:PS,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!.b-$!2';pf)O +ZMsp]JcC<$K)Yi=rS@Rg:4WCr!7:fH5Cifc!7CiMhZ)Gcs7X,/S,i#J:4N~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM"W8q?tn!7:fH5Cifc!7CiMf)O +ZMsp]JcC<$K)Yi=rS@Uh:,d`BJT&tTs/pokJT&tT"Q'18KR`Yi!s%e[:4N~> +ZMspZJcC<$K)YiJSihPs/gihJSihP"P`t2Jq*Gf!s%YU9n3~> +ZMspUJcC<$K)Yi9rRM%X8i;08JSW\Ls/LWcJSW\L"P3V(F+ +ZMsp]JcC<$K)Yi=rS@UhNOOK,JaS*WJaT3!!rq,fo619)S5+S~> +ZMspZJcC<$K)Yi +ZMspUJcC<$K)Yi9rRM%XM7%p"Ja.gOJa/on!rpiRo5aurPY-H~> +ZMsp]JaS*WK'iX$rS@S"eUd.>mXaeWm[ihHhXT_/S5+S~> +ZMspZJaJ$UK'`R!rS%@qdt-n;m=FYUm@N\Fg[X;)RSA;~> +ZMspUJa.gOK'E?orRM"gc[kA4l@J5OlCR8@f(%StPY-H~> +ZN#L4J_kt7J_kt7J_kt7L>Ds$:4N~> +ZN#L1J_Pb1J_Pb1J_Pb1L>)`t9n3~> +ZN#L,J_#D'J_#D'J_#D'L=QBi8q6~> +ZMsp]c0bV8Dkuh$Y(?$!mJq1amf0Gemf0G@mXaeWmXafNmddUak+#1s~> +ZMspZc0YP6D5?S!Xb#ltm/V"^mJj;cmJj;>m=FYUm=FZLmIIC]j-i\m~> +ZMspUc0>>0CS^7qWe'Hnl2YVYlMml]lMml8l@J5Ol@J6FlLL_PhNUW_~> +ZMss^s3L*8hIZcfVX=QCAq0N*mc*X9blIe(mK;p4;X_mXaeWm`t5#K_3(_6LUF> +J,~> +ZMss[s3L'7gh$QdVX4K@Aq0N*mGdL7bQ.\'m/u-UJ,cJ7"5IL +ZMssVs3Ks4g113`VWn9;A:O6&lJh(1aT2A$l3#aNJ,cJ4"57:8oRZ:Yl@J5OlH\YpF7d!F3TlW! +J,~> +ZMss^s3KO(]hSS-VVVEg:4M\_hW!AnS,iTLh?1GdJ,cJ("2.HZmXa5GhLXO7hTjsYK`Cc6"O-r8 +S5+S~> +ZMss[s3KF%\kN2)VV;3a9n2P]gZ$rhRK3BJgB5#]J,cJ%"1h3Vm=F#CgO\+1gWnOSK)bQ3"O$l3 +RSA;~> +ZMssVs3K6u[7UH!VUbjW8q6,Wf&G6^PQ:aDecW +ZMss^s3KO(]hSS-VVVEg:4M\_hW!AnS,iTLh?1GdJ,cJ("2.HZmXa5GhLXO7hTjsYK`Cc&"IoJ\ +S5+S~> +ZMss[s3KF%\kN2)VV;3a9n2P]gZ$rhRK3BJgB5#]J,cJ%"1h3Vm=F#CgO\+1gWnOSK)bQ!"I]>V +RSA;~> +ZMssVs3K6u[7UH!VUbjW8q6,Wf&G6^PQ:aDecW +ZMss^s3KO(]hSS-VVVEg:4M\_hW!AnS,iTLh?1GdJ,cJ("2.HZmXa5GhLXO7hTjsYK`Cc&"IoJ\ +S5+S~> +ZMss[s3KF%\kN2)VV;3a9n2P]gZ$rhRK3BJgB5#]J,cJ%"1h3Vm=F#CgO\+1gWnOSK)bQ!"I]>V +RSA;~> +ZMssVs3K6u[7UH!VUbjW8q6,Wf&G6^PQ:aDecW +ZMss^s3KO(]hSS-VVVEg:4M\_hW!AnS,iTLh?1GdJ,cJ("2.HZmXa5GhLXO7hTjsYK`Cu,!,qel +"IoJ\S5+S~> +ZMss[s3KF%\kN2)VV;3a9n2P]gZ$rhRK3BJgB5#]J,cJ%"1h3Vm=F#CgO\+1gWnOSK)bc'!,_Yg +"I]>VRSA;~> +ZMssVs3K6u[7UH!VUbjW8q6,Wf&G6^PQ:aDecW +ZMss^s3KO(]hSS-VVVEg:4M\_hW!AnS,iTLh?1GdJ,cJ("2.HZmXa5GhLXO7hTjsYK`D#-!cJ'W +rS@[I!:QFQJ,~> +ZMss[s3KF%\kN2)VV;3a9n2P]gZ$rhRK3BJgB5#]J,cJ%"1h3Vm=F#CgO\+1gWnOSK)bf(!c7pS +rS%ID!:-(JJ,~> +ZMssVs3K6u[7UH!VUbjW8q6,Wf&G6^PQ:aDecW +ZMss^s3KO(]hSS-VVVEg:4M\_hW!AnS,iTLh?1GdJ,cJ("2.HZmXa5GhLXO7hTjsYK`D&.!,qi: +!,qkn"IoJ\S5+S~> +ZMss[s3KF%\kN2)VV;3a9n2P]gZ$rhRK3BJgB5#]J,cJ%"1h3Vm=F#CgO\+1gWnOSK)bi)!,_]8 +!,__i"I]>VRSA;~> +ZMssVs3K6u[7UH!VUbjW8q6,Wf&G6^PQ:aDecW +ZMss^s3KO(]hSS-VVVEg:4M\_hW!AnS,iTLh?1GdJ,cJ("2.HZmXa5GhLXO7hTjs\K`BocDu9S? +Dr0?@!:QFQJ,~> +ZMss[s3KF%\kN2)VV;3a9n2P]gZ$rhRK3BJgB5#]J,cJ%"1h3Vm=F#CgO\+1gWnOVK)aT[D>XA= +D;3g6!:-(JJ,~> +ZMssVs3K6u[7UH!VUbjW8q6,Wf&G6^PQ:aDecW +ZMss^s3KO(]hSS-VVVEg:4M\_hW!AnS,iTLh?1GdJ,cJ("2.HZmXa5GhLXO7hTjs[K`Bocq>gQP +"IoJ\S5+S~> +ZMss[s3KF%\kN2)VV;3a9n2P]gZ$rhRK3BJgB5#]J,cJ%"1h3Vm=F#CgO\+1gWnOUK)aT[q>gQM +"I]>VRSA;~> +ZMssVs3K6u[7UH!VUbjW8q6,Wf&G6^PQ:aDecWgQH +"I&oLPY-H~> +ZMss^s81XV`_$dB(j#]5qu?p]^#%Ua?HrK+#gH[ohJZ`FhJ`An!sBp'df&#)]hSS-r8.;N!%G51 +"2.HZmY9R!X?HF\"&>q(Ds?*[S,iTeh>hKm!!jQkc/*AFc/*Cr<0p>,qB +!:QFQJ,~> +ZMss[s81OS^.A_5*-(u4r;['*34dl!D-tNdgBc#1D;/o7bhI"6!!jM;"=A]DrS%Fc9n33@g\UsN +/E>IdD-tNEgAq;gq:c"_9n2R/gAl*g!!*jfi7n21!XM6/jP'a[^&[f[!PSPbgO\+1gTfK6K)bQ! +"I]>VRSA;~> +ZMssVs81@N]h/P1(j#]-qu?p][FWoE?HN2t#g$Cgen\U6enb6b!sBp"ao0cm[7UH!r7:`F!%G5) +"1:aNlA!jjW'1"X"&>q(C['COPQ:a]ec9La!!jQg`R\g2ajt8,!! +ZMss^s8:^g`Xl=]`f(IEhVJ77cHa*i.Gip"?HrK+&C"O"hJZ`FhJZH.b.2jNDq=pE"2.G0s8:^S +!3Q1EkhZAbCXUMb"2.HZmY9R+g="9[]umm4 +/T95>h?1GdJ,fH'!*9J +h?X+`cGltY&C/khh?)9RC\QZg&BnhLXOgh>r<0p>,qB!:QFQJ,~> +ZMss[s8:Ud`tV[^_i>:BgY2_.a2bVW.,3Kn?d/N(&BnKugML%BB^A6Lc\kR\ArS%Bj@mk1]sdb@UG&";&<%b@Q7ZJ_Pb1Z.f;2s7Y1MRK2ZB9n3~> +ZMssVs8:F_]b")D]o3MDQ.G!'c?HN2t&BS6oen\U6en\0k`jpFJCX2q1"1:`$s8:FK +!2]V5kgffVB$&?L"1:aNlA!jtd`TS;;Id_dcH_ZEl.,p#!<;uJ'l-^T`k?^VJ(ACAajt6*[Dob( +/SiZ2ecW)sA +ed)8P`l>,Q&B<#XecO.:A,"OW&A?Z7@tE3^eq)DWecBjlp=9A2!9]S=J,~> +ZMss^s8:aR;Z5GUN!S:s!58Bg#0m4oeo+m>rn\",?>n\=?Hq>F?HrK+!fKgHrn[aj:&t=lh>mVi +j5'iU?HpdP"2.HZmXs?tc!G>Kh?'2Is8:^aDdL]FhUZ=Peo+m>hJZ`Frn[^7:8%UC"2.HZs8:^S +!57s[!mViqq_>R^&?dh3Fe`8h>mViJ_kt7 +J_mQd!JLLHh?9>Kn!m.'~> +ZMss[s8:UP;,.#D"cmrLD-tN4gB5#]J*hmC!mG"gj4aX(!<;uO%rG[tgY:1gA'o.#bhHuAbl6>uMbKZ?gB5#]J,fH$ +! +ZMssVs8:FK;G-o="cN`can(%PU?HM6Y"TZ6eec +ZMss^s8:^S#dsTe"NKn\=?Hq>F?HrK+!kDKarn[aj:&t=lh>mVi +qq_??>5m@o.n\=!.,O_@tYBn +A,b&hLeS%g6aT##YND!h>ich!#UsW(]Y,;r8%GS^&?eQPTbP5 +g6aT#.?)j7\QTOQ:9iZO4U<$Sc/*A2<_8gp!8bBhPSaBecJBB<&=gE6hVQOM#RFVbhVJ6s.231k +\UiUghLXP#h>r<0p>,qB!:QFQJ,~> +ZMss[s8:UP#d4-\"kUZHgAntegBPh$_p<1,bl6?(b[55AD-tNAD-tNdgB+lhIK%t'\kN2)r7_5P +^AHXc79TWE(+=1Wdo?e^8^#T;D+e:S%TTr_$j]kEceAQ59n2P_gB,T+D>r9!b[55AD#eu$"D*/r +"C_;a"k-H[#YE5CHM*"1h3Vs8:UP!5AR +L*Qm=P5X,;\kR\ArS%Bj?d/H&)j!jagY79QVUq%d$j]GJgY2^o/-HfQgDBo%Rf(n@!5AEd5aEO@ +gXhZD$7Le\gU(#n$r=+^U-&c(Hd(5]8QA6J^&Zp%!0.`r7EaF&!"bU5!8H8O8I,`-fA#$[Q7Nj_ + +ZMssVs8:FK#d+$U"MNC;!58B_#0$YVa_P5)rmhFu?>J,-?HLc6?HN2t!ju'Qrmh1Z8c\nhec>ca +qpkd7;Z>5_.J,-!.,O_@s\ae +A,b$_c;FoR9#gh\!gE]br72Bo?HLdLc=:2&CL5-6CL5-^ecN6[GQ-.q[7YrKr71lK^&$:r!0eo4 +(mNSg!"bCG!0ecepgGjdZ?$d#YN+fec:p`!#UsO(]Y,3r71lK^&?MIPSeVq +dZ?$d.>Z:#Yu1u99!-[73=$UOajt6"<^iCh!7ng`N">tU`mtt,&Rf%'hc.231k +[=Qn[eq)DhecBjlp=9A2!9]S=J,~> +ZMss^s8CdU`W4Tch?s=kf%.i5+l;'o?HrK+$I)mqhJZ`FhJZ`Frn[_$?>oi""2.G0s8:^S!4DaU +)r?ejhVPpGcJDGk`l:#!Mr&@lJ't,QF5ChAc-G/J]hX(FKAH[S?>oi"(fh?1GdJ,fH'!esK3g`l:#!hVJ5m`nkJ:hVJ7Gqq_>R^&?eQ]`r<0opc*an!m.'~> +ZMss[s8C[R_Z80]gC""ee'l3/,hq'j?d/N($HujogML +ZMssVs8CLM]`?@RedDJ[cHa*q+kG4[?HN2t$HZUien\U6en\U6rmh.i>&4,g"1:`$s8:FK!3Q1E +)qL5bf%.5/`n"$[]tHEdLXp5XGKHL[7Yr6K@U+G?>KPk(NeK@XfP@^3b_!7ng`3PDgT6Is]f^"0jsf%*/J]sT;!f%'gU]ucp- +Dh6Jjeq)DhecBjloooOYkEJSh~> +ZMss^s8CdU`W4TchY[?X0tlH#DdL]fh?qM6Dr,D=c/*AFc2QQ$c!G>fh?1Gd!<;uRqZ%U>hUVt/ +hJ5I7ec="PhSoP.eo+I&hT]AFhJZ`FcJAZ::4M\ah?(r.DuST0c!G>FDZI3%h>jt?h>k7Gec="P +hSoQ@rn[^H!<;uR!,qf9$5e!3hJZ`FhJZ`Frn[_$?>oi""2.HZs8:aN!"`>jh>jD/hRrpGh>k7G +h>j)Fh?A7,\XZ]fr8%Kn?HrH*'&%,6f&"hbB@bo>V;C(B!8\QWrn\*K!8cQ4^#%TGV;C(B!<(aV +]hX(FrS@Nmqu@]gSD%m^_5(+ng1t?PhVQ6PcJAPthVPpGhVJ7Gqq_>R^&?dt]`>Z/0Co^#%#I!8bBh^#%VR!7p!,^#$F&hUV\?hVOb>hVJ7/hVQL`Dh6brhLXP# +h>i6#!!;VZ:4N~> +ZMss[s8C[R_Z80]g\_$U0Y6#nD-tNdgBu/3D;/o7bhHuAbl6>uaB```gB5#]!<;uOqZ%U=gXck. +gM'+2df@SGgV*Z#eSS0tgWEf=gMLr9+b[55AD#glrgAnP6gAntBdf@SG +gV*[6rn@LC!<;uO!,_Z7$5dp.gMLgAntB +gAmiEgBDk(\=?Wdr7_9i?d/K''%^i/e(`)RB@Y`:U>+G6!8AKZrn@mJ!8H6.^>%BBU>+G6!<(XS +\kR\ArS%gY2_BqqD,O^AZdr[KLO#Bk>cJb[5#/ +Bh,?*f.M>O]%bW0YhO"j]\CZE!8G3f]%buF!7T[&^>$@%gX?/6gY8/6gY2_&gY9kVD1UGmgO\+r +gAlis!!;JT9n3~> +ZMssVs8CLM]`?@Rf),LP.CIaXCL5-^edBN*CY!9-ajt66ao9im`EI'VecW-1cf#:g.en\U6cIN**8q6,YecNs"C];m$a^/c6CB1Kfec;i'ec&4,g"1:aNs8:IF!"`&Zec;5kf!PM/ececg7u[?t!Zr71pb?HN/s'%1E&cIU*DA'WX*SCZ9!!7i!OrmhOC!7o^$^"1a7SCZ9!!<(IN +[7Yr6rRLsaqu@]cPgX>F\XZQVdV!(8f%.M7`mt-[f%.5/f%'i7qpkcJ^&?Ll[/b!jB4K6J8q6~> +ZMss^s8:^S#d+'^"kG-)h>k7ch?%V%c2QQ,c!G>FDdL]FDdL]fh?(5kJ,\:,]hSS-r8%GS^&$Ri +>#O2oSDM9_!"<2YDdL]Fc!G>FDdL]6h?1GdJ*qsG!mP%irn\42?>n\=!577G!577G!577/qZ$W0 +rn[^H!<;uR!cMDiqq_Nq?Hq>F?HrK+!kDKarn[aj:4NmViqVDY]^#%VZ!8cQ4^#%TG^&Hjl +TN7;$>/C-n!cMDirS@S'A,H<7?Hq=,^#%VZ!8[^hrn\*t!8cQ4^#%TG^#%VZ!<(aV]hX(FrS@To +?HrB(#Fnh^HWn,2A,?66^"rb&hVPpGhVJ7Gqq_>R^&?djPTbP)A,H<6?Hp<-.3N69DdL]dh?e+, +c/-^Y!8bBhrS@k]`ng^/hRrpG`W6"p$G?Grh>k7GhT]AFJ_kt7J_n9#!/1"E!s%e[:4N~> +ZMss[s8C[Rf*JPXgBH%WVV;*bqV)'g?-<0$$HcXkgMLCHM*"1h2,s8:UP!5Ar9$b[55AD#h<)gAntBgAntB_tsE6 +])LFcRK3BcgB)5!bkp-!D-tNAD-tNdgB+lhIK%t'\kR\Sr7_5P^A?Rn!4pq>^&ZpB!5@4B!4r0a +"e\qYF'HHXgB)5!bl-8scXH_O$XLp0!4pq>^&Zmf])LFn[K,(:!5@4B!4pq>^&\,d"1h3VmJYc@ +D-tNagBX*GR!DiGcXH\N$bG'm]%buR!8H6.^AHXc!5AEd"I:0YcXH_O$=1fgE@;r@\nt`qr7_Np +?-:pG^&Zp%!<1^\f)WP>!5@4%!8GBkrn@dk!8H6.]%bu`?YtAKJ_Pb1_V5'Bo`#!nl^COu~> +ZMssVs8:FK#d+'V"jSQnecJ,-!56t7!56t7!56ssqZ$W( +rmh.8!<;uJ!c),aqpkse?HLc6?HN2t!ju'Qrmh1Z8q6m=ec>caqUQ)U[FWpB!7o^$^"1a7[Jn_\ +S5t_m>.sjb!c),arRM"k?Mjd2>/f%m[FWpB!7h.XrmhOd!7o^$^"1a7[FWpB!<(IN[7Yr6rRM$c +?HN)q#F&,NG?VQ!?Ma^1[FP>cf%.M7f%'i7qpkcJ^&?LbPSeVd?Mjd1>/e$j.3N*-CKe^Ted6+q +`R`/E!7ng`rRM;U^"*1sf"D(7]`A&_$FKlbec<,/f#_66J_#D'J_%]h!-@f4!s%MK8q6~> +ZMss^s8:aR9)[TMMu_/[!586c!d@harn\!qF?HrK+!fKgHrn[aj:&t=lh>mViqVE*l +&>^6#hUV\XhVQhkakgrBc/-]ODr,D=c-G/J]hX(FKAH[S?>oi"(F?HrK+!fKgHrn[aj:4NmViqVDY]^#%VZ!8cQ4^#%TG +^&Hjka^._CHi)Y$DdL]eh@eOXCYIY!`Q?un!577G]`?n+PlBMKPQ9Rs!577G!577G]`A#f"2.HZ +metuDDdL]chAO:=>$cR4g1tKXhVQhkhVJ7GhVPpGhVJ7Gqq_>R^&?eQ3Fe`9CMVY\g;V1f_8Y9E +:1nKG^#%>-T\aTnc/-^Y!8bBh^#%VZ!7p!,^#$F&hUV\?hVOb6hVJ7GhVQL`Dh6brhLXP#h>r<0 +p>,qB!:QFQJ,~> +ZMss[s8:XO:&WfMNW@8W!5A%DV!8H6.^>%BB^ +Acji`a);=H2H=tD-tNcgCi+PB@Ybg_T(?b!5@4B^&Zn%QN#VJQ2o[o!5@4B!5@4B^&\,d"1h3Vm +JYc@D-tNagDRe0='^++f4emJgY:2_gY2_BgY9FBgY2_BqqD,O^AZeO4C+K/BkPuNf>>PZ^;AX99 +kA6G]\ChsSD.mfbhL@U!8G3f]%buJ!7T[&^>$@%gX?/6gY8/2gY2_BgY9qZD1UGmgO\+rgAup+p +=f_=!:-(JJ,~> +ZMssVs8:IJ9)[caqUQO\ +#b2a_f$49@f%/-K_:EC.ak"R?CY!9-aj/H>[7Yr6K@U+G?>KPk(caqUQ)U^"1cJ!7o^$^"1a7 +^&HRc_-0H/GPfqmCL5-]ee6DDB@>AZ]tr:N!56t7]`?UpPlB5CPQ9:c!56t7!56t7]`A#^"1:aN +lM]98CL5-[eeu/%;He:qdV!4@f%/-Kf%'i7f%.M7f%'i7qpkcJ^&?MI3EhfuB4oZDd_3KF\\6S% +8n2X;\^o&fR+?%Zak"SI!7ng`[FWp:!7'-q^"0jsf$49'f%-?&f%'i7f%.rLCOt&feq)DhecBjl +p=9A2!9]S=J,~> +ZMss^s8:^]`Xl%U`f(15hVJ7GqVE!d796P1WD5m`hJZ`FhJZH.b.W9VDq=pE"2.G0s8:^S!3Q1E +rn[\n+`%)6s%3jBc;HBPhJZ`FhT]AFhJZT>cJAZ::4M\ah?(r.DuST/c!G>FDZIK-h>k7Gh>k7G +hF^D[c;HCqh?'2Is8:^TDdL]ch@+=/c/*A>]un$h?1GdJ,fH'!k7GhRrpGEu;%1XW[]$h>jt?h>k7GhRrpf +h?1GdJ+!9l!cM,QrQZA5hVNT!7\nntPY-bOc;HCRh>k7GhRrpGh>jt\hF7LIcGm7a&C/k`PY-bS +O?nZUDcXQZ8o((lF2a)$LYcrUc/-^Y!8bBh3QA`e9&AD)[GJRshVLRc`kr<0p>,qB!:QFQJ,~> +ZMss[s8:UZ_[o\J_i>.2gY2_BqV)d]6;shuUeaCWgMLr9*b[55AD#h<)gAntBgAntB +gIY,Wa&+\lgB*fDs8:UQD-tNagC.q+bhHu7\Au1//o/l6gB5#]J,fH$!W&BE2QP"UVM +N'` +ZMssVs8:FU]b!f<]o35#f%'i7qUQFT5uOMlUeX4Oen\U6en\0k`jpFJCX2q1"1:`$s8:FK!2]V5 +rmh,f+`%).&jf4(`_%t#ajt6*[Dob(/SiZ2ecW`[&j\N(/H9LcpCAec`N(/H5`_%u:ec,Q&B<#HN(/H9 +LcpC=CJqRB7VA)XDnUriK@XgEak"SI!7ng`0tjtL6Is]f[FW"kf%*/J]sT;!f%'i7f%.rLCOt&f +eq)DhecBjlp=9A2!9]S=J,~> +ZMss^s81XV]g;u)(j#]5!FrW!$'-%F2^]MhVN#J +`r=g.ek-2R6H7I1DdL]Fc!G>FM]>L*ce\c;:4M\ah?(r.DuST/c!G>FDZIK-h>k7Gh>k7GhUY:) +#XZQ0h?'2Is8:^TDdL]ch?7b'c/*C8V_A('5jg5m`h#XZPfh>k7GhRrpGhDkS+r8.AP!XM-+rn]ckO=:G]K[S_b:'_8/\_`u> +#TS?BhJZ`FhT`_6hRro9&-,>JhVL6mS@npZhTe_!#[5g9h>k7GhT]AFJ_kt7J_n9#!JLLHh?9>K +n!m.'~> +ZMss[s81OS^-rG1*-(u4!gB5#]!<;uOq>^]JgY6EB +_uAC(cV4ZN6-%C-D-tNAb[55AM&oI(ceAQ59n2P_gB,T+D>r9*b[55AD#h<)gAntBgAntBgWrS" +#XQN0gB*fDs8:UQD-tNagB;A#bhI"6!!])!5AEd +"/7VZbkp,pD>XAZA^f1i.L7;QdG*CU^>%DV!8H7N(]Z+I!8H6f"Ie'F^>%DV!<(XS\kR\ArS%)r7h/M!XM6/rnBQcN[b;[K[JPY9aVA3[b7*+ +#TeE?gML +ZMssVs81@N]g;u)(i0-%!^]Ef%+=2 +^&HRrc:S?J6G:LlCL5-6a^/c6K,@4gcdi3+8q6,YecNs"C];m#a^/c6CB2'!ec9s +"<;d1en\U6f#bT*f"D'1&-,&:f%)PUPe@(Jf#C#^#[5O)ec +ZMss^s3KO(]hSS-VVVEg:4M\_hW!AnS,iTLh?1GdJ,eKa!j#FXina^+[Jo"`S/CeQ"2.HZm]>7G +]` +ZMss[s3KF%\kN2)VV;3a9n2P]gZ$rhRK3BJgB5#]J,eK^!if:VinFL&[f5"^Sf%"P"1h3VmB#%C +^&W`=J_Pb1V:u$&s7Y1MRK2ZB9n3~> +ZMssVs3K6u[7UH!VUbjW8q6,Wf&G6^PQ:aDecW +ZMss^s3KO(]hSS-VVVEg:4M\_hW!AnS,iTLh?1GdJ,eNb"4hZs[Gp$ICFQsR[4,VUh?1GdJ*sDp +!PJJahLXO7hPK'0K`Cc&"IoJ\S5+S~> +ZMss[s3KF%\kN2)VV;3a9n2P]gZ$rhRK3BJgB5#]J,eQ`"PM+B +ZMssVs3K6u[7UH!VUbjW8q6,Wf&G6^PQ:aDecW +ZMss^s3KO(]hSS-VVVEg:4M\_hW!AnS,iTLh?1GdJ,eQc"3":ePi1C%@h8VWA(L2,"2.HZm]>7G +c;K5MJ_kt7V;;6+s7Y:PS,i#J:4N~> +ZMss[s3KF%\kN2)VV;3a9n2P]gZ$rhRK3BJgB5#]J,eQ`"Mdq[NnNFH#1m8t!#/knn_4/W9n2Q3 +gB#JtJ_Pb1J_R$U!J:@FgB +ZMssVs3K6u[7UH!VUbjW8q6,Wf&G6^PQ:aDecW +ZMss^s3KO(]hSS-VVVEg:4M\_hW!AnS,iTLh?1GdJ,cJ("2.HZmXa5GhLXO7hTjsYK`Cc&"IoJ\ +S5+S~> +ZMss[s3KF%\kN2)VV;3a9n2P]gZ$rhRK3BJgB5#]J,cJ%"1h3Vm=F#CgO\+1gWnOSK)bQ!"I]>V +RSA;~> +ZMssVs3K6u[7UH!VUbjW8q6,Wf&G6^PQ:aDecW +ZMss^s3KO(]hSS-VVVEg:4M\_hW!AnS,iTLh?1GdJ,cJ("2.HZmXa5GhLXO7hTjsYK`Cc&"IoJ\ +S5+S~> +ZMss[s3KF%\kN2)VV;3a9n2P]gZ$rhRK3BJgB5#]J,cJ%"1h3Vm=F#CgO\+1gWnOSK)bQ!"I]>V +RSA;~> +ZMssVs3K6u[7UH!VUbjW8q6,Wf&G6^PQ:aDecW +ZMss^s3KO(]hSS-VVVEg:4M\_hW!AnS,iTLh?1GdJ,cJ("2.HZmXa5GhLXO7hTjsYK`Cc&"IoJ\ +S5+S~> +ZMss[s3KF%\kN2)VV;3a9n2P]gZ$rhRK3BJgB5#]J,cJ%"1h3Vm=F#CgO\+1gWnOSK)bQ!"I]>V +RSA;~> +ZMssVs3K6u[7UH!VUbjW8q6,Wf&G6^PQ:aDecW +ZMss^s3KO(]hSS-VVVEg:4M\_hW!AnS,iTLh?1GdJ,cJ("2.HZmXa5GhLXO7hTjsYK`Cc&"IoJ\ +S5+S~> +ZMss[s3KF%\kN2)VV;3a9n2P]gZ$rhRK3BJgB5#]J,cJ%"1h3Vm=F#CgO\+1gWnOSK)bQ!"I]>V +RSA;~> +ZMssVs3K6u[7UH!VUbjW8q6,Wf&G6^PQ:aDecW +ZMss^s3@,9:&t +ZMss[s3@&79`Y3iRK?35b^l5ARK3EgigKpCJ,cF8!_n0gJXV.YJXV.YcC+ccs7Y1MRK2ZB9n3~> +ZMssVs3?i18c\mfPQFI,aao]8PQ:daifjL:J,cF2!_RsaJWt_MJWt_McBJ?Ns7Y"HPQ9m28q6~> +ZMsp]JH16$JH16$JH16$JH2#:!JLLHh?9>Kn!m.'~> +ZMspZJH16$JH16$JH16$JH2#:!J:@FgB +ZMspUJH16$JH16$JH16$JH2#:!H\;7ec_3;kEJSh~> +ZMsp]JcC<$JcC<$JcC<$JcD):!JLLHh?9>Kn!m.'~> +ZMspZJcC<$JcC<$JcC<$JcD):!J:@FgB +ZMspUJcC<$JcC<$JcC<$JcD):!H\;7ec_3;kEJSh~> +ZMsp]JcC<$JcC<$JcC<$JcD):!JLLHh?9>Kn!m.'~> +ZMspZJcC<$JcC<$JcC<$JcD):!J:@FgB +ZMspUJcC<$JcC<$JcC<$JcD):!H\;7ec_3;kEJSh~> +ZMsp]JcC<$JcC<$JcC<$JcD):!JLLHh?9>Kn!m.'~> +ZMspZJcC<$JcC<$JcC<$JcD):!J:@FgB +ZMspUJcC<$JcC<$JcC<$JcD):!H\;7ec_3;kEJSh~> +ZMsp]JcC<$JcC<$JcC<$JcD):!JLLHh?9>Kn!m.'~> +ZMspZJcC<$JcC<$JcC<$JcD):!J:@FgB +ZMspUJcC<$JcC<$JcC<$JcD):!H\;7ec_3;kEJSh~> +ZMsp]JcC<$JcC<$JcC<$JcD):!JLLHh?9>Kn!m.'~> +ZMspZJcC<$JcC<$JcC<$JcD):!J:@FgB +ZMspUJcC<$JcC<$JcC<$JcD):!H\;7ec_3;kEJSh~> +ZMsp]JcC<$JcC<$JcC<$JcD):!JLLHh?9>Kn!m.'~> +ZMspZJcC<$JcC<$JcC<$JcD):!J:@FgB +ZMspUJcC<$JcC<$JcC<$JcD):!H\;7ec_3;kEJSh~> +ZMsp]JcC<$JcC<$JcC<$JcD):!JLLGS-&c[S5+S~> +ZMspZJcC<$JcC<$JcC<$JcD):!J:@ERKEQURSA;~> +ZMspUJcC<$JcC<$JcC<$JcD):!H\;6PQLpKPY-H~> +ZMsp]UAkCHDsi*nmXbChs/5l!W%ir%f`),)Dsi*nmeZqbmVdUQrrVV,J'7inmVdUQrrVWF!/(:Q +c4G&Js+14+rr@`D!!;VZ:4N~> +ZMspZUAkCHD=)gim=G:gs/5l!W%`euf`),)D=)gimJ?ham;7@NrrVS)J'7inm;7@NrrVTB!/(:Q +b7JfIs+14+rr@ZB!!;JT9n3~> +ZMspUUAkCHDsi*nmXbChs/5l!W%ir%f`),)Dsi*nmeZqbmVdUQrrVV,J'7inmVdUQrrVWF!/(:Q +c4G&Js+14+rr@-3!!;>J8q6~> +ZMsp]r;Qoo@gEWeXT&HRDsi*nmcaZO&DlOKs+gUR%K!7b$phH(s7bt[#S;(Vrs[IF!!lKks8VOc +#S;(Vrr@EE!"_0$s8To-$n\:Ps7`0<$phH'rsZ_9$n\:Ps8/p.#S;(Vrrg=j!%GV\rrpt?!!lKk +r;Qf;!<)p;_+G+fV#12WN$S`]SFcdXXYgMQ\c;^(TIgQ`F8c+AKFeDep\b$l`W5`4#3q-n#VDS< +rVunQrr3/]G8(a4hY$mKHi3pEF/T$?o5f9UfDc#(Dsi*nmeZqbpT_:,rrVV,J*[+7Hi3pEF8Z%> +`W1MWrrVV,J,Krr2tFr;[.Fs8UY:!!lJqp\k-lL\q,OL]7;_ +lAR"p/Y)G4qtFS+&9I^J!R:[rs+14+rr@`D]`RYm:4N~> +ZMspZr;Qoo@0dEeXT&HRD=)gimHFQN&DuXM!r[n2rr4/=X>C>P])Vg'lW"3%EVoe9f0KNFmMYj*)7SF5HTi!."MC!-P4>"7^AVq:GWMIr4S&D=.3"!qie/r;QicD1CmO!."MC +!-S5>!lG"fn,EIVD1DK`!q"_BcMmtELPc!0"NMnn.),TY!Q+p:rr@?C!"!fJs49O>#Z[Pjrr3&u +LPbs/!r[n2rr3GnN[>)eT(E!ZpL+==Fo)+=cNJh!JcFR+!.sgN!pc:LJ,~> +ZMspUr;Qoo@gEWeXT&HRDsi*nmcaZO&DlOKs+gUR%K!7b$phH(s7bt[#S;(Vrs[IF!!lKks8VOc +#S;(Vrr@EE!"_0$s8To-$n\:Ps7`0<$phH'rsZ_9$n\:Ps8/p.#S;(Vrrg=j!%GV\rrpt?!!lKk +r;Qf;!<)p;_+G+fV#12WN$S`]SFcdXXYgMQ\c;^(TIgQ`F8c+AKFeDep\b$l`W5`4#3q-n#VDS< +rVunQrr3/]G8(a4hY$mKHi3pEF/T$?o5f9UfDc#(Dsi*nmeZqbpT_:,rrVV,J*[+7Hi3pEF8Z%> +`W1MWrrVV,J,Krr2tFr;[.Fs8UY:!!lJqp\k-lL\q,OL]7;_ +lAR"p/Y)G4qtFS+&9I^J!R:[rs+14+rr@-3XTI[M8q6~> +ZMsp]r;Qr*8onoCafYRHIrFb)Dslih!=7QGrri5]]iKdbs!$*D??'5,jT!Dj;Km1[`W,s^:31JO +SH&VV;Km1[`W,sM3W<4#:7V:_!qU=1rr4>)OCiWCIG"S5CM@p%Fij[9I9pnuM<+][F_#W9;T8\9`EI>,9!S?p!qU=1 +pAYQ7CM@p%Fij[\k.OfJrr3/2@V9C_ZM"4uAi]j+!3rI%"7U;YrR_&QIrFb)DsmK%!rLX*r;Qid +Dh%*Q!G4@kCB1Xkq&"T.>p%[^4!qU=1rr3"23W +ZMspZr;Qr+8o\cAb,t[IIr4S&D=-Qe!=7NFrri5]]N0^bs!$*E?>s2-jT!Dj)O(WTCIFnJ2Che-*Fij[9IU6u!MWFf\G%Pl<;SrJ6``[G,9+Ap)5qqrYOlK`:uU\79/d@^H(-rrHalrbDMeH1=%JcFR+!.sgN +!pc:LJ,~> +ZMspUr;Qr*8onoCafYRHIrFb)Dslih!=7QGrri5]]iKdbs!$*D??'5,jT!Dj;Km1[`W,s^:31JO +SH&VV;Km1[`W,sM3W<4#:7V:_!qU=1rr4>)OCiWCIG"S5CM@p%Fij[9I9pnuM<+][F_#W9;T8\9`EI>,9!S?p!qU=1 +pAYQ7CM@p%Fij[\k.OfJrr3/2@V9C_ZM"4uAi]j+!3rI%"7U;YrR_&QIrFb)DsmK%!rLX*r;Qid +Dh%*Q!G4@kCB1Xkq&"T.>p%[^4!qU=1rr3"23W +ZMsp]r;Qp`GOFTs@aY2\dZAs:c2Y>Qrr4%$c2Z4RIuDSOs1O2?ON7(pkconsW8djWIrFb)DsmH$ +)ZA6lLP)Q"s8RT"R"(4Ks,-l$gAcZRON"c/rr3%]c0"a""Rrg9.=_?s,+_uDlC_Ifqhu\koBL[+ +s6:#goBqP`s7[\0oBL[+s8P6Drou6-qu<[1ZgP_sbPR6+amQKNXT,;7ZgP_sbQ"/;GOFTs@fQ$. +^%]JOoDe(aV"=#5-2miFPX,6"V'Hg:3e +R/R$dPX,`f!T[;@@Bs8VnEAD5mPIJNU:!BlPDl2^RVrrVV,J,B6OkconsXQJ$.pVdF0rVlq`5lgoa!BlPDl4 +ZMspZr;QpaH0sd!A^UM_dZK$=bl>2Qrr4%%cMu=TIuV\PrOdu@OiR1qkHK_qW8[dVIr4S&D=.0! +)ZA6mLP)W$s8RT"R=UFKs,@#$f`-HNO2SQ.rr3%^cK=j#"Rrg9."D6r=.WX&lChUkqhu_loBUa- +s5slfoC%Vbs7[_1oBUa-s8P?Fl0I[)qZ!U0ZL5Srbkm?-amZQOXoGG8ZL5Srbl=8=H0sd!AcM?3 +]_B>On,MP\V"=&6-N=#IrKKVKrr4=GBX7'CQd#/PA(:%c>EAfU>I3Ug@']Zg@EJ!(HX$OM=*a-% +Pa_UO"9"HN!;HL0m=&Qpn\SLks7!F5."D9sq1*&0h.M-0pAY:]O6Puror%fkm;7@Mrs7hoLk`%n +gUO9_rtU[As3d$ZNQhG$]SV36mJm45O+3)+rr3.OD=)gimJ?hip9qa9i2DBeV-AH8rrkZKl0I[. +rr3-"PX5A"qu8D6Y_Rq2iW&r9W.p/*>CZ\2W.p/*[K-@-J(0Ots80*WK<"\Gs3d$ZNQhG&dZK$= +bj59=n?m*]J,fQEo+&G-`!Yu4rr`4a63.#b'L7`Dlg+6Dk74 +ZMspUr;Qp`GOFTs@aY2\dZAs:c2Y>Qrr4%$c2Z4RIuDSOs1O2?ON7(pkconsW8djWIrFb)DsmH$ +)ZA6lLP)Q"s8RT"R"(4Ks,-l$gAcZRON"c/rr3%]c0"a""Rrg9.=_?s,+_uDlC_Ifqhu\koBL[+ +s6:#goBqP`s7[\0oBL[+s8P6Drou6-qu<[1ZgP_sbPR6+amQKNXT,;7ZgP_sbQ"/;GOFTs@fQ$. +^%]JOoDe(aV"=#5-2miFPX,6"V'Hg:3e +R/R$dPX,`f!T[;@@Bs8VnEAD5mPIJNU:!BlPDl2^RVrrVV,J,B6OkconsXQJ$.pVdF0rVlq`5lgoa!BlPDl4 +ZMsp]rVm)n#ho=YYoM3l.echRHY-%ITIp'@iY9N/;J1>g:A;S&F*1r.`VZH+@WPYCqu;.amXK0C +q>VSdJQdDA:7V@a!-a3J:1!sY!-d/s!,lYdTIp'@iY9MqpA\Llc)+6cs8VnCEU<_XArZJ+!:Tsf +`YAI8Dtj;3[1rZ,!:Tsf`YAIQ(iOnRW6bAaIk&:"os0WR?s"1oVU]CGpqh&.@Koiq5p4$`1q8lm$q0d8Ls4CqColYKQs8U(Qs7[)( +LP),Nrr3)68u)I]rtk*QBAE=)>`JdUc)+6cs8Vo*^&@/5J,/g<"W20-J#N+>rrVP(J,Kg:A;S&F*CB;s7]D`HYQ%Es8RRemXK0CqYpojDh$]#>&SOo.t?mb"W20- +J#N,@rrg#-U]>oBs"sVK:1A9HT)7H$>&SOo.t?K4>&SOog:A=YQrsn\GVG;X@s7u89qu>(QoD\al]h5(#rr3ZLDh!88p]'Aop](9$A;U?Yc)+6crr3c+ +c)+6cs8VeBBAE=)>`JQ-#ho=YYoNZ@!oa4WJcC<$hZ!S/o=Y4oS5+S~> +ZMspZrVm)n#h]1WYTVBo.ecqWH"g"IShBp?itf`1J?qYu%^m=0!? +q>VSeK3`bD:RqIb!-a9L:0mmZ!-Qro!,lYaShBp?itf_rpA\Llc)+3bs8VnCE9mPUBTMh/!:Bgd +`>JX:D>!r/Zl&i/!:Bgd`>JXT)KC1VWmLY?K3O^tOM'uRFa!KYibj\OK3O^tOM'uVI;mj=s0*XG +o7-Z9cj]CCj_a\js0NXBs"1fRU]CGpqh&+?L60(8p3pW-prH^#q0d5Js4:qFolYHOs8U%Ts7[)% +M1_DSrr3)38Yc@\rtk'OB%m%$>`SjVc)+3bs8Vo*^&7#1IJ]=gs.t?mb"WDB/ +JZAJDrrfo)U]>oBs"sVK9k&*DSbh9#>]=gs.t?K5>]=gs+pJkpQBhe?r +lp&G'!ArH]]c)+3brr3c+ +c)+3bs8VeAB%m%$>`SW.#h]1WYTWiC!oF"TJcC<$hZ!S-o +ZMspUrVm)n#ho=YYoM3l.echRHY-%ITIp'@iY9N/;J1>g:A;S&F*1r.`VZH+@WPYCqu;.amXK0C +q>VSdJQdDA:7V@a!-a3J:1!sY!-d/s!,lYdTIp'@iY9MqpA\Llc)+6cs8VnCEU<_XArZJ+!:Tsf +`YAI8Dtj;3[1rZ,!:Tsf`YAIQ(iOnRW6bAaIk&:"os0WR?s"1oVU]CGpqh&.@Koiq5p4$`1q8lm$q0d8Ls4CqColYKQs8U(Qs7[)( +LP),Nrr3)68u)I]rtk*QBAE=)>`JdUc)+6cs8Vo*^&@/5J,/g<"W20-J#N+>rrVP(J,Kg:A;S&F*CB;s7]D`HYQ%Es8RRemXK0CqYpojDh$]#>&SOo.t?mb"W20- +J#N,@rrg#-U]>oBs"sVK:1A9HT)7H$>&SOo.t?K4>&SOog:A=YQrsn\GVG;X@s7u89qu>(QoD\al]h5(#rr3ZLDh!88p]'Aop](9$A;U?Yc)+6crr3c+ +c)+6cs8VeBBAE=)>`JQ-#ho=YYoNZ@!oa4WJcC<$hZ!Rso;r)WPY-H~> +ZMsp]rVm)]!;lfrecaG$.dmA:pY'ugjqQ8DQneL,!3,_HHhUEVlL;uN[/7+if]$F_o)F2XmXK0C +qYq`;P\-T[mZ]$lrr@Q:s/ZM+rrCpUrr@,opZ<1o"IJa'EecbsO +6[r!Cs8Vuas7a31HiO-":8H_Hs8VY22fj+JGL?H"=C-8R@)_VTIJs32D1VM:?H_d%IfTN2!,m(& +8u2Leg.nI#!;?Es[ocs^urQWk%Q[ +'[m#K1&LD>WV>+npJPqppRl:$pM726aT1K's*ntTIrFcOrsJ14J#68:kj"fqJ,TBJlIPi^!7puG% +egrYIr@cKs8UeJUtu*$r;SVNZ&mVtA97==IWp9?Y!q'uVqu6_I;I]Y*(>;NZh>mTUpDdsuDag?!qsOLY]io0ar +r3&fDh%]b!q'uVrr3J;=C-8R@)_VT!;lfrecbmM!qR,>JcC<$hZ!S/o=Y4oS5+S~> +ZMspZrVm)^!;ZZpdg"8#.dmM=p"F]cjV6/CQnnL+"K;"IH1t0UlgW&M[/."gfAC(Xnc+)Um=0!? +qYq`;Q"Zi\n!#*lrr@Q;rN$;)rrCgRrr@2qp#Qn9s-PS;r:L"`m;7@Qs7()Vr;Z"tK)(*;VXs,' +!rr/bs7a0/HiN[SVXs,'!rqp7D1-CpFjT`$2fs4MGL?5??d.uH5_/HI2fs4MGL>r#I/3gBdg#gO +7/_O4@K$6'mJlpn=aU2Kg.nBs!<<)jF#S2a?['os[9$XY/s4U5BgAq!J#Q;&3?$KCX`W#l=m;7@OrrrD8BNhV=p\tHj*c6=F8><'_"9-bYN;W]+pN]fb +kGl&[jV6/CQnnL+"K;"IH1t0Ulh-obs7L09q8NEps8RRcm=0!?qYpoiD1AKdSET720n90%!UA85 +IfS!orsn]CD1-CpUAt8I:86JCJ,K='qQQ(5hd^ZrW*#SRlC]k]W*#SRlC_Ves8N(Ag@b=sT^hK8 +,kpa1W:ekip[86`p"F]cqu6TrlDsi4!NPDYrrVS)J,Kg5GZJ4knSnR)s8UOPqu6`f(;,4uJcFU,!.sgN!pc:LJ,~> +ZMspUrVm)]!;lfrecaG$.dmA:pY'ugjqQ8DQneL,!3,_HHhUEVlL;uN[/7+if]$F_o)F2XmXK0C +qYq`;P\-T[mZ]$lrr@Q:s/ZM+rrCpUrr@,opZ<1o"IJa'EecbsO +6[r!Cs8Vuas7a31HiO-":8H_Hs8VY22fj+JGL?H"=C-8R@)_VTIJs32D1VM:?H_d%IfTN2!,m(& +8u2Leg.nI#!;?Es[ocs^urQWk%Q[ +'[m#K1&LD>WV>+npJPqppRl:$pM726aT1K's*ntTIrFcOrsJ14J#68:kj"fqJ,TBJlIPi^!7puG% +egrYIr@cKs8UeJUtu*$r;SVNZ&mVtA97==IWp9?Y!q'uVqu6_I;I]Y*(>;NZh>mTUpDdsuDag?!qsOLY]io0ar +r3&fDh%]b!q'uVrr3J;=C-8R@)_VT!;lfrecbmM!qR,>JcC<$hZ!Rso;r)WPY-H~> +ZMsp]rVm)a!;lfrecaG$.ed7nK:LNms%7e@4cB@7??_$lf)H<#s8VoQk5XA>QBk-]mf.cTmXK0C +qYqB(FZXesIr>>HrrC=Ds5!bUrrCpUrrC1?rrbrDs#l;2rtOm>J,fQ>F++#el>;+P_hV!.G?tRS +s#%oNF+`WTs+a?jG?tRTpR'D/mXJ9'n8V:Fm^l_ks2/SZF_W^bn8V:Fm^l_knoK6Vs8UXQrVltL +;Hitss#nJVF+`WTo3ue8g&V$Mn8V:Fm^l_ks0Pa1GuS+hnoK6Xs6=HPs2/SZFa%-ts5F$Omd>lc +s79]@pY#WEp\tH)<*X]J?H)pD!q'uVr;Qo7Bi_84p\tHlR*u$&Ik@W!"7hU[^&7m\ql4F[O1FQU +s%7e@4cB@7??_$lf)H<#s8V]8s8Tp"R$7U-s8RRemXK0CqYpojDh!IOhZ*W6@"8.T!UA85IfS'q +rt"jkdf8`b/cYEOF8+AgIJs*D1#T'tQ:c)`Kk0'?s57:7Kk0'?s5:]ns8N)@s8V?aGCP*\!93`Z +GCuF:s7]iDK:LNjrrMDbr-naIlM(;[mVdUPrtBl[>5S?k@WV:t!;lfjR*u$&IkC[""j)C^9:T`U\'Mqu?]I"T&-"?:t*Gs+14-rr@`D]`RYm:4N~> +ZMspZrVm)`!;ZZpeHXJ%.ed@qK:C>IrrC:Cs4[PRrrCgRrrC.>rrbrFrB-)1rtOj;J,fQ>Ed[fbl>;.Q_hV*3H!L^T +s#%rOF+NHQs+aHoH!L^UopO8,mt"T,n8M7Gn%)_is2/Y_GAAsdn8M7Gn%)_inoB-Rs8UURrr3-" +IoBSKrr51)K6)V*s79fCo@F!^'.S`U\%sr;Z&!K)``)@s<3(#lii`U@7Q* +0`U`UF7[u`q>L-h#Q;#/>^'.S`W#l=m;7@OrrrD9BihA6p\tHjRF;'&JM*o$"7hU]^&7m\qlFU^ +O14?Qs%7k@4H9C8@<[?ndf0lus8V]7s8B`uQ]qL)s8RRcm=0!?qYpoiD1@:PhZ*W5@"8.T!UA85 +IfS!ort+jkdf&Wc0)tNSF7[u`I/EpBs"aT1X-T#CD=*'ihZ*W5@"3oChZ*W5^]=E7!6G3?lN23Y +Ir>>IpL/8uS'q?2@<[?ndes+GlDsi4!NPDYrrVS)J,K<\rI7aMqu?8!F7[u`q>KuSdf&Wc0)kkS +b(FbdZh"+q!psiSr;RJuD1DTcrj5X0G>qqgno=a+s8UURqYpV*9Da;?JcFX-!.sgN!pc:LJ,~> +ZMspUrVm)a!;lfrecaG$.ed7nK:LNms%7e@4cB@7??_$lf)H<#s8VoQk5XA>QBk-]mf.cTmXK0C +qYqB(FZXesIr>>HrrC=Ds5!bUrrCpUrrC1?rrbrDs#l;2rtOm>J,fQ>F++#el>;+P_hV!.G?tRS +s#%oNF+`WTs+a?jG?tRTpR'D/mXJ9'n8V:Fm^l_ks2/SZF_W^bn8V:Fm^l_knoK6Vs8UXQrVltL +;Hitss#nJVF+`WTo3ue8g&V$Mn8V:Fm^l_ks0Pa1GuS+hnoK6Xs6=HPs2/SZFa%-ts5F$Omd>lc +s79]@pY#WEp\tH)<*X]J?H)pD!q'uVr;Qo7Bi_84p\tHlR*u$&Ik@W!"7hU[^&7m\ql4F[O1FQU +s%7e@4cB@7??_$lf)H<#s8V]8s8Tp"R$7U-s8RRemXK0CqYpojDh!IOhZ*W6@"8.T!UA85IfS'q +rt"jkdf8`b/cYEOF8+AgIJs*D1#T'tQ:c)`Kk0'?s57:7Kk0'?s5:]ns8N)@s8V?aGCP*\!93`Z +GCuF:s7]iDK:LNjrrMDbr-naIlM(;[mVdUPrtBl[>5S?k@WV:t!;lfjR*u$&IkC[""j)C^9:T`U\'Mqu?]I"T&-"?:t*Gs+14-rr@-3XTI[M8q6~> +ZMsp]rVm)r#i>U][2dWp.f7&XLNcqrs.Csp(uG;i_5)j1>5&HEs8VoEgA@a_MkF$fmf.cTmXK0C +qYqB(FZk)$IsYhnrrCpUs5!bUrrCpUrrCpTrreuHeem97ru:BEJ,fQGIrk&Xg1ZKGmbXI`Mi]Cf +s060c[J0\##i;)$Mi]Cfk0Ys5[sLs6dR\KoK?%q0R>Ys5[sLqgW\Ds8TJHs7`>h +s3_&=s8T?9J$o%Ns3_0_HYWV.`S>fap&F)gL&$4-gA/cmNVe3Hmf2!SNW8F[NeNCQ.KAuBli7"-% +0+`]@s_tkHc>g\#P3FSgA/cmNW/qdmVdUTs81'gs6+6Dp\4[gk0/,e>2Q +@ESHqu<<@eem9Dqn](`G?X_L`W,u0[K#u%J!d/7hZ*V*Dsi*nmeZqjmVdT/B&N^hi*_'$rrM9,r +r3AG%0+`]@s_tk4eUg6s"sVMCQ"ibDsi5.p[rtk*AJ,fQGpS@ihdKK%=dKj?0CB/_#k0 +ZMspZrVm)p$JYU\[NO#u.f7)[LNQbos.V-o(uG;i_PW*2=n`?Es8VoEgA@d`MkF'emJhZQm=0!? +qYqB(F?FhuIsYhorrCgRs4[PRrrCgRrrCgQrrf&KdMUj3ru:?BJ,fQFIrarVg1ucLn)'^fNK>Xi +s0??e[e9Y"#iD5)NK>XikKa),Cr^:Fpj78Ys5[pKs6m[_LQ5Z)pj78Ys5[pKqL/,e>3Q +@J,fQFoq_Qdcj&n;dL'E.B`NFskKa),$-*PUs0??e[e9LqrrVS)J,K<\m +;7@Qs7^tQg%WKkO8=>5g].;^+T)0@q+Z#/JcC<$hu<\.o +ZMspUrVm)r#i>U][2dWp.f7&XLNcqrs.Csp(uG;i_5)j1>5&HEs8VoEgA@a_MkF$fmf.cTmXK0C +qYqB(FZk)$IsYhnrrCpUs5!bUrrCpUrrCpTrreuHeem97ru:BEJ,fQGIrk&Xg1ZKGmbXI`Mi]Cf +s060c[J0\##i;)$Mi]Cfk0Ys5[sLs6dR\KoK?%q0R>Ys5[sLqgW\Ds8TJHs7`>h +s3_&=s8T?9J$o%Ns3_0_HYWV.`S>fap&F)gL&$4-gA/cmNVe3Hmf2!SNW8F[NeNCQ.KAuBli7"-% +0+`]@s_tkHc>g\#P3FSgA/cmNW/qdmVdUTs81'gs6+6Dp\4[gk0/,e>2Q +@ESHqu<<@eem9Dqn](`G?X_L`W,u0[K#u%J!d/7hZ*V*Dsi*nmeZqjmVdT/B&N^hi*_'$rrM9,r +r3AG%0+`]@s_tk4eUg6s"sVMCQ"ibDsi5.p[rtk*AJ,fQGpS@ihdKK%=dKj?0CB/_#k0 +ZMsp]r;QpdK_t@M@ab9+pLi@[YlOCqjqM,ss8Vh+hYXPXo0m`cnB6*UpNL94i*^Egs*ntTIrFcO +rtkWsBW1OoW.Y-M!8dbUh>mTU!8@JQ!8d_T"6L;(:@\>cmVdUSrtD&7^\=a;YlD\#XnUs]HiN@c +J):1is8P0=XnUs]HiMMhanP(M@%dOgB>a/H?Cq29SC6huC;'Y9B>a/H?Cq1i@YFXM@UfB3/]mb+% +.jMa0n8'Zl2gqArdQMU]kIpgdq_-2s"rs\=6J,fQGP'c-DBRfjPrs$nHanP(M6)X_ag1H9@s"!uMU?pH5H +27L&'OFQ7s7^0_qlp.m7<@bgTMkgY!7(V\!8@JQIrFb)DsmK%$LVh^VHBGRlCp(arVlo`f)'psf +(o=OdS@pYl@2,"hYr!-DZE"rir8s7pNL94i*^EgVHBGRlCp(aVHBGRlCqnks8N)Us8VsFW:TVZK +E([YhYXPXoClnSqlp.mq>UBplDjc3!NPGZrsnI8J,fQGpFXM]`=2A*/cW6%o.,@Uf67!pTdNJcC<$hZ!S/o=Y4oS5+S~> +ZMspZr;QpgK),"JA^^T.ok3.WXoe.nk8%E"s8Vb)hYF;UnO@TcnB-$Tp3102hdLcm;7@PrtD)9^%SL;ZN%q(Y4^m^HiN=c +J_g=ks8P0?Y4^m^HiMPlb4k4O@\NgkBuKGJ?Ch,9S^HhuCq]kU]gIUU^q_-<$#rs\:3J,fQEO*TU;B79OKrs$qLb4k4O7&U%dg1Q?As"!rKU[-B2H +2.F&(10i:s7L$]q5seh7W[ngTMkgW!mU\\!8%8NIr4S&D=.3"$LM\[W*,\TlCft_rVlo`f)'pse +G9+Mdo"9^l[M>&hYr!-D#ceqir8s7p3102hdL&k +q[hGrp'RWrr3&eD1DK`'^]aes8VhB@FOb_>EJlY7"Y7+@qGQ;!p9XNJcC<$hZ!S-o +ZMspUr;QpdK_t@M@ab9+pLi@[YlOCqjqM,ss8Vh+hYXPXo0m`cnB6*UpNL94i*^Egs*ntTIrFcO +rtkWsBW1OoW.Y-M!8dbUh>mTU!8@JQ!8d_T"6L;(:@\>cmVdUSrtD&7^\=a;YlD\#XnUs]HiN@c +J):1is8P0=XnUs]HiMMhanP(M@%dOgB>a/H?Cq29SC6huC;'Y9B>a/H?Cq1i@YFXM@UfB3/]mb+% +.jMa0n8'Zl2gqArdQMU]kIpgdq_-2s"rs\=6J,fQGP'c-DBRfjPrs$nHanP(M6)X_ag1H9@s"!uMU?pH5H +27L&'OFQ7s7^0_qlp.m7<@bgTMkgY!7(V\!8@JQIrFb)DsmK%$LVh^VHBGRlCp(arVlo`f)'psf +(o=OdS@pYl@2,"hYr!-DZE"rir8s7pNL94i*^EgVHBGRlCp(aVHBGRlCqnks8N)Us8VsFW:TVZK +E([YhYXPXoClnSqlp.mq>UBplDjc3!NPGZrsnI8J,fQGpFXM]`=2A*/cW6%o.,@Uf67!pTdNJcC<$hZ!Rso;r)WPY-H~> +ZMsp]r;Qr.;L`mcajU2=qbh0UB3+p0s%Wj$s8VsoC3sSoqmfICF^f1+qjLK0LK$gRs*ntTIrFcN +rt`q/>'G0gOT5=\hZ*W4!<<'BCYJdghYmHU:'drkrrVV,J,]H\g4O*d@Z0=I`J[K8F'@;t\H$.< +C]FDqMb\J;>2'#A>$cDo>)D&7eph.M@Y*23`GB"+At&)deph.M@Y*23g4N:QG>?9[s'f/U>&XH@ +\H$.mQT-J!ZUF(0^Qs1;rWF(044s4W*>HX_oos2Ae`F`Ua?s8T3!F)t6ar;QfS!;?Es +]4bhu@WTZjrrVV,J,]HOY[2$XA@M:4#3oAuGB6ruVuHhbD1DK`/,c>OF)bNbrVumm#d"(+qbh0U +B3+o?:31VSMuWN7CPR]hDu]ieDsi*nmeZqjmVdU5LK\V?D)$P=rrME9qgSUsq>UZa>$cDo>.OS2 +!oIWp9?Y!q'uVrVm"0>'F.gqu7/_!<<)a>$cDo>.O[D!+YtC!4Dh+!q'uVr;QidDh%cd% +_HCeF(044s4XkYIs4Z0qu6]R!.k0$s5!\TK_)kYn!m.'~> +ZMspZr;Qr/;1Ejdb0p;>q,;'SB34p/s%Wp(s8VmnCO'Poq7'1@F^o:-qO(?/LfR*Vs*nnQIr4TK +rt`t0=a,'iOoPF]g].<.!<<'ACY8Xeg\q-R:("/orrVS)J,]H\g4O'dA;oUL`f3f=F'I;s[K'h9 +C]FDqNDOh?>M/uA>@D_u=GY`3eUM(M@Y!)1`c#C1B:8)ceUM(M@Y!)1g4N7PGtuN^rF8uS>AsK? +[K'h9C]+25gAq6Q-ImTUF(0[OrjuoYF_#R8s4W*=Ht82ss2JtfF`Ud>s8T0!F)t0^r;QfP!;HKu +rOZfXF_#R8rr3DoD1DTcrif^BBk=aPp\tH`>@D_u=Lb:/!pj`Qr;S;FVI+@_?`F.sofd/HMB[!=s*nnQIr4TLrsJ.1J)H)cDg-%QJ,TBJlIPi^!7^iE#4#K%GBI#t +r;QiQ!.XqH)u>t&H&,Km\)<$FDg-%QJ)H)cDg-&.!<<'!g]%3fU02\aC6l!U?``IfZS6p\tZhD1DTcrr$>0G?]"ar;R8]!<<)b>@D_u=LeC>!+YtC!4Dh+!psiSr;RJu +D1DTcrjuoYF_#R8s4XnYIsFf3qu6]O!.k0$s5!\TK(HDPl^COu~> +ZMspUr;Qr.;L`mcajU2=qbh0UB3+p0s%Wj$s8VsoC3sSoqmfICF^f1+qjLK0LK$gRs*ntTIrFcN +rt`q/>'G0gOT5=\hZ*W4!<<'BCYJdghYmHU:'drkrrVV,J,]H\g4O*d@Z0=I`J[K8F'@;t\H$.< +C]FDqMb\J;>2'#A>$cDo>)D&7eph.M@Y*23`GB"+At&)deph.M@Y*23g4N:QG>?9[s'f/U>&XH@ +\H$.mQT-J!ZUF(0^Qs1;rWF(044s4W*>HX_oos2Ae`F`Ua?s8T3!F)t6ar;QfS!;?Es +]4bhu@WTZjrrVV,J,]HOY[2$XA@M:4#3oAuGB6ruVuHhbD1DK`/,c>OF)bNbrVumm#d"(+qbh0U +B3+o?:31VSMuWN7CPR]hDu]ieDsi*nmeZqjmVdU5LK\V?D)$P=rrME9qgSUsq>UZa>$cDo>.OS2 +!oIWp9?Y!q'uVrVm"0>'F.gqu7/_!<<)a>$cDo>.O[D!+YtC!4Dh+!q'uVr;QidDh%cd% +_HCeF(044s4XkYIs4Z0qu6]R!.k0$s5!\TF7ZL8kEJSh~> +ZMsp]r;QosZ%I\>d/O6nMgpi>rr3%P*:Eh+)pO:jOOjI3[>0Bra7fPiTS98LP/715\+[SGpA+_( +qpDKtK;A,=s*sJ:s6bC:s.Fkqs*sJ8rrRfmmd^;Yp:%g9rt,.[RY.3es8Vf\S:?IAs8U@@J,fNO +m[Scbc2R_hg6@)B^#&ehrR7-dSCmf?p;N#FXQKQ_rR7-dSCmf?rRS6+K=V!]#MP8hPiD`Cc23"g +rrMP;rr4#;e!PcXf_tjB`K5Y:c2%DUBqp:%g9rrqbsLP`Y4p&>0KP^eJ%VZ-_cDh%Za"m2n?LU6:FrrRfmmf*5*c&7(5f)PcXLOYub +s8TcbLW,p=s8TjCp:n*1qYpor\%ht$VM0r9W5%Zu"PT)iLVOF`_Bq>^EjZ%I\>qYpTQ!.k0$s5!\TK_)kYn!m.'~> +ZMspZr;QosY_@eBd/O6oMgpl=rr3%P*q0+.)pX@kOjsF0[YKNuaS5_kTSBDPPJ[@7[e@JEpA+_( +qpDL!K;S8?s*sG9s6Y=9s.Fqqs*sG7rrRfon+$DZp9qa8rt,.ZRY@Bhs8Vi^SUldEs8U=?J,fNO +n"##hbl7VgfTgrC]\NMdr6gpbSCd`>pVr8LXl]T_r6gpbSCd`>rRJ-+Kt@9`#MP5gQ/hoEbklnf +rrMM:rr4#:d[,WWfDYaB`fYn>c2%DUBqp9qa8rrqbsLPib7p&>3JQ%+Y'rMTYsm;7@MrrqPeKo!)"rr3%P*q0+.)pX@kOjsF0[YKF. +n,NF%K8IUBL&_1f[e@JEpA4drp9qa9hkl*c[Agq,rrq&?Iu1i:r;QicJ%tm\)W%-AW3hc*s50]- +Mm]P!s50]-Mm^u_s8RT9rr3Z+XG);(oDej9Mgpl=s8UB&J!&7FrrVo&^]"08g5g`Kj88iWm=5-8 +#M>;kM7ifebklki^]+67p9qa6rrVo&^]+6Bou<,HQHf7*rVNgQKt@-\!S[U*s+14+rr@ZB[K>c` +9n3~> +ZMspUr;QosZ%I\>d/O6nMgpi>rr3%P*:Eh+)pO:jOOjI3[>0Bra7fPiTS98LP/715\+[SGpA+_( +qpDKtK;A,=s*sJ:s6bC:s.Fkqs*sJ8rrRfmmd^;Yp:%g9rt,.[RY.3es8Vf\S:?IAs8U@@J,fNO +m[Scbc2R_hg6@)B^#&ehrR7-dSCmf?p;N#FXQKQ_rR7-dSCmf?rRS6+K=V!]#MP8hPiD`Cc23"g +rrMP;rr4#;e!PcXf_tjB`K5Y:c2%DUBqp:%g9rrqbsLP`Y4p&>0KP^eJ%VZ-_cDh%Za"m2n?LU6:FrrRfmmf*5*c&7(5f)PcXLOYub +s8TcbLW,p=s8TjCp:n*1qYpor\%ht$VM0r9W5%Zu"PT)iLVOF`_Bq>^EjZ%I\>qYpTQ!.k0$s5!\TF7ZL8kEJSh~> +ZMsp]^]+B,bQ!qTH2JcC<$c2Rn8EI%BPrrVdmDh7qMrrVqr%"\G0s5!\TK_)kYn!m.'~> +ZMspZ_#FN8o3$"Irr`5k=`8lkJcF!p"7U8Tq=agkrr)FkDh7qMrrVqr%Y=Y2s5!\TK(HDPl^COu~> +ZMspU^]+B,bQ!qTH2JcC<$c2Rn8EI%BPrrVdmDh7qMrrVqr%"\G0s5!\TF7ZL8kEJSh~> +ZMsp]_#FMrF#h\/rrMJfqgSWt_ +ZMspZ_#FMoF#qV,rrMGeqgSWu_W_"%hfCj(JcC<$c2RqBO&37]p\tBbhfCj(j8T/GXS[JJK#3*= +\GlWnGBJK:JcFR+!.sgN!pc:LJ,~> +ZMspU_#FMrF#h\/rrMJfqgSWt_e?J,~> +ZMsp]_#FJqJ#MB,!UQobIfY,:kl1_DJ#IquJcEpn"4`^LqtL*mnD\qrir9&GXS[JJJ\cp;\GlWu +\`SJCJcFR+!/0sW!q2XSJ,~> +ZMspZ_#FMqJ#N>+rrMGeqgSWu_W_"%iI@g)JcC<$bl7dqQ+QqfrrhiMJ#N>+rrMGeqgSWu_L_`s +rrVZ!kCNYas5!\TK(HDPl^COu~> +ZMspU_#FJqJ#MB,!UQobIfY,:kl1_DJ#IquJcEpn"4`^LqtL*mnD\qrir9&GXS[JJJ\cp;\GlWu +\`SJCJcFR+!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]`r?=TKA,aCs8RT:JcC<$LAqA)R)/a=rrVo'^[qI+mXP-6!UbI(rrM,RqgS[Km(`LK"+cc_ +oDS[jc$uhmrrqhhLOYE%rr;kF!1TNiZ1n:;qL8P"s8.9LMp).8LWBAn"4<:@pA"Xr^UNq: +s82PuMgpYuqu-O#m\knjOMUts`VP&egAcZ3p;m78p:%f#rrMP;f)G`'o=Y4oS5+S~> +ZMspZ`r?=UK@oR@s8RT9JcC<$LAqA)Qb`O:rrVo&^[qI+m=5$5!UYC'rrM,SqgS[Lm(`LK"+llb +oDS[jc@<"prrqkkLOYB%rr;kF!13H[!5JG]"cLRbWP\Z6rrUZJe+it?IgEF]s*sG9s*sG5s8.9H +Mp(nq$iJVLKnPuEqV7aXqYpL'qi.o]q6l32M6Q[Q`qk/cgAh&""cLS(^PKu0rr^Z2[eBLr%)97E +s8Vrg[#9En]_h[-#k+dKIuhJHs2Onb"PWm*[e@fB!qs(;PQ([NJ(FW"K(HDPl^COu~> +ZMspU`r?=TKA,aCs8RT:JcC<$LAqA)R)/a=rrVo'^[qI+mXP-6!UbI(rrM,RqgS[Km(`LK"+cc_ +oDS[jc$uhmrrqhhLOYE%rr;kF!1TNiZ1n:;qL8P"s8.9LMp).8LWBAn"4<:@pA"Xr^UNq: +s82PuMgpYuqu-O#m\knjOMUts`VP&egAcZ3p;m78p:%f#rrMP;f)G_ko;r)WPY-H~> +ZMsp]r;QosZ%IhBd/OA3BA2K.s8N)Ug]%>m]Cs\Qs24j9%K4kHKqR6$s82PhK87#:rs\1cItO]o +s8VhlK87#:rrBb2Ih8.*s8V#QKpL-bs81-.KqR6#rs[h]KpL-bs8CNRK87#:rrh>GJ!Aghrrq\\ +ItO]or;QfWJ,TBVi1u'mdJa+;`KGk>c2%>[M2mcj`QmVdUHrrM7Bqu6]R!:9^u +i0n_&0fa;rVX/deCNFQ8=DsT*rr[0J1&LtM$hJRGF)PP\^&S*4A,UHKOSAbVIkbai+-io +s8W%^ +ZMspZr;QosY_._Bd/OA6A_?$(s8N)Rg]%>m]D'eS!rg*Urr4/?eX;,ah#IENors*kB(oDejfNtcHuq#;@aD=.B"]nKU+EGJ[Aqu?]f +H!PE.@84K9_K0d.!<.$Bf`(qgmF_F>L[Xj7!psiSq#:E5]D)%!!rg)ol2LqIPC\qSmb@aBL[Xj7 +!S[VKrrU*Mp\=amou3#EQI#F!rr@ZB[K>c`9n3~> +ZMspUr;QosZ%IhBd/OA3BA2K.s8N)Ug]%>m]Cs\Qs24j9%K4kHKqR6$s82PhK87#:rs\1cItO]o +s8VhlK87#:rrBb2Ih8.*s8V#QKpL-bs81-.KqR6#rs[h]KpL-bs8CNRK87#:rrh>GJ!Aghrrq\\ +ItO]or;QfWJ,TBVi1u'mdJa+;`KGk>c2%>[M2mcj`QmVdUHrrM7Bqu6]R!:9^u +i0n_&0fa;rVX/deCNFQ8=DsT*rr[0J1&LtM$hJRGF)PP\^&S*4A,UHKOSAbVIkbai+-io +s8W%^ +ZMsp]r;Qr*8onoCajL+o!7LoA\c;[0hV8&3K6-J2rri5]]iKdbs!$*D??'5,jT!Dj;Km1[`W,s^ +:31JOSH&VV;Km1[`W,sM3W<4#:7V:_!qU=1rr3\lOCiWCIG"S5CM@p%Fij[\k.OfJrr3PhOCiWZ9!SErYtB(U9$.+T +rG)Ht+n#.*%)$FjF_#X:s81rlrr`#HU^6r!"gL[@DbA-Trr_b1IJqt$!=7QH +rrVV,J+N[@pUU2$!T!hCrt>;)oAXJ0oCMeXIqdp6oB`ka-3!oIo-%iOdf07SV)>OCma>9)s8N)I +rUU$`p\t;EC#]-`$haDoN`O'ts6+<:kkP/Y!7Li;#lFZ%!8dbU!8dVQ!W^[.rpp0`qtU15QsR>k +m_;q^q6a%Xo)J;N>5/%ON99lsQlPktoCLB1rq$0i!jP326lH7dTSs:qt'F-!<2TirrN1=mb%O?#i>1Q!q'uVq#:DJF7/i'"T.>p%Y(prrED^oD\jJ!;HKpK6-JUrs$)E?uo^uc1Cr8K_)kYn!m.'~> +ZMspZr;Qr+8o\cAb0g4p!mgo>])Vd1gY;`0K6$D1rri5]]N0^bs!$*E?>s2-jT!Dj=AGi +!7Cc:!;6*d!e4nCrppNiqYH4R_Z0YuCi8J3rrE,IrUU`/<`:Q4oFo0lsZs"K>npZIndp&"@Pd/a1;"9&6"dJ`YGo_ST;'4oSks5S>G@WZ%#bk(i7K(HDPl^COu~> +ZMspUr;Qr*8onoCajL+o!7LoA\c;[0hV8&3K6-J2rri5]]iKdbs!$*D??'5,jT!Dj;Km1[`W,s^ +:31JOSH&VV;Km1[`W,sM3W<4#:7V:_!qU=1rr3\lOCiWCIG"S5CM@p%Fij[\k.OfJrr3PhOCiWZ9!SErYtB(U9$.+T +rG)Ht+n#.*%)$FjF_#X:s81rlrr`#HU^6r!"gL[@DbA-Trr_b1IJqt$!=7QH +rrVV,J+N[@pUU2$!T!hCrt>;)oAXJ0oCMeXIqdp6oB`ka-3!oIo-%iOdf07SV)>OCma>9)s8N)I +rUU$`p\t;EC#]-`$haDoN`O'ts6+<:kkP/Y!7Li;#lFZ%!8dbU!8dVQ!W^[.rpp0`qtU15QsR>k +m_;q^q6a%Xo)J;N>5/%ON99lsQlPktoCLB1rq$0i!jP326lH7dTSs:qt'F-!<2TirrN1=mb%O?#i>1Q!q'uVq#:DJF7/i'"T.>p%Y(prrED^oD\jJ!;HKpK6-JUrs$)E?uo^uc1Cr8F7ZL8kEJSh~> +ZMsp]r;Qp`GOFTs@ab8fO8sLYmXP9:!8dbQZ%mt1p\4\;kconsXQJ6!jrV"=#5-3*]_T_%Gq +>6"UcOT+N"nGE6cBs[6FQHT/a@*.iCCTRQ9Bs[6FQHT<">'KDs>%7O'%))DS#kS)]%&EL_N$\H& +!gGtNrr3\4Bs[6FQHSuO@asnb>`f!T[;@@Brr3P0Bs[7!PaMLN4a6>V>%7O/rosR=:71hV([cc^ +eF:@\OT4gXD(0u7s7q.Tg>6Fiq=agjieUJ4rVm$aGOFTs@^#e5mVdUPrsAYTS;!9TqujD;LP)Q"s8RT"R"(4Ks,-l$gA([_kconsXQK5c^]4?-\%h?LAu^QNchmM;$1<"k%JlrrCpHrrR[gmed"i]iLHts1_[%rV6Bn +!8dSP"T[EZrrCpQrrN2WmdpGsLK`%&s80F?qsK/?_>g/Sdf9=YV#12lpODr:rrLsVr;QcthYR6Z +IlUo6s8T]R3H+?\rs!skmf/_^M#I>XqdNn3s#$&>rrLPhrVloT!;uiu!,q<#s!%EmS;!9TqpV_% +s8U9Di;M63LP)Q"s8RT"R"(4Ks,-l$gA([j_2Ef"VHeWNkconsW8dir76@I7kconsW8djWn?m*^ +!:'Rho*i>,`<^nfr;Ru>esqG[chl1ogAh2rXPj'.S;!9Tp](8CesLrKf)LX.GM;il,/K1ePdpeo +s6-_CKrF#2`C2kem-(^0KrF#2oQs]`n@_qeoD\fDo=Y4oS5+S~> +ZMspZr;QpaH0sd!A^^SiNrX=Tm=509!8IPNZA4(4p\4\;kd$#!Xl\%rrhi!D('o5s&YdrZg+5lZMTS)T_%Jr>QQ=^gO6PurnG<0cBX7'CQd#>c@EIuECT[W;BX7'CQd#K$>C#W!>@mj+%_VPS$Le#X%]&^bN$eQ( +"9"HN!<3!4X(\g,_O&L?IUgmt_HW*$n?m*]J,]HZX(\g,nW]LBs#t_qk?DNtq>T[]fh@e.rtk'O +A(:%c>EAgR[;@=As8VnFA_GmPI/!@7"6#)6!<)p$5'cPY>@k#K(%s7Ls9rr3#R!;uit!8IAM +$@b'EVZ6\.FZF=+q#:PJD=.=j?AAFY#5_n9s8P!ArVlrEFo21OI/!R=,5p)_KpL*_e#qC(s3%P&rRRKmQI,C+s*rUfJWJ=\N<"+Rq#;PTNIR/# +!<<)^Y_Rq2iW$ne3iD<,Y_Rq2iW&Lq=j-?K=d&CV!.sgN!pc:LJ,~> +ZMspUr;Qp`GOFTs@ab8fO8sLYmXP9:!8dbQZ%mt1p\4\;kconsXQJ6!jrV"=#5-3*]_T_%Gq +>6"UcOT+N"nGE6cBs[6FQHT/a@*.iCCTRQ9Bs[6FQHT<">'KDs>%7O'%))DS#kS)]%&EL_N$\H& +!gGtNrr3\4Bs[6FQHSuO@asnb>`f!T[;@@Brr3P0Bs[7!PaMLN4a6>V>%7O/rosR=:71hV([cc^ +eF:@\OT4gXD(0u7s7q.Tg>6Fiq=agjieUJ4rVm$aGOFTs@^#e5mVdUPrsAYTS;!9TqujD;LP)Q"s8RT"R"(4Ks,-l$gA([_kconsXQK5c^]4?-\%h?LAu^QNchmM;$1<"k%JlrrCpHrrR[gmed"i]iLHts1_[%rV6Bn +!8dSP"T[EZrrCpQrrN2WmdpGsLK`%&s80F?qsK/?_>g/Sdf9=YV#12lpODr:rrLsVr;QcthYR6Z +IlUo6s8T]R3H+?\rs!skmf/_^M#I>XqdNn3s#$&>rrLPhrVloT!;uiu!,q<#s!%EmS;!9TqpV_% +s8U9Di;M63LP)Q"s8RT"R"(4Ks,-l$gA([j_2Ef"VHeWNkconsW8dir76@I7kconsW8djWn?m*^ +!:'Rho*i>,`<^nfr;Ru>esqG[chl1ogAh2rXPj'.S;!9Tp](8CesLrKf)LX.GM;il,/K1ePdpeo +s6-_CKrF#2`C2kem-(^0KrF#2oQs]`n@_qeoD\f3o;r)WPY-H~> +ZMsp]rVm)n#ho=YYoM3l&2i0rrCpUW*4Oh:7V+Z/Gl5JEH,0Vb@[";n9]*3Ug.nZ>,&i% +4/hMpW*4Oh:7V@a!-a3%3Hp*^3V2M8?hDU?:4NMp1R"^X6m.l'LgAh2X*<5V[qu>(QoDdrkp](9$ +'`S+a]h5(#s8VtK>aU5,]Bo6@BAE=)>`JdUc)+6cs8VtK>aU4tSD+#7#ho=YYoN]A!fL^cq#;/u +IqRR4a]si*oZa7.J,fQE\\7t*Dh%TVrr_PkS,iQg#4i,Ss8T>DYQ"[jD1DH_,1D*YG@5RMMZ@tTq>V6-Ug.nZ>,(;#J,fQ:Dh%1n2fj%DER":Y$/P[^_+nTd +F&i;*rrVV,J,TBQIr4KJqt.R02?*U^?;CNE.Js/:"QK@Z"RcmKR! +ZMspZrVm)n#h]1WYTVBo&GAu( +3iD;nWa0mk:RqIb!-a9*3d69b3:lD7?hDU?9n33Cq0d5Js4:qFolU9%s8U%Ts5e+js8TGCs6B[P +s8U%Ts8O!WI=\EFqtl"MR"g^8mIbUFpAa,iMu/-2R"g^8mJ20Lf`1uU+TM%_qu+kMn,MKdp&G'! +)#jOe\k/[ts8VtK>F:/,]^>EAB%m%$>`SjVc)+3bs8VtK>F:.tT%j;:#h]1WYTWlD!fLX_q#;/t +IV.=/aBXc*oZa7-J,fQE\\7q'DLM9Qrr_MkRK3?e#4i,Qs8T;GYQ"[jCk)?^,1M6\G%#O+rr@8"Inj;LN<"+Rq>V6-VI+=_>GCA!J,fQ9D1Cqi2KNtDE6\1X$/5I[_G=li +F&rA+rrVS)J,TBQIr"9Dq=D7.3<&pa>u(ED.f',7#3#O\"o[ogrrC[Lq#Lq3iD/:9k&*DSbiBYc)+28lMh+\J,B2k%"7mJs!#sFCNa-/c+dVVrq1hTg=1ajG%#N`s8N(C +M189+s,@#$f_POl_G=liF&rAAqkQqqEEA_-`^W"ekG1g]EEA_-nu8njs4M"BoD\fBo +ZMspUrVm)n#ho=YYoM3l&2i0rrCpUW*4Oh:7V+Z/Gl5JEH,0Vb@[";n9]*3Ug.nZ>,&i% +4/hMpW*4Oh:7V@a!-a3%3Hp*^3V2M8?hDU?:4NMp1R"^X6m.l'LgAh2X*<5V[qu>(QoDdrkp](9$ +'`S+a]h5(#s8VtK>aU5,]Bo6@BAE=)>`JdUc)+6cs8VtK>aU4tSD+#7#ho=YYoN]A!fL^cq#;/u +IqRR4a]si*oZa7.J,fQE\\7t*Dh%TVrr_PkS,iQg#4i,Ss8T>DYQ"[jD1DH_,1D*YG@5RMMZ@tTq>V6-Ug.nZ>,(;#J,fQ:Dh%1n2fj%DER":Y$/P[^_+nTd +F&i;*rrVV,J,TBQIr4KJqt.R02?*U^?;CNE.Js/:"QK@Z"RcmKR! +ZMsp]rVm)]!;lfrecaD#&-1Seh>mTU!8dT)T_%T9-2768/GF?fd-.L?mX90?b@Hq3G>urQWk&"V +DsmRg/YM_'PU6)(!1'p;@(o6cg +IfTNC*,C%B7Os!3(l@fQK*mf3$p>'p;L +g.nI#!<<)iF#S/_??afs[ocs^J,fQ>F#S/_F%W8.iW/lUs4.>OrrUV/f(T+J[ocs^ +`Rl`Fmd^lNq\cMT_%T9-27E>J+!?<#ljo)hY@*i:0uZKlC]k]qlu78iad-$s*ntT +qlu78iad-$rr3&fDZKY6!7q$s!7plD"+O3XYl+MFrKLp9nA/@ijDT2/dqFd9P\/&,_-A;\rr@Q: +s/ZM+rrCpNs!(9Fg@sH5!<;r$OP&Q'F7T?\DsmM-OP&Q'F7T;%pA +ZMspZrVm)^!;ZZpdg"5"&-1JbgAq9R!8IB)T^hK8,kq-7/GFEjcKD4;mX0*>b@Qt2Gui5RWjqqU +D=.:e0VIt)PU-#'!1g5GZJ4krr3]"D1DTco5cj4G&pUHqC&N;WY4qqtIl)0EGm]bd +s8AB_rrLjSr;QctC&N;dPLfb`D2iS`j`ul5D=.,u#(LLWnSrpMr;QrL"TRK^p\4[ggAq0O +!W[*)q>VZ;Q"\8._-Du\EHUWsBT;bOBV=naPU-#'!.XJ9X9ek+!8I;K)c9trpZeD+J,6C2fAC(X +nc+)UmJ=&KfAC(Xnc&Oim;2k"rrCXIIfS!mrrr@L;dlm/rVn)?Q"\8._-Du\EHUWsBT;bOBZT\Z +>U9(D!.XJ9X9ek+!8I;K,>hh%pYKB[s8&s]fAC(Xnc+)UmJ=&KfAC(Xnbr+Rqq6jA^&7m1f)'pu +eGk-l[K>c`9n3~> +ZMspUrVm)]!;lfrecaD#&-1Seh>mTU!8dT)T_%T9-2768/GF?fd-.L?mX90?b@Hq3G>urQWk&"V +DsmRg/YM_'PU6)(!1'p;@(o6cg +IfTNC*,C%B7Os!3(l@fQK*mf3$p>'p;L +g.nI#!<<)iF#S/_??afs[ocs^J,fQ>F#S/_F%W8.iW/lUs4.>OrrUV/f(T+J[ocs^ +`Rl`Fmd^lNq\cMT_%T9-27E>J+!?<#ljo)hY@*i:0uZKlC]k]qlu78iad-$s*ntT +qlu78iad-$rr3&fDZKY6!7q$s!7plD"+O3XYl+MFrKLp9nA/@ijDT2/dqFd9P\/&,_-A;\rr@Q: +s/ZM+rrCpNs!(9Fg@sH5!<;r$OP&Q'F7T?\DsmM-OP&Q'F7T;%pA +ZMsp]rVm)a!;lfrecaD#&-1Seh>mTU!8cu@GCP*\!93hS/GlG_I?1r(s.j2?Sn,h;Wb[$D\a'3E +Dsm7?!-`pBDZJes!8@JQ!8@JQ!8co='^fmhs8VY2IJs32D1V`)LJDo7?3pQ129CJlKBE46LJDo7 +?3pT*R*u$&Io_m)EDX^D]3La'_eNS%C,:M3EDX^D]3L`oF+*rcs4.>Orr[aK>5S=!*Q`qTKBE4* +@WV:t!;lfcEDX^D]3La'ZrC:U<*mQCrtOm>J,fQOrrHVVp&>6'<*X]J +?H)pD!q'uVr;Qo7Bi_84o`"sK!<3!&joG;Ys4.=Wrr_hT>eU(8,4p">H[g5uET6jQOT.n)s69T. +It)A:ir9#'s8UpUs8N)Uq#;0-Wb[$D\a&U$J,fQ:Dh%1nFnG_+F`m!Frrh0YrsZU?rrKq9r;QcJ +qgSXHlM(;[mVdURrtU#BGCP*h=Bp@Us/5p9IsV06s76BQrVm&uR,\,GDuG.cZ1n8"IlDTNIgMLp +s8U,CNkJhlpAY-nDuP4dL]7;W!8dbU!8dVQ!W[._rdOs9f_POXrT<>9fX$'(SGW?dk\1%%rr39$ +df8UOTO'tZrr3#U!;uit!,qgc%@GfJIrDr?VS.,]IrFcMrs!skmeM3LhYdBW]bLM<"QKC[!rJAE +rr3#U!;uiu!,q<$s!%)X4*uI$2f\8Q@`P9V_=Vk3GCP*\!94"Yc2[h#!<<'!hY@*O%))Z?(>rSM +s59oCQ:c)`s*ntTs59oCQ:c)`rr3&fDZKY6!7q$s!7plD"FjH[g5uEUK3'a/!=` +oQ<6BIs3gOir9#'s8UpUs8N)Uq#:@'^&J$Q_#XN8i2W*/8oO.tIrFcTi2W*/8oO.ts5^&(HaWGB +rrCXIIfe4$K_)kYn!m.'~> +ZMspZrVm)`!;ZZpeHXG$&-1JbgAq9R!8Hf@FanmZ!9!d0!BDZJht!8%8N!8%8N!8H]:'^]aes8VY1I/O$0D1_f*LJ`2>>mUH029LPmJ`Zq3LJ`2> +>mUK'RF;'&JQS6-E)F^F\m(O$_e`h,CGLP3E)F^F\m(NlEd[]_s4%>Qrri:M;d9.uruK77F+NHQ +o4<"7f)YXFmr2.Fn%)_irj5X0G>qqgrr3]"D1DTcn8M7Gn*bubs5a7Vs8UURrr3&u@^'.S`W#l=m;7@OrrrD9BihA6o`"sH!<3!&jT,,Vs4%=Xrr_hT?G6::,5$+@H@L,tET?pUO8hh) +s6B`/It)A:j8T,'s8UgRs8N)Rq#;0-XDEeqaZM4A#Il;KL +IgVRqs8U,EO1eqkrqHEn!,_^b!/LRR"T[VZ3Fut#"G;jO1R:O@6B>O7a4*?%&DZJht!6kKCgAq9R!8I;K!=ljC +rtaL"J,\<*QBb'ZmJhZQmJbtCQBb'ZmJd+em;2k"rrCXIIfS!mrrr@N;d6=$rVn)7Fut#"G;jO1 +R:O@6B>O7a4*lC#2ZVnc`9n3~> +ZMspUrVm)a!;lfrecaD#&-1Seh>mTU!8cu@GCP*\!93hS/GlG_I?1r(s.j2?Sn,h;Wb[$D\a'3E +Dsm7?!-`pBDZJes!8@JQ!8@JQ!8co='^fmhs8VY2IJs32D1V`)LJDo7?3pQ129CJlKBE46LJDo7 +?3pT*R*u$&Io_m)EDX^D]3La'_eNS%C,:M3EDX^D]3L`oF+*rcs4.>Orr[aK>5S=!*Q`qTKBE4* +@WV:t!;lfcEDX^D]3La'ZrC:U<*mQCrtOm>J,fQOrrHVVp&>6'<*X]J +?H)pD!q'uVr;Qo7Bi_84o`"sK!<3!&joG;Ys4.=Wrr_hT>eU(8,4p">H[g5uET6jQOT.n)s69T. +It)A:ir9#'s8UpUs8N)Uq#;0-Wb[$D\a&U$J,fQ:Dh%1nFnG_+F`m!Frrh0YrsZU?rrKq9r;QcJ +qgSXHlM(;[mVdURrtU#BGCP*h=Bp@Us/5p9IsV06s76BQrVm&uR,\,GDuG.cZ1n8"IlDTNIgMLp +s8U,CNkJhlpAY-nDuP4dL]7;W!8dbU!8dVQ!W[._rdOs9f_POXrT<>9fX$'(SGW?dk\1%%rr39$ +df8UOTO'tZrr3#U!;uit!,qgc%@GfJIrDr?VS.,]IrFcMrs!skmeM3LhYdBW]bLM<"QKC[!rJAE +rr3#U!;uiu!,q<$s!%)X4*uI$2f\8Q@`P9V_=Vk3GCP*\!94"Yc2[h#!<<'!hY@*O%))Z?(>rSM +s59oCQ:c)`s*ntTs59oCQ:c)`rr3&fDZKY6!7q$s!7plD"FjH[g5uEUK3'a/!=` +oQ<6BIs3gOir9#'s8UpUs8N)Uq#:@'^&J$Q_#XN8i2W*/8oO.tIrFcTi2W*/8oO.ts5^&(HaWGB +rrCXIIfe4$F7ZL8kEJSh~> +ZMsp]rVm)j!;HNnc3Vht#lri^h>mTU!8cT0!!(UFru'[G/L5Pos1)U;LM#EChLdC*GM<(HDsm%3 +qZ%#TrrCpUrrCpUrrCp=rtOm>J,fQCGC05ek%fVLmbU81(k`J"rr\K&C[_9&"l;QG(k`Its!#Hq +KCa/.p]'AkKE(A$+Uh+IEo5f-M +s5n*Ls,0IZo;"sJrr3]#Dh%feo5f-Ms8U@Ms6fs^s8U@MrVlmA^%MC0Mb1JNUe.*UrrVV,J,B6J +dT1kpo`"sK!<3!&mf<+^s3:nSrr_CrA)dFd!Uan(!"!Gls'oR=(q'D#qZ%#TrrCpUs5!bUrrCpM +rt+%>/L5PomVdUTs6afTmVdUSrrR[ghYI0Sh>mQUf)G[Mec=t7!q'uVrVm7TDsmZ*pT7%:s8RHD +!!GXgrs6pKrrr-O`r?%qo)Ac@DsmK%"lmKR!mTU!8dMN! +ZMspZrVm)f!;$6jcOA5$#lr`[gAq9R!8HE.!!(XGru'[I/gPSls12d?KkK9BhM!R-FkH_DD=-e1 +qZ%#UrrCgRrrCgRrrCg:rtOj;J,fQAFaqQn)$P:*.eb$rr\Q(DhKE(D',S3aCL[fA.p]'>hKCsA4oDej:%/g,,V@<4uruICBD!S[VRrs%choDej:%']a6k@nGbr;QfdDu9SACY/StAcEaYs53\R$ePCYg].<.!<<'! +g\:^`hM!R-FkH(uJ,fQ9D1CqiIJs3EI;e$=rrh'VrrC[MrrL^Ol2LhPD1DNa$\*$\s8Vhp81=N< +H2IXF7K<5jbPhGEo.\)^!8I)E!e5(SqYp`U@s*nnQs3tsB67Qj>s8U5C6EKkNrr@ZB[K>c` +9n3~> +ZMspUrVm)j!;HNnc3Vht#lri^h>mTU!8cT0!!(UFru'[G/L5Pos1)U;LM#EChLdC*GM<(HDsm%3 +qZ%#TrrCpUrrCpUrrCp=rtOm>J,fQCGC05ek%fVLmbU81(k`J"rr\K&C[_9&"l;QG(k`Its!#Hq +KCa/.p]'AkKE(A$+Uh+IEo5f-M +s5n*Ls,0IZo;"sJrr3]#Dh%feo5f-Ms8U@Ms6fs^s8U@MrVlmA^%MC0Mb1JNUe.*UrrVV,J,B6J +dT1kpo`"sK!<3!&mf<+^s3:nSrr_CrA)dFd!Uan(!"!Gls'oR=(q'D#qZ%#TrrCpUs5!bUrrCpM +rt+%>/L5PomVdUTs6afTmVdUSrrR[ghYI0Sh>mQUf)G[Mec=t7!q'uVrVm7TDsmZ*pT7%:s8RHD +!!GXgrs6pKrrr-O`r?%qo)Ac@DsmK%"lmKR!mTU!8dMN! +ZMsp]r;Qp3`rH(/1"$"3!8db4!<<'!hY.$Es8Vi=q#;oBhW"Rh>'K`'CKbA4s81[4s4UY#pO@,L +qg/>;s8Vi=s8N)Us8N)Us8N)UkPkVODh%cd'8LA_s35/Cs6ebchY7'MS3m8$ +SGrO7`bUA0K6QqeibO>Lmcs]Lc#99TK6QqeibO>LLMOp0s-thDmn3TZ"p!ids'n(PrVll2qZ%1d +`IiC+s5IgLo5f9Us6=BLrr3&fDh%cd%YJuss8U(Ms8NYMs8STDrr3#$+nbj?kAu"Is6=BLrr3Dp +Dh%fepRJ&Zs*o+]o`"sK!<)p$&B=bMS2%5I!m+o=r;RumTU!8dMN(]3(Ks4UY#k&5bLs5%(3mVcn+s6=BHp\Fglh>mQmXoA>$XT7Vc!q'uVrVlqK +DsmW)#3ot(s8Nphrq?Sm!:Tqk>5eI)V#]ua!8d;H!e5.Vq>USU<_d;epAY-nhYR6U!8dbU!8dVQ +!W_*Fo`#6lGAHLDs7^_]p\t0oIfS@)rrVWhf)G[Nmf;hUrrLsVr;QcthYR6ZIrFb1CM/"gIrFcM +rs!skmf0Om9)\bopF2($s&rV)rrT"sn,E=fh>mKR!W[0,q>VZ9H[GYiqr6cmTU!8dMN!>qs>XT8CqGA$( +ZMspZr;Qp7_uKb.1su=6!8IP.!<<'!g[bF:s8Vi>q#;oBhrFah=a0W'CKY50s81^6s4CIupO@&I +q0;u3s8Vi>s8N)Rs8N)Rs8N)RkPkVND1DQb'8^M`s3>>Hs6nk>s8SZZs8QXmj8Ao_htR0NSji\* +T)SaCa)-\5KmE:ij(jGMn*BlNc>fTYKmE:ij(jGMM/1$/s.2(IlUh!RqZ,[Vs(+=QrVll3qZ%1f +`eA^0s5RmMnSrpQs64j)KGCs64r;Ru:H$T5cqr?l>s2,D^*<6'1!;$6jpWrm> +!8IPRgAq9R!8I;K(]3+Ms4CIuj_]MIs5%+4m;6Y)s64USU=&EVhpAY-ng\UpR!8IPR!8IDN +!W_!Bo`#6lGAHOEs7^_[p&=smIfS7&rrVWhfDbdOmf;\QrrLjSr;Qctg\UpWIr4S0CLqeaIr4TJ +rs!simJjCj8cAYnpFM4%s&iM'rrT%tli-nbgAq0O!W[*)q>VZ7H$T5cqr?l>iFuqANW8g"H1V(9 +pWrm>!8IPRgAq9R!8I;K!?A-=rta*aJ+n5$jo9gImJhZQmItm=jo9gImJd+em;2jgrs.j]qu?Nn +EUj#-,5HM0q>^?PkPsLTB5NY.q0@8]s81j&s8N)Rs8UgRs8N)Rq#:@7WrE#;WW<(nH"Z7=Ir4TQ +Ir4TIH"Z7=Ir4TQZ;?VPnG`K?o +ZMspUr;Qp3`rH(/1"$"3!8db4!<<'!hY.$Es8Vi=q#;oBhW"Rh>'K`'CKbA4s81[4s4UY#pO@,L +qg/>;s8Vi=s8N)Us8N)Us8N)UkPkVODh%cd'8LA_s35/Cs6ebchY7'MS3m8$ +SGrO7`bUA0K6QqeibO>Lmcs]Lc#99TK6QqeibO>LLMOp0s-thDmn3TZ"p!ids'n(PrVll2qZ%1d +`IiC+s5IgLo5f9Us6=BLrr3&fDh%cd%YJuss8U(Ms8NYMs8STDrr3#$+nbj?kAu"Is6=BLrr3Dp +Dh%fepRJ&Zs*o+]o`"sK!<)p$&B=bMS2%5I!m+o=r;RumTU!8dMN(]3(Ks4UY#k&5bLs5%(3mVcn+s6=BHp\Fglh>mQmXoA>$XT7Vc!q'uVrVlqK +DsmW)#3ot(s8Nphrq?Sm!:Tqk>5eI)V#]ua!8d;H!e5.Vq>USU<_d;epAY-nhYR6U!8dbU!8dVQ +!W_*Fo`#6lGAHLDs7^_]p\t0oIfS@)rrVWhf)G[Nmf;hUrrLsVr;QcthYR6ZIrFb1CM/"gIrFcM +rs!skmf0Om9)\bopF2($s&rV)rrT"sn,E=fh>mKR!W[0,q>VZ9H[GYiqr6cmTU!8dMN!>qs>XT8CqGA$( +ZMsp]r;QqA6MKXlPgTLA!8db4!<<'!hZ$JWkN?#/q#;oBIq%-[s-ui`hK*;_s05UhhMY:>s1)<"pZEuis8NXpmu/+`s4.1irRUoH!T!hU +rsl;7Phq^?T`4rmmVdUSrsl;7Pht7WC]FEU6MKXlPlC[`;KML1#)bpPig(nHrs#E&T]_t>SGrNi(a48*rt/.4kN?#/s8N)U +s8UpUs8N)Mq#;-,Iq%-[$m +hYR6U!8dbU!65!#s8N-#`n(+O!;$!c#H0e,db^3\T`+llIfS@(rru@M^$!sE6N-obh>mKR!0c<-&8V7E!T!hSrrN11f)Fh4oDJV3 +W)fSSTLk[Rs,.jl^&S,2mTU!7prF)h]'9n()CgJ+n/"k.sB"lMl?LlM#L8 +k.sB"lMgebmV`+%rrB1u!!'.rrrisumu/*Aqu7h<L*s8T#uT]_t>3WK*ZhZ*W4 +!<<'!f(f7eKG_,c]cdCUpNL94_*Vr&s*nhLpNL94_*Vr&l>'nTrmh)7r;Qc!qZ$]"s+L!W!q2XS +J,~> +ZMspZr;QqC61a7fQI5^C!8IP.!<<'!g](8Vj6']1q#;o@I:LpT=*scCOW6p+s7q(QfZmQFq1!,C +s/K(%j6']1s8N)Rs8N)Rs8N)RkPkVND1DQb'"V;9m>#&ks.)8`lcU9Rs3gtgrR_)L3]q!>b8dhf +f*%E.f/QgBs0>dnh2+t8s.)ubgN.#[s0>dnh2+t8s12E#oB.Nfs8*@jmtqtZs3gtgrR^uI!S[VR +rsl>;QJIBo[/GnPXQ,aBT`4rmm;7@Prsl>;QJL:TC&e3U61a7fQN$mb;/u:/#Q'EKXQ,aBT`4s" +m;7@Qs8@XK^qs//e +!8IPRgAq9R!7U`C(AWAgfZmQFmr_LSmu&08m;4&1h2+\(q#:ZT!<7cj[dUpQ!;uisY582"rTF4\!W^9mrT=1Pq#:St5'><0P"D>FrrRZMg\q-V;(THEdnrF0rrLjSr;Qct +a8G*/oDa;WmBfD@T`9LYmJ-\tIr4TQon+5Y_rJ +ZMspUr;QqA6MKXlPgTLA!8db4!<<'!hZ$JWkN?#/q#;oBIq%-[s-ui`hK*;_s05UhhMY:>s1)<"pZEuis8NXpmu/+`s4.1irRUoH!T!hU +rsl;7Phq^?T`4rmmVdUSrsl;7Pht7WC]FEU6MKXlPlC[`;KML1#)bpPig(nHrs#E&T]_t>SGrNi(a48*rt/.4kN?#/s8N)U +s8UpUs8N)Mq#;-,Iq%-[$m +hYR6U!8dbU!65!#s8N-#`n(+O!;$!c#H0e,db^3\T`+llIfS@(rru@M^$!sE6N-obh>mKR!0c<-&8V7E!T!hSrrN11f)Fh4oDJV3 +W)fSSTLk[Rs,.jl^&S,2mTU!7prF)h]'9n()CgJ+n/"k.sB"lMl?LlM#L8 +k.sB"lMgebmV`+%rrB1u!!'.rrrisumu/*Aqu7h<L*s8T#uT]_t>3WK*ZhZ*W4 +!<<'!f(f7eKG_,c]cdCUpNL94_*Vr&s*nhLpNL94_*Vr&l>'nTrmh)7r;Qc!qZ$]"s)[e6!p>e? +J,~> +ZMsp]r;Qoo@gE?]d/OUVhZ)F4s8N)Us6j+s#YO:Ss!u>t$kR(&s8TbqDsmZ*butMeB@d*U+^3Uo +mofu&9'?6S!8dbU!8dbU!8co=!q'uVrr3Q,XYgAI\c;]thDkQQSH&VZqZ-Zr"Pu-=(nCU*-+,0% +&:;+js8/`L#W]0es6bdb$sLpUs8/`L#W]0es8/p.#RGMNrs,qT!%Gqfs.&rdr;QfS!<3!.qku4T +3O/J]lAQkh/Y)G3rrVV,J,]HWqku4T#]'27s7^"/#]otE!=6+*rs%bI+TsBeoD\akmVdUSrrq+l +&/9?JoD\jJ!<)p#pL=I7F0>NEmVdUPrs&4n0aK4qoD\akHN<7.rt=d(#RF&cs8N)Us8UpUs8PCc +[J9b:butMeB@d+%:'_\TUGEqZ$kJqu?]]CH8rurrE,VqYpWthZ*WQ!!2Qgq>^PCqt^6s +qlrEq#W\mYrVlqK!8d\S#5;mM!"`Z/rVloT!;uln!"=5OIrFc7@nOlCIrFcMrsXBqmf3=Yk@5&' +(j#HMrrq7W#QQ$$r;QfS!;ulo!!.< +ZMspZr;Qoo@13Qed/OUVg]-".s8N)Rs6j)!#YjLVs!uB!$kd4&s8T_oDt!`+cUJP])Vg!h`M#]RfEDVqZ-Zr"Q)Db +'EB+qoDS[ngJJ@Z+G0LhrrLjSr;ZWo!Hn)6s!%351(#M"oDej'4E9oks7` +qu?_HoD\fBo +ZMspUr;Qoo@gE?]d/OUVhZ)F4s8N)Us6j+s#YO:Ss!u>t$kR(&s8TbqDsmZ*butMeB@d*U+^3Uo +mofu&9'?6S!8dbU!8dbU!8co=!q'uVrr3Q,XYgAI\c;]thDkQQSH&VZqZ-Zr"Pu-=(nCU*-+,0% +&:;+js8/`L#W]0es6bdb$sLpUs8/`L#W]0es8/p.#RGMNrs,qT!%Gqfs.&rdr;QfS!<3!.qku4T +3O/J]lAQkh/Y)G3rrVV,J,]HWqku4T#]'27s7^"/#]otE!=6+*rs%bI+TsBeoD\akmVdUSrrq+l +&/9?JoD\jJ!<)p#pL=I7F0>NEmVdUPrs&4n0aK4qoD\akHN<7.rt=d(#RF&cs8N)Us8UpUs8PCc +[J9b:butMeB@d+%:'_\TUGEqZ$kJqu?]]CH8rurrE,VqYpWthZ*WQ!!2Qgq>^PCqt^6s +qlrEq#W\mYrVlqK!8d\S#5;mM!"`Z/rVloT!;uln!"=5OIrFc7@nOlCIrFcMrsXBqmf3=Yk@5&' +(j#HMrrq7W#QQ$$r;QfS!;ulo!!.< +ZMsp]V#LM8@[r#LJcDMF!q'uVJcC<$JcC<$f`(r)o=Y4oS5+S~> +ZMspZV#LM9AY"DPJcDMF!psiSJcC<$JcC<$f`(r'o +ZMspUV#LM8@[r#LJcDMF!q'uVJcC<$JcC<$f`(qmo;r)WPY-H~> +ZMsp]V>gYhKO`G]s+13Frr_k6F7Xt_JcC<$JcFF'!/0sW!q2XSJ,~> +ZMspZVZ-etnV(K"JcC<$U]1GfF*$r(s+13$s+14'rr@ZB[K>c`9n3~> +ZMspUV>gYhKO`G]s+13Frr_k6F7Xt_JcC<$JcFF'!-@b6!p>e?J,~> +ZMsp][f6?8q>^O!KDYZNiaXF9JcC<$U&P4r/U-sDrrBJ'!!*4Tm/I+?[JBk'#daO3[9EG-!^L$jSo87>5J:#"G=hr!SU`S!!*4TYl=^2q>^L$RK!8pq>^L$ci3uuo=Y4oS5+S~> +ZMspZ[f6?5q>^O#L&:lQiF+.3rIP!"s.]MqX=SSig]%8Zq>^O#L$\g?fWt>R!"?46!O>l*!!*:X +nc&XB=o&*u%-7EhfNJ(O!="8"rrLbTqZ$X$Ks:RWZMFP$%%7+qZMFP$%+"qRK(HDPl^COu~> +ZMspU[f6?8q>^O!KDYZNiaXF9JcC<$U&P4r/U-sDrrBJ'!!*4Tm/I+?[JBk'#daO3[9EG-!^L$jSo87>5J:#"G=hr!SU`S!!*4TYl=^2q>^L$RK!8pq>^L$ci3udo;r)WPY-H~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]UAkCHDsi*nmcsfQh>i-,JcC<$JcC<$JcD#8!/0sW!q2XSJ,~> +ZMspZUAkCHD=)gimHX]PgAlg)JcC<$JcC<$JcD#8!.sgN!pc:LJ,~> +ZMspUUAkCHDsi*nmcsfQh>i-,JcC<$JcC<$JcD#8!-@b6!p>e?J,~> +ZMsp]r;Qoo@gEWeXT&HRDsi*nmcsfUmrSO/&DlUMs+gUR%K!7b$phH(s7bt[#S;(Vrs[IF!!lKk +s8VOc#S;(Vrr@EE!"_0$s8To-$n\:Ps7`0<$phH'rsZ_9$n\:Ps8/p.#S;(Vrrg=j!%GV\rrpt? +!!lKkr;Qf;!<)p;_+G+fV#12WN$S`]SFcdGN$/ +ZMspZr;Qoo@0dEeXT&HRD=)gimHX]TlZN7-&Du^O!r[n2rr4/=X>C>P])Vg'lW"3%EVoe9f0KNF +mMYj*)7SF5HTi"SZ=1&9WR(JcC<$JcC<$qYpPIo +ZMspUr;Qoo@gEWeXT&HRDsi*nmcsfUmrSO/&DlUMs+gUR%K!7b$phH(s7bt[#S;(Vrs[IF!!lKk +s8VOc#S;(Vrr@EE!"_0$s8To-$n\:Ps7`0<$phH'rsZ_9$n\:Ps8/p.#S;(Vrrg=j!%GV\rrpt? +!!lKkr;Qf;!<)p;_+G+fV#12WN$S`]SFcdGN$/ +ZMsp]r;Qq91#C2!PcarhIrFb)Dslcf!3Fa7 +9)noX;WlUcN;roUF8j`=iqNJl?A%+!Ar8u2KF::\&s&H;\5(g?+Z8kT'^!p:C# +pAYP_9!.*C:8%X]c?gjerr3.[::\&5L\:ZNCGF\k+d@QXJcC<$JcC<$qu6YLo=Y4oS5+S~> +ZMspZr;Qq;1u$>!QEC/jIr4S&D=-Kc!rri)AHR++?s!"mJLXgTQb5\2)'`7q- +**qab8P/m\!p:C#rr4=b?\@0t=18'e9!73E9q_HC4JLl#8YlBG:Un-!'`7q-**qab8P/m\!p:C# +p\t^"NCpA6S5#[VpWe+2J,]HPNDI'u;2>,Drrm6rgWq".JcC<$JcC<$JcGWI!.sgN!pc:LJ,~> +ZMspUr;Qq91#C2!PcarhIrFb)Dslcf!3Fa7 +9)noX;WlUcN;roUF8j`=iqNJl?A%+!Ar8u2KF::\&s&H;\5(g?+Z8kT'^!p:C# +pAYP_9!.*C:8%X]c?gjerr3.[::\&5L\:ZNCGF\k+d@QXJcC<$JcC<$qu6Y;o;r)WPY-H~> +ZMsp]r;Qp3^&S+s1"$"+V',gUSH!Y.rr4#_SH#E/!#V7+s*G:_+cu-ldUNgs;T8\9IrFb)DsmH$ +(&P+C&-u2&s0WF$s2GW,h>mQm!"cR(rrhVW?7g[+s!!51hZ(],R.ke)n,NF-.K@s!n,NF5!<;Kf +n,NF-.KBDsXo&,NOE9F]o@DpqGB`f]icgXeOE9F]o@Dq$LM"7"s-,81=s3:WC +rrPN&!<3!FOE9F]o@DphDgVB]l>(n7CNoOQk%B28F*mfarrD6^ec>1=s3:WCrrPN&!;HL0hJ[c< +s6=BLs6?+\/V!g#o5f9Us*o+]pAY:-^&S+s0nK@Us+13$s+14Irr@`D]`RYm:4N~> +ZMspZr;Qp3^An5!1su=.VBc-]Rf@A-rr4#bT)YZ5!$%I-qfrka,`qEncs[Io;8iM7Ir4S&D=.0! +(&P.F&-u;)s0`X)s2P].gAq6n!"cI%rrhSV?S$^+s!*;3hZ(`0RJ1n*li7"&/H=0!li7"2"TRoj +li7"&/H>`$Y5A.s/rt#2s7=k&pNLcQs5A0es,V0Ys7=k&s+b7$s8SBAs5sCVs8)fVs3gu6s8UCJ +rr3)t6#?W$s!EeHiW&N$j6po5qu>npJ*CZip&F2hJb*r0qu-Blj8[R.li7"2"TJE'qa#'!p\u&e +DgVB]l"PYIl'u*3J,fQ?F*mf_I<"fNrritJs8SBAJcC<$JcC<$JcGWI!.sgN!pc:LJ,~> +ZMspUr;Qp3^&S+s1"$"+V',gUSH!Y.rr4#_SH#E/!#V7+s*G:_+cu-ldUNgs;T8\9IrFb)DsmH$ +(&P+C&-u2&s0WF$s2GW,h>mQm!"cR(rrhVW?7g[+s!!51hZ(],R.ke)n,NF-.K@s!n,NF5!<;Kf +n,NF-.KBDsXo&,NOE9F]o@DpqGB`f]icgXeOE9F]o@Dq$LM"7"s-,81=s3:WC +rrPN&!<3!FOE9F]o@DphDgVB]l>(n7CNoOQk%B28F*mfarrD6^ec>1=s3:WCrrPN&!;HL0hJ[c< +s6=BLs6?+\/V!g#o5f9Us*o+]pAY:-^&S+s0nK@Us+13$s+14Irr@-3XTI[M8q6~> +ZMsp]rVm)b!;HNnc3Vku.dmA>pU("ic3X1E`X)>-!3Q"'!:PRif&EJsSG.4f[G"r2p]#_]mXK0C +q>V/1<`8,;8kT-`CJ+=EV>oBPs8N)Ukl2IeDh%feo5f9Us5n*Lo5ap#s8U(=s7bCLs"^)ns5sCN +s8U(=s8N?Z!!#oip\/l$:(/_Ip[`HMp]'5_J+n.V:(/_Ip[`HQp](9=#lO]+V&974pYPoL!LtV< +ruHGah>mTUpNK)%$n\.Hs,^-lo;PKTqs*VLs35#;rVm)MV&974pYPoL!LtVm9L#*;`r +o;PKTrr3&fDh%Za!n(D5pAY?[!;HNnc3T"$JcC<$JcC<$qu6YLo=Y4oS5+S~> +ZMspZrVm)b!;$6jb7)i!.dmMCoV/3=]OM>8P/s^Bhe=IVZ5BNs8N)Rkl2IdD1DTco5f3Qs5e$Mo5ap#s8U+Bs7b:Is"^#os5sCN +s8U+Bs8N?]!!H;rp\/l#:C]"Op[`HMp]'8bJbO@W:C]"Op[`HOoDej6%/p21qkb5-s7b:IrrJQ> +rr4!a;;2*Ns7^_+:C]"Op\g(K]()^oSbq[=s8U;QQ2^dgrQqDYqu?EFrr3"f/H5YOGu<;rp\tKq +ND[:WW(ro_rrVS)J,K +ZMspUrVm)b!;HNnc3Vku.dmA>pU("ic3X1E`X)>-!3Q"'!:PRif&EJsSG.4f[G"r2p]#_]mXK0C +q>V/1<`8,;8kT-`CJ+=EV>oBPs8N)Ukl2IeDh%feo5f9Us5n*Lo5ap#s8U(=s7bCLs"^)ns5sCN +s8U(=s8N?Z!!#oip\/l$:(/_Ip[`HMp]'5_J+n.V:(/_Ip[`HQp](9=#lO]+V&974pYPoL!LtV< +ruHGah>mTUpNK)%$n\.Hs,^-lo;PKTqs*VLs35#;rVm)MV&974pYPoL!LtVm9L#*;`r +o;PKTrr3&fDh%Za!n(D5pAY?[!;HNnc3T"$JcC<$JcC<$qu6Y;o;r)WPY-H~> +ZMsp]rVloT!<3!!h>l"(.dmA6p](!frunNECKgc,!2]Gip\uDEs8V],c2[(kqs%s8mf.cTmXK0C +qYq<-H["rYs5!bEs7[Zk>6"WZ!<<'!hWXtRmVdUTs6afTs8VM*J,blj>3FHs!<)pNms>nss8OfW +kLMWns6h\=s5#b)W:"RQR,3p"XT+YVT\bGDJ*q5(R,3p"XR:"Vrr3#U!<)rt!!'/!ruCKS(s`0] +[3>;$!<<)eD^h(1OA;nWhHg0^"EX#1rri)(8TX%nrr[`N!3Q2!$L` +ZMspZrVloU!<3!!gAo\%.dmM7p]'jbrueKEBjLc-"JYYjoD]rAs8V]+bl-ehqrqg3mJhZQm=0!? +qYq<-IL_GJ+%;*Rbs3$XRC(Wrr3#R!<3!#qZ$Vrr;Rc*Go4a) +s0a3EgAq9Rmr'\EhiLR6s4gl[!!I +ZMspUrVloT!<3!!h>l"(.dmA6p](!frunNECKgc,!2]Gip\uDEs8V],c2[(kqs%s8mf.cTmXK0C +qYq<-H["rYs5!bEs7[Zk>6"WZ!<<'!hWXtRmVdUTs6afTs8VM*J,blj>3FHs!<)pNms>nss8OfW +kLMWns6h\=s5#b)W:"RQR,3p"XT+YVT\bGDJ*q5(R,3p"XR:"Vrr3#U!<)rt!!'/!ruCKS(s`0] +[3>;$!<<)eD^h(1OA;nWhHg0^"EX#1rri)(8TX%nrr[`N!3Q2!$L` +ZMsp]rVm)j!;HNnc3Vht'tm3N3OSneF&N#L^&S,G8e="jrrCXIrsdOt0gR7,mf.cTmXK0CqYpTa +Du0MDf)Pc7!7q2Mh>mTU!8cr>'^fmhs8Vh;Hi*j*CP2ZThEh2ZKFn._"-aXu,a9-]n,NFN&B=Iq!;HNb +F*IBYk%fVLMb1JNUe.*Vs2e;-XnVbn"4F"Op\t1$PSe3)s8V6n`qS$ap\4[lMb1JNUe.*UrrVV, +J,B6JdT1kpp\tHd!;HNnc3T"$JcC<$JcC<$qu6YLo=Y4oS5+S~> +ZMspZrVm)f!;$6jcOA5$'u!BR33rSaF]J;M^An5I9b9:irrC[JrsdIr1.!I-mJhZQm=0!?qYpTb +Du0MDfDkl:!7UuJgAq9R!8H`;'^]aes8Vb7H27L'D2&#YhaIYfJe7q]"-sd@li-nfhaIYfJe7hZ +,0+>KnSrdIs5dsIs6l*r&5PlQnSrdIs5dsIolYQOs8UCRr;Qhq%/9c$,aK9ali7"J'Z9Ok!;$*V +EHh0Wj_9AGMbLYLUeI?Zs2nD0XS)Mk"4F%Tp\t1%Q5FK)s8V6r_t2=WoDA@`#Q'EQ\F64eSc8Wj +m;7@Mrr^\PJc#3A#3u9\s8UCRJcC<$JcC<$JcGWI!.sgN!pc:LJ,~> +ZMspUrVm)j!;HNnc3Vht'tm3N3OSneF&N#L^&S,G8e="jrrCXIrsdOt0gR7,mf.cTmXK0CqYpTa +Du0MDf)Pc7!7q2Mh>mTU!8cr>'^fmhs8Vh;Hi*j*CP2ZThEh2ZKFn._"-aXu,a9-]n,NFN&B=Iq!;HNb +F*IBYk%fVLMb1JNUe.*Vs2e;-XnVbn"4F"Op\t1$PSe3)s8V6n`qS$ap\4[lMb1JNUe.*UrrVV, +J,B6JdT1kpp\tHd!;HNnc3T"$JcC<$JcC<$qu6Y;o;r)WPY-H~> +ZMsp]r;Qp3`rH(/1"-(OpW!75[/f[qc3UNqs8Vi5s8TJ,mhiJ4s7<4tpNL94s*ntTs*ntTIrFcO +rtYEXHh[R?pWig=mheYWs8UpUs8N)Ukl1_PDh%cd'8LA_s35/Cs6ebchY7'M +S3m8$SGrO7`bUA0K6QqeibO>Lmcs]Lc#99TK6QqeibO>LLMOp0s-thDmn3TZ"p!ids'n(PrVll2 +qZ%@i`IiC+s5IgLo5f9Us6=BLs05=\oD\akpRJ&YrrrJds8QRhkPbD\^&%d0N; +ZMspZr;Qp7_uKb.1t)CRpW*=6Z2j@ncNpNns8Vi6s8TA)mhiM5s7<8!pN^E5s*nnQs*nnQIr4TL +rtY?TH1V(9pWrm>lPN>Ys8UgRs8N)Rkl1_OD1DQb'8^M`s3>>Hs6nk>s8SZZs8QXmj8Ao_htR0N +Sji\*T)SaCa)-\5KmE:ij(jGMn*BlNc>fTYKmE:ij(jGMM/1$/s.2(IlUh!RqZ,[Vs(+=QrVll3 +qZ%@k`eA^0s5RmMnSrpQs64I^nc&OsopVWRrVHTTs8QXmj8JuX^A@m1Nqr\[j)KGCs64c`9n3~> +ZMspUr;Qp3`rH(/1"-(OpW!75[/f[qc3UNqs8Vi5s8TJ,mhiJ4s7<4tpNL94s*ntTs*ntTIrFcO +rtYEXHh[R?pWig=mheYWs8UpUs8N)Ukl1_PDh%cd'8LA_s35/Cs6ebchY7'M +S3m8$SGrO7`bUA0K6QqeibO>Lmcs]Lc#99TK6QqeibO>LLMOp0s-thDmn3TZ"p!ids'n(PrVll2 +qZ%@i`IiC+s5IgLo5f9Us6=BLs05=\oD\akpRJ&YrrrJds8QRhkPbD\^&%d0N; +ZMsp]r;QqA6MKXlPg]R]pB\J,XT8+qruhr#s8Vg!^%\W/pN&(?ihT/9pNL94_*Vr&s*ntTIrFcN +rtMD4>3G'P3WK+OF8m\Js5!bUrrCX6rrVV,J,]H\\QYNlLgJ4'S2k*Jc5ok:)Yqrt(F`N#;Tpec<_gf)5OK +h>mQT'!YK%hMY:>s,0=Jig'nTrmhMCs+aLg]o3g.s4.1irRUoH!T!hMrs"BMXl>^?T`4rm +mVdUSrrn0U_7`a)p&>2:6MKXlP_&jcs+13$s+14Irr@`D]`RYm:4N~> +ZMspZr;QqC61a7fQI>d_o*E)%WW_qns!/;,s8VZr^@S?-o5ub=iM&l4olXp/^dDi#s*nnQIr4TK +rtMJ7=l\[N4TGFQEVh/As4[PRrrCO3rrVS)J,]H\\m(WjLL84(SNLKLb8dhfdf@GefDkk34D!j% +*4#Ur"IAN.3**WjZ<F(a/*Z<d/J"a*-&k9uXIs+13$s+13$s82frK(HDPl^COu~> +ZMspUr;QqA6MKXlPg]R]pB\J,XT8+qruhr#s8Vg!^%\W/pN&(?ihT/9pNL94_*Vr&s*ntTIrFcN +rtMD4>3G'P3WK+OF8m\Js5!bUrrCX6rrVV,J,]H\\QYNlLgJ4'S2k*Jc5ok:)Yqrt(F`N#;Tpec<_gf)5OK +h>mQT'!YK%hMY:>s,0=Jig'nTrmhMCs+aLg]o3g.s4.1irRUoH!T!hMrs"BMXl>^?T`4rm +mVdUSrrn0U_7`a)p&>2:6MKXlP_&jcs+13$s+14Irr@-3XTI[M8q6~> +ZMsp]r;Qoo@gE?]d/O6=(]Y_]rr3%H!8d_T)k7`F+g(eECCgapORE.^5n$M$-%l5IDsi*nmeQkt +pS:su#YO:[PUZA,c3W"X!<<'c!4D(k!q'uVrr3Q,XYgAI\c;]thDkQQSH&VZqZ-Zr"Pu-=(nCU* +-+,0%&:;+js8/`L#W]0es6bdb$sLpUs8/`L#W]0es8/p.#RGMNrs,qT!%Gqfs.&rdr;QfS!<3!0 +qku4T3O/J]lAQkh/Y)FW:Ab)!J,e5<&/6S5s8SVd!;uith>m9L#3q-j">-/8rr3&fDh%cd"km`' +'R/R0rri(+#RGL&s+13$s+13$s8)`qK_)kYn!m.'~> +ZMspZr;Qoo@13Qed/O6?(]Yh]rr3%F!8meU)kIlH,c^nAD%I%"P4/Fb64Qk,.#%\ND=)gimJ6bs +pS:q##YjL^Q72M-cNr"S!<<'b"L@:l!psiSrr3Q,X>UJP])Vg!h`M#]RfEDVqZ-Zr"Q) +ZMspUr;Qoo@gE?]d/O6=(]Y_]rr3%H!8d_T)k7`F+g(eECCgapORE.^5n$M$-%l5IDsi*nmeQkt +pS:su#YO:[PUZA,c3W"X!<<'c!4D(k!q'uVrr3Q,XYgAI\c;]thDkQQSH&VZqZ-Zr"Pu-=(nCU* +-+,0%&:;+js8/`L#W]0es6bdb$sLpUs8/`L#W]0es8/p.#RGMNrs,qT!%Gqfs.&rdr;QfS!<3!0 +qku4T3O/J]lAQkh/Y)FW:Ab)!J,e5<&/6S5s8SVd!;uith>m9L#3q-j">-/8rr3&fDh%cd"km`' +'R/R0rri(+#RGL&s+13$s+13$s8)`qF7ZL8kEJSh~> +ZMsp]^Ae4m3J%3]s+13$s+13$s+14*rr@`D]`RYm:4N~> +ZMspZ^Ae4o4G!N`s+13$s+13$s+14*rr@ZB[K>c`9n3~> +ZMspU^Ae4m3J%3]s+13$s+13$s+14*rr@-3XTI[M8q6~> +ZMsp]^]+B, +ZMspZ_#FN8o3$!_s+13$s+13$s+13$s4mVSK(HDPl^COu~> +ZMspU^]+B, +ZMsp]_#FMrF#h\/rrMJfqgSWt_1DW;s+13$s+13$s+LCPK_)kYn!m.'~> +ZMspZ_#FMoF#qV,rrMGeqgSWu_L_` +ZMspU_#FMrF#h\/rrMJfqgSWt_1DW;s+13$s+13$s+LCPF7ZL8kEJSh~> +ZMsp]_#FJqJ#MB,!UQobIfY,:JcC<$JcC<$JcC<$K`;%*o=Y4oS5+S~> +ZMspZ_#FMqJ#N>+rrMGeqgSWu_L_` +ZMspU_#FJqJ#MB,!UQobIfY,:JcC<$JcC<$JcC<$K`;$no;r)WPY-H~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]q#:Ek[C1YdJcC<$JcC<$JcC<$U]1=Io=Y4oS5+S~> +ZMspZq#:Ek[C1YdJcC<$JcC<$JcC<$U]1=Go +ZMspUq#:Ek[C1YdJcC<$JcC<$JcC<$U]1=8o;r)WPY-H~> +ZMsp]qu6iq[7^iXpOW?qs+13$s+13$s+13Frr@`D]`RYm:4N~> +ZMspZqu6iq[7^iXpOW?qs+13$s+13$s+13Frr@ZB[K>c`9n3~> +ZMspUqu6iq[7^iXpOW?qs+13$s+13$s+13Frr@-3XTI[M8q6~> +ZMsp]rr36![7_;H!#k$Os+13$s+13$s+13$s.]MnK_)kYn!m.'~> +ZMspZrr36![7_;H!#k$Os+13$s+13$s+13$s.]MnK(HDPl^COu~> +ZMspUrr36![7_;H!#k$Os+13$s+13$s+13$s.]MnF7ZL8kEJSh~> +ZMt*bq6`j3&,lP0'[;4/JcC<$JcC<$JcC<$U&P+Go=Y4oS5+S~> +ZMt*_q6`j3&,lP0'[;4/JcC<$JcC<$JcC<$U&P+Eo +ZMt*Zq6`j3&,lP0'[;4/JcC<$JcC<$JcC<$U&P+6o;r)WPY-H~> +Zi:-T;$mI(!!*\-JcC<$JcC<$JcC<$JcDDC!/0sW!q2XSJ,~> +Zi:-T:^R@'!!*\-JcC<$JcC<$JcC<$JcDDC!.sgN!pc:LJ,~> +Zi:-T:'q.%!!*\-JcC<$JcC<$JcC<$JcDDC!-@b6!p>e?J,~> +ZMt3V^4er`"TSsOJcC<$JcC<$JcC<$JcDAB!/0sW!q2XSJ,~> +ZMt3S^4er`"TSsOJcC<$JcC<$JcC<$JcDAB!.sgN!pc:LJ,~> +ZMt3N^4er`"TSsOJcC<$JcC<$JcC<$JcDAB!-@b6!p>e?J,~> +ZMt3es4'2(! +ZMt3bs4'2(! +ZMt3]s4'2(!e?J,~> +ZMt0bN&oK[!%%;hs+13$s+13$s+13$s.95jK_)kYn!m.'~> +i;`iV!WDrqqu?QprVk[R#MG%seOTZdon!-os+13$s+13$s+13Arr@ZB[K>c`9n3~> +i;`iV!WDrqqu?QprVk[R#Ln\neOTZdon!-os+13$s+13$s+13Arr@-3XTI[M8q6~> +Zi:9e3I^l*)AV81rrp0Hp:n*1JcC<$VuHkJR)/aRrrp0Hp:n*1qYpWj\%hk@!qs+<_uBf3mXP-6 +!UbGirrV><\Ujd3s5j7\K_)kYn!m.'~> +jSoJaqt^$\p%7nToDeFdp%S7Wr;PdW#MqFhqu7QLii)k\^UEjN[e>:PJcDYJ"4NUPr7M#Q^UEjN +[eBRt!qs(;r;Qil[_K`[!qu#2qu6]aItRb&jbLEus+143rr@ZB[K>c`9n3~> +jSoJaqt^$\p%7nToDeFdp%S7Wr;PdW#MqCgqu7QLii)k\^UNpO\+YCQJcDYJ"4NXRrRh,R^UNpO +\+][u!qs+ +[/UQ'26d,\p'AY7Z%IhBXT&HRDsi*nmcaZOL[O:(s24j9%K4kHKqR6$s82PhK87#:rs\1cItO]o +s8VhlK87#:rrBb2Ih8.*s8V#QKpL-bs81-.KqR6#rs[h]KpL-bs8CNRK87#:rrh>GJ!Aghrrq\\ +ItO]or;QfWJ,TB`i1u'mdJa+;`KGk>c2%D/]8;BTmf2FHqgSk=s6?80K;eD@rrg/nK;e\Ars&,7 +P(T(Fq>L?n_uBZ>n@OO6\aK.j!58>\!4f$-"Sn'Y^\ugb"b1IYIrFcOrrVbQSG`BgmVdU=rr@iP +rscf"K;d2rs7F9lJX4XZnG`RXh>mHQ!T!h#rr@iQrrh>GJ!AgerrM,/rr2u3rI4j +kPknfqY9dVo'l,8mHjf;$LI0Jn*oo?pA"Rbkl2'[26QuZp'AY7Y_._BXT&HRD=)gimHFQNL[XC* +!rg*Urr4/?eX;,ah#IENordGWj-,%XhY[_JcC<$jo5=4o +kPknfqY9dVo'l,8mHjf;$LI0Jn*oo?pA"Rbkl2'[26-]Vp'AY7Z%IhBXT&HRDsi*nmcaZOL[O:( +s24j9%K4kHKqR6$s82PhK87#:rs\1cItO]os8VhlK87#:rrBb2Ih8.*s8V#QKpL-bs81-.KqR6# +rs[h]KpL-bs8CNRK87#:rrh>GJ!Aghrrq\\ItO]or;QfWJ,TB`i1u'mdJa+;`KGk>c2%D/]8;BT +mf2FHqgSk=s6?80K;eD@rrg/nK;e\Ars&,7P(T(Fq>L?n_uBZ>n@OO6\aK.j!58>\!4f$-"Sn'Y +^\ugb"b1IYIrFcOrrVbQSG`BgmVdU=rr@iPrscf"K;d2rs7F9lJX4XZnG`RXh>mHQ!T!h#rr@iQ +rrh>GJ!AgerrM,/rr2u3rI4j +[f6m#96'^_s8U69s051;DbA-RrrmmjmXK0Ckl1YhhWXtBqrYRmKDtlpfR7(G>`%PAbEu1OF&L!W +s*%4ZF&&8*s-Q].F&L!Ws(4:jCFG4qs5K,PF^TXMs3#OqF^g9gs5K,PF^TXMs4D`lF)O[ms8R"< +G?SVXrrmUAF)t*HrVlrjDZKe:(ugWhF^TXMs1N2^F_#X:s0lHMBje7HjI6$_&Nq<*s*IpnF&nP* +s2A/_G=o.7rsZSVBm!s/aoD8#]iKdbrrol@B5(bZp&>&)3W'"m[IG"S3@UO7bFj9FU!q+nFqu6]R!71ZIpXZ,GrVm"= +l2Me%q=aFMmd06&kN:mejQ#:]jQ5Oel0@X'nalJNrp9Xqk"B^gg].;rUAqMkEGnIlXoAQSD=)gi +mHFQN#i4_E"T.>o+Ap)5qqrYOlK`:uU +\79/d@^H(-rrHalrbDM"]rVlrjD#jS8!G=CkCBojNs81rktrbDeDs8U)AF`g?]qu6]I!eLB&s5j7\K(HDPl^COu~> +l2Me%q=aFMmd06&kN:mejQ#:]jQ5Oel0@X'nalJNrp9Xqk"B^gf)PcmUAqJjF)O[mXoAQSDsi*n +mcaZO#i=eF"T.>pirA#"Ap%L0f`0TP8onoCaoDBZ,8q#@l +]4,/h@!0``rrVWF!;lcsh>kn%"8CBB#lXc+DcV'cB)MZ1o5=X.rrH^lrbDb!f`1j2]iKdbrtir@YZ??'5,jSuMt3W'"m[Z2=P%ec::$JcFj3!-@b6!p>e?J,~> +\,R#;1;NqihZ*WJs8P6+mcI\^dJjI"OFN25s1,*-ruR9Hs3d!YMp;;%]n_6%a7fPsZ%n%3irB%. +Dsi*nmeQl$rRS6/K;ePEs*rUeIur7]MZ@tTs*rU]`OVrHrrS;,k4\fXn?m*^J,]HlSm&GbTO+l" +N$4kcl?c_7l3sK[mYiIlpE?4$l?c_7s#lJCl7qf*s/Ke>m_b@6qi"q)m^$]1s/Ke>m_b@6s.3D< +mcI\^s76Z4pVdF0s6:#goBqP`rVlq`5lgoa/uiWlm_b@6p4$Q$nAALmq0ur/hI_33rUT7=in@.p +s6^H+oBqths8Q,HlLU7-q#;/uIq$ps_d&<'n?m*^J,fQEIq79iApsLKrrG9ErosLXVuHhdDh%Za +#jAOKKrjFl\Z#N-'p-]?dZAs:c2[gWLOYubs8UN/IuD;FrrmmjmXK0CqYpor\%ht$VM0l(>^u)> +"6,,7!<)p-ldZAs:c0#&iZ%n%7k3N$LieUJ4rr32o%))DS#kS#p!gGtNrr3!^OT+McnGE7T[;@@Bs8VtT +@*.iCCTRQ->-dFd?EjI$4co[.#l"B! +li.Flq"=4Hm-3]pj5JnPh>>t:h;7&Jj5oIgm-jNq;!s8UjRs7H?k5'cPY>@l4^#L\`a +NQhF4WrE#>Oj=!TOaiA7s8BTeKosFLs6$VAKrEu1s*nnQIr4TKru1jeZA4(4p](8CesV)MeGkL. +FkH_De;qPXWrE#"Oj<+_rrhi!D('o5s&YdrZg+5lZMTS)T_%Jr>QQ=^g +O6PurnG<0cBX7'CQd#>c@EIuECT[W;BX7'CQd#K$>C#W!>@mj+%_VPS$Le#X%]&^bN$eQ("9"HN +!<3!NX(\g,_O&L?IUgmt_HW)sJ7[EiB70IOo'>W$fh@e5m1?_tn!5*rs&XL3nW]L;rtk'OA(:%c +>EAgR[;@=As8VnFA_GmPI/!@7"[J.5lg+5ArrVS)J,B6Okd$#!Xl\Vp"b1CVIr4TLrsJJ.^]35jL5Da2J*[+:iJCD3rVm@/H0b!bQd#&I=j-?K=d&^_ +1?YlsKrEu1s59i1M5@W\s8TrqJ!7;@s8RSuO2V(qZA4(4p](9AOaiA7jo=PMLk`%nli.(GM,=9P +rs&')]_B>On,E=hrKKVKrr4T9O6PurnG<1S[;@=As8VtT@EIuECT[W.>I3Ug@']g)5`b0qlh1;X +=*a-%Pa_LL!S[U*s+143rr@ZB[K>c`9n3~> +li.Flq"=4Hm-3]pj5JnPh>>t:h;7&Jj5oIgm-jNq;!s8U[Ms7H?k4a6>V>%5nZ#L\]` +Mp;:4W;cf6!jrV"=#5-3*]_T_%Gq>6"Uc +OT+N"nGE6cBs[6FQHT/a@*.iCCTRQ9Bs[6FQHT<">'KDs>%7O'%))DS#kS)]%&EL_N$\H&!gGtN +rr4UNBs[6FQHSuO@asnb>`ec2AD5mPIJNj4lK[B_:72(L&@M,tQm)M06Fiq=agh4co[.!;EDk!q'uVqu6rcZ%n%7k1mMGrr3^pc2Z4RIuDSOs1O2; +T^Mj[dZAs:`W#l@IrFb)DsmK%$MLsDs59c.M5FO-li.(HLf+6Orsi(VlLU7+bOg-`bOE2IP5P>8 +kconsW8djWi223bVRZ^,s1sVCPdpeos*rU]`W,h?LP)Q"s8UN/IuDS6s6-_CKrjG&rr_8:;?6^r +#4i8:pVdF0rVlq`5lgoa!BlPDl5fBks7!F5.=_Btqi"q)m^$]1s.3E"lC_Ifs.2B8rosd`s8Q,H +lLU7-qu6]R!.k0$s5j7\F7ZL8kEJSh~> +\c2d!=(Ch#rrCpSrs&'#gAh2X*7Fj:pJPXNAkr)n/c5S-'Zp)IB7"5cpVBaKF^f1+qkQtrF',%2 +s*ntTIrFcNrufdA>'kaU5,]Bo35GPD-sD2J.B>aU5,]Bo?A +ESCHmYoNf8J,B8o%.jM]Dtj;3[1rW?*PG(@!<<)rK3XdtOLslQIqRR4a]si$\\7t*Dh%T]rtEA0 +BD;Go&A@u8S/hS'&?1G;SD*c<([ccch"f&sNW8XqVG;X@s7s4=rTJ0Lq=agl(iOnRW;!4i!pjcR +r;S;EUg.nZ>,'35/c5S-'Zp)IB7"5cpVBaKG>=(rpJOV1C.@sts*ntTIrFcOrsJ14J)H)cDfoS@ +J*[+:lEQiZrr3N'&?1G;S@e.YWjDU&g16'=s"sVK:1A9HT)7H$>&SOo,(o\rr_PkS,iQg#4m?As3_>Irr3)68u)Ids"5.,It+ECqu?<5 +VG;X@s7LMYp]'5iM>;R>m/P^MNW4nRDh!88p]'t!WJ?+rgACmOiW/k,s+144rr@`D]`RYm:4N~> +mJd^qq=X:Glf[Bhhqd&?f[eUNeGn&.f%A[1h;I;Skj.[-pA4@["790"o)AXhg\q-Vo*F"Ks0*Wo +s!mbCBmFDepRX#7s5Om9pJkpQBhe?9;K[.mCrH4H9k&*DSbiA9D=)gimJ6c)e:V%PF&/>+rr@8" +Inj;LN<"+Rrr?tQ\t1POs5Om9j7`L[oZa7-J,fQEI;nNPf4^6iTj_a\js0NXClN$>P +s2>iTru,m*JZ@)oqh&+?L60(8olYHOs5IpSqh&+?L60(8q0d57s8T;Gs7:a;rQkuAs5e+js8TGC +rr4"M8Yc@ds8.FQR"g^8mIl!@gA&`oNqp6!r8qmFp\k+*`J\=#s6UB1q=g=!s7[)%M1_DSq#;/t +IV.=/aBXc*oZa7-J,fQE\\7q'DLM9Qrrj<\I=\EmVuHhbCk)B_/,Q2NEH,3Wb+:6_s5Om9pJkpQ +Bhe?9;K[1ZM?!p&G&AD=)gimJ?him;7@2Lg4nAALi8rrr_MkRK3?e&,-X4M1_D4mII6] +qZ#5\MuEZ4qkQqqEEA_-i.tWWF_+VSs2%QGGB6gcs8N(;CV'PG?$UKeSH&=`BmFDeUA(NlEH,3W +qs==bl*?`Xrr32oJ,B2k%.F2q"1h("!<3!N)KC1VWmLYCoZa7-J,fQAFa!KYibj\MI;nNPf4^6F +N#H\aJZAJEpCO3YK:^iprrV'Xqgncus5s=]K(HDPl^COu~> +mJd^qq=X:Glf[Bhhqd&?f[eUNeGn&.f%A[1h;I;Skj.[-pA4@["790"o)AXhf)>UQo*F(Ms03Ql +s!mb@B7"5cpRj,8s5Fa7pJPXNAkr*:;KHtiCrQ:I:1A9HT)8P;Dsi*nmeQl*dt(\LF&&8*rr@7u +Inj>MMZ@tTrr?tQ]q?tSs5Fa7jS&U\oZa7.J,fQEI<"WRfOg-9FTCn1s2GcQk&:"os0WR@lN$DR +s2GcQrto[(J#L]jqh&.@Koiq5olYKQs5ImPqh&.@Koiq5q0d8:s8T>Ds7:a;s3_>Is5n7os8TJ@ +rr4"P8u)Ies8.FRR"^X6m.PpAh"f&sNVU,urTJ0Lq>L=,`J\C's6^H3qtHU#s7[)(LP),Nq#;/u +IqRR4a]si*oZa7.J,fQE\\7t*Dh%TVrrj6XIt+EkVuHhbD1DK`/,Q,IEH,0VbFgHas5Fa7pJPXN +Akr*:;KI%WMuWNL8pb;Bp](8CDsi*nmeZqjmVdU5LK\V=A1N/qrr_PkS,iQg&,-X7LP),/m..0_ +qu>A\M>dH2qkQtrF',%2i.kKSF_"MQs1qHBGB6dbs8N(;CVBbH>'k +])Mi=0splY!8d\S#3#X[s8UXQdf1fc!3,_HHhZY:df5[hqsONapY'ug;K(jibD!>#Z&mVtA97>A +IrFb)DsmK%+oRX;T_%T9-27E>J+!?<#ljo)hZ*TUF5$BN'[m#K1&LqAs%Vp"J,fQ;+J +Kc$WWl@/g6qsOLY]iod_o,+:pl@/g6pDdsuDae`nnoHa2F^B:>q1W.um^59/noHa2F^B:>n8WmR +s8UXQrVn-m;I]\+qsOLY]iod_s4U5Dh>mTUnoHa2F^B:>s0l*:Gue=lrr3)8Bj.b>rr_J=3S+30% +<"R[G;Y-R`##59*QIr@cKVuHhI@#Os_/,+6ed +-.L?mcG53s-PPIWp9;+LM&^;\Dag?!joB=DF%W8*rrVd+]n-37s5s=]K_)kYn +!m.'~> +mf*mrp[[br#I/3gBdg#gO/cC)r@K$6'mJlpn +=aU2Kg.nBs!<<)jF#S2a?['os[9$XY-Ir)rtOXM3n430rG=iGG;P6Xk5]FGF%`>( +rs/M1='^&O@)`-F!psiSrVm'#]kCTQrqHEsoc@suECch&rr`8L@Z:6b/,+/2s-PS; +mf]T^g3NEiEU3:jC&djA[e[oCj8]./D=)gimJ?him;7?O=IeU?TJ:rprrME9qgSUqpAY0J!<3!/ +k5]FGF%^`)rUfg\fnsOXrVn_NY`RJo@r_%;=IeU?TOS!%;.&&OhI?Qcrr@2qp%pG9naD\fp[86` +p"F]$o6FB^kGl&hrr3#aX8IGJWp06W!S[VPrtYK\;doY)s4U5BgAq9Roc@suECb6!rr482D1DTc +q1W2"n$PE2nSrsSs64?OqM?81I;qfPs5sB#H#lWSqu6`f(;,4uJcFm4!.sgN!pc:LJ,~> +mf*mrp[[bo"IJa'EecbsO,_&^$s8Vuas7a31 +HiO-":8H_Hs8VY22fj+JGL?H"=C-8R@)`-F"2APRYl4S(k_2%Jrr3FG6@3o6MuVo>Dg-7pV"aln +[ocs^urQWk%Q['[m#K1&LD>WV>+n +pJPqppRl:$pM726aT1K's*ntTIrFcOrsJ14J#68:kj"fqJ,TBJlIPi^!7plD!T!hUrt"5lDg-7p +GL?B]pAf]$F_ntoHXkj"gM!<5f#g@sH5!<<'!F5$Bc/YM_'PU6(m!3,_HHb&`l +<7'j/\blC+!U?]_IfZS7pAY0M!;lctIo9bTrsmaXUtu+Os7[LKIr@bsci3qimVdUTs7q@TbO2c& +J+.H+rVu/"Jbt9DCO^7kT`=-SDg-7pV#1/no+\lhs+144rr@-3XTI[M8q6~> +]`/*)A6/'orrCpSrs%Tcqu?]I"Od<"pKi&rSC[`?8q6k(MuWNXGCuF:rt*0Ls7tU8s59oCQ:c)` +s*ntTIrFcOrtkmTU!8dbU!6P6?"A41D4cAn2'^fmhs8VY2IJs32D1V`) +LJDo7?3pQ129CJlKBE46LJDo7?3pT*R*u$&Io_m)EDX^D]3La'_eNS%C,:M3EDX^D]3L`oF+*rc +s4.>Orr[aK>5S=!*Q`qTKBE4*@WV:t!;lfcEDX^D]3La'ZrC:U<*mQCrrg)NmQT&)mS^md>m9R/d3IVL*Zto)/M"i2W*/8oO-OAD[@di5#En%))Z?*PhaUrrC1@ +s69T.It)A:iqHc'K:LNXql4F[O1FQSrrMDbr-naIlLt5Yh>mHQ'Rs"aqu?7sF8+Agqu?DXdf8`b +7>IJs32D1VT)R*u$&IkC][!2/cJLb\c+!a\LrJcC<$kPkO8o=Y4o +S5+S~> +nGa1$q=X7DkiC[Xg"+X"cHOGPa2Z*s`"L&/a2lEIcI(+lg>1`Kkj7j4r:p9onmkefqu6ZNrVm)` +!;ZZpeHXJ%.ed@qK:C>IrrC:Cs4[PRrrCgRrrC.>rrbrFrB-)1rtOj;J,fQ>Ed[fbl>;.Q_hV*3H!L^Ts#%rOF+NHQ +s+aHoH!L^UopO8,mt"T,n8M7Gn%)_is2/Y_GAAsdn8M7Gn%)_inoB-Rs8UURrr3-"IoBSKrr4Lk +K6)V*s79fCo@F!^'.S`W,u;^1gZLrVuosYAJRarso!C5'_9+MuV`9U@7Q* +0_kYQrNoO/G>qqgrr3&eD1DNa"oc5M=0);ors&.adf&Wc/u\dPnrd=mr;S;EXDEF5BijJR?NZh"(p')pagQBb'Zm=o60s8Uul!<3PCrr4"S!<<'!aT);+!d0!BDZJhl +@<[?ndcgS0CjhARkl(M^lDsi4!NPDXrrLjSr;RN1IoBSKs79fCo@F!QN$mcm;7@P +ruT'o@s;]DKCX&1r;Z&!K)3NUdf&Wc0)sl1U@7Q*0`(eN@8$KKs+145rr@ZB[K>c`9n3~> +nGa1$q=X7DkiC[Xg"+X"cHOGPa2Z*s`"L&/a2lEIcI(+lg>1`Kkj7j4r:p9onmkefqu6ZIrVm)a +!;lfrecaG$.ed7nK:LNms%7e@4cB@7??_$lf)H<#s8VoQk5XA>QBk-]mf.cTmXK0CqYqB(FZXes +Ir>>HrrC=Ds5!bUrrCpUrrC1?rrbrDs#l;2rtOm>J,fQ>F++#el>;+P_hV!.G?tRSs#%oNF+`WT +s+a?jG?tRTpR'D/mXJ9'n8V:Fm^l_ks2/SZF_W^bn8V:Fm^l_knoK6Vs8UXQrVltL;HitsruK46 +F+`WTo3ue8g&V$Mn8V:Fm^l_ks0Pa1GuS+hrr3,9Bi_84rr3&)DgVH_%;J%QG;5$UiW,Y>mYEUl +rs#i0>^9:T`W#l=mVdUQrrg)NaYQkjJI9N[IX7q&`B[TQ:c)`Kk0'?s5:]nrsZU?ruJoUs8N)@s8V?aGCP*\!93`ZGCuF: +lMISsI?1r(rVlo`Wr.>IWp95S?k@WV:t!;lfjR*u$&Io_m7rrVV,J,]Hh_eNS% +C,:M5F++#el>;+La-bmYEUorrQM!qgncus6'C^F7ZL8kEJSh~> +^&J/L18amI!8d\S#58D[s8TJHdf1fo_5)j1>5/&i/_1cqs80X.LNcqr%)r8Hq:+-qVJD*_<,_4) +IrFb)DsmK%(\Di5H[gNlBBoKshZ*W4!<<'!hZ*TUhZ!NXTIn_3hX^[cmVdUTs8.:emf2!SNW8G' +S:?HY.KBFQAq.5_rr38\S:?HY.KAPTrr6-@BncA0GP2!sCPDfV]oIf+>a5&2GP2!sCPDY8F5Hrs +[2f5@V#UJC"RlBjZ"AKnp](9A#\/3s!.2]?H[#/_jD0JFIq%.-a'+K"c-cRTCO,[Ss6=r\hZ!Nb +o*i2,pQus#o*"CZs2kcFrs/.F@bUXu>)iO]%.8%`s8Vu@dJrEGH2%(5!TqZ+rrL+JW;ctjQt!c, +s""')Z^-)SW;HUf/_1cqs80X.LNcqr%)r8Ho4+CQ"ibDsiaHU_#XN8!8dbUl2Z0\ +IsYhnqn](`G?Us@hQrMtBW1gnrrLsVrr3T)V#UJC"RlB=#\/3s!.2]Ic2R_E`bC/+rrVV,J,]H[ +m_+X7NEh_FIrk&Xg1ZK:g +nc(0=q"*q=jl,%Kf$_mgaiD?:_8!^t]tD"k]tV7t_o9[:iOlL4B@qYpW418amI!8IJP +#5&>Xs8TMMdf1fo_PW*2=nhrj0%(Tns80[1LNQbo%*&>Iq:+-qVe_3`?#4;Hj +Z>"`qp&G'?$=S3o!-uN:H[,5`jD'DCIUq.+`a"T$bg?@PCO,XRs64iYh#@<`mgcr)op?m%nHS=Z +s2b]Ers/"AAD-^s>`\ma%..n]s8Vr>d/N3DH1q"4!U%c-rrL.MW;ctkRUa#.s""')[$H/RW;HUh +0%(Tns80[1LNQbo%*&>Io="\n=+?R:!8IPRIr4S&D=.3"$LM\[L1fEDs5.46l2Le=!<3!.nHS=Z +s2f#0s4E4,CQ"icD=*'jhuE`6^]=BE_#F?T^]=E7!8IPRkl?!YIsYhoqnf4cF]k[= +hR&StB;k^mrrLjSrr3T'V#C>?#4;H=$=S3o!-uNGcMmhFa(gA.rrVS)J,]H[n%Oj +nc(0=q"*q=jl,%Kf$_mgaiD?:_8!^t]tD"k]tV7t_o9[:iOlL4B@qYpW418amI!7q,K +#58D[s8TJHdf1fo_5)j1>5/&i/_1cqs80X.LNcqr%)r8Hq:+-qVJD*_<,_4)IrFb)DsmK%(\Di5 +H[gNlBBoKshZ*W4!<<'!hZ*TUhZ!NXTIn_3hX^[cmVdUTs8.:emf2!SNW8G'S:?HY.KBFQAq.5_ +rr38\S:?HY.KAPTrr6-@BncA0GP2!sCPDfV]oIf+>a5&2GP2!sCPDY8F5Hrs[2f5@V#UJC"RlBj +Z"AKnp](9A#\/3s!.2]?H[#/_jD0JFIq%.-a'+K"c-cRTCO,[Ss6=r\hZ!Nbo*i2,pQus#o*"CZ +s2kcFrs/.F@bUXu>)iO]%.8%`s8Vu@dJrEGH2%(5!TqZ+rrL+JW;ctjQt!c,s""')Z^-)SW;HUf +/_1cqs80X.LNcqr%)r8Ho4+CQ"ibDsiaHU_#XN8!8dbUl2Z0\IsYhnqn](`G?Us@ +hQrMtBW1gnrrLsVrr3T)V#UJC"RlB=#\/3s!.2]Ic2R_E`bC/+rrVV,J,]H[m_+X7NEh_FIrk&X +g1ZK:ge?J,~> +^]+E1ED,EgrrCpRrrk]Aqt"9+df1fkBAWNj!;$6Q'OFQ7s7^0_qlp.m7<@b`bB^&\GA$'r?u>91 +IrFb)DsmK%(]BM1W:TVZKE(rOhZ*W4!<<'!gAh0QhZ!NWjqM,snc&[YDh%cd';/gLpSq],s2Bnu +pZ>Y+s6;=ghYug?s#Z.@pZ>Y+s3aZapZd69T`:Hn_sY!?T)Zd"ddEcC^&OO6_sY!?T)YHmK_t@M +@fQ0SanNa_oDe)1J):1i!WV$UIirY0J&UE9B>a/H?CptB@+=\^>*/WEHf3aoJ,0-E^LX/=rsnW/ +[I`gN+ohR"R-sBl4o#$^o7(/tnA/:irr3DpDh%feqiZ>kj"luJ,TBJ +lIPi^!7plD!T!hTrsgoRmd>R2T(J%FYN5!6o_neHpNL94i*^EgVHBGRlCqnks%[%WpYKN_s8N)U +s8VsFW:TVZKE([YhYXPXH1eA8m+,9aq>UBplDjc3!NPGYrrLsVrr38u/]mb+%.jMUrdP;o!.Vu9 +: +o)C??p[[_8jPS_De'H4Y`PTC']=GDZ[C*BK[C*HQ\\#Pj_SsU?cdUM#i8j.joD/@b"8?e.jnSiU +g\h'T7"Y7+@qF*g.eRH2q5sehs5t/):B1@eBAWHe!qAa$lgW&BW:kFJjkjK^li2HOm=0!?qYqB0 +O_CW^o;[NErrCgRs4[PRrrC[NrrCgQrr_F"?V^.k!psiSrr3Y*A%hT:A?u6TNNVs9?$lV914e9\ +li6uHNNVs9?$lV#;9JY!Iq#,ORq]h/[U,O)`gtNETkqFBRq]h/[U,O)Tjr]Mo4)A3o.7oU_@5o5 +kq[hGrp'UbhYr!ID#f(*igXZ#m^?GmpO6Q"n\JFkq2]gXgh2'0s8TiEU](3&mgc_rn!#$rs#?J9 +m=.7nrs/"A@FOb_>EJja%..n]s8VnUH/@=hIJc`9n3~> +o)C??p[[_8jPS_De'H4Y`PTC']=GDZ[C*BK[C*HQ\\#Pj_SsU?cdUM#i8j.joD/@b"8?e.jnSiU +f)5OO6%o.,@Uddc.edT4qlp.ms5k#%:B1@gBAWNj!:rX#lL;uBW:tLKk29Wam/MQRmXK0CqYqB1 +P%^fco;I'p;70n8'Z +lMplGMlla9>'p:t:*/a`%.8%`s8VtZHf3aoJ,/j=#1?8gpZd5oVuHhPArHTe/,QNEm+,9aq>]Rk +?;CP#pLi@[YlOAaF6`Lm;ZH1ccMt]\gAh2&Dsi*nmeZqjmVdTP=J"aAU,.;trrME9qgSUspAY0M +!<)p-243h"IpS`=N``LGlK\$?s"sIoBBJ,[D="-%T'5I6B)qs,@bUS5B)qu3!8dbUq^JK8o;ImQT#kopmpVdR4s5*a)$Z?#p`R7N'pZd69T`4rm +mVdUSruT.LddEcC^&Oa7^\=a;Yiq0DanP(M63$sBR-sBl4o>6\l2^+#s+144rr@-3XTI[M8q6~> +_#FJZ24=1?9.s!mnDC3sSoqu?[k#d"(+qbh0UB3+o?:31PeCrQ:?<-a6u8@SVK +Dsi*nmeQkuc[Au@G>aP&rrCpUs5!bUrul13rrCpSrrPq$XRZ;lmVdUSrt=%'F.gs8TV0A,U3@rrLsVrr3_aIq!J.J_:"7BP2BqF34Hd?>4+a\,QC/Q=.i4rsa0UF)t6Ds8T3! +F)t6ap\tH0BP2BqF34F]!q'uVrr3/+?>4+a\+]h+l<7M&SV%0n90%!UA85IfS'nrrLsV +rVm5,;KHnWJ_8a_0k^K$!4De*)uH('H&#?i\)<$FDg-&.!<9l1Ap8H/!<<'!hZ!NiTNZP`;NUqU +;JLPkmTUl<7MHX_oooV,MqGB6rus8T3!F)t6aqu6]R!.k0$s5j7\K_)kYn!m.'~> +oD]U(p[RV5in`;;d*0SL_7mOk[Bm-BYHIi+)Qp9JZEq!L]=u,#aNW#]g"tcQmIL&Ps3!9fpAY-I +r;Qr/;1Ejdb0p;>q,;'SB34p/s%Wp(s8VmnCO'Poq7'1@F^o:-qO(?/LfR*Vs*nnQIr4TKrt`t0 +=a,'iOoPF]g].<.!<<'ACY8Xeg\q-R:("/orrVS)J,]H\g4O'dA;oUL`f3f=F'I;s[K'h9C]FDq +NDOh?>M/uA>@D_u=GY`3eUM(M@Y!)1`c#C1B:8)ceUM(M@Y!)1g4N7PGtuN^rF8uS>AsK?[K'h9 +C]+25gAq6Q)qBFJF(0[OrjuoYF_#R8rif^BBk=aPs8J6iaSl,KrHVUiF]4P)s/].?F&9+9rs/M7 +C1qa#F34F]%..n]s8W)&?>+%a[f6%$#4#K%GBI#tVuHhbCk)B_/,Z8OEc>Bar;Zdl$EjF/q,;'S +B34o=:31VTMuWH6CPRckDu]ieD=)gimJ?him;7@2LK\V?D)$P=rrME9qgSUqpAY0J!<)p)XAa1] +:k70O>;9diCB1d@ru:g&&SV%@fZPG9kSZo@fZQ/!8IMQ'r"iTG?'e*q,;'SB32&- +VI+@_?`@D_u=GY`2rrVS)J,]Hh`c#C1 +B:8)cg4O'dA;oUBUK_/NF]c0Rs/].?F&9+ +oD]U(p[RV5in`;;d*0SL_7mOk[Bm-BYHIi+)Qp9JZEq!L]=u,#aNW#]g"tcQmIL&Ps3!9fpAY-D +r;Qr.;L`mcajU2=qbh0UB3+p0s%Wj$s8VsoC3sSoqmfICF^f1+qjLK0LK$gRs*ntTIrFcNrt`q/ +>'G0gOT5=\hZ*W4!<<'BCYJdghYmHU:'drkrrVV,J,]H\g4O*d@Z0=I`J[K8F'@;t\H$.2'#A>$cDo>)D&7eph.M@Y*23`GB"+At&)deph.M@Y*23g4N:QG>?9[s'f/U>&XH@\H$.< +C]+25h>mQT("RkDF(0^Qs1;rWF(044s0,gDBk4^PrrSKoao)/JG?T'/;O%4]X\s4];R,ur#.nP] +F(044rr3&fDh%cd"gDmNS$E[3IF&ISA +gfuRHrbDMArVmi6RT+Qf<(OLVLK\V?D.IWp9$cDo>.O\5X\s4];R--!!T!g-s+143rr@-3XTI[M8q6~> +_Z'`8ImMo`rrCpRrri5(K7g_]rrgT)Iur7\rrRfmmf*5*c&7(5f)PcXLP(]/pAa!BKr22>_>h9Z +p:n*1q>V6-dB%tQV"=WdJ+!@:mXP9:TRY\qJ+!:8!eVK\nc&[a\%hqB&H11KK:^lus7YlNIuDSO +s3:Fjs8N5iS:?IArr4:uP^eJ%hZ*WSe!PcXf_tjD`JoSQk5YJ[e!PcXf_tjKf!0j[\c2U6g5pfK +irB&'qg\PD!UbI:rtYL]R"Lp]rVuN/OF`_Bq>]fhLP`Y4rr3&9Y3c)d%I0U9K;eDAs8/mMK=1UN +rs&,7OF`_Bq>UBqp:%g9rrqbsLP`Y4p&>0KP^eJ%VZ-_cDh%Za"m2n?LU6:FrrRfmmf*5*c&7(5 +f)PcXLOYubs8TcbLW,p=s8TjCp:n*1qYpor\%ht$VM0r9W5%Ts!UbI9rs8M'K7gSarT:!TIfR7] +ru0aeKr22>_>iGjKo<@eJ,fPbMgq,BJ,fOumf*5#pT0""V"=Wdc&7(5f'<:tXG)G9lKS +o`#a*p[RV5iS<&6cH=,B^:Le[Z*(1.W2HNkV&?/.W2cr%Z*_!O^;@n4cdUP&iof\!om]9`p&>$H +r;QosY_@eBd/O6oMgpl=rr3%P*q0+.)pX@kOjsF0[YKNuaS5_kTSBDPPJ[@7[e@JEpA+_(qpDL! +K;S8?s*sG9s6Y=9s.Fqqs*sG7rrRfon+$DZp9qa8rt,.ZRY@Bhs8Vi^SUldEs8U=?J,fNOn"##h +bl7VgfTgrC]\NMdr6gpbSCd`>pVr8LXl]T_r6gpbSCd`>rRJ-+Kt@9`#MP5gQ/hoEbklnfrrMM: +rr3r8d[,WWfDYaB`fYn>c2%D+[>0XPnc/Xg^om#$rse:gJVC&os8VnrK8$]0p\tHj`fYn>c2%A= +!qs(;rr3/g[>0XPnb)ncfTgrC]`"c.!psiSqu6i^XbDS;l2L\aK-'L\ru97AJ!&7\rO.K4U@8-^ +^4V9^U4rXI^UEjN[eBRt$MLpCs50]-Mm]P!l2LeOJ,TBQq5f4&[+G9 +o`#a*p[RV5iS<&6cH=,B^:Le[Z*(1.W2HNkV&?/.W2cr%Z*_!O^;@n4cdUP&iof\!om]9`p&>$C +r;QosZ%I\>d/O6nMgpi>rr3%P*:Eh+)pO:jOOjI3[>0Bra7fPiTS98LP/715\+[SGpA+_(qpDKt +K;A,=s*sJ:s6bC:s.Fkqs*sJ8rrRfmmd^;Yp:%g9rt,.[RY.3es8Vf\S:?IAs8U@@J,fNOm[Scb +c2R_hg6@)B^#&ehrR7-dSCmf?p;N#FXQKQ_rR7-dSCmf?rRS6+K=V!]#MP8hPiD`Cc23"grrMP; +rr3`3e!PcXf_tjB`K5Y:c2%D+[>0UNnG`Fh_63/&rse7dItO]ks8VttK7gT/p\tHi`K5Y:c2%A= +!qs+0UNnFceag6@)B]r(f2mVdUPrrqPdKnm#"rr3%P*:Eh+)pO:jOOjI3[>07)mf3=# +K87F=KE(td\+[SGpA4drp:%g:i223d[A^k)rrMP;rVm0#XFl/5f_ss0r-n^]r;R\lTS98LP/71V +VM0r9dXV;s_2Ef1dXV;sJ+!=9'_e\]K;A,=s36IOOOi^sk,a8lZg-fT!UbI9rrh&;J!AsirrMP; +rr3/UP^eJ%hYmHUp:%g9rtY7HNJ4Irs8W&KRY.3es8VH>P^eJ%rr30!XFl/5qtpBoh>i-,JcFj3 +!-@b6!p>e?J,~> +_uBhh3f +p&?oJq"!e7iS<&4c,df;]=5,NXJr+oU7n3NSt2FDStD^OV5L>qYd:gN^Vn4 +p&?oJq"!e7iS<&4c,df;]=5,NXJr+oU7n3NSt2FDStD^OV5L>qYd:gN^Vn4s3:QGn8WaLo`#!ae?J,~> +`W$&=N&oJZrrCokrr_8%3RZsf!UQobIfY,:kl1bEF#h["s+13prri.Z:pp2prrhiNF#h\/rrMJf +qgSWt_1DWjrrV>,F+O@Bs5j7\K_)kYn!m.'~> +pA[&Mq=F":iS<#3bf@T6\[8TBWMQ>^SXZ%7Q^3o%QC!u,S"64IVQ$]%[CO#ga3;r_g3bcsoDJ7\ +!8G +pA[&Mq=F":iS<#3bf@T6\[8TBWMQ>^SXZ%7Q^3o%QC!u,S"64IVQ$]%[CO#ga3;r_g3bcsoDJ7\ +!7nsc"6+A^dc:?.m&^)6!J/)Nrr_8%3RWKYJcF!p"Sn-U[JT^u"RtDl3RZsf!UQobIfY,:JcDtS +!p4]JJcC<$jo5=%o;r)WPY-H~> +`r?.t6@Se$rrCokrrV20W8dgXm&^)6!J/)NrrV20W.Fu"s3(EEeX!iEp\t?bid[odrrMJfqgSWt +_1DWjrrVW!k(3P`s5j7\K_)kYn!m.'~> +pAZ$,o^:r>'lr`kfs02M) +n$qP3s+143rr@ZB[K>c`9n3~> +pAZ$,o^:r>'lr`kf +a8Z4/1s5:e!8`;,JcC<$JcC<$JcC<$Qi@&=o=Y4oS5+S~> +p\u00p@.>,h:L&u`kf9qZ)jjuT:;13OcGBXLkkq`)2F$*N/s*cR%0hFW3*>6]YShBVV'bdlL=Q@ +rrCf)s+13$s+13$s+13$s-N`cK(HDPl^COu~> +p\u00p@.>,h:L&u`kf9qZ)jjuT:;13OcGBXLkkq`)2F$*N/s*cR%0hFW3*>6]YShBVV'bdlL=Q@ +rrCW$s+13$s+13$s+13$s-N`cF7ZL8kEJSh~> +ao;J*9Q9aorrCo,s+13$s+13$s+13$s-N`cK_)kYn!m.'~> +q#;<4q"!b4hq?H%a2,BrYcFXqSXG_)NJ`LEJq8FMIN!]eK8#)c`9n3~> +q#;<4q"!b4hq?H%a2,BrYcFXqSXG_)NJ`LEJq8FMIN!]eK8#) +b5VO`1;E2U!8`;,JcC<$JcC<$JcC<$Qi@&=o=Y4oS5+S~> +q#EPF%f5VM6=P]Y_b9e_B'H +me6AS!8E))JcC<$JcC<$JcC<$Qi@&;o +q#EPF%f5VM6=P]Y_b9e_B'H +me6AS!7l`$JcC<$JcC<$JcC<$Qi@&,o;r)WPY-H~> +b5VM6o'?;UhLY]Xs+13$s+13$s+13:rr@`D]`RYm:4N~> +q>WSVp[RP/h:Brq_nEOaX/;PZQB@,]K7A8oG&qYBDf0H2E,fuCH$k-qLl@I^S"ZFKZFIWeb0ehs +jQl@5p\t6JJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +q>WSVp[RP/h:Brq_nEOaX/;PZQB@,]K7A8oG&qYBDf0H2E,fuCH$k-qLl@I^S"ZFKZFIWeb0ehs +jQl@5p\t6EJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JV8T-JV8T-JV8T-JV9>Bo=Y4oS5+S~> +q>VH3o^1f!g!\*`^U^YOVkKTFOGejEI +q>VH3o^1f!g!\*`^U^YOVkKTFOGejEI +ZMsp]JV8T-JV8T-JV8T-JV9>Bo=Y4oS5+S~> +qYqT8q!mY0gss`l_7R(WW1ofJOGegCHZjFIC1h'd@/XFP@:Ee\CMn0:I"@$1OdDT5W3c`9n3~> +qYqT8q!mY0gss`l_7R(WW1ofJOGegCHZjFIC1h'd@/XFP@:Ee\CMn0:I"@$1OdDT5W3 +ZMsp]JV8T-JV8T-JV8T-JV9>Bo=Y4oS5+S~> +qYrbWp$V#$g!\'^^:1>HUn*j7MhQ\-F`;/0A7/\I=]ea,=BSj6?t +qYrbWp$V#$g!\'^^:1>HUn*j7MhQ\-F`;/0A7/\I=]ea,=BSj6?te?J,~> +ZMsp]JV8T-JV8T-JV8T-JV9>Bo=Y4oS5+S~> +qYreUo'>Amf$DFR]!JN9TUD"'L4FVnDe`il?!2]mhW*(Z>Z+O%=^>HJCN+ECJVT5LR\6R\[(F2s +d+@.9m.:5PrrCf)I=ZftI=ZftI=ZftI@#?^K(HDPl^COu~> +qYreUo'>Amf$DFR]!JN9TUD"'L4FVnDe`il?!2]mhW*(Z>Z+O%=^>HJCN+ECJVT5LR\6R\[(F2s +d+@.9m.:5PrrCW$F+JC`F+JC`F+JC`F-^n=XTI[M8q6~> +ZMsp]JV8T-JV8T-JV8T-JV9>Bo=Y4oS5+S~> +qu7W7p[IG+g=+9b^:1>GUR[U1Lk:"tE,&rk>?JQ8ruUQG9M\Sg>@;#XE-?V]M3!snUT1T/^W+OJ +gZ%Jer;$?mgOXd(I=ZftI=ZftI=Zg5I/nlj[K>c`9n3~> +qu7W7p[IG+g=+9b^:1>GUR[U1Lk:"tE,&rk>?JQ8ruUQG9M\Sg>@;#XE-?V]M3!snUT1T/^W+OJ +gZ%Jer;$?meq%mnF+JC`F+JC`F+JCuF7ZL8kEJSh~> +ZMsp]JV8T-JV8T-JV8T-JV9>Bo=Y4oS5+S~> +qu8qZp$Uu"f[7gX]$trYEd3(fN0B[&VlmG? +_ogB[i90P'q>UHLJUN)tJUN)tJUN)tJUNl5!.sgN!pc:LJ,~> +qu8qZp$Uu"f[7gX]$trYEd3(fN0B[&VlmG? +_ogB[i90P'q>UHGJTHB`JTHB`JTHB`JTI,uo;r)WPY-H~> +ZMsp]JV8T-JV8T-JV8T-JV9>Bo=Y4oS5+S~> +qu8qXoBbPof$;=O\[&62SX,=mJU2BSB4+nB@f$,05!(ba5Nr-P6:OUEUHLJUN)tJUN)tJUN)tJUNl5!.sgN!pc:LJ,~> +qu8qXoBbPof$;=O\[&62SX,=mJU2BSB4+nB@f$,05!(ba5Nr-P6:OUEUHGJTHB`JTHB`JTHB`JTI,uo;r)WPY-H~> +ZMsp]\:an_Pja8,N6/p3KEB!3N.HY5KWTn0XldHU\:amIs+,jhKEI=XhPGdVJV:ajo=Y4oS5+S~> +qu8tXn`o,geBGnG\$)a(RZi\aIWoaF@pE&2PQ1YW2`!0B0Soc&5sRk2:f^k3C2nHHKoD7cU8bB+ +^W+OKguI_krqcWpgUMY4s,QPr!enY.q0`=Y`eAtlJUOYK"01OuJ[g+/NrOZYl[8knaPEM?I=Zg] +I/nlj[K>c`9n3~> +qu8tXn`o,geBGnG\$)a(RZi\aIWoaF@pE&2PQ1YW2`!0B0Soc&5sRk2:f^k3C2nHHKoD7cU8bB+ +^W+OKguI_krqcWpf!oc%s+Ki^!dhboq/ZVJ_0^`SJTIr7"/P(iGIV\pK`?UElZ3/\`S-]1F+JDH +F7ZL8kEJSh~> +ZMsp]r.G33f)PKa_LqsiPja8,N6/p3KEB!3N6m7X^&EZoLp?7BhPGmYLlBpbpWibk%%QM\s43-^ +KT+L^pWibk!65!;&E9O>KVdQ&lEQ.uLp?7Bii2p1%&MhXlEQ.uLp>P2pW!2c"JtSen"p#:"e=cU +s43/,KE7qLrIc/ehYZTCLkL_tmf2R[N.ck\hYZTCLkM#8p\4-Qqh,!Hs8IZO`r5o;`qY2f[G(QY +!Qg<"K`AabKE[1:lMKG7nq7#;rIb?EkPtS5N3@m:N60%pl\#<5Pj-]u>s*KEAj+N8TBh^&!B`ONkUm +KRnfjK_)kYn!m.'~> +qu8tVnEAibd`]P@[B6K8PeYTVns# +]u8.Dg>_AerVHNog\cI&K$X>g_npg&s,QPr!enY.q0`=Y`eDfg!e7kord>6*W9*j2TRD6>L=?%o +a8UJqP2H06f8>!)L=?%oa8UJe_u9THiLkUIS_Eo(Y(;"PXQTE:UAfQLS_Eo(Y(;"NW6bA8_u>&e +Rd:">T)O-AP2H06f8BKS!M9>iI2ng3q<>&5I>>8sqrt>;I=7[?q<>&5I>bu8p$84HI0#r>rd=da +rVll8pjE1Gf)5OP`eAhhJ%WAlI0G,!jn[`*o6gXkp\fLDP2H06f8?b[",4b3Ja7^bpQ"Qf#)BYQ +qrt>;rd=it]Xn)j"cL7jou)jeI0#)?jF%&of)5OP`eAhhJ%WAlI0G,!jn[`*l?r^-NpqGu]#'0r +I0.!sL#IF]K"S\kI0,8J^4C]`ID:11K(HDPl^COu~> +qu8tVnEAibd`]P@[B6K8PeYTVns# +]u8.Dg>_AerVHNof)0RlHd;Zc]u"gks+Ki^!dhboq/ZVJ_0aOMs0_iI%,1R+hH&W-<<8^%;!eSGR@.PlL`(!58@2 +!58/K!NjrNrrKr_rcA+Irc86eUuqR.R.>:up\f.:N8FU7epFTA"+.ktGO';NpOqjR#(O&Ks6?_5 +rH&"Hrc86q^%;!;L\l,nHbJK]!Kbn1rrKr_rcA+Irc86eUuqR.R-J_npOr$W!k#^gpi?ME]mJ[Y +s0_]E!e82"JTHB`^2rnJ!p>e?J,~> +ZMsp]r.G7!kEMM2[DRXus-2u*!fY7;q1Jgcac+]%"GiSem_SmU,,s;0R*!pLKX'CbPappMKS9*N +Pa(%]KS845PappMKS7q%rfegmLkMGL_3hpiWJ%P1[@"eYTS/ud_3hpiWJ%8)kEMM2[=j5-`f.V* +re(ILes)EUhYi*/PkY4WKH6`6_3hpiWJ%tAXd$6M[=k4$_3hpiWJ%tAWMI6%r.G-9p](5G!S5Qq +PQ6[;KE7$nrfd\jd]nI]SAjp4re(KTg:`lqesuGe",sA=s8@TSpXYRnhVPGh!fY7;l@]3])k^##`ag:c9p!Jlj:PR+D>Z%Rq'`U:SAKEeF0]pQpmSEkh/s-3#+!lm_SaQ!fY7;JV8T-^4c*k!q2XSJ,~> +r;T+_p@%2&f[7gX]ube[UdZd$K0^ +gXh=GkIQ(in'd/SgY7.)gXh=GkIQ(if>mlR0ogm7REiI,[aUt!XP'h<][3-rREiI,[aUt!U=J:a +QLD,-IIk1-[eGB%IIGXBNn`=eI0,;ts8IEfREiI,[aUt![bIa>Yh?1-REiI,[aUt!\CdOI[eGCP +I0,;ts8IEIf>mlR!/gQ"!JZR4OpJ)8Y'kke_!Sl8I0QFr\<=_UQhGh0L8N`*rI"k!\fV4tg]RSac!J-4/OpJ)8Y'kke_!Sl8I0QFr\<=_U +Qg9&#s,QSs!ku[.pjE4X`eEH$"GWf.mCrFF!enY.JUN)t^O6*6o +r;T+_p@%2&f[7gX]f%+n`hU[@=kI#APf#I]J."c_bPg6gqZ-o.`W7nA0\^$F\Pg6gqZ-o.`SCZkV +N:3liF76M`[J0ZA"n0`iK\"`QErq$hs8I'[Pg6gqZ-o.`Z.>S$WRe"hPg6gqZ-o.`Z-o#,[J0T? +!eg=Jrc8+k`r:nsHh_X_H\;)TS0;qW,m0E^$<0-Es8hsR$cI4giEL?KDpf8Us5iZGEBN'\c-XSSCY*(^!;`* +Erl;on8eZA_0bru!kH-oj)Y?MV:OqBqJu^b_7>, +ZMsp]r.G7JV1aN0kL"rDXmc;a[=j4Qp]'*7KS9B5n,N-aKS5;diqr#GLkMGTqtJ[u_1\ZAqsM"R +KYcBjp\WC+KS6c%s7a5+KGOTikPt:YKS7&5s6$fiKS5<0p]'N`M"lW+OL]*3^!;jqSBeNu +`W(mMKVe-jSH"P$KUi'qN;niiKVe-jK^Z&?KJ's`P_>!_OGLlpLkM#8\V.ZAP_>!_OG(S,N.ckSs8@TPkKa,,KH@hPP_>!_OGq<#LkLl7_1]MIP_>!_OGq<#LkLH]Pl67: +kKa,,KE:PDn:V+,_7bYXOL])l^!;jqSBeP9KEIaif)P`&#+Y^PKS5lqYClt9ahdk-$&GkO +qr4`6KTuN9KEd\@K_PN@XmLcXN60%pKTOHpKTNUilG8.,N60%pKYbchr.GB@s+H(*Xlo`YXltEE +N5<2`KTNUilGqqh,66f)PKiN.dGqre(K?s+L.HpT=4D*LP.Hqpq:Nqtng/SC70/ +g7X,4qtng/Lontss43.pKERgjf':*JKE@+S_1@8jKYE-2]`RYm:4N~> +r;T+^p$Uu"f?hUT\[&61S%22bG^kI.Q(=_O +Zb"#qdFd@>meZe[!8IF""o"U:IBqn\I6+Css7D2cfr1C +I3Z>GNI@/9Y^s[kMLBoEl[>*!L4+K9s*]Y%MLBoEl[=3IVLn_o0"/cZI=m]@J\o?XI>u1PIC@.T +I=m]@ICdRmI=8E]I>\&VI=hKZI?snfI=7F]rI"b)a8c-e*k'(JI=m]@KuUoXI>Q+\IC@.TI=m]@ +K>bQTItI]\rI"b)a8c-e!pc_8nU1^t^:])HM6P=j^A`Nb]#p"\QccQ+I05eYe,TDq#+#:DI=7F] +YC-J+`kM=u$%]8Dq;J3#I?+@,I0P`3IImL5VX8dJKuUoXI>Q+\I>P2Sl+VRoK>bQTJ&''Xr-\m3 +s*]=qVW\!JWp"j;Ktb3PI>P2Sl+[.F#.hs:I@&*\qgAa)fDG!2s*aM=pSR_6*L"_A +q9kV>q>8I"Q-T."esh)tq>8I"J#L]fqpU;cI0>kZe*4UCI0,,B^4C]`ID:11K(HDPl^COu~> +r;T+^p$Uu"f?hUT\[&61S%22bG^kI.Q(=_O +Zb"#qdFd@>meZe[!7q'h"o"7&F04WQF#p/ds7`X:F1q>JhLorks/kJan"k)HH_fZuk+m3BPg]kUXHd;Bck+m4rEs-ulb3$86 +F!A$2K6*-sX`qJYHZO7mkAuU^HZO7es)WPeHZO7mkAt_-V>G"C.Z,)'6 +H]3Su[DeAOF04VHK`D$m$3,["N;n6GF,^.tErt4Z,)'6H]3T( +_7BPd!WRiOErt4]m"OI`OU&>WQOA6F)u]E\T;i&_0c*$$&&>" +F8qcns6lfJEs[3([_UN5j0*\b^@KHbm7DF+JDHF7ZL8kEJSh~> +ZMsp]rIbB>s,;VaSH%#js6#W"^%[25f%.6n\_aTagGlEQ.u^##`IZ/2mE`ST>GlEQ0DKE@[Op\faI +`n$CSre(ITkPtS5N:_c)[E=m8KFEg]_1[K#ai0'hONkW6KE7>;re(<2Pl?=:[I:;P!g([Cqh,9\ +hRSleg80Ies8IZTPlHDis-2i&#//lmN3V6Ire(?3ahdk-!h@iTqh,9?s+H(*kELY>pZR83[E=m8 +KFEg]_1[K#ai0'hONkW=K`Bp/PQ^HpKTuN9KE[V?KS9Aipk0]E`R^QLf# +r;T+^p$Lktf$DCP\?W$-S!/eaI6E!G97\:&J;YTs04.44$uV;=C>c[G(,.)PanMK +ZFRfmd+@1;meHYY!8II##)`G8I=7R]dX>,Ms/(OFs+\8RP)@`KI=;.UMLE)1I=9GhY)KaBXIIe` +P`j,`^7EcfNiq-@ID56[K:CN)I>7d>L8+^*I=;.QNe5">ICAa=L7[l6I=9GjMN5'uXS.&YKuUiT +I=oJTJUNI!]S"0&L4+K=qgF'Vrd?Q-kC&6fL4+K=qgEnij8]/.XFYr>gt;GcXFZ#B_L[,nb.N;B +gt;GcXFZ#B_M3JeRf!&3#(ilHJpi62rd=gQkPflM_q1(OI=6[`gt;GcXFYl2et@\rf<0\Agt;Gc +XF^A!"Fl`;o7Hsi"2i`Rs8IEMqr@_Vdt[LC!jK\.rI#*.`kHkLK>bWXI>c7\oR-b#s8IEIJr]hY +!OCmlI0,2F^AEoI0Y83et@\rf<5+$!enY.r-\]tQHm'kr-\m3 +s*]=qhi<<&pZR#,Yf;n*I11hL^3tE^`57(RMor^0I0eHZNfrSCI?+@,I0GZ2I=;-UpjF37_U=a7 +d)(@J`eAu\iM;^b`.NKP`eAic`9n3~> +r;T+^p$Lktf$DCP\?W$-S!/eaI6E!G97\:&J;YTs04.44$uV;=C>c[G(,.)PanMK +ZFRfmd+@1;meHYY!7q*i#)*#'F*!TGdW8E8s."G+s*M0:N-ik5F*%BEKQFQqF*#XTXbEriE+rc9*neqF'EF*G=Of)+U*GB:%?S9q'_W->r/f)+U* +GPuRaN2?:IEs',,K`D$m"omccs3>u`Err;VN;[f+Hbm76F*ka:GB8DY\b'qDK`D$m!IKM!EriE+ +YB'bp`OYke$)H.SLq1s$F+jSmEs76sF*%A7lZ398esq)_eu8Rh!dhboqf;gtcF*Oj$&&>"F8t*M +F1q>3Err;VN;[f+Hbm76F*ka:GB8DY\bgIJecpedkIkqis8I'CK`?+7s+L/g*d> +ZMsp]rIb39s8IZPPlK0b7u[JUN.d.CpT9+%PdQ0ts.tC%PlHDqs2BeIN60&Td[5]@R+B*ed[5]H +Un!+SdZfEDah`X`P`19cs-/1is.tC%PlHF*Xoah`X`N6/nhKT+$h +KXL!oPapq:rIch_`qQ!VK]gpNSEKp*N9@U%Pipo*N61@mPe#MMKXK[ER)/C1N61@mPe#MMN6/p8 +KE7>;rIk4N!3Q0P(Q?B.S:lQtmu3M2KS5<+lEukeg9$$mdf07Qac)2#lEukeg9(@-"2Y$;Xos+H(*Pl-17Xo&+uXn[PMPg]R2!Qg<"KE@+S_>Jrg_>i)Rqh,9?s+H(*V1aMd +s87NMXo&+uXnIDLSBeP?KFEg]_1[K#ah`X`N6/p6KF*V;P_=^lpOne2re(H>s+H(*Pk]n6LrJ)@ +reMDE_9%XhN0&jWc,kot_9%Xl`PmdhKTuLqrIb0Qqu6Yup4NL3ahde+!kI$]JV8T-^P)3l!q2XS +J,~> +r;Ru=o^1bsf$DCO\?Ms,RZiY_I!':6='oQWFa\q%PFJ;H +Z+7Zkce%%9mJ$JW!8II#!KR3ZI0#N2dX=W?s/ps%Jpm*^I=7!YI=;-eI=7:YI=hL;K7/Hn_S"0X +I=7=/^:)+DI=n,XID4!dI>>nXI=dW6I?snnI=;-eI=7:YIIF'4I1)qPI=hKVI=6gh_L_H0'SXQ@ +I=6gh^3tEbaM*).kKf$Kj8](+08@C9RXKVIc\dnGs*]X^We-8kgSfhGl`uPZfr0>)gT6FHn\0^s +l`uPZfr0VE^A`N]P5kK/s8N)!r-]iF_t0=FIA61bNrONUKuWGaNjdW>I?2bTs8BrmKuWGaNjdW> +rd=m_s8T+uI1;q>L5u:^I>6d^Jr\m?omHn,aKYSj%#)^8I=6gh^3tEbaM.:p!KR3WI055Ao9uk2 +!hIcFr-\p%^rV:ZO34t\NrT*,"ciV2IK,?*I/p/ArrB(oI0PbZs8W&;L]2]'KuUk)I074`e,\TI=6ml^ANBcK)_!^IA6=]NrT*,"ciV2 +IK,?&I05&;esQGd(k'- +r;Ru=o^1bsf$DCO\?Ms,RZiY_I!':6='oQWFa\q%PFJ;H +Z+7Zkce%%9mJ$JW!7q*i!JLLPErh7"dW7p*s.t'`HZSYGF*!#:F*%AGF*!$7F*mr#GB8,I]sZ"A +F*!;g\Zs,/F*j:9F1KDFF*ka6F*!:eF,^-PF*%AOF*!hZ*Oq071D,P]Lp/c@h/5s)WPMV/q=Het@B+lE,`Eet@)dg8Tn9m^[\\ +lE,`Eet@B+\c-XNN;rits8N(qr,W[$^%[b4F.D]GK`?+7Hbo!LKX'+$F,/$>rs?YoHbo!LKX'+$ +rc81Ps8SqpEt+Z.H\DE?F*m)HF+iDqolC1n_5R9Q%"#h$F)u]E\T;hA`OYV^!JLLMEs$d&pQVk& +!gqEJBLWQrFK`D$m"bcnsF8ppkEr_a-rrAnjErh'@rr3#"F8qbOF,^.sEr_a-rrAnhErq<:W;L]FHbm76F*ka6F)uiM\bpLTHiK%GF.DuGK`D$m"bcns +F8ppgEs$X"dZFHP(j!7(HZO[hGCRTJHZQa6H[!0:HZO7]s*O]c!1`rd!2]C1!eeD#q/ZV>d[5T, +JTJSIo;r)WPY-H~> +ZMsp]rIbB6s,;VaSH$ui#Gp;OkH0F;Pl$+=s-/1iPlHE$qu?Z?#`s^mONkVXac-.N)8XFRKT+$h +KS6Jjn(.N'KS9AiKS5TiK`@SAKEIbEKTuB6!R6`#KG9Be_1[Jt`PmXdONkUpN0N3!pVut,KEJp^ +cA_aU"dImDpVut)KHd8@_2QLTLkLl7_1[cXmebkt]o:(PLkLl7_2-(LN.ckSs87NOXoF@ZKI+>+ +cA[F0PkV#5SH"P$N60%pKTOHpKY?6AN3V6IN60%pKTOHprIbQGXoF?5PkV#5SH"QFKE7qLon3C[ +cBS6[%#i/`d +SCYN4JV8T-^kD +r;T+\o^(\qf$;=N\?Ms+RZ`S]Hus4:?<0il5WDX/s8CHWir@`bs/7D&3C-24='fKVFa\q%PFJ;G +Z+7Zkce%%9mJ$JW!8II##)`G8I=7FYdA#I=6ml^:q[D +r-]HsaG51t`kHkLWorQss,MMUs,qeYNrOP-P5]k3P5frjqu6]Ko +huE;^p\oRC[e%Osrd=s1huE;^p\T@_R``9q`57(RMor\\KspIGf\3oh`57(RMor\b`577WI?sd. +I0-GCK)U0E[e%OsI=7"ESq<:.JqA`q_L[,nb.N58fV42'fs$+I_L[,nb.R\$%$R^cJpiOJSq<:. +JqEoK!LioYI0-juO8OD7KuUiTI=oJTJUNBr]_$UQNrT$*"1IgAMQ_F1M9=,'r-\p+`kHkLLWI2X +NrT*,"d8n6IK,>oI0Y85fV42'fs(I(!enY.qgAR1d(Fl+I0kr6I=;-YI=7.Ul$WWAaH-7I%#)^8 +I=6ad_L[,laLqG!!KR3ZI0>`5I?+@,I0Gf6I=;-UpO*j;]]nh%OaXs`Kmf0+kMPUFKmg\TKn[PD +rrA5II0,GK^A30ZQdiZtJUN)t_0l<8o +r;T+\o^(\qf$;=N\?Ms+RZ`S]Hus4:?<0il5WDX/s8CHWir@`bs/7D&3C-24='fKVFa\q%PFJ;G +Z+7Zkce%%9mJ$JW!7q*i#(-AsF*!oQ(%ds86p?XnThgYB'eq`Qr,Vmls8I'BN;msPs8I'CK`?+7s+L,f(PB-XlFhso\\WW6N5k&;lG7gg\\WW>_>F64 +N9YHcKYb4pEs%NP\T`UGJTJVJo;r)WPY-H~> +ZMsp]r.G7JV1aN0kL,#RN2;2\XoF@SV1aMdc@glNP_=^Ss+H@2`JB2'P`WT,R"U-[f"o&FP_>"# +WJ&+0N.d/3_1[dIS:lRqP_=_iP_=^Ss+L-Kre(W\s+H@2N.ck?P`YTl!fY7;re(rucBO!PcF&=a +P`19K[GHRicKCGiKF!g$KS6biKUfILKI*/1]nF5DN.dG@]nD?+LkM;?Z%Ts8N.dG@]nF)EV1aN0 +kCefNre(LUP_=_8kEPpu!2]Sm)nho\ac(n;cFo1Y`JB2/c,#(Lac(n;cFo%AZ2TCYs-/25qu6`2 +S@/?L!M&oXKE@Oh^&*HaN6/p8KEf-8P_>RP[If+QPlL]8!m]tnXFpYFcF*S'$Ac[CN.dSH]nDXM +re(HOs+H(*PjF&.N60%pKTOHpre(]=ah`X`LpaL@KYbchr.GB@s+H(*P_=^Ss6>7=R*r\FKE@+S +_>])l^!;jqSBeP?KF*V;P_=^lpOne2re(HOs+H(*Pk]nQLmXE[R+B64_9%Xl`QaKt_9%Xh_9%Xl +`PmdhKT)GcKE@C[_>8feLrIW#JV8T-^P)3l!q2XSJ,~> +r;T+]o^1brf$;=N\?Ms,RZiY^Hus4:?<0im5WLPf[.jV#qP5:ps-5)i3C-24='oQWFa\q%PFJ;H +Z+7Zkce%%9mJ-PX!8IF""o"U:IBMVYI7JmXI=8!mL@_-fI>Z'nIK,=UI?+>UK)`]=I=I34[`jq` +I?W0p\B9k\K;bMm^;bkTKuUiTLB!]jIK,=UIK,=UI?+>Uo;;M8$E!iOK)^RRI=I34p3d!r`kMG# +'=u>GI?hmXI>*]5I=89QI=9;rNr4;2Ne2\-Y1nAjY5Wi&R`N'g]Y];LMo`JXKnt5,Q,KbP]Y];L +Mo`JX]#pUmIBMUYMVE`2K)^jZIEUN.rI"[;qu7kKRCpChI>c1XKuUoXI>Q+\ID!FLI>c1XKoI0YJG^jgchb.dk'%#)^8I=6aRY((r:`eF>=$',%6IK,=UI?+?m +I0,PR[ekISKuUk+I0R:*NI@)5Yl&nUK)_!^IA6=]NrT*,"ePaBIK,?&I3*luMg^`9_L]G@Kn7+b +Mg`=ZKmg\TKn$nTL4+K%QK`f!NlJg0I04u7aG5=pJUP=^!.sgN!pc:LJ,~> +r;T+]o^1brf$;=N\?Ms,RZiY^Hus4:?<0im5WLPf[.jV#qP5:ps-5)i3C-24='oQWFa\q%PFJ;H +Z+7Zkce%%9mJ-PX!7q'h"o"7&F04WRF%:SAF*!lOHhX%OF+hPSF8po7F+jR7HiLd'F*E7dZ-&#H +F,\PX[E42FH_m6R\\WW6Hbm76HiK=OF8po7F8po7F+jR7pSIk1$Cq-;HiJG6F*E7dp2^:^_7BPd +$+7a)F-+PHF*iT>EsAc'F*#@XK`#lkK`6#qXkn,XXok6>H[BgTON"/>\\WW6 +K>k6>[DeAOF04VHHe/1;#60?tF2m'irGqt'qu7VCPe"VWF+_H>HbmC>F+;0>F1K,6F+_H>GGq>: +Es)GqF-QS%!k!uhrc8+)p\&Y0N68F&Erp^)\c-XS[Ddc>ONO]tErgsorGr%[hLqtO!g(R,r,W3h +]to&:LWQrFK`D$m"e>U6F8pp[EsI'.]m"OI`OYth%"#h$F)uQ(UiUUf_0c*$$&&>"F8po7F+jSY +ErqU6F8ppgEuoO\K6)gm]m%$'H[!0F +K6+T>HZQa6H[!0:HZO7TN9PBbKYb4qEs$X"`IEG[JTJSIo;r)WPY-H~> +ZMsp]r.G6nhMd]f[F0_.N;o`5XoF@"kELqVkCeNgP_=^Ss+H'Vf!UaMhR0$1cBt!ApVE]W]o]MU +[=lBs+H'Ves)E,hR3'1!fY7;re(o\d^>1Qd[Yuu +kK]$VpT9+=s.'%Ps+EGrcA\:fXb<5E[>^X=etn&!f"%$Yg8Tb5g9$U]d[5]ef"%$Yg8Tb%db]qI +n$Rh%pUuO!pOn3is.'%Pr.G*8s8IZlWReVE^"TH=^#H;][GJ1Qd[]r_!L3W`KE@\DSGe\UKE@7[_>AluZ/3$U_:k`= +N;p;=Pir("K`@S?KE/.OrrB1rKEf-LZ';ZQ[Jkg\N6/p8KE]'CUnl_Xqh,9?s+H(*P_=^Ss87NM +Xo&+uXnRJMZ.>oOKE@+S_>])lV:)c5_:"kNK`Bp/PR[*$KT,r=KTt["KS9Aqpk0]E`R:-Df# +r;T+^o^1bsf$DCP\?W$-S!/eaI?WU&q696nR,pkc=*"XFrs(X,B4$lP:=C>cZG(#('PaeGJ +ZFRfmd+@1;meQ_Z!8IF""ioMTMX.RrI8u%!Sq=!BL;W2;MUUU6IK,=UI?+>UIFm#PMQYX8S(>`H +P/d71T[g]8Y1p1/^;bkTKuUiTMZ8uo])RqAIK,=UI?+>U]'l5FRd:!%IFlr*MQYX8p3d!r`kMG# +&u*j\Lq:luIBMJNMOO.6I?so%rK@2/3oA,IBM,#P/b_.IAG?! +OLj&>IA53JL5P<&I=h4%L:Zu)I?so%rK@),!KR3ZI3#-4[#hlNUj]]+W/Rt6Y'm+p[#hlNUjp&. +StN[dqgAg3s*]js*]=qQMc.2VYgAnWq^uH[bIa>Yh?2YI1(bK^3tEZ][i[If!tU*$',%6 +IK,=UI?+@*I/p/ArrB(nI0-FpUAK??KuUk+I0QFpZA?*;Qhu1Cf@u1GkJr".s/pd1j8XOUs-EG. +*eD,NOI"D5J\K9`K>bokT%qDBID3j\IB(iHMMfm?r-\RBqu6Yrp3d"&`kM:t!enY.JUN)t^O6*6 +o +r;T+^o^1bsf$DCP\?W$-S!/eaI?WU&q696nR,pkc=*"XFrs(X,B4$lP:=C>cZG(#('PaeGJ +ZFRfmd+@1;meQ_Z!7q'h"hic>K^Z%kF&dSeP^B8,HbJU$Hd:JhF8po7F+jR7F4Sd=KW3P$Ph*s7 +LV`_qR*rNtUt2Jc\\WW6Hbm76HiK%G[Ju2,F8po7F+jR7[I9?,PjeWnF4SWiKW3P$p2^:^_7BPd +&t$qFJ%,*9Z.c%-XkK`QErp^)\c-XR[F1;,f!YBr$&&>" +F8po7F+jSkEr_a-rrAniErr/_SGR@/Hbm8lEsA#VXFIe+OSa,!edmFmkIkq`s.sdihZ%Y<>LQKrtGIb\BGJ1tFPhO*+F1K8>F/770KS7Y-r,Vk.qu6Ymp2^:g_7BD`!dhboJTHB`^2rnJ +!p>e?J,~> +ZMsp]r.G33f)Pcid"GB+p]']aKS7YFs5&unK`@9iKTuLiKT*2$s6dFVKVdQ&pX7d3KW3u.m^\:0 +_9%XhN6/nhKZ=&d^#&dBK`@9iKTuLiKY@-[kH4c;KT+L^s6dFVp4NL+ahdt0&;[UVs5Kl.KT)/Y +s7`pSKXL^Ws+CN>kPt:are)T"n,N.%V1aMXZ0MAOXb_Xh^%;-PS:lQOZ0MAOXb_XdZ.AsT`r:Vr +V=4HQS:lR'qu?SI!L3W`KH5lRlMp"KLkLl'mf2R[N.d"llMp"KLkM#8p\4-Hqh,!7s8IZSXmc;a +`q=ud_:G"NKE@+S_>])kLp?7Fihc7"!L3W`KE/FWs0%(WN6/p5KG00HlMp"KLkLGpp]'EYs+H'^ +s/kX@#*?Ods6@"Nre(?3ahdt0"dI1(pVi6kKF*nCKS9AiKS5Til@]5TesQ>f!fY7;re(H2Z0MAK +V>>iXN7S%/cA[F0^%_QiXoF'-hZ'_QKH6T6qtJC'KX'srR&/g>g +r;T+^p$Lo!f?hUT\[&61S%))`GCP@,Q(4YN +ZF[lod+I7=meck\!8IF""G4tspW(X/<3Gimn#Cefa7oVkNdZ?UNdZ>9s*]9s*]=,oDe!Rs*]?- +W9*j4U4%HDNoU02Z@./,qu?\E"HMOBo@;PI=\K_qr=Q'I>,3$q9kUXI=\K_qr=Q' +I=J3AqtJ/1I0ZM8s6#i!IBrbMr-\U+s8IEeJZS&3j/.I0,,B^A`NaJ#MK)hkK^j!KR3ZI/pSMs0$hPKuUk(I1q17l1s;8 +IslaRp]'9Rs*]=Qs/,.2#)B_Uqrt>;rd=j%`kMG#"cpb!o>$:]I0kr6I=;-UI=7.Ul[8kb^ +I0,,B^A`NaJ#MK)hkKsq&r,$8pWJ/:ICAnMj/W--IGb(%pjF0Jd/!F[PC9V&dZL*Uq:<^S\p^aM +dZK*^e,0$oMWoNjNlJg0I0,,B^4C]`ID:11K(HDPl^COu~> +r;T+^p$Lo!f?hUT\[&61S%))`GCP@,Q(4YN +ZF[lod+I7=meck\!7q'h"FJGopV"pp<3>opn"k)H`qTMiKQD57KQD3fs)W7.Uuq^BXa>,VgACK] +d^aFhhYZ`GHZQa6HZOPM\T;i*s7`qXpN(EUKQD3fs)W7hn,MRIs)W7.cN!q0XaBb_!dhborc8Wp +UuqR.Qud=qK\latX`JQcqu?\;"GGY3n#cS1-(O/upUur8F*FGBqqduiF*k1[pWekAF*FGBqqdui +F*FG*s7a5+EsJ6(s6#VfF05?>r,Vmls8I'[GGOBuhOo(6J$n_ElE,DSGGOBuhOo(6LW0#ZkEPjb +!JLLPEs/#rs7a5"ErrnsL\l,oHbm8lEs6c`k53l"o5aqcs8I'>Xo&.%Erp^)\bgFOGGsg(hOORd +$FTX.kGA3"F5HqeluNA\[I4([XaC%g!dhborc86u`V8u;KDT]rK`?+7s+GH7K`C=Y!kHR2qf;gc +_7BPd"a?d)qq@G6Es-j@s7aP4Esf>Ls6$6YF*$4, +ZMsp]JV4'+SGn0BV>#OoJV;1!!fY7;JV9SI!fY7;JV8T-^4c*k!q2XSJ,~> +r;T+_p?q,$f[7gX]!JH5SX#4jIs>sJ@Tuf,7mK*k/LVkf+!f\BYV%an5t=[M>[qMhH%:^3QCaqS +[(F2tdFmIAn,3%^!8E'Q!N)1^I0$)2JUN)tb'a>F`kI"PSUCQpaM*4RJUP:]!.sgN!pc:LJ,~> +r;T+_p?q,$f[7gX]!JH5SX#4jIs>sJ@Tuf,7mK*k/LVkf+!f\BYV%an5t=[M>[qMhH%:^3QCaqS +[(F2tdFmIAn,3%^!7l^B!M#,JErhg"JTHB`b&[W2_7>,, +ZMsp]JV46In!o'$kK\X3JV;1!",t@DN.HYSKEI%0hS"JnJV:ajo=Y4oS5+S~> +qu8tVn*&``d`TJ?[&p3tQ]R&UH?3n6?<9ur6rH3)/h8>+\c;:k1c[ch92\l!Ao2X:Jr,VWT;Sj" +]Yr"Bg>V;crVHNogOXbW_rm22QKQ;>I=ZghI05,?`.NVhT7$fmUt`D1I=Zg]I/nlj[K>c`9n3~> +qu8tVn*&``d`TJ?[&p3tQ]R&UH?3n6?<9ur6rH3)/h8>+\c;:k1c[ch92\l!Ao2X:Jr,VWT;Sj" +]Yr"Bg>V;crVHNoeq%lH^#t>qN9A33F+JDTEs$d*_0^`ST5t*YSCXrrF+JDHF7ZL8kEJSh~> +ZMsp]JV45\cM.A5cA[=-JV;-u"0Lk-Lk16DKE/.MrrBmPKEI1<[?,rmJV:ajo=Y4oS5+S~> +qu8tWn`f&fe',eE[]cX'R?EJ]I3) +^;\@HgZ.SirqcWpgOXbWK#dK_o>cH6I=ZggI06G4_L[8dl$WQ/qYpTn]#&jtKrV;4JUN)t^O6*6 +o +qu8tWn`f&fe',eE[]cX'R?EJ]I3) +^;\@HgZ.SirqcWpeq%lGF2mtSpVqc*JTJnR"/+eiGCai"Er_a,rrN#)_K,g+V5\rqF+JDHF7ZL8 +kEJSh~> +ZMsp]JV8T-JV8T-JV8T-JV9>Bo=Y4oS5+S~> +qu7T2o'>Ame]u4M\?W'/S<]+iJ9c3PAm\\?:6k_V+5UNg4?u;+:K1M*Ao2U8JV]AQSYWc`9n3~> +qu7T2o'>Ame]u4M\?W'/S<]+iJ9c3PAm\\?:6k_V+5UNg4?u;+:K1M*Ao2U8JV]AQSYW +ZMsp]JV8T-JV8T-JV8T-JV9>Bo=Y4oS5+S~> +qu8qZo^1euf?hXV]UHLJUN)tJUN)tJUN)tJUNl5!.sgN!pc:LJ,~> +qu8qZo^1euf?hXV]UHGJTHB`JTHB`JTHB`JTI,uo;r)WPY-H~> +ZMsp]JV8T-JV8T-JV8T-JV9>Bo=Y4oS5+S~> +qu8q\p@%5(g=+6`]sb,DU77F.LOjepDeW]f=]JUHLJUN)tJUN)tJUN)tJUNl5!.sgN!pc:LJ,~> +qu8q\p@%5(g=+6`]sb,DU77F.LOjepDeW]f=]JUHGJTHB`JTHB`JTHB`JTI,uo;r)WPY-H~> +ZMsp]JV8T-JV8T-JV8T-JV9>Bo=Y4oS5+S~> +qYqT3o'58je]u4N]!JK8T9te#Kn"AiDJ?=d%;#O-/:f::q>[V,XDfg;UL5_=aTVeit]"uJ6 +f%oEQoD&=cgOXd(I=ZftI=ZftI=Zg5I/nlj[K>c`9n3~> +qYqT3o'58je]u4N]!JK8T9te#Kn"AiDJ?=d%;#O-/:f::q>[V,XDfg;UL5_=aTVeit]"uJ6 +f%oEQoD&=ceq%mnF+JC`F+JC`F+JCuF7ZL8kEJSh~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +qYrbWo^1euf[7j[]sb/EUR[X3MM-J)FDko+@U<8A=&r=$<``F/?XdV_Dfg;TKo;([SYN3h\%]f( +db3RAmeQYX!8E))JcC<$JcC<$JcC<$Qi@&;o +qYrbWo^1euf[7j[]sb/EUR[X3MM-J)FDko+@U<8A=&r=$<``F/?XdV_Dfg;TKo;([SYN3h\%]f( +db3RAmeQYX!7l`$JcC<$JcC<$JcC<$Qi@&,o;r)WPY-H~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +q>WVPnEJree]u4O]!ST;Tpq=.M1gA)F`D84An,4U?c`9n3~> +q>WVPnEJree]u4O]!ST;Tpq=.M1gA)F`D84An,4U? +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +q>VH3oBbVsf[@s]^::GKVP'BBO,AXAHZsOMD/*`rrFHp\BPMC'FEi1`Ko1qVR\6OZZFIZgbgP2& +k3_p;rrCf)s+13$s+13$s+13$s-N`cK(HDPl^COu~> +q>VH3oBbVsf[@s]^::GKVP'BBO,AXAHZsOMD/*`rrFHp\BPMC'FEi1`Ko1qVR\6OZZFIZgbgP2& +k3_p;rrCW$s+13$s+13$s+13$s-N`cF7ZL8kEJSh~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +q#;<-nEJrfe^)@S]XG&EV4X3@O,JaEIX63[Ec>ppD&[A3EccMPIY*<4OHl9-V5pl1]u.t=f%oBO +nG`1^!8E))JcC<$JcC<$JcC<$Qi@&;o +q#;<-nEJrfe^)@S]XG&EV4X3@O,JaEIX63[Ec>ppD&[A3EccMPIY*<4OHl9-V5pl1]u.t=f%oBO +nG`1^!7l`$JcC<$JcC<$JcC<$Qi@&,o;r)WPY-H~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +q#;P5eCrmE +meQSV!8E))JcC<$JcC<$JcC<$Qi@&;o +q#;P5eCrmE +meQSV!7l`$JcC<$JcC<$JcC<$Qi@&,o;r)WPY-H~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +p]!8Jn`o2lf[7m^^q-nVWhlAYQ]mJhLkUJ4IsufnI!pHnJV/]6N/s0iS=ug[YdM*Y`lul`hW=(o +rq??lgO]BUs+13$s+13$s+13:rr@ZB[K>c`9n3~> +p]!8Jn`o2lf[7m^^q-nVWhlAYQ]mJhLkUJ4IsufnI!pHnJV/]6N/s0iS=ug[YdM*Y`lul`hW=(o +rq??leq*jPs+13$s+13$s+13:rr@-3XTI[M8q6~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +p\u01p$_,(gt'iq`P9!lYcFXqSskt/O,]$QLP>_\s+M;FLl.1RPEqZ/U8P&r[CX/mbL+qtj6?%0 +p&>$HJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +p\u01p$_,(gt'iq`P9!lYcFXqSskt/O,]$QLP>_\s+M;FLl.1RPEqZ/U8P&r[CX/mbL+qtj6?%0 +p&>$CJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]`r?=TKA,aCs8RT:JcC<$LAqA)R)/a=rrVo'^[qI+mXP-6!UbI(rrM,RqgS[Km(`LK"+cc_ +oDS[jc$uhmrrqhhLOYE%rr;kF!1TNiZ1n:;qL8P"s8.9LMp).8LWBAn"4<:@pA"XorRdcs +K:K4@s8VtG"IT5hJ+!46qgSU5rr3;ubF!p;W8R^8UAKE@XR+43!qs+ +pAZ$,oBk`"gXXZn`PB'nZ)jn"TUhO +pAZ$,oBk`"gXXZn`PB'nZ)jn"TUhOaK3IfPl1rrBh3Ift\QjJ]uprrUZJe+it?IgEI^s*sJ:s*sJ6s8.9HMp(nq +$iS_NKnGoFqqRjZqu6U(qhq`ZqmD?3LU$UR`VP&bgAh&""cLS(^PBl.rr^Z3\+]Us$2rMLKnGo# +p](9jIfu/=s*sJ6s8.9GR/[*lp=!T!hC +rt=7YF)*KjF*"5FIkb_[F)P0!R/R$eD[\Atrr3AtG?T'+@nOH7rr?R-Er_[M2mdpG[mVdUMrrU*Mp@S@bLZ\X:_qP+kL[Od6 +!q'uVq#:E5]Cu"!#jS(4K;ePEs+L!W!q2XSJ,~> +p&>m)o'GMsgXXZn`PB-qZ`gF-UnOBLR?s5)rKJ+`QC+)1StVsXX08k>]YVV2d+6t1k3Va3rrCgO +rri5'K87%brs1_uf],$mrrCg.rrU*MpXB-Drkl\Rruq?kR>%QA(sbrr<#UV1t;`r;Z`GY_._Brr3,]PC\qSrr3/fTRh`q +p&+gjiICh+%H"":QI5X1qTkI]TA';CrsJ8"^]4?(]8DK0hZ!NXj-,%XhZ!NYors*kB(oDejfNtcHuq#:`MIV!YD@T/Kjrr?R-Es.L&rrCgN +rrE+0rGr^5s8Vo'CM.U"=,#MhRr`@"3HK8=leVRAL[Xj7!psiSq#:E5]D)!u!W@l +p&>m)o'GMsgXXZn`PB-qZ`gF-UnOBLR?s5)rKJ+`QC+)1StVsXX08k>]YVV2d+6t1k3Va3rrCXJ +rri5(K87"ars1Vtg?(EqrrCp1rrU*Mp\&=hPUs59],QI5[2qp1R^T\TPH%,dq8QI5[2rRS6+LUmEa"QGYqPhuE>"mVb1K;ePB +rrM,/rVm;aUkP,^rVuc@QA)!er;R,nZ+p>=n@OO6PhuE>"QGYqPhuE>"S\jILUm0Z!R7M>If[%P +rr<#9rr30#es_;ch"(IFL].5Uqltd/\ZYr8q2SLjrS@JSL[OF,!q'uVoD\jR3W&gWh>lpB'&]4" +CG@hjF/J;R0l(iODcgqFrVlt<$o.YYrsSFB>'"Ul9"k9)!+YtK!0HsT!e2q?rc8Hua5CKJm/R+a +PZGQorrE+0rGr7Ls8N)Us8N)Uqu6]t@r)'b!J/;drsR;*A9Ds$F33-J@+>2U&A8dWs/8t>GAgBD +s1rbt@fZLL"5lF7ZL8kEJSh~> +ZMsp]r;Qr*8onoCajL+o!7LoA\c;[0hV8&3K6-J2rri5]]iKdbs!$*D??'5,jT!Dj;Km1[`W,s^ +:31JOSH&VV;Km1[`W,sM3W<4#:7V:_!qU=1rr3JfOCiWCIG"SCF_#W(MrOa9$i.%dJ,fPX@V9CYB)_f5DcV'cB)_f6 +Qr8-L;T8G2!Qgf0CC!lmL\CW-]iKdbrs%,(??'5,jS&TUpXZ,GrVm&+8onoCafk^Hn8WmTh#@B\ +hWb%AmVdUHrrMl)qu6]R!:9^urV,pCBmK=Aq>YqYiq)d.[8\IOrrhp:C';uarsPY\Z0Lc*>!`2g +!7Li;!;H6f!e4qDrppNjqtlCS_uKc#D/JM3rrE,JrUU)E'_:lOuFo0upXs"TJppZ@h_p\XRRdK':DsmE#$^b82 +md@iW*;BI5df&b?p](6nhY[p +p&?lJp@7J1i7c`.bJqE4\[A]FX/MkiTq@pIS=?%=SXuIIUSaujY-PLI^;@q7dFR(1k3MU;oD\gF +r;Qr+8o\cAb0g4p!mgo>])Vd1gY;`0K6$D1rri5]]N0^bs!$*E?>s2-jT!Djs2-jS&TUpXZ&GrVm&,8o\cAb-1gImr*UOh#@B\h=AGi!7Cc: +!;6*d!e4nCrppNiqYH4R_Z0YuCi8J3rrE,IrUU`/<`:Q4oFo0lsZs"K>npZIndp&"@Pd/a1;"9&6"dJ`YGo_ST;'4oSks5S>p&G$lg\_!O!7Cc:(%qV,d;F*Om,+((T`"QUib3W/oD&=@rrED]oD\mZD1D?\!eYO] +p\t?ietNN9rri5]]N0^>rrED]oD\mZD1D?\!eYO]q#:[!Gu&R(:R_=`K(HDPl^COu~> +p&?lJp@7J1i7c`.bJqE4\[A]FX/MkiTq@pIS=?%=SXuIIUSaujY-PLI^;@q7dFR(1k3MU;oD\gA +r;Qr*8onoCajL+o!7LoA\c;[0hV8&3K6-J2rri5]]iKdbs!$*D??'5,jT!Dj;Km1[`W,s^:31JO +SH&VV;Km1[`W,sM3W<4#:7V:_!qU=1rr3JfOCiWCIG"SCF_#W(MrOa9$i.%dJ,fPX@V9CYB)_f5DcV'cB)_f6Qr8-L +;T8G2!Qgf0CC!lmL\CW-]iKdbrs%,(??'5,jS&TUpXZ,GrVm&+8onoCafk^Hn8WmTh#@B\hWb%A +mVdUHrrMl)qu6]R!:9^urV,pCBmK=Aq>YqYiq)d.[8\IOrrhp:C';uarsPY\Z0Lc*>!`2g!7Li; +!;H6f!e4qDrppNjqtlCS_uKc#D/JM3rrE,JrUU)E'_:lOuFo0upXs"TJppZ@h_p\XRRdK':DsmE#$^b82md@iW +*;BI5df&b?p](6nhY[p +ZMsp]r;Qp`GOFTs@ab8fO8sLYmXP9:!8dbQZ%mt1p\4\;kconsXQJ6!jrV"=#5-3*]_T_%Gq +>6"UcOT+N"nGE6cBs[6FQHT/a@*.iCCTRQ9Bs[6FQHT<">'KDs>%7O'%))DS#kS)]%&EL_N$\H& +!gGtNrr3J.Bs[6FQHT/a@*.iCCTRO8*6Dt=J,f=oAD5nZ#kS)f%))DS#kS)j+e\GlHWp4E%K#_] +lJe%LbQ%(>D(0u6rs#&uZKe)hYkJ)!ieUJ4rVm$aGOFTs@^#e5mVdUPrsAYTS;!9TqujD;LP)Q"s8RT"R"(4Ks,-l$gA([_kconsXQK5c^]4?-\%h?LAu^QNchmM;$1<"k%JlrrCpHrrR[gmed"i]iLHts1_[%rV6Bn +!8dSP"T[EZrrCpQrrN2WmdpGsLK`%&s80F?qsK/?_>g/Sdf9=YV#12lpODr:rrLsVr;QcthYR6Z +IlUo6s8T]R3H+?\rsOTcci*n@pODr;!8dSP!lJ(C!:OOjGi!-d/ls!?U# +Ko)[3J,e`;LP;\ds2@DgW9i`QLP:Z*s8V?mV"=#5-3+!uo=Y4oS5+S~> +o`$]Gp@.D0i7li1c,di=]XYATYH4b&Vkp2bUSFW\V5L8lXfo%<\\,_raj&8cgu7D]nbr%Y!8IGO +"[75!k?DNGrt(4kFkH)2s8N)Rs80*WK<"\?s!m6On,N,+T_%Jr>PS4-q=V>jW.p/*>CZ\9Y_Rq2iW$ne3iDc`9n3~> +o`$]Gp@.D0i7li1c,di=]XYATYH4b&Vkp2bUSFW\V5L8lXfo%<\\,_raj&8cgu7D]nbr%Y!7q)J +"[.(tk#u6Brt(7lGM;J7s8N)Us80'VK;eP=s!m6;LP;hlgp#9-s2V;)kconsXQIJj4/hNEqltp/ +W;$>lJ'!Uu\]KVZ\`j%rn?m*^J,]HlSm&GbTO+l"N$4kcl?c_7l3sK[mYiIlpE?4$l?c_7s#lJC +l7qf*s/Ke>m_b@6qi"q)m^$]1s/Ke>m_b@6s.3DlJ(C!:OOjGi!-d/lrtXb&LP;hlp:%g:s7Wq:mVc^;KpL'^q#:ZgJ,fPbMgpu%!9sLbmVdURr +rR[gmf*4fk%OttrrfkDhBd[NrrG!ArVm&LF8u7?hX^[JIrFcPrs6;=QMpiu=I/p\rrE,VqYp^!h +Z*TUhY[mKR!Ud"esqG[W;$=j[ +Jp1AR)/h"Z%mt1p](8CesLrKf)LX.GM;EZru'alKo)[3J,e`;LP;\ds2@DgW9jQ4OFN2-rr3)[L +f+6;rrhi!D(0u2rsAYTS;!9TqpV^Urr3_#_>jD;LP)Q"s8RT"R"(4Ks,-l$gA([oi223bVHeWNk +consW8dir76@I7kconsMoG_rl3sK[mYiIls)[e6!p>e?J,~> +ZMsp]rVm)n#ho=YYoM3l&2i0rrCpUW*4Oh:7V+Z/Gl5JEH,0Vb@[";n9]*3Ug.nZ>,&i% +4/hMpW*4Oh:7V@a!-a3%3Hp*^3V2M8?hDU?:4NMp1R"^X6m.l'LgAh2X*<5V[qu>(QoDdrkp](9$ +'`S+i]h5(#s8VtK>aU5,]Bo35GPD-sD2J;mhLg#MJ,f>T^&@0C%.jMjJ,B8o%.jMb!:Tsf`YA(F +'?,7#qu?<5VG;X@s7q(_m/P^MNV'kM1Ps6afTmVb%*F'MMZ@tTm/$`*i.kKSF_!"PqkQtr +F',%2`C2kekk@tIHYQ%Es8VAhS,hgR"SBs9:4N-?,1D*YG@5R<^Ks)'s6:H/s/9+JF&&8*rr@7u +Inj>MMZ@tTq>Vf"LK\V=A1N0,UeYB?=J#E/76@I3UeYB?C.@stk&:"os0WR@s+L!W!q2XSJ,~> +oD]U)p@.D1iS<&6ccX;G^V%+c['Hp=XfVK%'s+O?Yd1XF\\,_qa32fXf\PNKlgaoCrrCgPrs&'# +f`1uU+O^9$NrX=TgAq9R!8IOR?$UKeSG<+8qkd_?EEf0[AsE8aHuIo>?ZC'iU;BA\VsDj4?$UKe +SH&ThGDTTdGD0B`oBH9^oZa7-J,fQEI;nNPf4^6iTj_a\js0NXClN$>Ps2>iTru,m* +JZ@)oqh&+?L60(8olYHOs5IpSqh&+?L60(8q0d57s8T;Gs7:a;rQkuAs5e+js8TGCrr4je8Yc@d +s8.FQR"g^8mIbUFpAa,iMuWh4JTaq@s7s4=r8u?7n,N!/qu+kMn,M\RmJm4*)"dhA`H\N]s7FR5 +9n33Cq0d5Js4:qFpAY6ZZC1dmrs&'#f`1uU+KteHlth1Ks!#sFCNa-/c2W8WrVu1#[f<@5H#lcO +s8N(CM189+s,@#$f_PO_qkd_?EEf1%D1DTcm;7@?D)QHW>&_a]rs@E[s2%QGGB6gcl2LhPD1DNa +#_-XRq>'Wp@Q=Ae$!g=+G:NFgnHRtNrrrAirVllLrV6Hjp\t;ECZtcn$Ms`$kA>)!rKLR*p\t6o +fDYOErriDWs8N)Rqu6]tfBE50!W)cirs#i3A!"%Kp&=spcspq$162A6"_gGUU;ZBqrrLjSr;Qct +fDYLSqYgGD?>Y;cr/l56D=.,u#..HAOL*R)rr3!!fDYLJqu?Zrg\_!O!8%2F"oJ>iHZS]Urs8Vt +pAY*lm;7@Ks!-$GCNa,^SH"(&rVu1#[f<@5H#lcOs8N(CM189+s,@#$f^&D6+lJ/]E-5_BJ,?$9 +@W>J?qSkucVs!eO8pP,>p&G'VZC1dXrrhu=VG2R:s!#sFCNa-/c+dULrVu1#[f<@5H#lcOs8N(C +M189+s,@#$f_POoi.tWWF_*(QqkQqqEEA_-`^W"ekG1g]EG7l7s5e+js8TGCs8R]B[K>c`9n3~> +oD]U)p@.D1iS<&6ccX;G^V%+c['Hp=XfVK%'s+O?Yd1XF\\,_qa32fXf\PNKlgaoCrrCXKrs&'# +gAh2X*7FiuO8sLYh>mTU!8daS>'k]FaeU;98[W9i!3>'kDs7:a;s3_>Is5n7os8TJ@rr4jh8u)Ie +s8.FRR"^X6m.GLFp]'5iM?!V4K6L4Cs7s4=rTMZ(QoDe+Vn,NF-'_MD=`HeZas7FR5 +:4N&hg^rs@N^s1qHBGB6dbl2LhQDh%`c +#_-^Vqtp$!?o@r`$!pC,G:EFho*"(NrrhcirrCdNqtL*lIr4KLq[*6$k\Y2#rKLL'p\t6ogAUpJ +rriDZs8N)Uqu6]tg?SY6!<)]m#..KAOL*U3rr3/LA8JY.OT,7`C.3WF`TmC$!T!hSrrE,Rr;-p$ +s*n?kJb&c&G@GLYq#:Q*=CRAuf]rJ9!gl56r/W*4Oh:7V@a!-a3J:1!sY!-d/m +s!HZZ>&SOo.t@Gl:1A9HT)6Jq4/hA=:1A9Z7ecrFDtj;3[1rZ@F7ZL8kEJSh~> +ZMsp]rVm)]!;lfrecaD#&-1Seh>mTU!8dT)T_%T9-2768/GF?fd-.L?mX90?b@Hq3G>urQWk&"V +DsmRg/YM_'PU6)(!1'p;@(o6cg +IfTNC*,C%B7Os!rRs@fQK*mf3$p>'p;L +g.nI#!<<)iF#S/_??afmKjMsj[6K0@m$)e3Dh%`c"2APJ@f666Io9bUs77)'oBLf*p&>-XKnnsl +rs\=6J,fQ;+ErrLsVrr32^!;lfrec`2V!n1P8r;Ru>P\/&,_-E#pdL>"4W%%?8/YM_' +PU6)(!.XG:X9ek+!8dMN(\`,Qd-.L?l>M1Ps6afTmVbS.m($/Yqt^6uh>mRN?J>/1@K??+!7q$s +!NPGZrrVV,J,TB^Il2:oHZ`"kSH&WS(p*b_!6P9(!;HrrE+8rd+Wtrr3-%hZ*TUhY[XB7h>mKR!J+!?<#ljo)hY@*iVH05NlC]k] +qlu78iad-$s*ntTqtFm6qoJj#rVloT!;c]qf)'psf(T+FmVdUPs!%E+B$'PY>`Rl`%,V#_.J*Eg +T_%T9-27E>J+!?<#ljo)hY@*tVH05NlC]k]qlu78iad-$s*ntTqlu78ilM2_s826apUsaWs8RcD +]`RYm:4N~> +o)BI(p@7M4iniA=dEThR_na!u\[T#T[/I#\Js7::j +FEV#8dJ8K'bjVr)JH+u#GeS$Err30$]kC>QrVlg*rI%UQrVuI+VXs,'!qlQqkCn]UrVm;nD1DTc +nSrsSs64?Oo`"sH!<3!&irJoTs3h1Vrr`8L@Z:6b,5md?aRI#NNVCj4gAe*"oD:57naD\fp\t5C +n,9UHqC&N;WY4qqtIl)0EGm]bds8AB_rrLjS +r;QctC&N;dPLfb`D2iS`j`ul5D=.,u$gk?MCM7Et_Z0W9C&N;[Y5eM%g\_!O!,;CW"g7mcEI7TX +rrVS)J,90hrKV';nA1>7pX0W,s/7@;q_4]5n!#*lrr@Q;rN$;)rrCgKru7n=SET720n9&(OOrB" +EUj'YD=.2mCUX&Y!9=%X!S[VNrrCXIIfS!lrrVS)J,B6irKV';nA/@hjDbabs/7@;q_4]5n!#*l +rr@Q;rN$;)rrCgKs!F[HSET720n9&(OOrB"EUj'YD=.2(OOrC0!9=+VmJlpn=aU2KK(HDPl^COu~> +o)BI(p@7M4iniA=dEThR_na!u\[T#T[/IUQiW/lU +s4.>#rt#1es5!bUrrCpQ/YM_'PU6)"s"*ng<7'j/\aFlekf[M&oljB^kc22jIrFcRPV&R"mZ]$l +rrAG`g&RN;g&U-qs%Vp"J,fQ;+JKc$WWl@/g6qsOLY]iod_o,+:pl@/g6pDdsuDae`n +noHa2F^B:>q1W.um^59/noHa2F^B:>n8WmRs8UXQrVnBt;I]\+qsOLY]iod_s4U5Dh>mTUnoHa2 +F^B:>q1W.um^59/s6Ptq_/Fi%rr^#M;I]S($\)&`s8V[-V""4W%%?8/YM_'PU6)(!.XG:X9ek+!8dMN)l?Z+kj"fqJ,?L4f]$F_o)F2X +mec`o[JS&Gk5G;\h>mEP!7q$s!7plD!q'uVqu7l=P\/&,_-E#]D[bM&W%%?8/YM_'PU6)(!.XG: +X9ek+!8dMN-Djh6kj"fqJ,?L4f]$F_o)F2Xmea8Nf](!Qk5Y>Hs7a31HiO+eo;r)WPY-H~> +ZMsp]rVm)a!;lfrecaD#&-1Seh>mTU!8cu@GCP*\!93hS/GlG_I?1r(s.j2?Sn,h;Wb[$D\a'3E +Dsm7?!-`pBDZJes!8@JQ!8@JQ!8co='^fmhs8VY2IJs32D1V`)LJDo7?3pQ129CJlKBE46LJDo7 +?3pT*R*u$&Io_m)EDX^D]3La'_eNS%C,:M3EDX^D]3L`oF+*rcs4.>Orr[aK>5S=!--:d\KBE4* +@WV:t!;lfcEDX^D]3La'_eNS%C,:M5S@"LJDo7?3p6(!jQ[W +r;R2nDh%fenoK6Xs6=HPo`"sK!<3!&joG;Ys4.=Wrr_hT>eU(8,4p">H[g5uET6jQOT.n)s69T. +It)A:ir9#'s8UpUs8N)Uq#;0-Wb[$D\a&U$J,fQ:Dh%1nFnG_+F`m!Frrh0YrsZU?rrKq9r;QcJ +qgSXHlM(;[mVdURrtU#BGCP*h=Bp@Us/5p9IsV06s76BQrVm&uR,\,GDuG.cZ1n8"IlDTNIgMLp +s8U,CNkJhlpAY-nDuP4dL]7;W!8dbU!8dVQ!W[._rdOs9f_POXrT<>9fX$'(SGW?dk\1%%rr39$ +df8UOTO'tZrr3#U!;uit!,qgc%@GfJIrDr?VS.,]IrFcNrs\kgkk*N6IpPJ9rr@!9Ig!%VrrCpQ +rrE+>HrrC=Ds5!bU +rrCpNru6Z&hZ*W6@"847Wg8rCDsmXTDsmZ*_3C=O!8d\S!T!hQrrCXIIfS'nrrVV,J,B6ioQ<6B +Is3hkm[,kns%.kCl2Z$XIr>>HrrC=Ds5!bUrrCpNru6Z&hZ*W6@"847Wg8rCDsmXTDsmY`Wg8s8 +!8d_T#K_U>KBE46K_)kYn!m.'~> +nc':%q"!h9jP\hGeBuO`aMl'5^V.;W]+Vcj^VRh,aNMlVe_&[8jQZ((rpTjeg\q-VjT,,Vs4%>$ +rt#1bs4[PRrrCg>!d0!BDZJhns"+&KCjhARkl7Gj^RSlTqlFU^O14?QIr4TGFolf9Ir>>IrrC[N +rrC[NrrCg:rtOj;J,fQ>Ed[fbl>;.Q_hV*3H!L^Ts#%rOF+NHQs+aHoH!L^UopO8,mt"T,n8M7G +n%)_is2/Y_GAAsdn8M7Gn%)_inoB-Rs8UURrr3-"IoBSKrr4RmK6)V*s79fCo@F!@s;]DKDDTbZfPtMqu6Wp^1gMMqu6U(rI7aMqu?\O;0@.i#kn9#ri^1Or;R2mD1DTcnoB-U +s6=HQo`"sH!<3!&jT,,Vs4%=Xrr_hT?G6::,5$+@H@L,tET?pUO8hh)s6B`/It)A:j8T,'s8UgR +s8N)Rq#;0-XDEeqaZM4A#Il;KLIgVRqs8U,EO1eqkrqHEn +!,_^b!/LRR"T[Um$jll?g[!u@Kp\t51rI4sUs8N)Rqu6ZsD>eqfZN'B= +Eq').#lMcVnGiOUD1DE^,5$+@H@L-W!9 +nc':%q"!h9jP\hGeBuO`aMl'5^V.;W]+Vcj^VRh,aNMlVe_&[8jQZ((rpTjef)>UQjoG;Ys4.># +rt#1es5!bUrrCp@!-`pBDZJems"+&ICOM8SlMmYj^ReuTql4F[O1FQUIrFcIFT?W8Ir>>HrrCdQ +rrCdQrrCp=rtOm>J,fQ>F++#el>;+P_hV!.G?tRSs#%oNF+`WTs+a?jG?tRTpR'D/mXJ9'n8V:F +m^l_ks2/SZF_W^bn8V:Fm^l_knoK6Vs8UXQrVltL;Hitss!>d>F+`WTo3ue8g&V$Mn8V:Fm^l_k +s2/SZF_W^bq5S5mQT#3Gp_s8UXQYl=grP$G]us!%)X4*uI$2f[jS23@i0KE(3:GCP*\!94"Yc2[h#!<<'! +hY@*eql4F[O1FQDDh%femVdUCDft^PoQ>UTS!<3J@rr3#8!;uisf)'ptWp9?Y!q'uVrVmU^ +2g9b$HWXO&s8T"tDh!*6Du]D3ir/l\qj?CC!,qgc!3u:u!e3.QrdP6Af`1ul>)p:(T(`0b!,qjd +!/LRR"T[EZrrCpQrrN1=GQ*'mR+V67&,kYSo[pL":n7F_s6+.sZ2Xb/qpPN0`LpO(p\t0nh>mKR +!<@':Igqdts*nt!F/HWn[sr>aq>Um%k32HiZ[Z:Jp\t53rI4sTs8N)Uqu6ZsDuG.hZ2aEBF7B2/ +#lVlYnGiOVDh%W`,4p">H[g6X!93l5OT.n)s69T.It)A:ir9#'s8UpUs8N)Uq#;:eAD[@di*_'7 +i2W*/8oO.tIrFcTs1sqePQ9S=rrLsVqYpQHqgSUspAY3^Dh%Za,4p">H[g5uEUK2OOT.n)s69T. +It)A:ir9#'s8UpUs8N)Uq#;:eAD[@di*_'7i2W*/8oO.tIrFcTi2W*/PQ9S>rs-YuF+`WTs)[e6 +!p>e?J,~> +ZMsp]rVm)r#i>U][2dTo&-1Seh>mTU!8cu@H[gNlBBohs3_&=s8UrGIuBC;pAY6[ +H[kJ]rs\=6J,fQGIrk&Xg1ZKU][2cFN"7hgg^&7mSoQ<mKR!rrVV,J,B6ioQ< +n,Fg4oC(u,inrMCe^Ddfb/hWB`5BL0_Sa:0`Q-'Bc-Ohgf\>"`qp&=sshk#CYO=L:ncMmiZa(gA(H[,5` +jD'DKn%OjI"-QkBC#S^3o1k1s60N/ +It)fXli-t>s8UgRs8N)Rq#;:fBA`ahhdCs2Ve_3`I"-QaWV+At3o1k1s60N/It)fXli-t>s8UgRs8N)Rq#;OmBA`ahhdCs2Ve_3` +n,Fg4oC(u,inrMCe^Ddfb/hWB`5BL0_Sa:0`Q-'Bc-Ohgf\>U][2dTo +&-1Seh>mTU!8cu@H[gNlBBohs3_&=s8UrGIuBC;pAY6[H[kJ]rs\=6J,fQGIrk&X +g1ZKU][2cFN"7hgg^&7mSoQ<mKR!rrVV,J,B6i +oQ< +ZMsp]r;QpdK_t@M@aY2d!8db4!<<'!hYSb#oCJo5q#;oBYG1C7=F'`5I5t?*s80!om+,9aq1!8J +rK8V.oCJo5s8N)Us8N)Us8N)UkPkVODh%cd';/gLpSq],s2BnupZ>Y+s6;=ghYug?s#Z.@pZ>Y+ +s3aZapZd69T`:Hn_sY!?T)Zd"ddEcC^&OO6_sY!?T)YHmK_t@M@fQ0SanNa_oDe)1J):1i!WV$U +Iku!CJ&UE9B>a/H?Cq29SC6huC;'&'ouC!l&9A +qu6`cDh%cd#,#G?pSq],o`"sK!<)p$6%o.,@UcP@!nq7Er;Ru>P%`,3c'H3Gs5F`28cShf.B)k0 +W.Y-M!8dbUh>mTU!8@5J(]1D1m+,9aluQn)p9Zu4mVbS.m^lYeqt^6uh>mRN@bUS5B)ql0!7q$s +!NPGZrrVV,J,TBKIrFcSrs%h,!<;$eZ2XV2c31u`TJ`h5o?pf"p\t6Ar:B[_!e4qDrUU$brr3+q +9/1_]pAY-nhYR6U!8dbU!7Li;s8N-#dc15s!;ZHj$2]f`Y3>;m@#Y!^rrRZMhZ!N\pF1UKoBMtl +p\t0nh>mKR!#(+TIrFcNrs\dd<3GHQ`LnCKrrCLFoDegk!7Li;s8N*"der_: +rsm@iE67J+Q:R>=s8VM*J,90hrK:j=o?F4ds8V'mDb:!/q^JK8o;I +mf*msp$qG6jl51Qg"+[$d*Bn[bPfQpbKS5Vd*gFpg"bKEk3D@(qs==_g\h'T7"Y7+@qF'f%fkAa +gAq9R!8Is.&'P0js.2-OkOHo'2YI(MdJWV1rrR[`ir/HKq>UBsU.d@hnFlk_!8IAM"T[Um#O&`FJm_kb@p&>$>r:Bsg!r3drrLjSqYpQHqgSUqpAY3]D1DH_,5dR:bOWrF_Z0,a(3*%Js7n.Anaic7s8N)Rs8UgR +s8N)Nq#;J8=e4dATe_,kGA$$p@;G3/Ir"BDGA$$p_>r3fkq[hGrp0Ua!.sgN!pc:LJ,~> +mf*msp$qG6jl51Qg"+[$d*Bn[bPfQpbKS5Vd*gFpg"bKEk3D@(qs==_f)5OO6%o.,@Udab%fkJd +h>mTU!8dT%W:TVZKD>Htqlca\el[4nnpNN!VZ6OsU?pH5H22r,m/<(rW:TVZKE(rOhZ*TUhZ*TU +hWOn?mVdUSrtD&7^\=a;YlD\#XnUs]HiN@cJ):1is8P0=XnUs]HiMMhanP(M@%dOgB>a/H?Cq29 +SC6huC;'Y9B>a/H?Cq1i@YFXM@UfB3/]mb+%.jMa0n8'Zl2gqArdQ_B!.VuIRV0P+[pG[+`LPBE +TPD1.Y((qF2a=%nP'c-D`=2AmNS"[Rh=o3u14rrUndMu@+Nder_2rrR[air/HKq>UBsU.[4dnFlk_!8dSP"T[EZrrCLG +oDegl!7Km*o)SU^rsARa<3GHQ`F+Z%rr3%L!8d_T#kocthXg$*+nu!9!T!hSrrE,Jr::?ms*ntT +Sm"mLqgWPPq>Um#OArCHm_thBp\t6Ar:Bsg!91Ir4QH +mf:Z5_#W3frrLsVqYpQHqgSUspAY3^Dh%Za,5m[j#a'Q6SCs8+7DoCJo5s8N)Us8UpU +s8N)Qq#;J6=J"aAU,.;nGA$'r?u>91Ir4QHGA$'r_#W3hl7meFrp'O`!-@b6!p>e?J,~> +ZMsp]r;Qr.;L`mcajL,!!8db4!<<'!hZ'6PGBZrHq#;oDVI"=`?E!i&VCPj#s8Ag$F)bNbrK'LC +m*D>.GBZrHs8N)Us8N)Us8N)UkPkVODh%cd'&%tPHX_oos2BmkGB6sAs1&,>rG2H53f?aWF'@;t +l<7MrG2?2!T!hU +rsmV0@WQ"0f`/p,@Wc:Om/?qkmVdUTs0,gDBj,HSrrce7G?SqarrnaP&rrCpU +s5!bUrul13q>V6/VI"=`?E![k:i1i53IgI82fj+HFj^!a$/P[^_+nTdG@LXNrrCXIIfZS7p\t<_ +Dh%`c!e5.Vrr32nErc6-*:Eh+&,S(ns7:G8CNEuX[K$7,A,LBKqu-?j!e2q?rGr4QrVuoF<[OR6 +rrE,VqYp^!hZ*TUA,UHOp\t5'C]/;T\,H.&#LRa!Df]N9bPhGBIfS@(rs#8kC3+H"R/R$ch>mKR +!&SIo>+#?i!+YqJ"8i)uA,UHNp\t5'r,W5Bs7s'7?uKg] +J(jo%!q'uVq>UZFI;!h8;NUnX!_Es.rr3Y)>'G0gOT5=\hZ*W4!<<'BCYJ.Oru9mO>&SV%0n9(h +<-a6u8@SV\7\],@4_oh/+`I>Z!T!hQrrCXIIfS'nrrVV,J,90Lc[BJNA9#ZhrrPb'Z2Xb9TNZP` +;NUqY!8dbUh>mTU+`#g-ru^0S>&SV%0n9(h<-a6u8@SV\7\]+8<-a7T+`ICk!+YtCs8N(Po=Y4o +S5+S~> +mJdasqt0CEl/q$ahV6`8f%&:!rR)5*eCE.&g"bHBj6#UnnG3(LrrCgOrroi.IsFf3dJj^Wg]-". +s8N)Rs.WY?G?'e#s"+)F?ZL*mW;Q[m/6pd+r2*hAE*oRAO%,l0d%XZeG?'e*rrCgRrrCgRrrCg: +rrVS)J,]H\g4O'dA;oUL`f3f=F'I;s[K'h9C]FDqNDOh?>M/uA>@D_u=GY`3eUM(M@Y!)1`c#C1 +B:8)ceUM(M@Y!)1g4N7PGtuN^rF8uS>AsK?[K'h9C]+25gAq6Q%b6&=F(0[Os2JtfF_@-.rVmi( +D1DTbY[2!W>AsK?rF8uS>AsK?s,Bj!F'I;krr`7b?c`-E!psiSrr32VK4oXCN9'X2!S[VQrroi. +IsFf3YQ"[jCk)?^#1.d3G@kp;rr3$l'!VB8&u&NQG?'e*rrCgRs4[PRruc+0q>V6.VI+@_?`<^j +;/V)93.L=43-B@KFj^!a$/5I[_G+ZeG@LXNrrCXIIfZS6p\t<^D1DNa!e5(Srr32nEWH-**:Nn, +&,S%ls7(;7D/iuU[/^.+A,LBKq>C'g!e2q?rGr4Pr;ZfE<[XX7rrE,SqYp^!g].9RA,UHOp&>#% +C]/;T[f#t$#LI[!Df]N7b5M>AIfS7%rs#2kCNFQ"QMpgagAq0O!AnRp=dK*f!+YqJ"8VrsA,UHNp&>#%r,W5@s7is6@W,s\IbF`#!psiSq>UZGI;!h:&SV%0n9%f&SV%0n9%f +mJdasqt0CEl/q$ahV6`8f%&:!rR)5*eCE.&g"bHBj6#UnnG3(LrrCXJrrof.Is4Z0dJj^WhZ)F4 +s8N)Us.EP>G>aOts"+,G??:*mW;Zan.pCL'rMEnBEF,UBO@Gu2c_+HcG>aP&rrCpUrrCpUrrCp= +rrVV,J,]H\g4O*d@Z0=I`J[K8F'@;t\H$.2'#A>$cDo>)D&7eph.M@Y*23`GB"+ +At&)deph.M@Y*23g4N:QG>?9[s'f/U>&XH@\H$.mQT%b?,=F(0^Qs2Ae`F_7*/rVm5m +Dh%feY[2$X>&XE?"CbPY>&XE?"cH-"F'@;krrSKoanu)>mVdUSrs%20BmXmNS"gq6X +G>?8`rrVP(J,B6Mc[BJNA9#ZhrrPb'Z2Xb9TNZP`;NUqY!8dbUh>mTU+`#g-rtbR1??:*mW;3#] +D1d6sJ*q56F*1`1dJ3_Jh>mSl:1e]o@fZH,!7q$s!NPGZrrVV,J,TBKIrFcSrs&%8!<8E+mf*4s +q_;^_o645NF(7hMs8N(/r,Vo9rV?HpIkba;EsK2Qs8UJI-DL25![6H&#?i\,VOZU?M>7CPR]hDuK\: +h>mEP!7q$s!7plD!q'uVqYpcGI;!h8EQA(Y!_Es.rr3V(>'G0gOT5=\hZ*W4!<<'BCYJUa+5hoX +Dg-%QJ,>[6H&#?i\,VOZU?J%FH&%2!Du[?JA,U3Crr@-3XTI[M8q6~> +ZMsp]r;QosZ%I\>d/OW+mf2^)s8RT:s7`UIK;A,5s"!65Knm#"s8To3L%bQHk,a8lZg.S:OK@gG +pT0""V"=WdJ+!@:J+!@:J*uM"!qs+J!Asms3:Fjr;QfcJ,]HWrR7-d +SCmf?p;N#FXQKH\$MLsDs8VGnLP_8Trr3,TOFNMSrr3,hS:?IAp&>*0Y3c&c!qs+&BmeZqcJ+!@:qgSY@s8%3G^&@d,#Q<;J +KnGoFqu-Nr^OP\Mrs&A.MgpMep\k*mmXP07qL8perP,i8n?JeMs1c&:q#:[!fV!qNS?_hgs8%3G +qu?PC!VcWjIgQ#Ps7XX%JVU2^rVlrm\%hb=#5cf8K9W2'rr3&5J+N[>'DJS\K;A,=s*sJ:s6bC: +s.Fkqm.pZ"i223d[A^k?hOoS"Z]`0)c]G6ps1O&?`Lqk^rrMP;kPkVW\%he>#5cf8K9XRTrr3&5 +J+N[>')/J[K;A,=s*sJ:s6bC:s.Fkqq#;>lVM0r9W5&AhTS98LP/71EOK@g*TS98^TS*9hqg\YG +!/0sW!q2XSJ,~> +li.IoqXa4DlK@9hiSWMIgY;\\s4[q:hV[;Pjll!snG*"IrrCgOrri5'Kn[+brssVHs6Y=9s*sG9 +pT0($VXsQ^/*lYXG);(oDei?mJm39mJm39mH=KNp9qa8 +rt,.ZRY@Bhs8Vi^SUldEs8U=?J,fNOn"##hbl7VgfTgrC]\NMdr6gpbSCd`>pVr8LXl]T_r6gpb +SCd`>rRJ-+Kt@9`#MP5gQ/hoEbklnfrrMM:rr3H*d[,WWfDYaD`fGnWjo#,cp9qa9s6R.KMO!6$ +rrh&:J!K'nrrhe]JW7nIrr`89Y3Yub!qs(;rr30#eXD2ah"1OHm=5*7"T,EWKt=Ge!psiSqu6lt +d?oTObk_8C'g!5JD\ +#JC!7s7E[fg\(RIJ*m+4"+T_s8VqF!VucmIf]6BqL8kJs8Vf8Q@amjhZ!NS +!qs(;q>UZrd?oTOVXsfe!ka$Qrr3Z+XG);(oDei?mJm4RJ,fPAJ_9;aru0dlKo<@<^]3/`L8_JC +_Z.rDXm?#$K8IUBL&M#Pm=4=!!qs(;qYpcsd?oTObk_8 +li.IoqXa4DlK@9hiSWMIgY;\\s4[q:hV[;Pjll!snG*"IrrCXJrri5(K7g_]rssVIs6bC:s*sJ: +pT0""V"=?\/*5dp:%g:s6R.KM3R$! +rrh&;J!Aslrrhb[IuDSErrU$>m/-eap:%g9rrrAPRY.3eoD\jZJ,TBMqltd+\ZYr6mVdUPrs&AJ +R"LXHq>UBq]moYQrt>(-K7fuks8RT:s8VM:s8SaAgA([_k,a8lZg.SMVLsf9Wk\8:VQ,?9chmM; +$1<aK2If]HFq>UH0qgSja +rVuoi[@H%FrrIW;qYpYImf3=aIf]$2ac&pVAkVM0r9W5&AhTS98LP/71E +OK@gK]n;*ZTS*4C!UbI#rrVo'^\[s7qpCd`Pfrn%rrTl`oD\b&pT0""V"=WdJ+!@:mXP9:TRY\j +ruBsoKo<@;^]3/`Kr22>_>hiCXR+f:Kr2h^KE'3nJ,fNKF7ZL8kEJSh~> +ZMsp]V>gYnUggfas+13Frr_b1H23$kJcC<$JcFF'!/0sW!q2XSJ,~> +l2M.jqXj=Hm- +l2M.jqXj=Hm- +ZMsp][f6?]qL8Ns_>F68n>fTDk^ibbs.fSsq2eF\qqM,NgA6:"J\g^Q!USP:IfP,-rrLiTqgSWt +_=@O+m&^&5!/'A4!UQobIfY,:ao;D.XS[JJJ\e/^!SXXPIfY,:Rf +k5Q4qqXsFKmd9B,lK[WulK[^%mI0Q:q>KCS!8Fs^!8.-t!J82crrhei=+o^es+13Frri.Y:pp2T +rrC^JIfY/c`9n3~> +k5Q4qqXsFKmd9B,lK[WulK[^%mI0Q:q>KCS!7nUY!8@:!!J/)arrhhl +ZMsp][f6?]qL8Ns_>F67n?J5,JcC<$U&P5FQFm%KrrCdLIfY,:m/I+QgA6:!K +j8T>_r;$-]o^_tN"nVE]q>:0MrrCf^rrC^JIfY/GQ+QqJrrC^JIfY/< +m/I+Pf_U'tKsUd[fW=t"!J82YrrMGeqL8L!jSo8HXS[JJK#5Y0!UHiaIfY/c`9n3~> +j8T>_r;$-]o^_tN"nVE]q>:0MrrCWYrrCdLIfY,:qu6cfZ'<^gs+13Drr^f;]DL@_!8@:!!J/)R +rrMK?qL8KtZi:'[XS[JJJ\gmV!UQoaIfP,^rrMJfqgSWt_9)]Xm&^)6!J/(_rrLiTqgSWt_4(B( +gA6:"J\g.A!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +h#IBQgA_0-JcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +h#IBQgA_0(JcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]JcC<$JcC<$JcC<$JcD):!/0sW!q2XSJ,~> +ZMspZJcC<$JcC<$JcC<$JcD):!.sgN!pc:LJ,~> +ZMspUJcC<$JcC<$JcC<$JcD):!-@b6!p>e?J,~> +ZMsp]`r?=TKA,aCs8RT:JcC<$LAqA)R)/a=rrVo'^[qI+mXP-6!UbI(rrM,RqgS[Km(`LK"+cc_ +oDS[jc$uhmrrqhhLOYE%rr;kF!1TNiZ1n:;qL8P"s8.9LMp).8LWBAn"4<:@pA"XorRdcs +K:K4@s8VtG"IT5hJ+!46qgSU5rr3;ubF!p;W8R^8UAKE@XR+43!qs+ +ZMspZ`r?=UK@oR@s8RT9JcC<$LAqA)Qb`O:rrVo&^[qI+m=5$5!UYC'rrM,SqgS[Lm(`LK"+llb +oDS[jc@<"prrqkkLOYB%rr;kF!13H[!5JG]"cLRbWP\Z6rrUZJe+it?IgEF]s*sG9s*sG5s8.9H +Mp(nq$iJVLKnPuEqV7aXqYpL'qi.o]q6l32M6Q[Q`qk/cgAh&""cLS(^PKu0rr^Z2[eBLr$2iDJ +KnPu#p&G'hIfu,c` +9n3~> +ZMspU`r?=TKA,aCs8RT:JcC<$LAqA)R)/a=rrVo'^[qI+mXP-6!UbI(rrM,RqgS[Km(`LK"+cc_ +oDS[jc$uhmrrqhhLOYE%rr;kF!1TNiZ1n:;qL8P"s8.9LMp).8LWBAn"4<:@pA"XorRdcs +K:K4@s8VtG"IT5hJ+!46qgSU5rr3;ubF!p;W8R^8UAKE@XR+43!qs+e? +J,~> +ZMsp]`r?mHQ!T!hCrrL).qZ$\$g40&$!sC3G +kPbD^S,cajrrq7W&-*l,rr;iq!&=6J!.XnH";mNJ;MY5N!iT"qq#C3k#]'J?!8dbU!8dVQqZ$X6 +R/$[hqlrEq#W\mY\Nt`>rr3E$(h^Q(s8)d"(mt>Q&TrV!8dVQqZ$UNrr3;mQn\jX;SiCP7f*/d>2%7$!q'uVPQ(^PDh$gI!/0sW!q2XS +J,~> +ZMspZ`r?-Nqr_V[f?4)"W3U+IhInBrr]&BD=.,u$2Lel +$jo%(li7"^!!G"UrrCgNs8)cr0`M(Yn!Z!;&6$IG`^fn+!F+:&rrVS)J!9m6m;7@5rr@ZB[K>c` +9n3~> +ZMspU`r?mHQ!T!hCrrL).qZ$\$g40&$!sC3G +kPbD^S,cajrrq7W&-*l,rr;iq!&=6J!.XnH";mNJ;MY5N!iT"qq#C3k#]'J?!8dbU!8dVQqZ$X6 +R/$[hqlrEq#W\mY\Nt`>rr3E$(h^Q(s8)d"(mt>Q&TrV!8dVQqZ$UNrr3;mQn\jX;SiCP7f*/d>2%7$!q'uVPQ(^PDh$gI!-@b6!p>e? +J,~> +ZMsp]r;Qoo@gEWed/O@O`rGLKs8N)Ug]%>1GOEoZs+gUR%K!7b$phH(s7bt[#S;(Vrs[IF!!lKk +s8VOc#S;(Vrr@EE!"_0$s8To-$n\:Ps7`0<$phH'rsZ_9$n\:Ps8/p.#S;(Vrrg=j!%GV\rrpt? +!!lKkr;Qf;!<)p/_+G+fV#12dTIgR<\c;]C4oPEa3P#1'4oG?cF8u:7@gEWerr3,q@gEWep\t8a +:Ab(mDsmW)s+gUR"oGDZ$phGprr+,PrrVV,J+N[@mrSC+!T!hC +rt>4tkLO-akNDI:Iq.'mkLtPS9)\bl65U"&rr3Ah&7a;JV%A"RrrC(;k5auFrrR[[f)Fh?lhC-t +>2'#VqgVf#q#:?p`r5'+p](6nhZ*TUhY[+,Z +rrVV,J,'$FLNi1\rs71B!!lKks8RcD]`RYm:4N~> +ZMspZr;Qoo@0dEed/O@S_uK%Fs8N)Rg]%>1GOO#\!r[n2rr4/=X>C>P])Vg'lW"3%EVoe9f0KNF +mMYqlDU_6Fudg!W*T0rri()#S;-]rr_e2H267q!>+/QrrVS)J+N[@lZN+) +!S[V@rt>4tkLX3bkNDC6Iq.*nkM(\W8cAYk5o^=-rr3Ah'P5bNU(i%UrrC+M/uUq0cJuq#:?pa8P0,oDegjg].9Rg\_!P!6<^jk5ji?q#:`'9o.]aMEma0MaF`Krt'br +n,HNVcgT1K4TG0=kJmT%k6(V]!6>'$%I*QcIfa?Is8Vh?"E!Q$rsPo&Lu7*I76LVM!6>'$"RH*f +!8IDN![Keht-jLm;7@JrrRt"n+H\]q\82m!o[R; +g]%9cht-jLm;7@JrrRt"n+Zhff0KNF +ZMspUr;Qoo@gEWed/O@O`rGLKs8N)Ug]%>1GOEoZs+gUR%K!7b$phH(s7bt[#S;(Vrs[IF!!lKk +s8VOc#S;(Vrr@EE!"_0$s8To-$n\:Ps7`0<$phH'rsZ_9$n\:Ps8/p.#S;(Vrrg=j!%GV\rrpt? +!!lKkr;Qf;!<)p/_+G+fV#12dTIgR<\c;]C4oPEa3P#1'4oG?cF8u:7@gEWerr3,q@gEWep\t8a +:Ab(mDsmW)s+gUR"oGDZ$phGprr+,PrrVV,J+N[@mrSC+!T!hC +rt>4tkLO-akNDI:Iq.'mkLtPS9)\bl65U"&rr3Ah&7a;JV%A"RrrC(;k5auFrrR[[f)Fh?lhC-t +>2'#VqgVf#q#:?p`r5'+p](6nhZ*TUhY[+,Z +rrVV,J,'$FLNi1\rs71B!!lKks8R03XTI[M8q6~> +ZMsp]r;Qq91#C2!PgTL3!8dYR!ehN-`rtnr& +hF^E?s$BWHehN-`s&*q;egTPPs2A;shNV'Ks.DuShLf.Rs2A;shNV'Ks0YTVhTd:/s8Nphmu.hW +rrj6CkMAg'rVlr^(]aR8)T?$8hNV'Ks.DuShLf.Rs/9D&f%/jTs/9D&rmhYLs8QjhhTd:/s8Qjh +hTd:/q#:Bhk5Np;W*Z.spWe+3J,]HP[8)M.G?W,^rr__l8c\hi"`#jLc68FUrrVV,J(su(!8cu? +!q'uVli."B!9sLbmVdURrrR[gmf*4fk%=\nrrhKb]`>26rrHV5rVm%t#ljo)hX^[JIrFcPrs5Du +XT/=O>(?GErrE,VqYp^!hZ*TUhY[&F#k6Rpars73TrrVWhn,E=f +h>mKR!3Fa7 +9)npEo=Y4oS5+S~> +ZMspZr;Qq;1u$>!QI5^5!8IGO!pWe+2J,]Hl[SVb0G?`5iU.&)Ff//Bbq]&o) +hapE>s$]rNf//Bbs&F(FUs2JDuh3(jJs0b`\gWq".s8O'jmuA+Z +s8*dAkMJj&rVlr^(]aR8)TH-:h3(jJs.W5Zhh>FUs/KS)f@T!Ss/KS)rmq_Is8QpmgWq".s8Qpm +gWq".q#:Bdjo3j;W*c;!pWe+2J,]HP[SVb0G?`5`rr__m8HA_h"`6*NcQJOWrrVS)J(su(!8Hc< +!psiSli."?!9sLbm;7@OrrR[emJd+ej_"_qrrhKb\cf&4rrHP6rVm%q%/^&)g[b@GIr4TMrs5K# +XT/=M>Cl\HrrE,SqYp^!g].9Rg\_!P!8Ho4rtL*&l2UeVGB``8?"`@#k6Rsbrs7*QrrVKhli-nb +gAq0O!qYpQrg\_!O!8IAM!jH%7rr38N=+gJO +s6XZQg&D'Qg[kFHm;7@JrrR[emJ$V_n#f4^kPkYUIggh]rrE,SoD\mZD1D?\!e5(Sq#:Zt**qab +8P/s^K(HDPl^COu~> +ZMspUr;Qq91#C2!PgTL3!8dYR!ehN-`rtnr& +hF^E?s$BWHehN-`s&*q;egTPPs2A;shNV'Ks.DuShLf.Rs2A;shNV'Ks0YTVhTd:/s8Nphmu.hW +rrj6CkMAg'rVlr^(]aR8)T?$8hNV'Ks.DuShLf.Rs/9D&f%/jTs/9D&rmhYLs8QjhhTd:/s8Qjh +hTd:/q#:Bhk5Np;W*Z.spWe+3J,]HP[8)M.G?W,^rr__l8c\hi"`#jLc68FUrrVV,J(su(!8cu? +!q'uVli."B!9sLbmVdURrrR[gmf*4fk%=\nrrhKb]`>26rrHV5rVm%t#ljo)hX^[JIrFcPrs5Du +XT/=O>(?GErrE,VqYp^!hZ*TUhY[&F#k6Rpars73TrrVWhn,E=f +h>mKR!3Fa7 +9)np4o;r)WPY-H~> +ZMsp]r;Qp3^&S+s1"-(5+TN(]h>mTU!8dbM@h8oDn+Zi3dUNgs>0XTeW;lmZ?Hpo.&.DaeM[Tl# +gACVS&-u2&s8N(`(]\WA&.!@@rrhVW?7g[+s!!51hZ(],R.ke)n,NF-.K@s!n,NF5!<;Kfn,NF- +.KBDsXo&,NOE9F]o@DpqGB`f]icgXeOE9F]o@Dq$LM"7"s-,81=s3:WCrrPN& +!<3!2OE9F]o@DpqGB`f]icgXeP\\kJrrSF#f),IV&AA,DPVN4<&AA,DPVMh1$1<;!p]'NK?7g[+ +rs"6jhZ(],R.gO^emhk`rVm$4^&S+s0sCTYmVdUPrsAS)3=IL4p]"-,rr3]fK`CdV&-u2&s8N)$ +0`WB!rtk_I[J9b:dUNgs>0ZJRJ,fQ:Dh%1n:0%cTT_J6]$/P[^s+D3h.?jhsrrVV,J,TBKIrFcS +rrV=u#lai,N$83n;Z?[s#gWAJ"Q'1Y!8d;H!e5.Vqu6oP>(?GLW)g"orrE,VqYp^!hZ*TUhY[mKR![mf*4fmVdUNrsAS)3=IK;n,H:$rr3`gK`CdV&-u2&s8N)$0`WB! +rtk_I[G(HV)8T&V'OFC]s3bBg$rY(EM[Tl#gAdum!#U[orr^h^=odac"/&[PJ,B6PqlMje/YMj: +?>KQ?'etCApL=a?;XaYc!3IsU+g(b]!"cR5s!>KV$l!#?J,dr,&.DIUs,.9k@bT7?&.BD7s8UXM +n,NF5!<<(Po=Y4oS5+S~> +ZMspZr;Qp3^An5!1t)C8+92tYgAq9R!8IPJAIo,In+Zi3dUa%$>KjTeWrN*]@F!;4&e8-iN=H5& +f_bDS&-u;)s8N(^*!CDM'F8^BrrhSV?S$^+s!*;3hZ(`0RJ1n*li7"&/H=0!li7"2"TRojli7"& +/H>`$Y5A.s/rt#2s7=k&pNLcQs5A0es,V0Ys7=k&s+b7$s8SBAs5sCVs8)fVs3gu6s8UCJrr3)t +6#?W$rtCH5iW&N$j7db?p&EupQ2d+deG]@G"-m`QrVZ[)&AJ2EQ8JXA&AJ2EQ8J76$13.pp&F9H +?S$^+rs"6khZ(`0RJ-X_dq)Y^rVm$4^An5!1p?o\m;7@MrsAS*3XdX8p]"*)rr3]eLB%!Z&-u;) +s8N)%1^"r$ru;"MZM=G7dUa%$>KlJOJ,fQ9D1Cqi9ihfUT(VmY$/5I[s+VKn.$=SprrVS)J,TBK +Ir4TPrrVA#%0$80MBMpn;?$Rr%*SVL"oI3SrrCgErrR[emJHnge6.:us/K5)p\t6og\UpR!8IPR +!8IDN!W_!BoD\oN7'ujgrroJpS,Zb[rr3$:EVK;/!S[VPrrE,SqYpnP:LTCcs/'p7D=.,u",fRS +qtpBo!8IAM!tKPGcs[Io;8iKo%L?Lqs.UV9(lJ=m"4Lga!9jFcrLR(KJ,B6P +qlVsg/u&*=?"s<<'ekCBpLOmANZ%MW2?J,dl(&.DFSs,@Ko@G&q8 +&.BD5s8UOJli7"2"TSLRo +ZMspUr;Qp3^&S+s1"-(5+TN(]h>mTU!8dbM@h8oDn+Zi3dUNgs>0XTeW;lmZ?Hpo.&.DaeM[Tl# +gACVS&-u2&s8N(`(]\WA&.!@@rrhVW?7g[+s!!51hZ(],R.ke)n,NF-.K@s!n,NF5!<;Kfn,NF- +.KBDsXo&,NOE9F]o@DpqGB`f]icgXeOE9F]o@Dq$LM"7"s-,81=s3:WCrrPN& +!<3!2OE9F]o@DpqGB`f]icgXeP\\kJrrSF#f),IV&AA,DPVN4<&AA,DPVMh1$1<;!p]'NK?7g[+ +rs"6jhZ(],R.gO^emhk`rVm$4^&S+s0sCTYmVdUPrsAS)3=IL4p]"-,rr3]fK`CdV&-u2&s8N)$ +0`WB!rtk_I[J9b:dUNgs>0ZJRJ,fQ:Dh%1n:0%cTT_J6]$/P[^s+D3h.?jhsrrVV,J,TBKIrFcS +rrV=u#lai,N$83n;Z?[s#gWAJ"Q'1Y!8d;H!e5.Vqu6oP>(?GLW)g"orrE,VqYp^!hZ*TUhY[mKR![mf*4fmVdUNrsAS)3=IK;n,H:$rr3`gK`CdV&-u2&s8N)$0`WB! +rtk_I[G(HV)8T&V'OFC]s3bBg$rY(EM[Tl#gAdum!#U[orr^h^=odac"/&[PJ,B6PqlMje/YMj: +?>KQ?'etCApL=a?;XaYc!3IsU+g(b]!"cR5s!>KV$l!#?J,dr,&.DIUs,.9k@bT7?&.BD7s8UXM +n,NF5!<<(?o;r)WPY-H~> +ZMsp]rVm)b!;HNnc3Vht%fkJdh>mTU!8d`Z;Wln/9)/E;pNKZdelm:SCOc6@B7KMs5,E)>5,E)kNDj]mVdUTs7:;Yqu>eoKCa-an,NF-!<;fMrr4aD.KAQ2n,NF- +!<<')0`V2OT_J2)7SX@DT_J&!GPD-s@t45s7SX@DT_J&!Hh[R?c3X@J"f3(9s7bCLrrJW=rr4j& +;VhERs7^_,:(/_Ip[`HMp]'5_J,b-@(]Z"Xp]#jc(]XP6n,MPNp](9=#lj&fp](9=#k\,uS9*9[ +rs\=6J,fQ?F*mfak%fVArrLsVrr32c!;HNnc3UWR!pXQNqu7h@>A#i>R\"hb`Y6B'LnrrLsVr;Qctf)57Nqu;.a +HZS?HIsZBXmeHedhJ*TJBCBs*G=WrrCpNru8dLB%#7F%"J"q3ORRIGPD,eDsmAg!-@nJ!:Tmd +!T!h?rrg#IQu@KOrunIESDoc*Ut/i`s8UXMkPn^`kMAg's8N'chZ%p6s8N)Uq#;DO:1hr*C(,Ud +G<*(b??c/DIrFcLG<*(b]`@O$pYPoL"Ipq@s+L!W!q2XSJ,~> +ZMspZrVm)b!;$6jb7)eu%fkAagAq9R!8INZ]PlL%B?cn,NF."TS5Nrr4aB/H=l5n,NF. +"TSK-1]RYYUA+D+78FCGUA+8#GPD-tAV'T"78FCGUA+8#H1V(9b7+@L#5b87qu?EFrr3"f/H5Z, +Gu<;rs8Vh;78FCGUA+8#GPD-tAV'l6*Z>)2UA+\?*Z>(_ufqB +8P/s^!(#N>I1ZGY!8I;K(AE)Wd*u9Dj_]MIs6XZQm;3u-gP8+mq#:ZT!<7cj[dUpM!9sLbm;7@O +rs4*gj7Dm*P#l5ors:B-s.TYfs5O+Rq>UHqe,Akrr>@BqgB%UrrCgKru8gNB@>@J%Y+4s3jdIDFnPcaD=.)d! +-RnE!:0U`!S[VM-KrunOJSDf](Ut/rgqu>7JkPngfj53F"s8N'bhtqg5s8N)Rq#;DP: +M8,,D%;'iG<3+^?$5i?Ir4TIG<3+^\cD'rpY5]I"I^nAs+9jN!pc:LJ,~> +ZMspUrVm)b!;HNnc3Vht%fkJdh>mTU!8d`Z;Wln/9)/E;pNKZdelm:SCOc6@B7KMs5,E)>5,E)kNDj]mVdUTs7:;Yqu>eoKCa-an,NF-!<;fMrr4aD.KAQ2n,NF- +!<<')0`V2OT_J2)7SX@DT_J&!GPD-s@t45s7SX@DT_J&!Hh[R?c3X@J"f3(9s7bCLrrJW=rr4j& +;VhERs7^_,:(/_Ip[`HMp]'5_J,b-@(]Z"Xp]#jc(]XP6n,MPNp](9=#lj&fp](9=#k\,uS9*9[ +rs\=6J,fQ?F*mfak%fVArrLsVrr32c!;HNnc3UWR!pXQNqu7h@>A#i>R\"hb`Y6B'LnrrLsVr;Qctf)57Nqu;.a +HZS?HIsZBXmeHedhJ*TJBCBs*G=WrrCpNru8dLB%#7F%"J"q3ORRIGPD,eDsmAg!-@nJ!:Tmd +!T!h?rrg#IQu@KOrunIESDoc*Ut/i`s8UXMkPn^`kMAg's8N'chZ%p6s8N)Uq#;DO:1hr*C(,Ud +G<*(b??c/DIrFcLG<*(b]`@O$pYPoL"Ipq@s)[e6!p>e?J,~> +ZMsp]rVloT!<3!!h>kt'&-1Seh>mTU!8dGMn,NFE!:Ta`"8@";cMm\Js,^mE[:663G>c`cq\T9^ +DsmLL!:Tsfh>m$E!7q2M!7q2M!8co='^fmhs8VM*J,fQ:Dh%eS0j3FHs +!<;LBV>oC47B#Wm.@AtR:9=J_7A/Lm*.RBp.@AtR:9mNSrr<&$r;SG=HPk'.s0X$E +h>mTUmVaPAhN1L6s-Q6JhM3)#pQ01=mYaN4pQ01=mbTs%s5!bTrrh0Ys5!bTrrLsVp&>*ADk-b*% +.8%`s8VM*J,fQ:Dh%EZ!T!hUrrLsVrr3#U!3cD(]iLNus!%>eGPD.*Dg1[E#eg7b;ZHIkn,NFE! +:Tpf^&S,h!<<'!hY@*QpNKN\rqd8pDh%femVdUCDenY(b@q>UTS!<3n4rr3#$!;uisXnr)!< +n@$F!q'uVrVlkIqu@61Dq=sgec=S,pAg``k5b8Ps8)crA,$!'J,B9Q0sUcYYu[[O?AJ7Squ?^.r +r3-%hZ*TUhY[?M!!+gZpAY<]Z"G4H_>aH:dS^'uqu6fMN'[MtrVloT!;ulo!"CZfs*ns_F1p"CT +RUnJpAYB_Z"G3[K`D)L!!Hg3rrCpQs8)d"A,k_5Hi*Um$E!58F4h>mTU!8dMN)iJ%Ns8UYNJ,efrqs%s8mf.cTmf3=mEP!3Q+u! +3Q%r"kmaNl>M1Ms!%>eGPD.*Dg1f&#eg7b;ZHIkn,NFE!:Tpf^&S,h!<<'!hY@*iMdFj3s415#s +6AbDl>(>+s*ntTs6AbDlIGslrVm)jHPk'.s+L!W!q2XSJ,~> +ZMspZrVloU!<3!!gAoY$&-1JbgAq9R!8I5Nli7"B!:Ta`/GFEgbkq53s,UgDZt$95GuMraq>(&? +D=.4K"RH*fhZ3-F!7UuJ!7UuJ!8H]:'^]aes8VP+J,fQ9D1DSP0OL_GJ+\k>d-Zf`WqDEid-]^Q.7q<`$Ks8VJ'J*h)gli63`H1Uk3"PWqV*2``8!NH.trrB5! +!!-*Lp\t<^D1DNa!.XnH%g6aus8U[Ng]-jJT)[gNp\Fjd!!$U)rr@QH!"(Kes8T<)VUf+.rV-?i +!!!T0rriDWs8N)Rqu?Nn!A^tSrrqYkBd,p]rr3)I;#l4Frrgf270&)@rrLjSr;ZWo%5P6hIr1p" +][CqsIr4THrs.emBd*P3s8Vrr"D.W5!8IDNqZ$e2s5S*GqtU0mm;7@Ls!%>gGP2")hZ3->%(cIa +;?-@nli7"B!:Tpf^An5f!<<'!g\CdfNFC98s3t%urTWMBl"P#%s*nnQrVP4@s3gu&rVloQ!;c]q +Y582!WqcStg22c2D1DK`,5Ze4p&FK!GOp@o])R%Qs7ZZ^s8UsVn,EC%s8UgRs8N)Rq#;:m@FG2U +e5_"sl0%s.Bl38;Ir4TOl0%s.dfA/$rs%q;(sDsZK(HDPl^COu~> +ZMspUrVloT!<3!!h>kt'&-1Seh>mTU!8dGMn,NFE!:Ta`"8@";cMm\Js,^mE[:663G>c`cq\T9^ +DsmLL!:Tsfh>m$E!7q2M!7q2M!8co='^fmhs8VM*J,fQ:Dh%eS0j3FHs +!<;LBV>oC47B#Wm.@AtR:9=J_7A/Lm*.RBp.@AtR:9mNSrr<&$r;SG=HPk'.s0X$E +h>mTUmVaPAhN1L6s-Q6JhM3)#pQ01=mYaN4pQ01=mbTs%s5!bTrrh0Ys5!bTrrLsVp&>*ADk-b*% +.8%`s8VM*J,fQ:Dh%EZ!T!hUrrLsVrr3#U!3cD(]iLNus!%>eGPD.*Dg1[E#eg7b;ZHIkn,NFE! +:Tpf^&S,h!<<'!hY@*QpNKN\rqd8pDh%femVdUCDenY(b@q>UTS!<3n4rr3#$!;uisXnr)!< +n@$F!q'uVrVlkIqu@61Dq=sgec=S,pAg``k5b8Ps8)crA,$!'J,B9Q0sUcYYu[[O?AJ7Squ?^.r +r3-%hZ*TUhY[?M!!+gZpAY<]Z"G4H_>aH:dS^'uqu6fMN'[MtrVloT!;ulo!"CZfs*ns_F1p"CT +RUnJpAYB_Z"G3[K`D)L!!Hg3rrCpQs8)d"A,k_5Hi*Um$E!58F4h>mTU!8dMN)iJ%Ns8UYNJ,efrqs%s8mf.cTmf3=mEP!3Q+u! +3Q%r"kmaNl>M1Ms!%>eGPD.*Dg1f&#eg7b;ZHIkn,NFE!:Tpf^&S,h!<<'!hY@*iMdFj3s415#s +6AbDl>(>+s*ntTs6AbDlIGslrVm)jHPk'.s)[e6!p>e?J,~> +ZMsp]rVm)j!;HNnc3Vht#lri^h>mTU!8cT0!!(UFru'[G/L5Pos1)U;LM#EChLdC*GM<(HDsm%3 +qZ%#TrrCpUrrCpUrrCp=rtOm>J,fQCGC05ek%fVLmbU81(k`J"rr\K&C[_9&"l;QG(k`Its!#Hq +KCa/.p]'AkKE(A$+Uh+IEo5f-M +s5n*Ls6bsl$r0EMr;QiXB7p*]&]P+Vmf<+^s3:oMmf<+^s3:oCrrS:#hYdB^mVdUTs7^_aqu>eo +KCo0Eh>mQT#4DQds8U@MYl=gfB4Ks!rrMP+qu@%=f)K5irtr0DhYR9\f)Ga,s8UpUs8N)Up\tf] +K.S?mg?rm/s8VM*J*q6)rr3%LDr1mTU!8dMN +)h2Ygs8VA"J,e)<0gR7,mf.cTmf3lj@#P3:XhWAU#qu-NqmVi"'$#Ah2@q5NU +[K#+\!"7BX!8dbUh>mTU!8dMN)h2Ygs8VA"J,e)<0gR7,mf.cTmf1jV0gS\;hZ!NWPSe3)rr2tO +o=Y4oS5+S~> +ZMspZrVm)f!;$6jcOA5$#lr`[gAq9R!8HE.!!(XGru'[I/gPSls12d?KkK9BhM!R-FkH_DD=-e1 +qZ%#UrrCgRrrCgRrrCg:rtOj;J,fQAFaqQn)$P:*.eb$rr\Q(DhKE(D',S3aCL[fA.p]'>hKCsA4oDej:%/g,,V@<4uruICBD'h%..n]s8Vb7H27L' +D2%W^!S[VRrs%choDej:%']a6k@nGbr;QfdDu9SACY/StAcEaYs53\R$ePCYg].<.!<<'!g\:^` +hM!R-FkH(uJ,fQ9D1CqiIJs3EI;e$=rrh'VrrC[MrrL^Ol2LhPD1DNa$\*$\s8Vhp81=Ns*nnQs3tsB6<+$?rr3(d(iAU< +rr@ZB[K>c`9n3~> +ZMspUrVm)j!;HNnc3Vht#lri^h>mTU!8cT0!!(UFru'[G/L5Pos1)U;LM#EChLdC*GM<(HDsm%3 +qZ%#TrrCpUrrCpUrrCp=rtOm>J,fQCGC05ek%fVLmbU81(k`J"rr\K&C[_9&"l;QG(k`Its!#Hq +KCa/.p]'AkKE(A$+Uh+IEo5f-M +s5n*Ls6bsl$r0EMr;QiXB7p*]&]P+Vmf<+^s3:oMmf<+^s3:oCrrS:#hYdB^mVdUTs7^_aqu>eo +KCo0Eh>mQT#4DQds8U@MYl=gfB4Ks!rrMP+qu@%=f)K5irtr0DhYR9\f)Ga,s8UpUs8N)Up\tf] +K.S?mg?rm/s8VM*J*q6)rr3%LDr1mTU!8dMN +)h2Ygs8VA"J,e)<0gR7,mf.cTmf3lj@#P3:XhWAU#qu-NqmVi"'$#Ah2@q5NU +[K#+\!"7BX!8dbUh>mTU!8dMN)h2Ygs8VA"J,e)<0gR7,mf.cTmf1jV0gS\;hZ!NWPSe3)rr2t> +o;r)WPY-H~> +ZMsp]r;Qp3`rH(/1"$"3!8db4!<<'!hY.$Es8Vi=q#;oBhW"Rh>'K`'CKbA4s81[4s4UY#pO@,L +qg/>;s8Vi=s8N)Us8N)Us8N)UkPkVODh%cd'8LA_s35/Cs6ebchY7'MS3m8$ +SGrO7`bUA0K6QqeibO>Lmcs]Lc#99TK6QqeibO>LLMOp0s-thDmn3TZ"p!ids'n(PrVll2qZ%Ro +`IiC+s5IgLs6f1Ls35JTk-`J6s5IgLk-`J5rt36^s8NYMs8STDs8NYMs8STDpAY3FB9<#j!q'uV +rr31XCZ>B=Asi5j!T!hTrritRs8STDYQ"[G@[R)n,5Z_6qu?QQk5YJ$&A8q\s7ZNfs8Vi=s8N)U +s8UpUs8N)Uq#;0-hW"Rh>'K+IJ,fQ*?@VB]C[1rbCO>gOrrh0Yrtqm;rrK5%l2LhQDh%`c!e5.V +rr32fCB4D7(khn_"f24fs&rV)rro5!n,ECEo)Ac@DsmH$"H-;UBB&Xb!mKR!RjHhW#.meQkk +pNLE +ZMspZr;Qp7_uKb.1su=6!8IP.!<<'!g[bF:s8Vi>q#;oBhrFah=a0W'CKY50s81^6s4CIupO@&I +q0;u3s8Vi>s8N)Rs8N)Rs8N)RkPkVND1DQb'8^M`s3>>Hs6nk>s8SZZs8QXmj8Ao_htR0NSji\* +T)SaCa)-\5KmE:ij(jGMn*BlNc>fTYKmE:ij(jGMM/1$/s.2(IlUh!RqZ,[Vs(+=QrVll3qZ%Rq +`eA^0s5RmMs6o:Ns3>VYkI/\9s5RsQkI/\8rt39cs8NeNs8SZIs8NeNs8SZIpAY3FB92ri!psiS +rr31ZCZ5<=BpnVn!S[VQrrj+Ss8SZIYQ"[HA!d,n,5HM0q>^?PkPtS"&AB.as766^s8Vi>s8N)R +s8UgRs8N)Rq#;0-hrFah=a/tEJ,fQ*?[qH[C[;#bCOPsQrrh'VruA'o)Ac@D=.0!"H->YBArRa!5UiH1cZ(mJ6bj +pNLE=s8Vi^!:0Xbg\UpN!8IDN!rrCgRs4[PRrrCgKru6r-eGoQurrLt` +qu?af`r>u^q0@8]s81j>s5Cj,^)[1Qo)SC^s7ak>rrCgRs4[PRrrCgKrud;2eGoQuc`9n3~> +ZMspUr;Qp3`rH(/1"$"3!8db4!<<'!hY.$Es8Vi=q#;oBhW"Rh>'K`'CKbA4s81[4s4UY#pO@,L +qg/>;s8Vi=s8N)Us8N)Us8N)UkPkVODh%cd'8LA_s35/Cs6ebchY7'MS3m8$ +SGrO7`bUA0K6QqeibO>Lmcs]Lc#99TK6QqeibO>LLMOp0s-thDmn3TZ"p!ids'n(PrVll2qZ%Ro +`IiC+s5IgLs6f1Ls35JTk-`J6s5IgLk-`J5rt36^s8NYMs8STDs8NYMs8STDpAY3FB9<#j!q'uV +rr31XCZ>B=Asi5j!T!hTrritRs8STDYQ"[G@[R)n,5Z_6qu?QQk5YJ$&A8q\s7ZNfs8Vi=s8N)U +s8UpUs8N)Uq#;0-hW"Rh>'K+IJ,fQ*?@VB]C[1rbCO>gOrrh0Yrtqm;rrK5%l2LhQDh%`c!e5.V +rr32fCB4D7(khn_"f24fs&rV)rro5!n,ECEo)Ac@DsmH$"H-;UBB&Xb!mKR!RjHhW#.meQkk +pNLE +ZMsp]r;QqA6MKXlPgTLA!8db4!<<'!hZ$JWkN?#/q#;oBIq%-[s-ui`hK*;_s05UhhMY:>s1)<"pZEuis8NXpmu/+`s4.1irRUoH!T!hU +s":QWPhq(nHrs#E&T]_t>SGrNi(a48*rt/.4kN?#/s8N)U +s8UpUs8N)Mq#;-,Iq%-[$m +hYR6U!8dbU!65!#s8N-#`n(+O!;$!c#H0e,db^3\T`+llIfS@(rru@M^$!sE6N-obh>mKR!r9++[#kNC#PhO,\4gk:h +!q'uVq>UYt3G'P3WK*ZhZ*W4!<<'!f(f7a_+nUjeo)+LpNL94_*Vr& +s*nhLp[8+-s+CC'rVloT!;c]qXnr(uXnD\qmVdUPrs#E&T]_t>SGrNi(a48*rt/.4kN?#/s8N)U +s8UpUs8N)Mq#;GP:1hr*D\.EmGA$'S6>PldIr"?DGA$'SKE0U'ec<_gf)PaMK_)kYn!m.'~> +ZMspZr;QqC61a7fQI5^C!8IP.!<<'!g](8Vj6']1q#;o@I:LpT=*scCOW6p+s7q(QfZmQFq1!,C +s/K(%j6']1s8N)Rs8N)Rs8N)RkPkVND1DQb'"V;9m>#&ks.)8`lcU9Rs3gtgrR_)L3]q!>b8dhf +f*%E.f/QgBs0>dnh2+t8s.)ubgN.#[s0>dnh2+t8s12E#oB.Nfs8*@jmtqtZs3gtgrR^uI!S[VR +s":T[QJIBo[/Z[nW7uB+f^!BK_W-DP]'F3-_W06-FoVJa61a7fQN-u-61a7fQM1=[W+!45rrVS) +J,]HP\m(WjLL83rrrLjSrVm%D61a7fQEU;ilYD"Irs#K)T&l\?Sc8Wj*$ft3rt/76j6']1s8N)R +s8UgRs8N)Jq#;-*I:LpT=*sCY;:+ci*.R?m,*Lc=8?DUd$/5I[L)@Af^*!FRrrB5!!!-*Lp\t<^ +D1DNa!e5(Srr32iD#jUR!8meU&,-?ms5Of!cf:`2D?'V9a8G,prrR[[fDXk4oD\al\j@0lp&>$m +g\UpR!8IPR!6>'$s8N-#a4L:Q!;$!c#H0h/dbU'XT`+llIfS7%rru=O^?Ir4TJrsPbhFjf*/;*k6^!6>$#s8N*"a8P3$rrE,?r9++[#k<7$QJ',X5.:Ij +!psiSq>UZ!=.\NJ>s/,d!ZWJ6rr3R1=l\[N4TGE]g].<.!<<'!e+iq^_G=gmeo)+LolXp/^dDi# +s*nbIp$2V(qh>+"rVloQ!;c]qY582!WqHAnm;7@Mrs#K)T&l\?Sc8Wj*$ft3rt/76j6']1s8N)R +s8UgRs8N)Jq#;GQ:M8,,D\.EkF_BgO6YYfbIqe0?F_BgOL&f^&df@GefDkjNK(HDPl^COu~> +ZMspUr;QqA6MKXlPgTLA!8db4!<<'!hZ$JWkN?#/q#;oBIq%-[s-ui`hK*;_s05UhhMY:>s1)<"pZEuis8NXpmu/+`s4.1irRUoH!T!hU +s":QWPhq(nHrs#E&T]_t>SGrNi(a48*rt/.4kN?#/s8N)U +s8UpUs8N)Mq#;-,Iq%-[$m +hYR6U!8dbU!65!#s8N-#`n(+O!;$!c#H0e,db^3\T`+llIfS@(rru@M^$!sE6N-obh>mKR!r9++[#kNC#PhO,\4gk:h +!q'uVq>UYt3G'P3WK*ZhZ*W4!<<'!f(f7a_+nUjeo)+LpNL94_*Vr& +s*nhLp[8+-s+CC'rVloT!;c]qXnr(uXnD\qmVdUPrs#E&T]_t>SGrNi(a48*rt/.4kN?#/s8N)U +s8UpUs8N)Mq#;GP:1hr*D\.EmGA$'S6>PldIr"?DGA$'SKE0U'ec<_gf)PaMF7ZL8kEJSh~> +ZMsp]r;Qoo@gE?]d/OUVhZ)F4s8N)Us6j+s#YO:Ss!u>t$kR(&s8TbqDsmZ*butMeB@d*U+^3Uo +mofu&9'?6S!8dbU!8dbU!8co=!q'uVrr3Q,XYgAI\c;]thDkQQSH&VZqZ-Zr"Pu-=(nCU*-+,0% +&:;+js8/`L#W]0es6bdb$sLpUs8/`L#W]0es8/p.#RGMNrs,qT!%Gqfs.&rdr;QfS!<3!=qku4T +3O/J]mY`%n>0[*Ig2@Z27D8a$g2@Z2&:=EV"SZC3#]p"F"SZC3#]o\=!eXh5qu6`cDh%cd"oGDZ +#X,`irrLsVrVm#p@gE?]Y5\RkDh%Za#5=oJ#Uu/4rr3%H!9a@]'(9!0#YO:[rrCpUs5!bUs$?^n +q#;)R=ptsTf)N]a#T.J\J*q5MmTUKGX\DV#]c[!q'uVrVlqKDsmW)"o!3@s(aXm +rs;jhs8UqR#QQ$$rr;fp!VcKerr@QG!!dlJs8VA"3P"h_!U]s +YsAd(3N;cOrrRZMhYmHYpM1TG&7b2-rrLsVr;ZTn%"%l$DslUp9$.*_DsmE#$2^tn$j\k'n,NFa +!!2ioqZ$Zcs7u^%p](9]Qn8FP;Si>3!q'uVq>UZnUbN-(9'?3R!daq1rr3Z#=pPC+kPtP^hZ*W4 +!<<'c!4CPVru/^K$lEbXJ,d2J$r1F]KE%Sf>2'"'#S;q]#lXc(h>l^ +ZMspZr;Qoo@13Qed/OUVg]-".s8N)Rs6j)!#YjLVs!uB!$kd4&s8T_oDt!`+cUJP])Vg!h`M#]RfEDVqZ-Zr"Q)Km-IfP_N47_8QufP_N4'RBTUs7]q1%!DRL"SZ=5%!D:D"9!dVf_b[Mm;7@Prrr;$ +2@Mj^oD\jG!<)p#pL+I=Fft`Gm;7@Mrs&4n1(#M"oD\akGlZn(rt=d'$j]Sjs8N)Rs8UgRs8P@f +ZM=G7cTJq>^Qfq>($jJ,93NH27L?l"N,ep&>$mg\UpP!8IPRqZ$Z_s7u]rH27:9 +#PkSj$jo%mp&4mlIfS7%rs&(3(]Y7Yli$hagAq0Oq>^qLq1!8KiF,C%s*nnQq#:ZrYX/a*3aq@S +s7u]roDe[f!UKd^!"/>ts6Z+)"qXYGrVlfum;7@Krs&4n1(#L*kPkJ_GlZn(rtFj($j]Sjs8N)R +s8UgRs8P@fZIo!Q)Sf&U(i*'"s1Uf0;/%G4s.CQ?f`-@*'TPTjrVloQ!9X:_m;7@Mrs&4n1(#M" +oD\akGlZn(rt=d'$j]Sjs8N)Rs8UgRs8P@fZM=G>^eG4SCK#`_^-W<,Bde]8TH[Um^-W<,N^XBV +Rem*brr@ZB[K>c`9n3~> +ZMspUr;Qoo@gE?]d/OUVhZ)F4s8N)Us6j+s#YO:Ss!u>t$kR(&s8TbqDsmZ*butMeB@d*U+^3Uo +mofu&9'?6S!8dbU!8dbU!8co=!q'uVrr3Q,XYgAI\c;]thDkQQSH&VZqZ-Zr"Pu-=(nCU*-+,0% +&:;+js8/`L#W]0es6bdb$sLpUs8/`L#W]0es8/p.#RGMNrs,qT!%Gqfs.&rdr;QfS!<3!=qku4T +3O/J]mY`%n>0[*Ig2@Z27D8a$g2@Z2&:=EV"SZC3#]p"F"SZC3#]o\=!eXh5qu6`cDh%cd"oGDZ +#X,`irrLsVrVm#p@gE?]Y5\RkDh%Za#5=oJ#Uu/4rr3%H!9a@]'(9!0#YO:[rrCpUs5!bUs$?^n +q#;)R=ptsTf)N]a#T.J\J*q5MmTUKGX\DV#]c[!q'uVrVlqKDsmW)"o!3@s(aXm +rs;jhs8UqR#QQ$$rr;fp!VcKerr@QG!!dlJs8VA"3P"h_!U]s +YsAd(3N;cOrrRZMhYmHYpM1TG&7b2-rrLsVr;ZTn%"%l$DslUp9$.*_DsmE#$2^tn$j\k'n,NFa +!!2ioqZ$Zcs7u^%p](9]Qn8FP;Si>3!q'uVq>UZnUbN-(9'?3R!daq1rr3Z#=pPC+kPtP^hZ*W4 +!<<'c!4CPVru/^K$lEbXJ,d2J$r1F]KE%Sf>2'"'#S;q]#lXc(h>l^ +ZN#L4J_kt7J_kt7J_kt7QJMUpo=Y4oS5+S~> +ZN#L1J_Pb1J_Pb1J_Pb1QJ2Cko +ZN#L,J_#D'J_#D'J_#D'QIZ%Wo;r)WPY-H~> +ZN#L4J_kt7J_kt7J_kt7QJMUpo=Y4oS5+S~> +ZN#L1J_Pb1J_Pb1J_Pb1QJ2Cko +ZN#L,J_#D'J_#D'J_#D'QIZ%Wo;r)WPY-H~> +ZN#L4J_kt7J_kt7J_kt7QJMUpo=Y4oS5+S~> +ZN#L1J_Pb1J_Pb1J_Pb1QJ2Cko +ZN#L,J_#D'J_#D'J_#D'QIZ%Wo;r)WPY-H~> +ZMsp]JV8T-JV8T-JV8T-JV9DD!Uk^5]`RYm:4N~> +ZMspZJV&H)JV&H)JV&H)JV'8@!UG@/[K>c`9n3~> +ZMspUJTHB`JTHB`JTHB`JTI3"!U"OqXTI[M8q6~> +ZMss^K_59E!.k0$s+13$s+13$s7ZHnIpR]>_n?',o`"smmtC?5!s%e[:4N~> +ZMss[K(T'C!.k0$s+13$s+13$s7ZHnIp7K4_mTR!o`"sml[nd/!s%YU9n3~> +ZMssVF7fJ4!.k0$s+13$s+13$s7ZHnIo_-&_lWpio`"smkB$:q!s%MK8q6~> +ZMt!_K`Cc&!h98jJ_kt7J_kt7J_kt7ptc(c:0rLb_n?*-me5K?S,i#2s7Y:PS,i#J:4N~> +ZMt!\K)bQ!!h',hJ_Pb1J_Pb1J_Pb1ptGk]9j<1W_mTU"mIo9;RK2Z*s7Y1MRK2ZB9n3~> +ZMt!WF8tsb!gE]bJ_#D'J_#D'J_#D'psoMS8lgMF_lWsjlLrd3PQ9lfs7Y"HPQ9m28q6~> +ZMt!_K`Cc&!h98jJ_kt7J_kt7J_kt7ptc(c:0rLb_n?*-me5K?S,i#2s7Y:PS,i#J:4N~> +ZMt!\K)bQ!!h',hJ_Pb1J_Pb1J_Pb1ptGk]9j<1W_mTU"mIo9;RK2Z*s7Y1MRK2ZB9n3~> +ZMt!WF8tsb!gE]bJ_#D'J_#D'J_#D'psoMS8lgMF_lWsjlLrd3PQ9lfs7Y"HPQ9m28q6~> +ZMt!_K`Cr+!!)uS!h98jJ_kt7J_kt7J_kt7ptc(c:0rLb_n?*-metuB!;kUUS,i#2s7Y:PS,i#J +:4N~> +ZMt!\K)b`&!!)uP!h',hJ_Pb1J_Pb1J_Pb1ptGk]9j<1W_mTU"mJYc>!;kLRRK2Z*s7Y1MRK2ZB +9n3~> +ZMt!WF8u-g!!)uK!gE]bJ_#D'J_#D'J_#D'psoMS8lgMF_lWsjlM]96!;k=MPQ9lfs7Y"HPQ9m2 +8q6~> +ZMt!_K`Cu,rrE#S!h98jJ_kt7J_kt7J_kt7ptc(c:0rLb_n?*-meu#B!;t[VS,i#2s7Y:PS,i#J +:4N~> +ZMt!\K)bc'rrE#P!h',hJ_Pb1J_Pb1J_Pb1ptGk]9j<1W_mTU"mJYf>!;tRSRK2Z*s7Y1MRK2ZB +9n3~> +ZMt!WF8u0hrrE#K!gE]bJ_#D'J_#D'J_#D'psoMS8lgMF_lWsjlM]<6!;tCNPQ9lfs7Y"HPQ9m2 +8q6~> +ZMt!_K`D#-rW)oR!h98jJ_kt7J_kt7J_kt7ptc(c:0rLb_n?*-meu#A!<(aYS,i#2s5)W/!<:mY +S,i#J:4N~> +ZMt!\K)bf(rW)oO!h',hJ_Pb1J_Pb1J_Pb1ptGk]9j<1W_mTU"mJYf=!<(XVRK2Z*s4c<)!<:dV +RK2ZB9n3~> +ZMt!WF8u3irW)oJ!gE]bJ_#D'J_#D'J_#D'psoMS8lgMF_lWsjlM]<5!<(IQPQ9lfs45ct!<:UQ +PQ9m28q6~> +ZMt!_K`D&.r;cfQ!h98jJ_kt7Mr"Q_L0DrThLXO7hX]M)]hVmb]u.MahX:@!r;ciR"IoJ\K`D&. +quH`Q"IoJ\S5+S~> +ZMt!\K)bi)r;cfN!h',hJ_Pb1Mq\?YKiu]PgO\+1g[a)#\kQC[[_oNSg?njnr;ciO"I]>VK)bi) +quH`N"I]>VRSA;~> +ZMt!WF8u6jr;cfI!gE]bJ_#D'Mq/!OJlg'Feq)D'f(.An[7XJNXi%7Af'<4br;ciJ"I&oLF8u6j +quH`I"I&oLPY-H~> +ZMt!_K`D#-rW)oR!h98jJ_kt7NnsrdL+Wo<[=R1chLXPRh?(Ac?@ff-]`I!XrSIMRrS@[I!:PU@ +rSIMRrS@[I!:QFQJ,~> +ZMt!\K)bf(rW)oO!h',hJ_Pb1NnX`^Ke3`:Z[pk^gO\,LgB+r\>Cj6#[K5+LrS.;OrS%ID!:,7: +rS.;OrS%ID!:-(JJ,~> +ZMt!WF8u3irW)oJ!gE]bJ_#D'Nn+BTJh.?6YCY8Ueq)EBecN6O +ZMt!_K`Cu,rrE#S!h98jJ_kt7Okp>iL+X,E!"Y +ZMt!\K)bc'rrE#P!h',hJ_Pb1OkU,cKe3oB!"Y6DgO\+1g[Nr!\kQC[[_oNSg?njnrrDuO"I]>V +K)bc'!!)rO"I]>VRSA;~> +ZMt!WF8u0hrrE#K!gE]bJ_#D'Ok'cYJh.N>!"Y-Aeq)D'f'q5l[7XJNXi%7Af'<4brrDuJ"I&oL +F8u0h!!)rJ"I&oLPY-H~> +ZMt!_K`Cr+!!)uS!h98jJ_kt7PhlPiL+X/FrVus-COt>nhLXPPh?(Ac?@ff-]`I!XrS@MSqq_IG +!:PU@p>,qB!:QFQJ,~> +ZMt!\K)b`&!!)uP!h',hJ_Pb1PhQ>cKe3rCrVus-Bn>#igO\,JgB+r\>Cj6#[K5+LrS%;PqqD7B +!:,7:p=f_=!:-(JJ,~> +ZMt!WF8u-g!!)uK!gE]bJ_#D'Ph#uYJh.Q?rVus,B7\Wbeq)E@ecN6O +ZMt!_K`Cc&!h98jJ_kt7QJMbrIM;bfr;Zj&6%RmDhLXPOh?(Ac?@ff-]`I!Xp>,qB!:PU@p>,qB +!:QFQJ,~> +ZMt!\K)bQ!!h',hJ_Pb1QJ2PlHkZPdr;Zj&5_7[@gO\,IgB+r\>Cj6#[K5+Lp=f_=!:,7:p=f_= +!:-(JJ,~> +ZMt!WF8tsb!gE]bJ_#D'QIZ2cGn^5ar;Zj&5(V:9eq)E?ecN6O +ZMt!_K`Cc&!h98jJ_kt7PMQPbRlpiR!%$6JhLXO7hX95%]hVmb]u.MahX:*o"IoJ\K`Cc&"IoJ\ +S5+S~> +ZMt!\K)bQ!!h',hJ_Pb1PM6>\R61QO!$p'FgO\+1g[VK)bQ!"I]>V +RSA;~> +ZMt!WF8tsb!gE]bJ_#D'PL]uSQ9,0K!$omAeq)D'f'_)j[7XJNXi%7Af';t["I&oLF8tsb"I&oL +PY-H~> +ZMt!_K`C_8! +ZMt!\K)bM4! +ZMt!WF8tot! +ZMss^KRj,S!4;h,! +ZMss[Jq3oQ!.b-$!.b-$!9X=^>Cj6#[K'P'!!2D;o)JjXRSA;~> +ZMssVF+F=B!.b-$!.b-$!9X=^ +ZMsp]Ja\0YL@5B&9j?TFR/ib.mt'qYmt'qbmfAqRJ,~> +ZMspZJa7mQ\*F:`k2bR]i8 +ZMspUJ`hUI\*""Xio&_Mh;$c_g'?Bfh;-uHj6#Id#LlCKg?4\#Q\"=NkCMfIkDJF'PY-H~> +ZN#L4J_mlmrSR2(rnlkp!h(O,rn[Z_B7\ojhLXO7hML(iS5+S~> +ZN#L1J_R`i$/!hYda6:abK@s'a9fu2bg"G[daZn&l.Z9,.%1(h!FBAhgO\+1gO\+9gB!a;J,~> +ZN#L,J_%B_$e*MLc-+8N`l?!9`;[[S`!OE&a2lEHc-Xnhl.,p#-^Ob_!F0,ceq)D'eq)D/ecD"- +J,~> +ZN#L4J_mrornm_5o&9T+rnm%u"4:%>c2HK!LOn<5hLXO7hMC"hS5+S~> +ZN#L1J_Rfk$J +ZN#L,J_%Ha%F`\LbK7cC_nj1(^:h5Z]FDQg^;.S$_o9[ +ZN#U7R_8^Gp>2ma$GO9j_8=(,_o0OCo]#l/rnmgm^[Bq[Ua_E&h>lC2h>kfh]`?=Oh>kfp]qX?b +c+Ufdh>kfh^%U:_c(_o@h>s-AJ,~> +ZN#U4R^rLAp=l^\%_&gNYcXt*Vl$8bYJ%T]r4E!A%)'Bj_Sa@6b0@/rf%TWJ!hq-(qqD,)rn@Fq +R^rLApY,\jU:R)R!m8*cUtYmlR_&4I!6V%InCmu49n3~> +ZN#U/R^E.7p=?@R%^N=@X/MnlUS4?PX1>aLr3d*E\@K2_]t_D$`lP3_dFIa;!hLfuqpkbtrmh(g +R^E.7pXT>`U:$`H!l_RTUt,ObR^Mk?!6(\?nC@W)8q6~> +ZN#U7!1]QS!KGY'h>j(M:(9us<**4,?=75QAnR^io],r1s5=+=UX7oUo%jK"?U2[*h>t<4S5'+0 +eb@o$]lg,G!6q7p!kktcUtu-cFdu2,c1]up]lg,G!6t#i!Luo8~> +ZN#U4!1KEP!K>M$gAm\H9b9uk8P)EA779L16:#e"[^NUAZk'RQ[^`l[]YD>$a,RgZe(=3G"4LR@ +`:hpd\oXB9!R0$egB"m,UeLj5U>#a\Zco6"!P3?B9`WE2gB"m,UeLj5nCmu49n3~> +ZN#U/!0j!G!K#.rec:r=8e=Qb7n,p86UF((5 +ZN#U7!Ls.iJ,_b-If_iame5KnWdb!Kp4!,Ds6fXnZ@;iSp4!)Cs.\?O^!6SQh?(Ac5H925!;:mC!<;cL!kh>nZ[`!> +p4!)Cs7"kGS5+S~> +ZN#U4!La"gJ,_b-If_iamIo98RK0XC&O&@'GBItHDf0B,C,BV>Yck5-XV%_>Yd(OB[^j#`^kq6r +c-Xtop=fYM.tdKK!kM)jWdb!Kp4!,Ds6]R8!kM)jZ@;iSp4!)Cs.\6L]$12MgB+r\5H925!;:mC +!<;cI!kM)jZ[`!>p4!)Cs7"bDRSA;~> +ZN#U/!L*SaJ,_b-If_ialLrd0PQ8"=&O&@'GBItHDf0B,C,BV;XK/B!W=c/2XKA\2ZF.-O]7f@d +aNMr\p=9;D.Y.0B!jtWbWdb!Kp4!,Ds6B@0!jtWbZ@;iSp4!)Cs.\'G[E8HEecN6O5H925!;:mC +!<;cD!jtWbZ[`!>p4!)Cs7"S?PY-H~> +ZN#U7!Ls.irrIUnp[/*,J+!$e!Ls.qrrIe%rpTdSs7$/RRdp(4rT+"=#ND`_iS`YOhtZ%1fkWR` +oA0P^:4K;D!!)`\!W`8gpYGtb:4KSL!!)`\!,ka:4KVM +!Io=emK!=fnD429:4N~> +ZN#U4!La"grrIUmp[&$*J*lsa!La"prtG6TDqimmd*9\P`50/nA@SYX9n02C!!)`[!W`8fpY,b\9n0JK!!)`[! +ZN#U/!L*SarrIUlpZ_g&J*QaY!L*SjrtG6TDV3Idc-")F_7mQfA@AA2USIg_s.^1%VPgAmXKJh7 +[Xd>P_oBjEeG7Ytd:YAKo@ +ZN#U7!Ls.irrIUfpYGsiJ+!$e!Ls.qrs+=*iSieUj5^.""6GW9o(D/Ejo4iSi_Phtl13 +[kN*_o%jG]:4K;D!!)`L!W`8gpYGtb:4KSL!!)`L!mWVp>,ka +:4KVM!Io%]h>mWVnD429:4N~> +ZN#U4!La"grrIUdpY,adJ*lsa!La"rrtYP/oQOib_S3Xk['?g9W`*<2US=HTrgsmtTq\.K!<;H\gB+r\J$T(S!;>.J!<8eJ!kQVUUtYs^ +9n02C!!)`I! +ZN#U/!L*SarrIUbpXTC]J*QaY!L*SlrtPJ.oQOc\]t1YYYcXt)VG^g+T:VVOS.hHbT:r!TVPpPt +Z$b9:]tq\0bgQC>"0jJ$eFM/l[7YqKrr<&mecGjOlM&j2[7YqSrr<&mec>dNU=KCRaoA0f!jtXM +WrE&!p=96Es7Y"F[7YqTrrIUbp=96Es7"S?PY-H~> +ZN#U7!Ls.irrIUfpYGsiJ+!$e!Ls.qrs+F1io9"YjQ-=%"6PiAo(D2Fk5OHKjKAOCio8qTi8=Li +"5J)U\a\GW]hX'Wrr<&mh?!]Wme>Q>]hX'_rr<&mh>mWVU>>sbc2XTr!kh?YWrE&!p>,fMs7Y:N +]hX'`rrIUfp>,fMs7"kGS5+S~> +ZN#U4!La"grrIUdpY,adJ*lsa!La"ss"F?GoBp^/^q@1bZ*(.,VPI$nX.5ZJR@0G1R@9V8StD^P +VQ$W"IFG^=^r4==dFS#a\bl=Kn +!kM*UWrE&!p=fTJs7Y1K\kR[\rrIUdp=fTJs7"bDRSA;~> +ZN#U/!L*SarrIUbpXTC]J*QaY!L*Sms"F?GoBp^-]=5/PXfA:qU7b7dW0ip2A+bgHF@"4V?FZLHEH[7YqKrr<&mecGjOlM&j2[7YqSrr<&mec>dNU=KCRaoA0f +!jtXMWrE&!p=96Es7Y"F[7YqTrrIUbp=96Es7"S?PY-H~> +ZN#U7!Ls.irrIUfrSIROrS@ToJ+!$e!Ls.qrs+O7ioB([\[qe&"6Z&JoC_>HkPjTTjfndFj5T(W +i8EMLhS2!eg@*l"]hX'Wrr<&sh>hKlh?!]Wme>Q>]hX'_rr<&sh>hKlh>mWVU>>sbc2XTr!kh?Y +WrE&!r8%Elr8%GSs7Y:N]hX'`rrIUfr8%Elr8%GSs7"kGS5+S~> +ZN#U4!La"grrIUdrS.@JrS%BjJ*lsa!La"trtbS.oBka]?G!YPYCM(uU7[uN=JM1HPPgR^PE_B# +R@Be@USXr8c*Ojj^Ve.:d+@$b2Jq(7gB+r\J#`MK!;tROD>`,i!<;H\gB+r\J$T(S!;tROD>`,h +!<8eJ!kQVUUtYs^9n02C!!)rO!,_Yg! +ZN#U/!L*SarrIUbrRV"ArRM$cJ*QaY!L*SnrtbS.oBka]?+.&BX*o>gSt)3A=J:n?OSk.XOHG]j +QC"&1T:r*'c*+F]]>)8(bL4tO2/:S.ecN6OJ#`MK!;tCJC])`b!<;?YecN6OJ$T(S!;tCJC])`a +!<8eE!k$/MUt,UT8q3l@!!)rJ!,MM`! +ZN#U7!Ls.irrIUfrn[aS2`IW5h?%VFme5Ks6'FE%C?)q +jQ#7ZiS`YOK.aFrh?(AcJ#`MK!<(aUDZF_Sh?!]Wme>Q>]hX'_rr<&th?%TqDuJMn!<8eM!klkY +Utu0d:4K;D!!)uS!cJ'WrS@PTs7Y:N]hX'`rrIUfrS@To!,qhm!s-AJ,~> +ZN#U4!La"grrIUdrn@ON2E%B1gB)5AmIo98RK0kt)#j=%mHESJ>dgo>IjldiS=,^8i2i!<8eJ!kQVUUtYs^9n02C!!)uP!c7pSrS%>Qs7Y1K\kR[\rrIUdrS%Bj!,_\h! +ZN#U/!L*SarrIUbrmh1E2)V'+ecKW:lLrd0PQ85n)#j=%mHESJ>-Y3/Hmg@aR$Nq,cecN6OJ#`MK!<(IMCB//KecGjOlM&j2[7YqSrr<&t +ecKUeC]2fb!<8eE!k$/MUt,UT8q3l@!!)uK!c%dOrRLuLs7Y"F[7YqTrrIUbrRM$c!,MPa! +ZMss^XSQi:^!1KYh>jGNDZPR*ZJGVP!36&/IrF3#VDeJT2l>HgDh%3Kh>s,HZMtDtLZIO-BLGC3 +_&j5].Xa?X7ep"fc#Su2DA!kh?YWrE&!rn[WnrVuq:rn[\Vs6fX< +!kh?YZMsn)rn[WnrVuq:rn[YUs.\?O^!6TUh?D>lGAhi,e_U>GNV[Pp^!1KXh?(AcJ#`MK!<1gT +DuK_:DuSSo!<;cL!kh?YZi:&UDuSSnDuK_:DuSSo!<;QF!Luo8~> +ZMss[WqpQ6]Zb6UgAn&ID#o=%ZJ,DK!36&/Ir3rpUc&2Q2Pf*^D1CsHgB!`C\GmB@oBti%h.p2u +XF;Pt!$uWKO[`36M2-i`KboK!M2R=ROcu-#ST2HDXK]+C^;@-9?./E#!n3^9qqD1`9n02C!!*#Q +!,_]8!,__i!W`8fpY,b\9n0JK!!*#Q!,_]8!,__i! +ZMssVW;:92\]JXMec;B@CB9!qZIT&@!36&/Ir!WdTJZ]L25&OOCObXCecD!8\GmB@oBti%h.p,o +W-Tfk!$uNENC?^/L4k3WJes&mL51VENKB?jR;Ka8W3!83]"Y:+>Kuil!md=.qpkhV8q3l@!!*#L +!,MQ6!,MSb!W`8cpXTDR8q4/H!!*#L!,MQ6!,MSb!dNp=9;Q8q42I!Innaec9Lb!!$g6ec>dNnC@W)8q6~> +ZMt*bW+TaD!<-U4]u;klh?%UG?N+RQA)!es8UrhrI4gXrr3ABLP)Q&s8VhlK87#9rsZ`!K;d2rs8CN;KqR6! +rrdgihVP..D[!#phJ[o3p>,h?!<3!$ifnqWhZ!NZlC`WZW;$2frrV\p^]+6Bi1u'mdJa+;`K5Y: +chmY?!TJV.rri5(K87#:rri(pK87#7rr@iNrsk+Lo_%PLlMp,KkR>!$k2k^c +j5Z7QWSl\,h?(r.Du8Am]hX(Ts8U"9rrrAPRY-C+rr3,qW.Tl9rr3DlTRVTmp](9fW.Tl9rr3#U +T)O3NSD=/Ei1u'mdJa+Bc^'9Xh#@<^i1u'ZW;HVpqltd/\blC,!<1jQDuSSp!<;K]h@mRtJ,fQ< +]8;BTmf3=Z`K5Y:W;$5i"QFq2s3:Fj!:Kgc!TJV-rrp67KpL-brVuo8rVm&ZUkP,^rVccr_tsB5 +!<1jQDuSSo!<8eM!klkYrn[n.CJnVO;F.,7h?'^Qf_sY1_hVl2mWVp>-Ir:4NmWVnD429:4N~> +ZMt*_VIO+9!<-I0]#6AegB)4A>lJ46I(7SY!iVgAo@j>FFj0R6!La#drrN(TrVn&^KfWe6):rVlg*^km]DdJX%CeX;,a +h#.0TIr3rpV#>-6UtaX@J*lsa!La#frrhAGJWnsirs%e^JV0uqqYgEqn?B\,rsdk^L6g6bs7O?m +JsOaZr;QfVJ,]HNqlk^.])M^5or9hS&oNJg.uO+Mo]IfOrs +Ii!ZfKSG;@NffQnD7ZL?X0B"BH7#!'db=EL!mG"gqqD1`9n3-A!rg*Urr30#eX;&F])M^IorZCe'@fj6[1?R/K<"VEs7NCELUd<_ +s1J;[!4r10*8dTIQI5X1qTkI]TA';Es50Z-QI5X1rRI/urVmDDM1_i(s8VDZK8$>ss8Tb1IfR(YrrW2;`;]cHou3#EQI#F- +s50Z-QI5X/rrIUdrnI?frn@GRs7"bDRSA;~> +ZMt*ZUgmn4!<-I0\\U#`ecKV9>5i"4HaV8P!i2F6o@QA)!es8UrhrI4gXrr3ABLP)Q&s8VhlK87#9rsZ`!K;d2rs8CN;KqR6! +rrdgef%-MmCB^B^en]d#p=98/!<3!$ifnqWhZ!NZlC`WZW;$2frrV\p^]+6Bi1u'mdJa+;`K5Y: +chmY?!TJV.rri5(K87#:rri(pK87#7s#5`1o^D&)h:QCAVk\H191h`iMMFMmNIQA)I!g9gI!pHn +JV/Z3MiEj`BtBq3Vl[/2G9rHoc.2O=!m+baqpkhV8q6d=s24j9"o[#@K87#:rri(pK87#:rs\1c +ItO]os8VhlK87#:rrLuJrdPKLh#IE4UkP,^rVuc@QA)!err3DbUkOH"qu?]nZ%IhBqu6Wrrmq!_ +rmh,Ns6B@0'=CG^s8VT#M2Ae2s8V`1OF`^rp\b$oid_".c22tjmf!.did^q,"i7!gQI5[0s8U"8 +rrq>VKpL-brVuo8qYpNqrmq!_rmh)Ms.\'G[E8IJed'\!4i+/%,dq8 +QI5[2qp1R^T\TPH%,dq8QI5[2rRREkLUm<^!!*#Lr+l;_!G`J,fQ<]8;BTmf3=Z`K5Y: +chm\@!q>3!rVmDELP)Q&s8VAWK7g,os8Te2IfR%Ws8U"9rsn\?OF`_Dqu?]SUkP,^rVc`sIr#&7 +r+l;_! +ZMt*bg=Fi_!<1ONhU\Lch?%V!`W"Ehf\Fm2!cqnuoA0Oi#f65k!Ls/hrr`#HU^6r!,-aa]Dd8.[ +s3#OqF^g9gs1;`JF's.8s3#OqF^g9gs0?5srbE[^s8U&AF*C6]s8SHaGB6[$s8U&AF*C6)f`0TP +??'5,jSf)ZIrG),h?!<3!$DcV'cB)_f7G><4#:7TT.rtP:62h1l-ifIbp@"$]-]4,/h +@!0`nrr`)gDZKe:'MlMp)W +]Dq'VjlOo_=kDWjht>h-c!G>ch?gkjJ,fQGk.OfJrr5L@I9pu59$.,5Qr8-L;T8\9G><4#:7V@a +Qr8-L;T8\9Z;=G7CQ@'IifIbp@"$]-bB7?<@Z07GifIbpG=o.?s051;DbA.Lrr<&mh?!]Wme>QO +]hX(Ws0lHMBje7Hs1;`JF'qb5rVm&mDZKh;ao,uV4X'7mrrVe1!<3!,`EHkr@"$]-qrYRmKDtlZ +ifIbp@"$]-qrYRmKDPTJ!;>7M!<8eM!klkYrn[m=0q?A6[8&a`%PAbB6^,F&L!Ws*%4ZF&&8*s-Q].F&L!Ws(4:jCE]t_s5K,P +F^TXMs3#OqF^g9gs5K,PF^TXMs4Da+EGnIkr;Q`sp>,fMs7Y:_]hX(Ws0lHMBje7Hs1;`JF's.8 +rr3)rZ#9XArt34RF*C6]s8RFLGB6fLs8Q[krbDbDs8ViU]iKdbrslYM?uo^uc2ZdbAp%L0f_tgN +IrG&;!s-AJ,~> +ZMt*_f?r!Q!<1@Ig!cVWgB)4q_Z%p`e(E(&!c__oo@j=d#ep#e!La#frr`#HU'gl!,-jg\DdA7] +s3#RtF^ptrbE[]s8U)AF`g?]s8SHdGB6[!s8U)AF`g?*fDjKP +?>s2-jSf)ZIr4r7!cAm +@L!!)`I!s2-jT!DHAm +@ +ZMt*Zda$.C!<17Ff$9fKecKVh^])LZd+$Io!cMPio@<4#:7TT.rtP:62h1l-ifIbp@"$]-]4,/h +@!0`nrr`)gDZKe:/?qfgDbA.Os-Q].F&L!Ws7b*?#PRZPjPJP;AlGFqR[0+sMhct;5>$ZUG]s"- +(j:.LH[UBqKnkSHA[@hsU8OSD8?oqJajBb4!m+baqpl(]8q6m@qrYRmKDtm@fR7(IDbA.Os-Q]. +F&L!Ws*%4ZF&&8*s-Q].F&L!Ws0>I!CMSs_s5K,PF^TXMs3#OqF^g9gs5K,PFa$H_s8T>rF)O[m +r;Q`sp=99Fs6B@0'=CG^s8TQ;B5(bZs8T]??uo^NSGiHko5=X/s2k4T!^.^prr3&kDZKe:%)lC^ +F^TXMs81rlirA#"Ap%L0f`0TP??'mUao)/=!;=tE!<;cD'=CG^s8TQ; +B5(bZs8T]??uo^uc2R_Fq6=i'"m[Z2aiN>'G$gPlLbq3WdNnC@W)8q6~> +ZN'm[!PJL3h@.YIhVN1PHZ,R^GAj/Rh?McILNA*UgAKe/DdL]ch?p-ERZk_4R!WaqXT%&^Dle;( +TWr]ZT`3dOg735*Ph,Ze*m:'tFdC(L4Yr5Fad@3lOi?26VNdO5P6"W^Lf+KSs%SrEk3(pk +l07Kulg([^p$M.na3;*'b2_mVfXIugd-Bo\^&R9Yk+!W+MPJnai-)Y`DgT%OhSYIuGE#@9hUS3p +hVPq@J,fQ<[;@@Brr5K[>-dSY>%7O++e\GlHWpI7%&EL_N$\Mu+e\GlHWpILSn>\$lL=]NWbJg- +_j8LCN*scqZYQZBWbJg-o9>XBs#kSok#u6lrr<&mhB2gumbZgDS9K+Aa55[d:4N<>Iq79iApsLI +HW\J\]o499rrnWQ!<<)rrosR=:72"[!gGtNrr3C-HaE.XQHT,fMs8LjaDn1L=Hb&"ibFEd!QH->g!klkYrn[Z-KDjX0[:e@@DuST:c#;IqDn14rhJ]rO +PJu=hLNA*Ug>8]kSp>IIbH*i_Oj3d[!mSCYrS@t-Dr0>hXF"lYZ$1,Zrn\FES9&b9f`Su1B$ +]hX(Ws7!F5.=_?s,+_uDlC_Ifqi"pUoBL[+s6:#goBqP`s7[\0oBL[+s8P6Drot[$s8T*5ZgP_s +bPR6+amQKNXT,;7ZgP_sbQ"/;ZKgRU@f66+!;>7M!<<&T)nCH:Ha1WJhRu[6s7q.Tg>6Fiq=K#9 +bOE2IP5bI`a_O0Brr3R2Hg:3eR/cRc[I`gR+ohR*OT+M_p](9Q[;@@Bs8V_<=Ng3I=H`]aBs[6F +QHT7,!Io%]h>mWVrn\0(\V"gI\_c2RIrB\=hOI>k~> +ZN'mX!PSR4gC2;CgY6SHH#K@YF`3lNgBQ??KlVaMfDOA)D-tNagBsa>R#o/(Q[W)A`g:dfO29T*Ulq+,O?!<3!#iJCD3 +rVoAOH0sdg@']Zg@EIuECT[2S=j-?K=d&Z@@EIuECT[W/5`b0qlh1;X=*a-%Pa_XH,,"PnHX$OM +=*a-%PaBVoSm/PdTjY2)rrIUdpY,adJ*m6i"LNSoH*I;&17A4Cs76`5pV[F-s5slfoC%VbP5kR] +aD2rKs8T-5ZL5Srbk$-`bON8KP5kR]aHT!krr6XqH0sd!AcMKKT_%Jr>Q=`^M,43ClfI$We'2B# +U7I[9OGo$MJq#pQJTZ$79Mmu9:MFlm@RWFLBSD;%B!%GlU2^r7?$s3%b%J,7DgAeGgVJtmG)f=7 +gXD^hgY9;6J,fQ<[;@=Arr5K[>I3_[>@mj/,,"PnHX$O5%]&^bN$eT!,,"PnHX$OMT4kn&lgXcN +X(\g,_O&LDN+'isZYQ]CX(\g,nW]LBs#t_qk?DNqrr<&mgE6LrmG$C9R<T[]fh@e4rr`4a63.#b$s._[m_Y=7s7!F5."D6r%'+'Im_Y=7 +s7!F5."D*n!!)`I!W)AqqD1r^&\,d%*WSCgMO$;DkO7UDk?s_(>"ppEK\f* +gXD^hgY9;6J,fQ<[;@=Arr5K[>I3Ug@']Zg@A*5XHX$O5%]&^bN$eT!,,"PnHX$OM5`b0qlh1;X +X(\g,_O&LDN+'isZYQ]CX(\g,_O&LHSm/Pdk?DNqrr<&mgAqanW]LBm1?_tn!5*rs$2Y0lg+6Ds5CA&."D9so6OQ_n\&(g +s/Th=m_Y=7rVlnJD>)]b!<<&Q%rJh/DgAeGgU)BGH*H%l9n3~> +ZN'mS!PJL3edTZ9f%+W8GAE_IF).^LESGq(CdZeZgOO![Q*lF4^Dihf44#;l8_3/eVMnR]mTTGJ!NB7,P[FWH8qUP\3!<3!#ieUJ4 +rVn')GOFUd?Ej6"W^Lf"0ClfI$We'2B" +Ssbk+N/<:@Isa@JIr]O.92@Z2:1nQe?pZtBB7bkp@]boaSo,<.>^Nio`auH*CNm2;f"I&_F,Ne/ +f$BeXf%.6$J,fQ<[;@@Brr5K[>-dSY>%7O++e\GlHWpI7%&EL_N$\Mu+e\GlHWpILSn>\$lL=]N +WbJg-_j8LCN*scqZYQZBWbJg-o9>XBs#kSok#u6lrr<&mefXtmlIOP(P]:W'_:I2F8q6m:Iq79i +ApsLIHW\J\]o499rrnWQ!<<)rrosR=:72"[!gGtNrr3C-HaE.XQHT6Fi +q=K#9bOE2IP5bI`a_O0Brr3R2Hg:3eR/cRc[I`gR+ohR*OT+M_p](9Q[;@@Bs8V_<=Ng3I=H`]a +Bs[6FQHT7,!InnYec>dNrmhTq[=;t9[FWp6GAD9%es&dW~> +ZN'm[!PJL3hA3d)hVJ6EG>9l*F&"I@hVPea>\@#FYl*>ZDd:E`g*5>/hJY,TJ%^k.4_n4af` +Ap7!)B6-g&fJ;J0W.KDOFHg5o3A@SVr`4Yr5BN^6WR9SqK[D_YRJO$*JT^#$jtqVD7C!<3!# +lEQiZrr5O\#ho=YfOg-9Fa*T[ibaPCWjDU&g16'5Fa*T[ibaPPM\pA^J#N,ApCO(QoDdrkp](9$ +'T)kihLg#MJ,fCu>aU5,]Bo*gZhsdVA;U?dhLeFr!<<)j#ho=YYoNf0!:Tsf`YAIQlEQlfqYti3 +LX4>qe(*%$f\+uV]Ct[\\!sn=Xea/,lCk,YUQas.lG*A_g/gFWLP'!!iSY;aGAfcBhPbQk?tE@a +Dr/rsOP\j-:4NMp1R"^0_gAgaMgAh2X*;og:!;=)K!<;KDg5o3A@SVs=hRu[6s7s4= +rTJ0Lq=:Plqu>BX'`S+7]h5(#qu6_UBD;E*,J?^F!<<)n&?2%NOLslZoZa7.J,fQGK3XdtOLslZ +oZa7.J,90F!;=),!<<&T%KL*bDa@.Tg5o3A@SVs=[,)&$c2ZYWdKIPeh?23_]`A&g+h[m4h>h"g +Utu)\>(=B!F'N?W@^+^-1N.>:8!^5>1QfrZh?(s8!<(bo]`?pG!+pFM0kYQ&1"P4aU5,]Bo35GPD-sD2J.B>aU5,]Bo?AEU<`*YoN]A!!)`+=TJKYhUY;&CKa&bgto_>J,f>T^&@/5 +J,/Z8ZhsdVA;U?dhLg$J!<<)n&?1G;SD+#2&A@u8S/hS/(iOnRW;$>llHQ2$J,fQ>WjDU&g16'; +K3XdtOLslXrrIUUp:UJ,s8Ljc!+pFn5u-:f8m6"66J@r)J,~> +ZN'mX!PSR4gD7L'gY2^@F\so,EDJ:9gY9/U>@g]BYPd,VD-Y-\f-8o&gMJTLICbD)5\F:^dG*CU +B6@',B5p^$dG*E=:M40(KDO=EfT&g<@8;cZ4>W)>NC$TQ8qkjND(f1CN'%)Q]%b4lqV)%>!<3!# +l*?`Xrr5O\#h]1Wf4^6P +s2>iTpCO3YK:]aCq0d5Js4:qFrVlnJ?2(pT?%;7rgXSeuBj!gRgY8.ss8V\?qu+kMn,MKdp&G'! +(lSFogjsWHJ,fCu>F:/,]^>9hZ24LRArH]hgjr%m!<<)j#h]1WYTWu3!:Bgd`>JXTl*?TTmcWQ` +eBZ-D7"p2?H$47ICh[K:6[EWe2&J20.O.N$CjmW`3&)b-KSO$8P`IpQ2c3C1E5BQp@!;khLOM'u\oZa7-J,fQGK3O^t +OM'u\oZa7-J,90F!;G0^N?X%rl[W#G^;IcobD)-!L=@e^W4>W)AqqD1r^&\,dEP;G-gAkl/;)2d(:GC2NgQG?F +@mYh&gX(V7gY9;6J,fQ@c)+3bs8VnCE9mPUBTMh/G4#>0`>JX:D>!r/Zl&i/!:Bgd`>JXT)KC1V +WqZPjK3O^tOM'uRFa!KYibj\OK3O^tOM'uVI;nNPs0*XDrr<&m\lf%5gY:7K>A75-N7cmr9n33= +\\7q'DLM9MWNl="fk-0Bs4i0KU]CGppCO3YK:^itm1?r+pR30'ru,m*JZAJEs67e*9n33Cnu8nj +s4M"Bqh&+?L60(8rVlnJ?1ti1!<<&Q%fg0bD*h%QeONhX=[U`.9n3~> +ZN'mS!PJL3eeYq!f%'h1F&"H!Db;b0f%.0G=CP09Wr1ELCKeXTdN[2lenZpDHaSYo4_IeUcIUPH +@WP:!@W+jncIUR.:1@ZuJ,7_M)t]L[FW;`qUP\3!<3!# +lEQiZrr5O\#ho=YfOg-9Fa*T[ibaPCWjDU&g16'5Fa*T[ibaPPM\pA^J#N,ApCO5,F'>(>hgf$[#gB3%@If%-&cs8V\?qu>(QoDdrkp](9$ +'T)kihLg#MJ,fCu>aU5,]Bo*gZhsdVA;U?dhLeFr!<<)j#ho=YYoNf0!:Tsf`YAIQI-Ru(!:K%/ +gXONi@7#NoH?O=JCh[Kl0LL,%CGZka2(9_?C25(H2_QJ'JV7@.Oc2=H2GHt*D7m^c??9mJ:=c4^ +9jV4,,\tuUK8eC4[7YrKs7FR5:4NDs6B[Rs8U(Qs5n7os8TJ@s6B[Rs8U(Qs8OF( +6[+$EchmTl>aU5,]Bo35GPD-sD2J.B>aU4tSD+#7#ho=YYoN]A!!)`#+9;H,f$oXR?=$<_eCM$" +J,f>T^&@/5J,/Z8ZhsdV[1rW?"2.=&!;lctMeDe`s!,3LU]CGppCO+F.D?hK3XdtOLslPFa*T[ibaPLK3XdtOLslTI<"WRs03RArr<&m[93M0f%/8=5#?)!<<&L%fg'[CI(_HdRI8M<^P6!8q6~> +ZN'm[!PJL3hA0Znc.\V7]>IZYdGAdtOic;+JKjMsj[6K0>o_/19Me]s$KjMsj[6K0rUfg\g5B^Zs6PtqUtu+Os5F%Us8UXQs77)'oBLf*rVloU&,Q;qT9.ja +hV[8MioB([YeTo#n%>FEmGG"YlfWl?l.iH,lg)Ffkt%.OWSH6o5MP-3G1,63"PuR?LWR_t.rX;" +`Q-il]hX(Us%Vp"J,fQQo,+:pl@/g6qsOLY]iod_o,+:pl@/g6pPD7[G?T(6noHa2 +F^B:>q1W.um^59/noHa2F`U0Ns5F%Us8UXQr;Q`sp<=*Js6eapB1TANPbQsi]hX(UrtE.]>-S&* +o_/19MctK-s4U5Dh>mKR!n'*[rr3J^:8H_Hs8V3]Df^&-GL?Ei%.8%`s8VY22fj+JGL?Ei!q'uV +qYpNqp<F*1f5dI;.(rVuoK"T89"!;=YJ!<:p4c2`sW]ka@thRu[6rVnF`Bj.b@rUfg\g5B^Z +s6Ptq_;51ls5j<"GB6BPs8R"(HZq?*s7[LKIr@cKrVmT"Dh%ferUfg\g5B^ZnoHa2F^B:>rVlnJ +B(jIM!<<&T%fgOrhNS0"c2`sW]kc>l:4N~> +ZN'mX!PSR4gD@R(gY2^Zf=V*5f=Ct!gY%'ZXNdP8H.`)M.;^VEBk_F]gY6R#ON5h;I%8`e!5dLF +!.ib7!.WV3!5dLB.#Q?pI3-r9gE4sZ8[69uT@e!fbh&50\A)$PcJ*1kON5si!5@4BeZeo)!La#d +rrLjSrr5OL!;ZZpl"ktIKjW'm[6T9@o_/18MJBj#KjW'm[6T9=M&pM^ECci(k5]FGF%`>/o,+@r +l[K!9k5]FGF%^`)nSrsSs64?OrVlnJB(sL[Aq04&gWoq_e%08DgY8.srVo%ZI8aSQs823`pUjXU +^&RG`GeS$Es7::jFEV#8dJ`_2qq6jA^&RG`GbWpms8V*Zq>^KD#6493VXs,'!ri3rrR:i4j5&;5 +b/@UXNerI@I!(^p83Hoq(?D#\Js5sB#FEV#8dJj.Sm;7@Qs7::jFEV#8 +dJj.Im;7@Lrr<&mbQ._Drn@n_CX:[t#2@[A8[69uT@gO*#J/.ZgY9UGe,%f"^&\/e+MIj1gAkft +e(Xk6]Z8QP=fflD9mPB.24`7*9Q8a'?d/B$!mJ@YrS&F.!8H6.J_.O7J(M73_V +ZN'mS!PJL3eec""f%'hJd^]@)d^9(kf$o+MW6(c,Fjg6>.;LG?B4l"Sf%+UkNPj&+H'm$U!5[7; +!.3/)!.3/%!5[77.>Z*fHQ:?.efW7M8?]pmSCD=\ajQE![C]=EbLUG`NPj1[!56t7d]NAs!L*T^ +rrLsVrr5OK!;lfrl>;+JKjMsj[6K0>o_/19Me]s$KjMsj[6K0rUfg\g5B^Zs6PtqUtu+Os5F%Us8UXQs77)'oBLf*rr5gnf)Y*rh:Brq +`*PunLP()'GB7\=BeqtoAn3V47:&h711=Pj882KlFaJUBU5U[3*G(\3A/DPd!+F*2OrV#TESqu?]I"T89"!;=MF!<;?8ct,bO_iUDF#S/_??afr +rrVV,J,90F!;=M8!<<&L%KL+_erU$cct,bO_iUD<[+5JiaoBrC]`?@SecEGarmi4*!7o^$@_'e8 +!+NLVd!]#paeDFNdVCY+ca-5\en\U6qUP_i]`A#^+2%[*ec:'F\H%!)[/e%odOC!oaa5#Qf$qIN +f%.6$J,TCAmVdUTs6t#VrVu/"Jbb"+V"o"IJs3GecbpN!!)`7%flX\f#duScFISXeCM$"J,TBu^M-lUs8DT` +qq?sC^&RG`H,8>;s8V3]Dg-7pV#UI56@3o6MuWMkCO^7kT`+m)mVdUTs8DT`qq?sC^%-5'F*1f5 +dJa(GIq88p! +ZN'm[!PJL3hA]"n^bMI/Dc/-@&JUp$lc/*A>`STHi!58*_!Ls/frrLsV +rr5OO!;lfrl>;+P_eNS%C,:MBs5^&(HaWGE_eNS%C,:M>a-bmYEUts+a?jG?tRT +iW,Y>mYG,8noK6Xs6=HPrVlnJGPCM4GCT&;hUYS;F,.=*h>s,Hr;QkK;Hitss#%oNF+`WGq5S5< +k\YbHn8V:Fm^l_ks8V/WH[E0kq5S5J,fQ>F++#es4.>Qs+a?jG?tRSs#%oNF+`WTs+a?jG?tRTpW']*p:"(gn8V:Fm^l_ks2/SZ +F_W^bn8V:Fmd>lcs5j=Ys8UXQr;Q`sp?iFks6eatP[&'RTZc3K]hX(UrtN4^g] +!<<&T%03ZihRrpGg6>iZIA"cih?Ua=s5)V\"Mt&n"PSLI]`A&g"haomh>jYVhA60nhVQgr1M^oC +0k/OK=NB:C?>n\=?Hr?'!mSCYrSA='!8cQ4\_c0C\_c0C^#%$7B5_]u!6+il!kh?YrVmT"Dh%fe +noK6Xs6=HPs2/SHF*Cprrr4jtK6)\-s8RjLF*Cprs7_o/s6b@gs6t#)a6pQER/b'=@!-3?KCEkW +a6pQER.>gk!<:p4el$.%LUkUbhRu[6rVnF`Bi_84s8V/WH[E0kq5S5lcs8QgsHZq3&s7_o/s6b@grVlreDh%`c%HOC5HaWG6EDX^D]3La%rrIUnp?hq]s8Ljc +!3tD;]`?p?;J1'+\_`iOJ,~> +ZN'mX!PSR4gD@R(gY2_\e\XaP1Q=bhBgpJ:BaibhHu8_qWp_!5A0]!La#drrLjS +rr5ON!;ZZpl>;.Q_e`h,CGLPBs5^))HF*2B_e`h,CGLP=a-kBSmt!+ki;fV@mtNOrs+aHoH!L^U +i;fV@mtP&5noB-Us6=HQrVlnJG5(A2G(8o6gXT56EJ:k#gB!`CrVm$!IoBSKrr4juK6)V*o(fA' +ZfPtMqsSK$aR?]EQiI*HVgE`ro(fA'Zh)cKq>KCTq>^KF#64^Y;0@.i#laioqTo6)hq?H%`P5YU +TUV7/N/*%7H?M;1Cgpd*,u\&L-neq8-8.8D??LOA>Fg\5M#^+F<(m:h^]:(Ug:RF?$s::!gV6X' +qqD1`9n3-A'^]aes8VY1I/O$EeHZ'RLJ`2>>mUH029LPmJ`Zq3LJ`2>>mUK'a0t1)\6,'dE)F^F +\m(O$_e`h,CGLP3E)F^FmtNOrjT,,Vs4%>Orr<&mm103smG$9X>\e\XaP5I\9n30B)udQc=0)<" +s5^))H@b2Xo4<"7f)YXFs8W%+H2dgSo4<"7f)YXFi;fUnn%)_irr3DoD1DTcn8M7Gn%)_irr3&e +D1DE^!!)`[!D-+[1b?f&?D-tN`gB,U5!<(Xf^&ZpB!4^e@9;0@.i#ljRcdf&Wc0)tE[;984;>F5B- +Ch%[)15#(8;984;>F4o$I/O$EeHYsO!!)`[%flXagXT56EJ:jBgY9;6J,]I"rk<)JX8W(ujJR?N +Zh!pnCpW6T!;ZTP!2ArNL+rZ+CHd#d15l2*RF;'&JM-p$!psiSrVm>fVgE`ro'gTqaR?]EQi6pb +Is1PQ! +ZN'mS!PJL3eec""f%'i/f%'i/f%'i7f$.YYB5;$2?HN/s.;LSICMRjaf%+V9^XguLOF)J$!56t7 +!4CD/!4CD/!56t&!+Te[?3%\9ef3%t>&/DP_q&U0ak".eI="%^ajt6,^XgtO!58*W!L*T^rrLsV +rr5OO!;lfrl>;+P_eNS%C,:MBs5^&(HaWGE_eNS%C,:M>a-bmYEUts+a?jG?tRT +iW,Y>mYG,8noK6Xs6=HPrVlnJFnb/.Far]-f$7$'DhG=oecD!8r;QkK;Hitss#%oNF+`WGq5S5< +k\YbHn8V:Fm^l_ks8V/WH[E0kq5S5[HR=naYQ/C1)1!1P>1::ri"lLb\ql!;lfrecbpN!!)`X%flXpf$]R^CO)Adf%.6$J,TB]^M-`KrVuoY +VL*ZBk5Y$^F8+Agqu-Nq@se4T&+a7OpY#WEs5F$Oa6pQER/[*omVdUTs6t#)a6pQER/[*emVdUO +rr<&ml2^nbrmhMYW7KP1!7oSY>&/DP_m@o![E8I"f#\'.rRM,"J%`Mqrmh7`!7o^$W;b?a!3"K" +c!iD?CLf))a_Okjf#M$2en\U6qUP_i]`A#^(;0_!ec<,/ec<,/ec +'^fmhs8VY2IJs32D1V`)C//ho?3pQ129CJlKBE46LJDo7?3pT*R*u$&IkC]iEDX^D]3La'_eNS% +C,:M3EDX^D]3L`oF++#es4.>Nrr<&ml43mpf%/,AB5`F-_q*DH8q6g>/AGn"Wr;ttjJI9N[IX-n +CUNB[!;lfU!2/cJLb\r0C-?of0oQ)+R*u$&IkCX!!q'uVrVm>fVL*Zto'gWqa6pQER/R$cIs(JM +! +ZN'm[!PJL3hAk7G +h>k7Gh>k7Gc2_WuDeiP$qq`:I_46-q>*@TX?Hq2c>'Gls?Hq>F?Hq?`DZIKEh>s,Hr;QfS!<3!B +pB]XUs4V.Gs6dR\KoK?%s4E6NRb%B9s6dR\KoK?%o%M@fs!#Gks76BZs8U4Ms8UrGIuBC;s76BZ +s8U5WL&6@@mf2!SNW&kXIrG)<;/^sPhVQtcOEY;3fR!<<)nV#UJC"RlBjZ"AKnp](9WD)H6Q +2*IVfH[#/_jD0JLfSag>f),L2D)H6YCB/_#pB]XUs0WjHs5'8qMi]CdrrM!mqYr^SSE]>(lg4!* +mdBPV`qT#n`4kR-lb:=,n(Gt$[\rEXm)K(jl/#7%Q'$rLe`%Rchql$Hh@*T*hVQ.LDu8Am]hX(U +ru:BEJ,fQGIrk&Xs0WjHs5'8qMi]Cfs060c[J0\#0&E+JMi]Cfk1\tts5IaHq0R>Ys5[sLs6dR\ +KoK?%q0R>Ys8U4Ms7ZfUs8TJHr;Q`sp>.D%s6eb!_46-q>*@Ur]hX(Wqp!Ogk\GJmWVrn\+b^#%VZ!8cMIS:b]_P1m;"!klkYrn[Z)Hi;e(YnXmb!<1gYYlNY;!58Bg(BD/*hUZYs5[sLs6dR\KoK?% +q0R>Ys5[sLqgW\Ts8TJHr;Q`sp>.k2s5)VdWK;=9:6O>f]hX(Wqp!Ogk\GJYs5[sLrVlnJDt`#g +!<<&T%fj<"hRrpGg8U0XAkppI:4N~> +ZN'mX!PSR4gD@R(gY2_BgY2_BgY2_BgXB^iC2I`pOja3_!c;;gqqE61?H_*UO'?IPOoW5NgAntB +gAntBgAntBb5c0lD/!.qqqE(C^7'Ui=ch6P?d./`>Bboq?d.8A?H_*XD#hmJkmUO8](ZIr4r7;f.$OgY:>WNceo-e(`WG!<<)lV#C>?#4;HjZ>"`qp&G'UDDc9N +1d%DaH[,5`jD'DKfSss?eb]=0DDc9VB`NFsoa9IRs0a'Ms50E!NK>Xis%*+_c2ckYe]u7P]3.af +MhZe1G]Re=B/2GY>?WR(:f^C>/6Q.H5;b)@CNF`&Q\LEQ!(fsa;Fmb!!3FJr^&Zmr=hreH^J\qG +gB+r\J,TBdm;7@Qs8%4cmJm3p+TMK!Sq2l_.f]OSBmmG_rr4V.Sq2l_.f\\ck5YJAC52M1GkM*t +C5)]V^5n&0?'P,2GkM+:aUJ4B$JYU\[NPMJ!!)`I/cbq=gY'<0JSePOfA!l2J,fAhd/N3DH1q9j +NH:N.J!<<&Q%KO/rgV*[BftNo+D-;tP[+bhsbl?GPfd_FKgB=_r\@qtBgBF_[gY2_@rnA4h +]\D2iAM\(I!-uNQ#4);GkM*tC5)Wd +!Int[gAq +ZN'mS!PJL3eec""f%'i7f%'i7f%'i7f$.Y[BkqEhNm7OR!c),aqplm*>fYFFN`p4JMu^QBec'5Ng?HLc6>fYFHCB2'AecD!8r;QfS!<3!B +pB]XUs4V.Gs6dR\KoK?%s4E6NRb%B9s6dR\KoK?%o%M@fs!#Gks76BZs8U4Ms8UrGIuBC;s76BZ +s8U5WL&6@@mf2!SNW&kXIr"f0;epmJf%/9BN,rH#cIUR2!<<)nV#UJC"RlBjZ"AKnp](9WD)H6Q +2*IVfH[#/_jD0JLfSag>f),L2D)H6YCB/_#pB]XUs0WjHs5'8qMi]Cfs%*+_ciE([e]u7P]3.^a +LP(&$F`D83AMH/T=]d1":/k"8.p#hA4u=l;BlS8pQ\1*G!(fp_:dq1i!3+)e]`?Ula5&2GP2"9ape=E#i>U][2f,E!!)`D/cbq:f$q6rIqi#CdakfuJ,fDjdJrEGH2%?k +MfY?echuPf%'i3rmhkc +\^o?V@P__6BjQhECL"p2a^/c6CL5-ZecNt,!<(Ia]`?X7!56t7!56t7!56t&!,$(_B3*"KecN6O +J,TBdmVdUTs8.:emf2!SNW8F[S:?HY.KBFQAq.5_rr38\S:?HY.KAPTrr41^%0--MGP2!sCPDfV +]oIf+>a5&2GP2!sCPDY8F70).[2f,E!!)`D3ro<3f$^4MF(.b#dakfuJ,fDjdJrEGH2%?kMfY?< +qu>ks2/dPO!.2]V"RH*fape=A%(Z90QlQ.gc2R_D`XMhG!q'uVrr3J\MfY?dNrmhS[\^o?F!7oSkN,rH#cBLqO~> +ZN'm[!PJL3hA3d)hVJ7GhVJ7GhVJ7GhV/Ap[!7K`\S,iThpFXM]`=2AY+rVloV)>aA&VP!,Qfs8PB-eis6eatR#]G%9PN/9]hX(WqiZ'p;70n8'ZlMpl:Mlla9>'p:t:J,fQ@N``LGlK\$ARV0P+[pG[) +rrIUfp>,fMs8Ljc!577G]`?p?>/Ap[!7IB4J,~> +ZN'mX!PSR4gD7L'gY2_BgY2_BgY2_BgY%!XY0aG*Vu59ND-tNagE19>bhLWZ=KpJU!4pq>!5@4B +!5@4B!5@4>,`^3uP&pKS+4c^8b-juVe88L6e7Mh$^JeF\gMLAo.Nt`rAGYq=A*-s8S(]p#TJ+s8Vrfb5gGPdE9D@[oZ+Z +LOsr!F)G`)@P9QFZa?s"9;6p#TJ+s3m-ji:43aU&UTr +`9t'?Sc?^#dI!WD^&OR9`:!#B4TGFLK),"JAc2Q.!;>.f!<;H@es209[S1>`gU^+/s7q_$hV;al +p[jGKYN5]K!WV$UIg9fjJ&_NgIn9P%DV!8H(iMorJ%B@O%BB^>%BB^>%6bNmH+9F5,I% +_St9a\kR\QrrVS)J,]H]Tjte0X(8O9`gs:*j^!65kq[hGrp0Ua/rd%,j^!65do"9^l[M>&s-m`@ +m^?Gms2LVWm@e@0s-m`@m^?Gms.Nr9p@Dd*r;Q`sp=hh4s4c;U=h`LR!b^$;\kR\Sq2]gXgh2'0 +oSm%XjQl.)m]V+iG&75/`rA#en*PTes6gH)oC%nfs3jifpZm;srVmT!D1DTcoSm%XjQl.)s-m`@ +m^?GmrVlnJD>)]b!<<&Q%fj>ugV*[Bd9438Q3,="9n3~> +ZN'mS!PJL3eeYq!f%'i7f%'i7f%'i7f$ntGWlqMmUAWRDCL5-[efS[6ak"gMY+s8Vrfc2cbSdE9D@[oZ(V +KR\;jEGT;u?nO9B;c+nd8PVi$-r<_a9j9j_@r63_P(/.?(1E;mCKRgL!34&_]`?VP8V=puLF[/k +eA&iNf!S,"rVlreDh%cd';/gLqt"9+s8S"ZpZ>Y+s6;=ghYug?s"953pZ>Y+s3d!ei:+-^T`:Hn +_sY!?T)Zd"ddEcC^&OO6_sZrB4obOJK_t@M@f66+!;=ta!<;?8dZTC)Yt/HQf!S,"s8.t+i8/-s +q=K\LYN5]J!WV$UIg9llJ&VHfInK\>!8d^)$Z?#p`W%f__sY!?T)S`kmVdUSrs"p)_sY!?T)S`k +mVdUOrr<&mec>dNrmhPZ^"1cJ!7oS]LW-MhA^Ig8!k$/Mrmha8/WdfjYt?Un_#Uaka^tP(ec"f%/>rJ,->fZ]l!m/+UrRN((!7o^$^"1a7^"1a7^"1UTN60A(DqQfs76Z,oBqhds3aZapZd5orVmT"Dh%feoT!%WjQc%&s-dT< +m^HMorVlnJC\H<[!<<&L%fj;of"D(7c;qL(O9!Ff8q6~> +ZN'm[!PJL3hA3d)hVJ7GhVJ7GhVJ7GhVP_Y=D:rJXSgoVDdL]chB-ZBc/.0.8mZ^c*+Id]!577G +!577G!577GO@j/$8pgEk+5E#W@UN#qgjM8Bab.re76MfO>S,iKe!T!hT +rt;_;Is5@[jT!28@Wc:Om+bi#BDu[$[K"3^@Wc:Om.@#U??^Ho\c;]3;KHnWZ2ai^:3Ub_gAh2T +;KHnWJ_:"VK5#[AMraj:"Fk@HhPU6i&uOOBDh%33hV(S*CK=']hVOb&rr3:6>'F.gs8TV0A,U0C +[Jg+9mVdUTs42[/F(0^QgfuRHrbDMAr;QfS!<3!-[8MKu:2&rT!T4sos$ir\lKcWs +ZF79nnF=H@q""%$aR8]gaNa;G]"Z%iY3kZFb5^enlF)A1PBN;Rj!2M#hn9$HW-(a>B3Zm(hU@1? +hVPq@J,TBKmVdUSrt=%%aoDC!:3Ub_gAe\`A,U3Cs"94)GB6sAs6?=`CNX6Gh#GrT@WQ"0 +f`/p,@Wc:Om/PXd@WQTgZ2aj3;L`mcao)/=!<1jQDuST5!<;KDglbQE?r3$AhRu[6s8T<0Bk_+e +s4gj6BDlWDrrLsVrVlmMPkk=[h>mQT#-Cd4F(0^Qrr3&fDh%cd#1e95F(0^Qrr3&fDh%W`!!*#T +r,;Sk!^!6TVh@n=L8mZ"*4eUZ_4\fp>@K>9A!58Bg!2'#A>$cDo>.O\5eph.M@Y*23 +`GB"+At&)deph.M@Y*23g4O*dG>?9Xrr<&uhY_&./2P2+p-:4NVTmjCB1d@rrVWF!<3!5X\s4];R-9%G?T'/;O%4H>$cDo>.OV3#juV\s8UkZ0k^K$#ds]`Iq!J. +J_9r$!Io%ehY_mWVrn\.c^#%VZ!8cF%>&./2g71og~> +ZN'mX!PSR4gD7L'gY2_BgY2_BgY2_BgY9&N=_:cGXSgfSD-tNagE19>bhLa&8R6C[*+@UV!5@4B +!5@4B!5@4BN(mkr8U^Hi+4uTN@UN&pfR#`=`e6osF@\TegMLM/uUq!lE*gss]j^q-jP +7Yub(4?5>X94;'V0j.:H,p5TI6m*dL9c>6+,p?c5Dc2REKS`ni7S>[LYdOP2aNhBtgST$c?tN[o +D;3EjO5&F#9n3-A!psiSrr3VbK4oa[:M/ta!+YtCs8O^2:j6tbf`194?>OdtLths[ +Iq*P/JCjh@De+!'RHsn'Iq*P@:pC!"[S_O":+%a[f4ub0k^H#r;QfP!<3!"r@F*,rrLjSrr32(;e'lXJCjf#!psiSrr32QIq*P/JCjf#!psiS +qYpNqrnI?frn@GRs8La_!5@4B^&Zp?N_s7n;39G;gB+t1s8LadcY#mV@9#L]gXA8ZDfoIggY2_B +rn@GR^Ack#ZYWcV=C\GeD-tNAb[55AHT[5/qqD1r^&\,d+2.a0gAntBgAntBgAntBgPej!<1aND>r90!<:g.eONhX=%eY-gU^+/s8K6/ +BPD%drn:R2BDuZH[Jg+,mFqX?rtVV6F)t0^s8@4IF`gWIs6EltGBI#trVm/jD1DTcg06:ErbDeI +s4)U/F(0[OrVlnJD>r;fD>r8j!<<&Q%fj>ugV*[BeONhX=%h)49n3~> +ZN'mS!PJL3eeYq!f%'i7f%'i7f%'i7f%.$=-hF%8B^en\U6f%,$^>.)r#6Mf76PQ:X]!T!hT +rt;_;Is5@[jT!28@Wc:Om+bi#BDu[$[K"3^@Wc:Om.@#U??^Ho\c;]3;KHnWZ2ai^:3Ub_gAh2T +;KHnWJ_:"VK5#[AMraj:"Fk4'F.gs8TV0A,U0C +[Jg+9mVdUTs42[/F(0^QgfuRHrbDMAr;QfS!<3",[8MKu:2'#Vq!lN-gss]j^q-jP +7>?=t4#f,T8mY[O0j%1D,TfBD6Q[RG9H#'(,9LB.D,-([gkc +CY$a_N7QRf8q6g>!q'uVrr3VbK5#gZ:2'"e!+YtCs8O^0:3Ub_gAgH4>\eIoM;8-^ +Iq!J.J_:"ACgqO!RdC(*Iq!J?;R-9%[8MKu:4+a\,Y5g0k^H#r;QfS!<)ou/X6)2!T!hUrs#T'@WQ"0f`(mPmVdUSrs%&(@WQ"0f`(mPmVdUO +rr<&uf)0=_ec>dNrmhPZ^"1cJ!7oYY:0q1!N7>5c!k$/Mrmhb)Ak#s1;E:,qc8eX8F'\jteccarmhadBi\bF?Dc:h?HLd??>J,93JYTNecNt,!<(Ij]`?X7!56t7!56t7!56t7Le;,h7X*G7 +c%Cu+f!S,"rVlreDh%cd'&%tPHX_oos2AeLGB6sAs1&,>rG2H5/rNJKF'@;tl<7MHZr6,r;Q`srmq!_rmi%hs45cL7TO/"7Z>jJ[7YrKs0,gD +Bk4^QgfuRHrbDMArVlreh>mQT's1ATF&K:Cs*%XfF&J8&l<7MVTmjCC%?J +eph.M@Y*21rrIUbrmq!_rmh)Ms8LR[!56t7]`?X37TO/"7adMnJ,~> +ZN'm[!R5Q]hA4@ShVN1qhVN1qhVN1qhVR%sK6)UKf_jS-VOl?8hB/Vde_]0!MfX*DMf[b3Dp@.q +Dp@.qDp@.qg6?Z"OOEs]*m'^hFdgM1[!e7b_2EGuS:@KoVOl>phR&2Z^#%"PqVD7C!;uitmXP38 +&Gr_bK:^lus7XEkKrjG:k,/'+&&8/<`JoSQk5YJJg6@)B^&J$?qlGF&[JU"(m[Scbc2R_PqlGF& +[FkHXrRREiT%s8D%Y&ERhPN43!&pe3hJ[o3rn[dcGAhi=rn[[G!<3!'g5pfKirB&'qgSU\rVm?# +\%htCrR7-dSCmf&VYkoD^\n*4mXP69"T,HVK=V!]"R_e0Mp;2"!Ul'Fs$j#`lKc]ZI=n9GnaXZH +q=F7=i:?R3i8XD#jikceiUH=(bl@%qlK>[KR]P<*j+Pn?i6H8thSYCqH(:%phUS3phVPq@J,TBK +p:%g9rrrAPRY-7'rr35kS:?IAs8U@@J,fNOm[Scbc2R_hm'hh(U>GqMrR7-dSCmf?p;N#FXQKQ_ +rR7-dK=1UVs80'RK=UmZ!!*#T!,qi:!,qkn)?Bg*hVQA?F*3qphVPq@J,fQ8[>0UNnGhXJqg\PD +!UbI9rrIc+q>UK`J,]HPqlH0;SCmf>rrVo'^]+6;rR7-dSCmf>rrVo'^\[s1!<1gTDuK_:DuSSo +!<<&T%<'MJhT]uqhT;()FdgL[h?(C8s8CdXc&d!bH`6r#$*8>hK=PpWhJ^[qQ>rn\=D[tAaQ +Z.Rp1T%2]JSu1AOI?r.Th?)6PDuJN7c#;IqDp@.qDp@.qDp@.qg6?Z"OOE"BeXiYphRu[6rVlrm +\%hqB&H11KK:^lus7XF%IuDSOs3:Fjs8N5iS:?IArr3,TP^eJ%rr4#;e!PcXf_tjD`JoSQk5YJ[ +e!PcXf_tjKes_5D\blC,!<1gTDuK_:DuSSo!<<&T't&7(Hb%>VhRu[6s8VGnLP`Y4s5p3VIfR7^ +rrVo^J,]H^qlGF&[JU"(lC`WZW:U&hg6@)B^&7m7p:%g:s5p3VIgEghrR7-dSCmf=rrIUfrn[Wn +rVuq:rn[YUs8LjcDp@.qc#;Iq[ +ZN'mX!R,EZgD8"MgY6ShgY6ShgY6ShgY:AfJoQ:Df)48(Un-!3gE35^e(`WlMK*d=M0%G+D9Ubh +D9UbhD9UbheWb&nNm[[W*lX@aFIC5)Z@%q[^P[)nR=;'gUn,uhgTlZS]%bGGqV)%>!;uitm=5*7 +&Gr\cKqI0#s7aNoLTT\AHfTgrC]`%m1q5f4&[/9n'n"##hbl7VOq5f4& +[+G9VrRI?jTA9AE%Y&?MgS?_-!&gY-gMMH-rn@R[GAV]8rn@IB!<3!'g5g`Kj8]/'qgSU\rVm?# +[_MkBr6gpbSCd`$VYkoD^\n*4m=5-8"T,EWKt@9`=mqo4NQhG&rUoU0DqWR^`4`U_<(2Lj>9!VC +(e@n5.5"Li7l)ha69m4]7nGio.5=P*Ant2!H@U^*@SK^6TWYY,Zc:81D;3g[NH064Oega\SYY#p +\kR\QrrVo&^]+6:rRI?jKt@9`#Oe47NQhG&bklnirrhe]JW7nQs!@!?MM._Zrr;oEQ\:m\rVuW3 +O,'k!s8W#FQ\:("qu?]nY_@eBqu6Wrrn@EirVuq8rnA=ks6\Rq`KYCbRE4.;\kR\Ss6R.KMS/KZ +jehp)r;QfbJ,]HLrI`burrMM:rr32uXI"U1fDY^K!qs(;rr33#d[,WWfDY^K!qs(;qYpNqrn@Ei +rVuq8rn@GRs8La^D9Ubhb\c+h`KYCbRE2_h!kQVUrS%M"OE58cVZ, +ZN'mS!Qf-UeeZACf%+WYf%+WYf%+WYf%/?UIrBb8chu>qTUO4(efUQRcdp[ZLMq70LMtbpCWY/Y +CWY/YCWY/YdZABcN61nH*l3qVEKnAlXETcF]8(BaQ@#@WTUO3XeuXL>[FWH8qUP\3!;uitmXP38 +&Gr_bK:^lus7XEkKrjG:k,/'+&&8/<`JoSQk5YJJg6@)B^&J$?qlGF&[JU"(m[Scbc2R_PqlGF& +[FkHXrRREiT%s8D%Y&9Fet=l#!&^G#en]d#rmh4SF)-!)rmh+7!<3!'g5pfKirB&'qgSU\rVm?# +\%htCrR7-dSCmf&VYkoD^\n*4mXP69"T,HVK=V!]=mhf0Mp;;%rUoU1DqWR^`4`U_<'u7c=W@DA +(e.\/.5"Ig75?M\5X-nW77]Nj.54A#@q\PmG^Y0u?VF7/R]qu6Wrrmh'brVuq6rmhtfs6A1d_2rSTPf))'[7YrKs6R.KM7`9W +k,/$*r;QfcJ,TBJKA-/$!UbI:rs&A%R"Lp]rVlfup:%g9rs&GNR"Lp]rVlfup:%g5rr<&uec9Lb +!!$g6ec>dNrmhNoak#%bCY%(QODf-'_m@nq[E8IIecj0SDejQirmhCOF)-91CY%'Pao9ilCWZS, +'@a::DgeY3etAJXf$BeXeuX(W`V\3fccKu$efW"Lf%+WYf%+WYf%+WYf%/92Df:TVf%/09T[q]% +8q6g>!qs+dNrmhgdF)-!)_q*DH8q6m@m'VV*]C>j_ +VYkoD^]"06p?mG0rtP@3K7gT/s8VAWItO]ks8Uf;Itt`8rs&2+^]4>rVYkoL^]48]R"Lp]rVc`s +Ir#&7!,MQ6!,MSb! +ZN#U7!Ls.irrIUfrn[aS2`IW5h?%VFme5K\:4K;D!!)uS!cJ'WrS@SUs6fXmWVnD429:4N~> +ZN#U4!La"grrIUdrn@ON2E%B1gB)5AmIo98RK1)%*W#BojPAD5aMGHoXAVE;I!8O.)+e(7?Xo8( +X^+-g4<-ON6V(mQYdUE;B2"#'I=d98Pa\5@XKo@M`6-HsgB+r\J#`MK!<(XRD#eGOgB%BTmJ#?: +\kR[[rr<&tgB)3lD>i2i!<8eJ!kQVUUtYs^9n02C!!)uP!c7pSrS%>Qs7Y1K\kR[\rrIUdrS%Bj +!,_\h! +ZN#U/!L*SarrIUbrmh1E2)V'+ecKW:lLrd0PQ8Gt*r>KpjPAD5aMGHoXAVB7H#uq&(e7b0?"9&& +XBRhLr]2&]4[28&B99=)V-db)KlhWjKo;([S=uma[(=&mbOO-b[7YqKrr<&tecKUeC]2fc!<;?Y +ecN6OJ$T(S!<(IMCB//Kec>dNU=KCRaoA0f!jtXMWrE&!rRM$c!,MPa! +ZN#U7!Ls.irrIUfrSIROrS@ToJ+!$e!Ls.qrsGm+lg4$,eCO-]rVZZqq"aUaqu6Qo'`@IhcikmOh?(AcJ#`MK!;t[RDuAGm!<;cL!kh?YZi:&UDuAGlDuAGm!<;QF!Luo8~> +ZN#U4!La"grrIUdrS.@JrS%BjJ*lsa!La#&rtbD"l/LIIc,IE+Yc1GcIsH*P:.\`HkktG^D`BZS +2$>fs41k4brtXHt@nD5mH%1O+P*hi:Wj&qE_T:$Uo%O5W9n02C!!)rO!,_Yg!W`8fpY,b\9n0JK +!!)rO!,_Yg! +ZN#U/!L*SarrIUbrRV"ArRM$cJ*QaY!L*SurtbD"l/LIIc,IE+Yc1GbI!9RG9LrBAkktG]D)_DM +">r+u](Z+&(#N5(7Y,Q'I=d98Pa\8BXKf:L`6-U#ecN6OJ#`MK!;tCJC])`b!<;?YecN6OJ$T(S +!;tCJC])`a!<8eE!k$/MdaeE=eCF]/8q3l@!!)rJ!,MM`! +ZN'gY!P>Ckh>s,HWrE*LDti)iDh%3Kh>s,HZMtQ@WU0$>mdKZ8p](9jpAb0fp\4[^s7[K+rVulq +rVufcnBh4&mHj0(l0.7N!<;K]h?(AcJ$T(S!;>7M!<8eM!klkY +e_UDKGq>*-h?(AcJ#`MK!;>7M!<;cL!kh?YZi:&UDt`#g!<;QF!Luo8~> +ZN'gV!P#+fgB!`CWrE*LD>2cdD1CsHgB!`C^],DTp@%2&g!\$\]X=l>:I';NEGK,m>*f3gn'1n4 +C,%FA/fl]%2;eH4iTL6u>$sjKEHch`M3!pkTr>-$]"uG3f(.Ps\kR[Srr<&mgB%BTmJ#?:\kR[[ +rr<&mgAq#a\bl?5J"2oC6O4;nc\kR[Srr<&mgAq +ZN'gQ!OS\^ecD!8WrE*LC\QB]CObXCecD!8^],DTp@%2&g!\$\]X=l>:Hs,GDJ3Nc=I&pdn'1n4 +C+q:>/KQN!1uJ?3iTL6u=C"@DDKL2TL5_:^SYW9i[_0JudIPii[7YqKrr<&mecGjOlM&j2[7YqS +rr<&mec>dNU=KCRaoBoB"2K".N7?D[[7YqKrr<&mec>dNp=9;Q8q42I!InnYec>dNnC@W)8q6~> +ZN'p\"M8:_+OZ5*!Ls.irrIUfpYGsiJ+!$e!Ls.qrt)E7m-X60nF?&@p@Zqls8Voop]1-hp`0&( +q"==Vs8;EWdf8b&m-Es$ki_*ijQ,@\iS`Vah?(AcJ#`MK!;>7N!<;K]h?(AcJ$T(S!;>7M!<8eM +!klkYf\QePGpF%+?IRa4]hX'Wrr<&mh>mWVp>,ka:4KVM!Io%]h>mWVnD429:4N~> +ZN'pY"Lr%[+4#o#!La"grrIUdpY,adJ*lsa!La#'s$?SUmH39Wd)a&7ZE'cr5^INiBOY4IDM;@= +3WK+k.k)ko-n6`#H=fT3XoI6Z>X`sVFF&ImNK]d&VQ@)6^W"FHo%O5W9n02C!!)`I!W`8fpY,b\ +9n0JK!!)`I!"C0F1!kM*UWrE&!p=fTJs7Y1K\kR[\rrIUdp=fTJs7"bD +RSA;~> +ZN'pT"LMYS+3TVo!L*SarrIUbpXTC]J*QaY!L*T!s$?SUmH39Wd)a&7ZE'cr5'V*aAmeeAD1l+8 +3WK+j.OZYk-RgMtH"B?-XT.-W>!dOOEd3"cMNF-oU8Y6&]>;P6o%!lM8q3l@!!)`D!W`8cpXTDR +8q4/H!!)`D! +ZMt-@B,hQc%`+([!Ls.irrIUUpUpW7J+!$e!Ls.qrsuB.g>(QCioB.`l0?^Us8W&n!;c]js7dN, +p@\:^r:9C,s6f:@lg!a!ki_*ijQ#7Yi8=+^!kh?YWrE&!p:UM-s6fX +ZMt-=AfMEa%_dkU!La"grrIUTpUUE3J*lsa!La#'s$?PRm,[!RcH!]0Z)OHl2.g9$<`)O\69R:G +2uine,pOQU+X8<`/i,FLX8h$U>!mLNF*W7hMisI!UoL]/^;S1Bo%O5W9n02C!!)`(!W`8fpY,b\ +9n0JK!!)`(!"98Q_g=+dX\kR[Srr<&m\cDg2p=fY[9n0ML!InDK\cDg2 +nCmu49n3~> +ZMt-9@iH$]%_@SL!L*SarrIUQpU('+J*QaY!L*T!s$?PRm,[!RcH!]0Z)OHl1h0ip<)6+T5W^nB +2uine,U+?Q+1o%!lM8q3l@!!)`#!W`8cpXTDR +8q4/H!!)`#! +[JpNaG9IV#!!!M,MV\B(!35GfY*Sgq!g^=`p>,h?!57h#n,MnWnc/1[o+:Q>_VgElqAf>+p\4CXs8;HXeGnt(mHj0(l0.,ka:3X&4!Nqa^]`E'O +nD429:4N~> +[JpNaG9IS"!!!M)MVA0#!35DeXd&Rk!gL.\p=fV:!5Inekht1Cbf.<*Yc=FfQ"O',?WpH*8ju!, +2'tWhs'?fg)AsG6+=&Ei1ch];h,7?AF_Yo?I>!K>Q^sqPYdV3^ajBV5!kM*LWpKe'p::H=,SC7Y!!+.fdb=i79m3Q(!,_G@!H%l1gB+r\G-gQ:Xd&Oj +!H%l+gB!a;J,~> +[JpNaG9IS"!!!M&MUhfm!35;bWfZt_!g'hTp=98/!5IebjkeV9ahk]tY,@q]P@d^$>Zap!84,R$ +1aPHfs'?ce)AsG5+!`9f1HDK8h,%-:F_GZ7H@^j1PF8)@XKo@N`QRi'!jtXCWp0S"p9as;s6B@0 +!jtXCZK_F*p9ap:s.\'G[E8I*ec`d1,7t(W!!+.adaeK-8p.0!!,M;9!Gh`/ecN6OFgL?6WfZq^ +!Gh`)ecD"-J,~> +\Gl]^IN&Uqr;Zj-Iu3"_S:@*t!VYsTh>s-r^$bphs7$'Ys763]s7H?as7ZKes7lWis8",@s8Vum +qtp>t% +k5X>q"hnLq&/5KN!!*bNdG=r +\Gl]^IN&Uqr;Zj,IYle[RX^mq!VYpSgB!am^[4ANhq?H$`4`U_W1f]ENJ2k,EGAug<_l7P4%oVR +>8n@m',26%+=/Nm2lZi5:KCM;BlA0BJr,PSS>3'e[_0JuceA!F!kO?!Sa?IXmJ#?:\r=(om/[0\ +gB,jcs52Q7]ON<#'FOg>!><2qgB+sF[[ldcs7Y1K\r=(om/[1UgB!a;J,~> +\Gl]^IN&Uqr;Zj,H\p;SP^f7h!VG[NecD"b^Zn/Hh:Bon_7R%UVOs9=MhHM&DeWZbD;fl2^jY +ecO1Ys52B2[p^To'FOg>!><#lecN79[@QR_s7Y"F[>D;fl2^kRecD"-J,~> +\Glm+`/[V<%fcS7^:=qu?]nqu$Bj +r;?Hgq=s^XqZ$Kbo(VnImd9B,lKRNqk2k[aio/hQo%jDleXPt,k4d^5c./TWU>>t6mdAid"cj?) +!"Z$Th>tmOS*p",!R9/imdT'6S5+S~> +\Glm+`/[V<%fcS7;i'0.\sr'ejS%=.]"[Puk24h>b/Cs$Y,J"^P)P'CG&V/)>Z=Hg5WMC8s&^'M% +1E^Z(EF_L0/]a/h+q$\BkVC,H\.'7Q(+MHYI2$[aNiN2gB#IGS*^4:pY,_ke"c<)gB-I'mG$A!N +>)^"&#R_.!Qrl\mIo98b1!-QnCmu49n3~> +\Glm+`/[V<%fcS7;2Ed'[@?F]i:b_%[D(omj5&83aMGHoXJVSVOG\[#S-b5<)15s&TsJ% +1E^Y(*"MH/i9O,geC^VBP)($G^kF+P*_c9X0K.I`6-X%ecEb9S*C"3pXTAac_Kd"ecOdplJ'knM +A-Bt&#.G%!QEBSlLrd0`Qt=EnC@W)8q6~> +[JpHo:,r:U0\_08J_nc15i:MYioB+]k3(smlKdg(mdKZ8o(2MHp@n@^s82]krVlcnqtg-aq"O[c +r:BLLnF,f4m-Es$ki_*ijQ#7Yi89+BJ_lgO#1d-GCB+fqJ_kt7leVZ4:4N~> +[JpHo:,r:U0\Cs2J_ST,6Ia<%_7R(WVkKQDNJE%1F)>Pu>#\3d5WU_hs8Q.7%1!.E%MTg)-S@6S +s4pFL>@D/]FF&LnNg-$,W33P?_T:$UJ_Pb1RbJ%q?9W''%=j#aJ_TMF!Lc`5~> +[JpHo:,r:U0%5C&J_&6"6IWl^"%=NfYJ_'/ +[f6Q22T.cZ)ps5nJ_n`05i:MYioB+]k3(smlKdg(mdKZ8o(2MHp@n@^s8;fnrVlcnr;-9cq"O[c +r:BLLnF,f4m-Es$ki_*ijQ#7Yi89+BJ_ljP#-KM1hE_?HJ_kt7lJ;Q3:4N~> +j8T,Yqu$Noh#@SX2T.cZ)pX#hJ_SQ+6Ia<%_7R(WVkKQDNJE%1F)>Pu>#\3d5WU\gs8Q+5$jH\9% +20X&-S70Rs4pFL>@D/]FF&LnNg-$,W33P?_T:$UJ_Pb1S(e.K.tZM@#$$6/J_TJE!Lc`5~> +j8T,Yqu$Noh#@SX2T.cZ)TmW^J_&3!6I +\Glg$=_RI-((tVkPtSSoCi"Vq=aCJm-*Nh +nGi73ccaDJ^qI:eZEC7+UnF9HQ'%)aJV8T-a+OGHqM,$(i.MAe5Tt0$KF]&SJV8T-l\#?.S5+S~> +l2M"fr;-6`p@\+WoEP!^p\=R^ro="`m9+,prtZ5dJV&H)c[nK4H?O=ICM.-a>?Fj$9LqQ;4ZPAS +/Li(k+#Ers:CIL5"9o/@&/,j!+.iMa/i#=G4[DM.9Mnbk>[LuRCN"9!pc:LJ,~> +l2M"fr;-6`p@\+WoEP!^p\=R^ro="`m9+,prtZ2VJTHB`cZ;EkCh[Hi?<^K1;,0_R6U3ao2DQp: +-m^&Y)__6k9aV+/"9f&=%M9Em)k?rZ.4d,.2EEud6q9jE;,gY%?XdS\CiY)JJTJtTs)J*ZqJcIS +s)R+?#]M^B@rl]#(ib+JJTL-u!p>e?J,~> +\c2rD27W\dnH>Y@o`"mkrk7_!!InF,]n*lj]cXu(_SjI8b08/XdaZh!g"Y??iT'%_l0I^(nc/Xa +p\Fgb*r5R'o^_M>pAb!Pjl>=Wh:pW7e^MpmcHOGP`l,a0J\?WJbe="irkJ6Ds1eTJjh1qj.VHB, +]t#;kJ\?WJpq?>"!s%e[:4N~> +m/Iq%qt^!ZoC;>=md9E.m-X3.n*ol=p%S=]kPkgr27W\dnH>S[_5\EHce]L5V1[ +S=uj^J[U-Y-+o(XT,C+Y-5+9['f)*#aZrL['mEK+KL(%J[Y?^o`#!nl^COu~> +m/Iq%qt^!ZoC;>=md9E.m-X3.n*ol=p%S=]kPkgr27W\dnH>G)o`"mkriPSf!In.$Xb!VJX[/n\ +Ssbe'M1gA)F`;,.@9cl39h.Q73&E9<4TGF\&e>BY$kF!f*$HUXU]9%36V'pL='T0HCN4NDJ:rf@ +PaS, +]Di!)Am4X%rrg0*hLtq&h>s,Hrk/=3n(n,Z:0rLbJ\Ccj+25P-`5]m@bg+P^e(*('gYLcGj5f@d +lKms.s8Vfdp\sq/p\+7NnaGl=s8(m=j5AhNgY(3/e'ZLdbfRrG`59<@]n*lC^&PhH^]2+K_>1tI +_>h=N^Au"5]`YlZ.\6OO!N;cE]n*lk]`A*+h?9>Kn!m.'~> +mf*dqqY0XPn*TH*ki_*ijlHF&$KgO8lKmp-o(DbSli.(YAm4X%rrg0*gOfJ!gB!`CrjDh,n(RoT +9j<1WJ[Y9\6*[MhU7@R4N.un2GB.M4@9cl39h7T63&<0Os8Q.:'G:uj'Gqc3,q:REs4]n39i>%u +@V9OuGC4ssN09NuU8P,E[Xkm6[L'@JZ*1=3X/W(srhodnrhg%#WMur"Y-5+:la?o&@k!cC[K3+n +J[U- +mf*dqqY0XPn*TH*ki_*ijlHF&$KgO8lKmp-o(DbSli.(YAm4X%rrg0*eoUlbecD!8riH2#n(%QJ +8lgMFJZ\XJ6)^QMRZrkjKn+MnEGT8s>Zap"8OPd(2)$REs8Q.9'+kcf',MQ.,:G+=s4T\*8PW2d +?".G_EHZ\ZKo;"WR\-C,Xb!V$XU;/0W2?DeU7n6QTDP2dT:hjNU84W_W2f0c"05`UO8Z0]R[&H+ +JZ\[K!<;cD"I&oLPY-H~> +]`/&M26-TS"R5sCK`Cc&!Ls/h]`A*$h?(Ac?@fe@^%T,]^;.V'`lH3Ec-Oede^rL/gtpuKjQ>Xi +lg=0@s7Z*Xrq7E+o^hYCmd9`JqW?kui838Cf[n[&dEg(\aiDB<_7tQ7J\BIE#JRsf^qmk(_Z%IK +`;[[S_Z.FR^q[YO]`Y#`?+GO"J\CQd!<;cL"IoJ\S5+S~> +nG`srq=aFLmHWosjPo+ThYc40h@&6"ioK7clg=35p\aIZ!l?gpr;Qoas4`/*p=fV:!<0D,s6n\D +\kQC[[Xkm\[R%4#VkT`LP)Y6KI#sVZ5C=8P`;g +?=RYcF*N+cLlIUcSYE'cJ[U-8#[KE'Q=gEUe +J[Y'V!<;cI"I]>VRSA;~> +nG`srq=aFLmHWosjPo+ThYc40h@&6"ioK7clg=35p\aIZ!l?gpr;Qoas428kp=98/!<0)#s6nM? +[7XJNXb!VJX[/q^T:2%,MhZb0GB%J5@pW;;:J+&A4?##J5l^je)]0>+(Dn)4+XJTjV>o:978$HV +=^GTOD/siJJVK)EQ("ACJZXL*d]EqZWi)\hU7e-MS=>t9qjIJQ$(IHUStMgQV5L?]XTOh> +^Ae<1F&(oorrV&1s81XQ!<(aTS,iQ%!<;NE!kh?8J\?WJot;E]_8F74aihrTdF6Urf\5-;hr<\X +kNV9umf3=[oCMtRs776#nF,c2p&FmMj5JnPgtLE3eC2djc-+5L`P]O,J\?WJe%H.'^V@S#_SX4/ +`5T^i`rF$X`;d^X_SO%'^[SodQQh?9>Kn!m.'~> +o)B:#qY'LKlfdHji838Cg"4g+rRDG0f@\d2gtq#Mk3;7%oCr1Rrr`%;:?VHQ!o&>,qqD)NrS%@A +!<0D,s6n\D\kQC[[Xkm\[R%7&W2#oNPE(KPIX-$QBk1RSVRSA;~> +o)B:#qY'LKlfdHji838Cg"4g+rRDG0f@\d2gtq#Mk3;7%oCr1Rrr`%;:?VHQ!nMGmqpk`IrRM"6 +!<0)#s6nM?[7XJNXb!VJXWOR>TUV4/N/*"4G]Rb:A7&M@;+jDH4ukJS6N@'j+!(>"*ZZ=H,q1E# +VZ5C<7ncc\>$toUDKC&NJqo;IQCFPFJZXL*e?'7_Wi)\fTUq^DR[BG-Q'@O2P7 +^]+A[31TaC!oAV1r8.GRrS@RF!<0Y3s6neG]hVmb]n*li]cFl(_o9[&Ys2>2\_ns7*^V9?R"0+:Y[t25D^$WK5s8CgS!;t[VS,i#J:4N~> +oD]F"p[dk>kiC^ZgY(3.e'ZOgcMc!"cHjkbe(*('gu%,Ql0Rp3q>U'e!mN]pp\tCj56[e@-O['$C(T:2"*MM-J*F`;,.@9cl39h7W94#TV/s'[2urZWC/,q(8s +1H)33gdt4<='T0GCN+EBJ;&lBQ(">@WNq)`J[X+;&@8XEX/VtlU7\$JR[TV2Q^7T8s-E\P%%3QS +StDaQVPpPuZ*a24"/IeNY^s66[dCL's8C^P!;tRSRK2ZB9n3~> +oD]F"p[dk>kiC^ZgY(3.e'ZOgcMc!"cHjkbe(*('gu%,Ql0Rp3q>U'e!mN]pp\taK78)fDeirn>ZXfu8OYm,3&F)(s'Huo+WqpP,:4ij +0Jo^-gI4\/;cm:5Anu=,H@CL'NKTWuTrEUFJZ[J)&?;\*U7e*JR[9>*PEM&iO,s7"s,RYOOcbil +QC"&1StMgSW2oTn".V,?Vh(t$XmN4js8COK!;tCNPQ9m28q6~> +_>aW7JOJDhrrV&1s8CgR!<(aTS,iQ%!<;NE!kh?8J\?WJot;?[_8=.1aND`Pd*^=mf@ep6hVdDR +jn\cLqu?]orqclinF,i6oD\Rfr;Z`pp^luNg=Y$-e'ZLebf\#H`59<@]n*lI]aVWi^qmn*`5Ta9 +a2l@$ana*YaoBEha2Z-<`5BI.^q[VX]`NR-Ne+lp^$E?3s8CgR!<(aWS,i#J:4N~> +p&>^)qXsCGl/gm[g=Oj%ccsYTaN)="`W*pia2lEHc-Xnhf\>9Bk3;=)pAOdd"8dX;htI'OgOfJ( +g\q3OgB!`CrjDh,n(RoT9j<1WJ[Y6[5d78bTUM.-MhZe1GB.P6@pW;;:J4F5e^W7Os4u)Lcq#Q6 +.kQ;#e(!aapXn0Q\mAl[D/siJJVK)FQCFPEX0dGdJ[X1=&[epJXJr(mTq@mER$X)&PEM(*O:[P; +PE_?!R$sS +p&>^)qXsCGl/gm[g=Oj%ccsYTaN)="`W*pia2lEHc-Xnhf\>9Bk3;=)pAOdd"8dX;htI'OeoUli +f)>[JecD!8riH2#n(%QJ8lgMFJZ\UI*2fO%R$3PeKR\;kEGT8t?s&U8rjIJZ[P+&Zht/US+3KR?s2&OcPN^Mi3JiM%GQ& +MiEaXOcu&sR@Be@USb'cXTEJdJq:%TXm<(hs8COJ!<(IOPQ9m28q6~> +_Z'_i4c]O7rrV&1s8LmR!<(aTS,iQ%!<;NE!kh?8J\?WJot;*S^r"%0aN;WMcdC4kf%A[1h;@/M +jnJcYna>f3m.C)Q&GFl3f[eR$dEg(\aiDE>_SC`9J\BXJ&&,fp_Sa=2`lH-@aiV]KbP06\bQ#]m +aiMQD`l5m6_SO%%qRm!75t\J7]n*l^]`A*2hYdHRh?9>Kn!m.'~> +pAZ]Bq"4%?k2P7Of$i!jb/hQ>_SEq#^:h4o^;%M$`5]mAcI(.ngY_&Sm-s]GqYpZL4c]O7rrUr, +s8LdO!<(XQRK3>q!<;NB!kM*1J[U-aK78)fDe`lm>ZXfu;>:(o="Q'Z17@q; +&Ct9%CN+EAIY3K;PF.o8VlbNYJ[X1=&[\gGWi)YeT:D@;Q'7AmNfK(tMuS\6M\1o.Nf]BeQ'[r0 +T:r'YWi`P6qmHU+5"D]*[XkmP[K-@+g\h-OgB +pAZ]Bq"4%?k2P7Of$i!jb/hQ>_SEq#^:h4o^;%M$`5]mAcI(.ngY_&Sm-s]GqYpZL4c]O7rrUbm +s8LUJ!<(ILPQ:]b!<;N=!jtX$JZXL*orSt-URmp>O,JaDI!B^MBk:^X=&Vjd:&"Yk<\#^R0UVY8 +&Ck#oASQ.)G^P*uMia3mT;7%?JZ[P+&Z_k,U7[sEQ^!YpNf8mQLPLT_K`?](KFronLP^nINfT?f +Q^OA8U84`hqlKsn4%#fpXb!V>XT8D"f)5UJec_3;kEJSh~> +`;]r=N]bnarrV&1s8CgR!<(aTS,iQ%!<;NE!kh?8J\?WJoXu3Z_8F74ai_iQd*gFof@em5hV[;O +lKdg(mdKWDnG_bolg!j)mHj0(l0._ns7*^\kbHL*_S*]n*l\]`A*2hYmNRh?9>Kn!m.'~> +p\ts+q"*qq!<;NB!kM*1J[U-\BPhd6H[pg-O-H')UoCVM[Xkm>[OnhiXJr(kT:MF;Q'7;iN/NOKLPCM: +KS5&5L5(M@Mi<[XP*MB&SY)XQWN<>3rO)cY,CtESJ[XdN!<<#PrW)oO"I]>VRSA;~> +p\ts+q"*q0LIX6-UChI6a>#nNr8p#&kCh[QpARo:3 +0JP=>:24T`EccMOI=QTL@qTUtFaALhLlIO`S=lg3Xb!V,XV7_4UnF[Jec_3;kEJSh~> +`W$%t7">+)rrV&1s8:aR!<(aTS,iQ%!<;NE!kh?8J\?WJoXu6Z_8=.1aND`OcdC4kf%A[1gtprI +ioK4`kNM0pr9F=H)ai_fN +c-=Q5chYrecP=aMc-4ARaiMNB`Pf[1^q[RrZq +q#;--p[[_8j5/J>dETeP_S3[n[^'b0JGdgY_)Vmdp;Ts5-4oqssag +gOfJ'g]%9PgB!`CrjDh,n(RoT9j<1WJ[Y6[5II\pV4aX[kXTJ[U-Pr7_@C!:-(JJ,~> +q#;--p[[_8j5/J>dETeP_S3[n[^'b0JGdgY_)Vmdp;Ts5-4oqssag +eoUlhf)GaKecD!8riH2#n(%QJ8lgMFJZ\UI5HL`VSX>V%MM6S.GB7Y:ARSkI[Df^/NJ;&lAPF.o8We%:'XkBfkWhuPaS=5b-Oc>9ULP180IXQTj +H@#O8s*>N/I!pHoJqSl8MiEg^QC489USb)oUdR;;JZXL*kH+aarRUuKr72"8!9]S=J,~> +`r?+12TtUi!oAV1qq_;QrS@RF!<0Y3s6neG]hVmb]n*lh]c=c%_SjF6aihrSd*gCnf@\g3h;7&J +ioB+]k3(q*kl9f`k2k[aio/eOh:pW8f%&6sd*BkXaiDB<_SC`9J\BaM'Y_>t_Sa=2a2lBFbKS5V +cd0tbdF%d +q>W>Np[[\6in`89ccX8E^:Uk]ZELC2WMl_mVP^8hW2co#Yd1[H]YDA)bL"blhW*hfoD>%9bOGN5 +gOfJ&gAh6PgB!`CrjDh,n(RoT9j<1WJ[Y3Z*3lTCU7I[8NerI?HZsLIBk:^Y=]J?p91VK=5sY?3 +)aH\V77^'H;H6k*@:j7lEd)n]Ko1qTR%9tLXb!p3[b8)(Z`gI.UnF9IQBRDjMi!1AJq/?!I!ba: +(OLOXIt3*'L5:bJOHYuuSY2dVX07@+GCd2R[cOpts8C[P!;kLRRK2ZB9n3~> +q>W>Np[[\6in`89ccX8E^:Uk]ZELC2WMl_mVP^8hW2co#Yd1[H]YDA)bL"blhW*hfoD>%9bOGN5 +eoUlgec5^KecD!8riH2#n(%QJ8lgMFJZ\RH*2oX(R['"oLOsu$F`D83A7/YE<)HC_7n#a05!Ag* +)a-AM6:FC::/P"n>[_2XD/j`FIY3H8Od2B-Uk,Y!XkBfkWhuP`S=#S)O,SpNKS"]%I!U$]G'3\( +(NjnFG^=^bIt<9.M2[LXQ'e)6U8EMeEIk6CXlZYbs8CLK!;k=MPQ9m28q6~> +aSuA*9l]prrrV&1s7Y:MS,iQ%!<;NE!kh?8J\?WJo=Z*X_8F73aND`OcdC1je^rI-gYL`Di8WeW +jQ5M$k5XN\jQ#7Yi8_n9&2We%j7^#Zj,s7Y:PS,i#J:4N~> +qYrGPq"!e7inW/6c,di=]=>5PXfA=sUS=ERT:VXHTV8*UVPpPuZ*h*S_8XRBe(EL:kh&IGp[A+` +gOfJ!gB!`CrjDh,n(RoT9j<1WJ[Y3Z*ODoJUn=*@OGnsIIX6-VD.mKh?!13+:JFGP7R]`E6QAK_ +7n?6H:fCCt?"%;YD/sfGItNQ9Od;H.UoCVM[Xkm@[M?-PXJ_keSXGb+O,JjLKRnW#H[0gZrcS6_ +rcS6b'7"nOIt<6,M2RCWQ'e)6US:I>UOfk)[cFjss7Y1MRK2ZB9n3~> +qYrGPq"!e7inW/6c,di=]=>5PXfA=sUS=ERT:VXHTV8*UVPpPuZ*h*S_8XRBe(EL:kh&IGp[A+` +eoUlbecD!8riH2#n(%QJ8lgMFJZ\RH*NGs/SB4G=S=B/6o91_WB6U=$;5TE'V +6UaL:9M\Pd=Bo6EBP_X0G^P'rMN +ao;FA1V`>W!oAV1p>,h?!<0Y3s6neG]hVmb]n*lg]c4`%_SjI7ai_iQcdC4kf%8R.gYCZCi8N\T +j5^'us5X.=s54UKh;$c=f[n^(e'ZLebf\&J`P]R.J\?WJftA-8^r"".`lH0CbKS5Vcd:(fe'umt +ebRerec4,2e'cXkd*L"]bK@rI`h"Z&^qYH6J\C-X!<;cL"IoJ\S5+S~> +qYqE.oBk`#gt0rta25R$[BZj5VPBfUS=5k5rKe@gR@B_=TqnTdY-PLI^Ve.;e(EKC0=(6Kn,EID +K)bQ!!La#f[K-?rgB+r\>Cj56[e$pIYcOasSX>V%MM?\1G]dtABOtUY>$"[$:JFMT9)_E^8fCAt +:Jt.n>@(`LBPh^2H$t6uMiX*iSYE$`[=Pc;[bJ5I['6X0Un=0EQ'%)aLP15.I!U!ZFE2A?E,TW3 +DfBZ8EccGJH@10mKSGADOHc-%Ij8O+Y^s66[cFjss7Y1MRK2ZB9n3~> +qYqE.oBk`#gt0rta25R$[BZj5VPBfUS=5k5rKe@gR@B_=TqnTdY-PLI^Ve.;e(EKC0=(6Kn,EI? +F8tsb!L*T`XT8CiecN6O +bPq\4=CUjfrrV&1s7Ml^!<0Y3s6cBX:0rLbJ\CWf)Sa,+`Q$!@bK\>ZdF6Urf@\d2gtgiEi8ESQ +r8Rb8(uX +qu7Q1o^:r&h:L&ua2,EuZ`^=*US"'EQ^!\rOSt4XOHG`lQ^XJ,opPj\rjDh,m[=/N>Cj56[e$pH['$C(T:;.0NerF>I!BaPChREi?X-`8<)ZXjr(e5-;H$Rs +=^5r6ChmeaBc(T# +CMds.EclSPI"-a&M2[OY9du!rW3!7T[XkmK[K-@#RKEQURSA;~> +qu7Q1o^:r&h:L&ua2,EuZ`^=*US"'EQ^!\rOSt4XOHG`lQ^XJ$"^%:eseZr(@r%:/=_c +s$TrEUFJZ[\/)6Bd4TU_C6Oc>3PJq&/nG'%_BD/3iuB4YZQAH-3V +AnPgnD/XE9G'SIbJq]&=8LK1]TVSN;Xb!V9XT8CoPQLpKPY-H~> +bl7aQ0sg0F!T&M$!<0[u!!$>V]n*lf]c+W#_Sa@5aND`Ocd:(geCN7(g"P39h;7#Ghu)F4huDOJ +h;$c=g"=p,eC2glcHXSUaN)9;_SC`9J\BdN(V[Z#_o0R8ai_iPcHjnde'uq!f@S[.g%jA%fbE)j +f@JL%e'cXicHWFFDoTf8_nj->]n*lY^%24*n!m.'~> +r;R]5p@.A.h:U0"`kf$PBB@qTOn +EHQMRJ:r`;Od2B,USk5F[Xkm@[McBRWMH2XR$<\kM1pM0HZsUQE,BB)BP(gdrF6^UAS,UiCMe$2 +Fa&.\JV8l',>skuTVSQhZ[oQ9[cY$i!!;JT9n3~> +r;R]5p@.A.h:U0"`kf3OJUMfeEGfQ)ARSnM>$"a(;Gm?gs&'Y4<`iO1?=IJZ +CN"6:H@CF!MN3jcS"HL-Xb!V.XVn+7TU_C7Oc>3OJpr&kF`D>:C1q3i@prbPrE^@K?t!PUAnYpq +DfKlDH[^Qc+\n)_R%0hFWe%:'XlcbW!!;>J8q6~> +cMn" +r;SePo'>Dpg!e3d_7[4]XJ_eaR?`npMhm(>Jq/B$It3*%KSG>AO-5ftT;/?eZE?ke\^&^Yi90M! +rpg!ggO\[AlcSN4l/CFJd)s>B]!SZ@UnF3DP)bHUK7SJuH$=CRrcJ0_(jC7PIY!-,MiO!gS"QUX +YdD!V`QHTZgus=TJa;(V)sH;:f$V[\^q7"YX/;V^R?`noMMHk9Isl]jHN&1.H@1-jJV/`8NKKKn +SYDrg.[[f^`QHQXgu7OLl[eBSlN*GKJ,~> +r;SePo'>Dpg!e3d_7[4]XJ_eaR?`npMhm(>Jq/B$It3*%KSG>AO-5ftT;/?eZE?ke\^&^Yi90M! +rpg!geq*"8kK;s,jk\S;bf@Q4[]up3U7I^:OGo!LJUVulGBItJrc8$[(j1%JI"$X#Ll@F\R@U+N +Xg,=I_8jgLf]7VHJ`keN)s#l.e'5tN]sk;LW2#uRQ]dDfLP:>0I=$9bGlDn*G^=^bIt<J,~> +ci4'j1Ss:7JXh:]aIF* +rVnqUo^:o$gXONi_S!=^X/;S\Q]mJgLP15-I!U'_GB\:WH@13nKSYSKPaJ#9VhuaD^;J(>f%f9L +n+ZAPJXV.YaI4$5P)kKUK7JAqF`MD:BkCma?$YKDA7fOlDfU#H +I=R!+MiX'QRXpp^RQ?k%Oc>6QJpr&jF)Yu2An5=V>?Fp*;G^.`9MA)O8kViO9i"S`<*!+*?=78T +=#Y# +rVnqUo^:o$gXONi_S!=^X/;S\Q]mJgLP15-I!U'_GB\:WH@13nKSYSKPaJ#9VhuaD^;J(>f%f9L +n+ZAPJWt_MaHRU*NJW@@IX?<]EGfQ*AR]%Q>?P$-;c-@e:/:d_s%XA+:f1.k='8a6?t*\\CMn-6 +G^FplL5CqAP_#(RPWG"hMhm%:I$PED +;`/9-EclYUJ:r]9Ob&aJPh2-"8q6~> +ci4%'jQHPo!.b-i!#Z%P'H/)@.krqD5Xe:@;cd11@V'4fCi401rc&fpDf0B+BOtXZ>?=`u8ju!, +2D?X-*uP_#!J(6%!8@K8":GhZ+!iEl3'K`&:K:S*A8,q%G'\UgK8,2=MMmCMM26n>JUVohF)G`( +?s?W-8jkj&1+Xao)%m>YJH16$f)U=~> +rr3u;p[RP/h:Brq`4`XbXJV\\QB@/_KReJrGB@kFrGW]qF*2\QI=[*/Nfoa"U6;"P\\H,,db*F< +m.1)Gs+(0$!.b-$!.b-$!.b-&!.Y~> +rr3u;p[RP/h:Brq`4`XbXJV\\QB@/_KReJrGB@kFrGW]qF*2\QI=[*/Nfoa"U6;"P\\H,,db*F< +m.1)Gs+(0$!.b-$!.b-$!.b-&!.Y~> +Zi>THJXj]L(8%:rVPpMrYHY=>[^WfZ]Y;.r_8=+-qS`EO'>hT$^V7@m\[T#SZEUL5Wi2eiTn/_b +SCsDNT:r'XWN32+ZaI9R]Y;2!`Q-*Cbg+P]dF-M@e/HfadEp4bbf\&J`P]O+]t1_^Z`pU6WMcPc +Sq3D_SCa9]~> +rr3u8o^1f!g!\*a^U^\QVkKTGOc+sGIPqY@>?kH??t!PUB5)1"E-$2JI=Qs) +MN*acJXV.YgR9+MQ&pu^Kn4YtF`MA7B4G=T=]SHt:/"8M7RTU1rBVAh6UXC68PDlU;c[%,?t3e` +D/j]DI=R!,N0'?VRXppZRXb~> +rr3u8o^1f!g!\*a^U^\QVkKTGOc+sGI?Y03qH"G7=^#'9?XRATB527$EclVRIY!0. +MirXUJX"oR)j?>@LP(,)G][qBC1^s_>Zk*+:eaSS7n#d362Nkb5sdq-7S-6I:f::r>[LrPBl.g2 +G^FpmLPq7FP_#(NP^i~> +Zi:#qJH16$`W.dr$4msl+=8Tm2*!ie7S6EQ<**4,?!guJ@:EYR?sd2D=]\R"9hIlA4ZP>P.O?2V +'b:Ct!.b.(!#u+L&fDc +s8O,=q!mY0gss`l_Rm4YW1ofJOc+sFI!9XMCM79h@fBaU@:EbZBl%a1G^Y1!N09R"UoLZ-]u/"> +fA>WTo)84\!1JH=JH16$JH16$`;frO)[_,rJH3RfJ,~> +s8O,=q!mY0gss`l_Rm4YW1ofJOc+sFI!9XMCM79h@fBaU@:EbZBl%a1G^Y1!N09R"UoLZ-]u/"> +fA>WTo)84\!0i$7JH16$JH16$`;frO)[_,rJH3RfJ,~> +Zi:&r!.j!XhSn=[hr*GOio9"YjQ5Lck5OQCkk=9?kl0cFk5XNJjQ#:[io/hRJ_kt7fA7D&i8N\U +jQ5OdkNM0qlKdg(mI'N:q"t!eo_J%ZpA4aertG>(mHs9+lK[WtkN:pgjQ#7Yi89+BJ_o#8J,~> +s8P:\p$V#$g!\'_^:1AIUn3p8N.uk0G&_>3A78eL>$4s0=^#';@V'7jEd)n^LQ.LcT;AWp\\H// +eD&sGn+lVU!La">gO\,!gDAMic,df;]=,&LX/MhfSsu+6P`h/jO,j4!(64H;OckrqR%'\@Uo:>t +Za[Q]`5p3SJ_Pb1g=mt.eBZ.P]XG)GV4a9BO,AX@HZjCGH^D1ki8AZT;,C%a:f1D\a7'$-'^<)O +FaAOkMN=!jT;AQl[_0JucIRR$J_Si3J,~> +s8P:\p$V#$g!\'_^:1AIUn3p8N.uk0G&_>3A78eL>$4s0=^#';@V'7jEd)n^LQ.LcT;AWp\\H// +eD&sGn+lVU!L*S8eq)DleecfZaMYd)[^**;Vkg#WR[9;'OcPK\N/R[m(5n-1NK93cQ'[u2TqnTe +YHt[L^r4=AJ_#D'g=@:pd)s;@\?`67Tq%I4N/*"5G]Rb;H'Ykgi88QP:]4#k:KWh$n,E>$l*YYS +G^Y1!N00HsTr4ut\%T]$d=Kl"f%AQ(~> +Zi:&r!.j!XhSe7[hr*GOio9"YjQ,Fak2uX*p?;M=roX7Bs5a4?s5F+=i8B1CJ_o#8'Aqa,io9"Z +jlYail07L!m-X3.p&"ahp&=R_o`+L_qYpNprr2p1r9s%>lg!a!ki_*ijQ#:[iS`UEhLXP9hLG~> +s8O,8o'>Amf$DFR]!ST:TUD"'LOjepE,0&p?!:-%]Yhn> +f\biYo^r.aRK.onJ_S/u("gpYaMbm,\?rKBWi)YdSt2@=R$O$8PSBCKR$jG7T;&-ZWiiV7\@fVq +aN`7dgO\,4gD&;gbf7H0['$@&SX5LtL4O_rEc%`OrrR@;9)V<^9#CT-!W2Qhrt=[MG'eanMij?r +U8Y3#\\H,,d=L&'gXt82~> +s8O,8o'>Amf$DFR]!ST:TUD"'LOjepE,0&p?!:-%]Yhn> +f\biYo^r.aPQ69cJ_%fk(":CJ`5'!o['6X2VPBfTS!fV/P`h4-OoLRWPa7Z(S=ZFLVlHl([(*`` +`5p;Veq)E*eeHTXa2,BrYc=LkR?NYeK78)fDec0Hrs3^=8Ou?A84gYsrrN#irr3VsIrfj_Jr#DM +R%C+QYI2!Y`ls"bJ_&K)J,~> +Zi:&r!.j!XhS\4Mh[8<#iSrkWj5f:_jo+?8kPaQCjo=EAj8\-=i;qloJ_kt7f%q>&i8N\UjQ5Od +kNM0qlg*p)mdKiKrVH?is7Z<])uKU/p%%\Dp\t3imHj3*lKRNqk2tddj5T%UhgsX8hV-fes6bC~> ++92?7nEAibe',eF\$2j,S!K?Q_("RZFRck +cIL_1lLFiGrrJPgJ_Pb1`7l!Zda#tR_S*OhZ`pR3W268_T:VUDr0n@iSXuIIUSXohXg#.?]"Pu# +aj/LhgO\,3gE"hjahtg$Z)a^nR?W_gK78&dD.ogHl,U12s)CLrrB_Mm_<@P-;,q23rVtbqG^Y1" +Ng#m'UoLZ-]Y_b9fn%n/gY:H`s6Y=~> ++92?7nEAibe',eF\$2j,S!K?Q_("RZFRck +cIL_1lLFiGrrJ>aJ_#D'`7>XPc,mr@]t(SWYH4_$UnOEOS=5k5r0J(aR@B_;T:r'YWN<;/[^j)f +`Q?PZeq)E)efE,\`P8siXf%k_QB@)[J9uEXCM'FCl,U.1s):@nrBVGk^uqA*:K(c,rVt_mFaAOk +Mia6pTr4ut\A#o)e:H2%f%\aVs6>+~> +Zi:&r!.j!XhSS.LhuDU9iSrkrj8\3=jn@j8jo=E@j8\*@iS`YOJ_kt7f&$Ga);sK5j5f=akNM0p +lKdg(mdKW7nac8Cs8Vfhoc*Yuo^qbGo'uJSs7,LClg!d"ki_*ijQ#7Yi8B1CJ_o/ +63$lUmH37emRV6PZH%:X/PFA2DYI;-_ +bL5)&kjS??rrJPgJ_Pb1_qQ]qeBZ4W_nWjp[^33@X/`+qUnjc[U7n-Iu+NH9Ap +Ko;(ZS>)sb[Ca8qc..C"J_Su7!rn_#J,~> +63$lUmH37emRV6PZH%:X/PFA2DYI;-_ +bL5)&kjS??rrJ>aJ_#D'_q$?gccX8E^Upt_Z*17/Vl$8aTqJ$LSt2IET:hmPUo(&iXKSn:\@]Mm +`llk_eq)E)ej.d3ahtg#Z)a^nR?NVdJphlaD.d +Zi:&r!.j!XhS@tPhr*GOiSrkrj8J'4jo+9>j8\-=iW%g9hgsX8hUUI%hr*JQj5]4^k3(smlKdg( +mI'H4naZ2Ap&G'dq=aj]s7?<_rpp*h&bPJXm-Es$ki_*ijQ,@\iS`UEhLXP>h?3eJS@sF~> +('".%lf?mPcH!`2Z)XUjQB-iQHDp?2rsE=,69[Lm3B9,_WVc]1dqs#GI"R9;Q_(%TZb"#qd+@.: +mI^)O!La">gO\+rgCr;jd*0SM_Sur&uhn2X/rJ.ZF%'N]YD>&aN`2fJ_Pb1 +f@rRCd`f_G\[/E9TUM+*M1^5#EGT5q>?4TpA,lRA3B&iR2)[BR4?l2(9Me`.s8Sm7G'nmtNg-!* +VQ@)5^W"CEJ_Pb1h:i&3R[Ug<~> +('".%lf?mPcH!`2Z)XUjQB-iQHDp?2rsE=,69[Lm3B9,_WVc]1dqs#GI"R9;Q_(%TZb"#qd+@.: +mI^)O!L*S8eq)Dhee?T\bK%Q;]t:b]ZEUI4Wi2hmVPX9f&uDJ'Vl6VsY->4>\%BAj`5p6SJ_#D' +f@D"lc,[Z4[BHR)S3&^^l*B#l<5!h_2:/c$0s.XCPH%1O+OdDT4 +W33M>_8a`Neq)E.ecYr:PdQ#~> +Zi:&r!.j!XhS7qIhuDX7iVqj8j7_R4j8S'3XooCDGOs7PgIm-Es$l0. +6N?iNlJgRJbf.<)YGe.`PDk3EGI[\QnD+'D?pHe41bpd?1Pu/3Vd!Dmp]&3JEd3(gNKfs.Wj0(K +a3N5kjQlF:pAY/^!.imUgVDPRf@%sfaMbs1]XkV][Bm.;Yl:j*YRRnE[C3TW]YD>&aNMl[J_Pb1 +e_ +6N?iNlJgRJbf.<)YGe.`PDk3EGI[\QnD+'D?pHe41bpd?1Pu/3Vd!Dmp]&3JEd3(gNKfs.Wj0(K +a3N5kjQlF:pAY/X!.i^Pf"fiHd`oqS_n`st\@/cMZ*1;/XT#:"X:;>9Z*LaF\@]Gj_oBjHJ_#D' +e^d%7bJq?.Z`U.!S!B%kK7.ubCh@'Z +Zi:&r!.j!XhS%eGhuDX6iVhd0j8J!:iW%g9hgsX8hU10uhr*JQj5f=akNM0qlg*p)mdKZ8o(2MG +pAb0kq#C0hp`&u$p@\(MoC`.`na>f3m-Es$ki_*ijQ#7Yi89+BJ_o;@!rnd\rn`/~> +6N?fKki(4CbJV!"Xeq_XOGSR9FH2Q'=F'rH=unMq/Lr8"2XpaF4$cA2N;rp4DKUA\MisL%W3Vi_8F75bKeVkJ_Pb1eD!:> +ccEu9[]la+SX5FqKRS/dCh@'Y<)6(Q>6"V0/Li1s-RgMs0JtjS6V1%.s8RO`EHch`MNF-oU8b?( +]Y_b8fR_e.gYgffs-fDUgOK~> +6N?fKki(4CbJV!"Xeq_XOGSR9FH2Q'=F'rH=unMq/Lr8"2XpaF4$cA2N;rp4DKUA\MisL%W3N/)t2F`1u(?!(!!7m^AYs'@6*-mg8h.4d,/3'BSu9SWTII;!M?H@Ud0P*hi; +X0K.I_ogGZeq)E1ecPl9PlB6a~> +Zi:&r!.j!XhRhYEhu;R5iUl./iVqa8hgsX8hTt$uhr*JQj5f=akNM0qlg4$,n*fc9o(2MHp](9h +q"je*q"OLUp%7kRs82 +6N6]HkMOq=ahkWqX/);PNe`.0EGAugu;!%_mCiaoRM3+'rVQI8= +_opK^iT]k0pAY/^!.imUgV)>Ng=FWrbK7fD_ns4(^:jHW%)'Bi^r"".a2uQNeCoK.J_SW-6.F3$ +_7R+XW1f]GO,/C8F`1u(>uslr7R1)Us'$ls+sA*R,UY)r2**rj:?)<'@VB\&H\%!5Pa\;DXgG[U +a3E4egO\, +6N6]HkMOq=ahkWqX/);PNe`.0EGAugu;!%_mCiaoRM3+'rVQI8= +_opK^iT]k0pAY/X!.i^Pf"KWDe^;R^`l5g2^V.7k]".aK%(Ws]]=ktq_SsR?+Hj6p=ZOs&pcq+WqmN,UOun1c[`e:#Z-%?tO7rG^b@)OdDW6WN`hD +_T:/Veq)E2ecu/=Ph+Fcl@8~> +Zi:&r!.j!XhRVMBhu2L,iVh^7hgsX8hTjsuhr*GPioB+]k3(smlKdg(mdKZ8o(2MGp%SLdqYC$b +s7mT.p\+:PqZ$QdnF,f4m-Es$ki_*ijQ#:[iS]:DJ_oAB#O]j,^#&2%eUR~> +6N-TEk2+_9aM>?lWhZ&JNJ2h*DeNQ^;KVs=<%odI*ul1;YQ)fs1,qKf9s=T`C2nKKLQ7[kV6%#8 +_TL9Zi99Y,pAY/^!.imUgUl2Jg=Og"cHF>Na2Z'9r5/l[`Q#s>aihuWe_5T/J_SQ+6.F0"^q-kS +VP'BANJE%1FDYZ!>?+Hj69J6Is&UEd*#]_8*[2p]0fD-\[f?,G@:sFuH%1R-P*qr>XL#IP`m!"b +gO\,=gB[,iR_%8@bgV(~> +6N-TEk2+_9aM>?lWhZ&JNJ2h*DeNQ^;KVs=<%odI*ul1;YQ)fs1,qKf9s=T`C2nKKLQ7[kV6%#8 +_TL9Zi99Y,pAY/X!.i^Pf"9K@e^Dadai;<;_SO%'r4`TS_84",`Q$$Dd+*]uJ_&3!6-mWh]=+oB +U7@R3MM-D&Ebf5n=Aqpa5s&$Fs&L+#O-Z<1W3 +Zi:&r!.j!XhR;;>htH"-hgsX8hTOb@hr*JQj5f:`k3(smlKdg(mdKZ8o(2MHpA"[fqYL*dr;?Hh +qYBp]p\+F_rq>mOn*]T0lg!a!kND!hjQ#7Yi.9a9hW!Atm`h98hX8XY]mp~> +6N$KCjkeS7a2#3iWM5iFMhHM$DJ!6W:icU9:b3q8)&3btec23g/i>aY98roNE,TrLL5hIgUoUf4 +_9('VhrjG(pAY/^!.imUgUPuEg=Oj$cHaYWb0'_*s2kPhbg+M_f%P]0J_SK)6JBo4`kT'iXJMPV +P)P*EH#mh7?s-E&7mT16s8Q%>)&;RK*8J=^P`VEd<.gMisI!V5po3^W"CGJ_Pb1 +j4ak,baQ,tmED$1J,~> +6N$KCjkeS7a2#3iWM5iFMhHM$DJ!6W:icU9:b3q8)&3btec23g/i>aY98roNE,TrLL5hIgUoUf4 +_9('VhrjG(pAY/X!.i^Pf!s98e^Ddeb/q^'`r4cH!c5[B?I%S<].l +Jphf]C1CON;+j>B3DohV;]Q_j',)&s*?lj_1HCj&rf:,d@qfk)I"I07Q(+JFY-bgW`mB:fJ_&l4 +#jTNr[FXWbPdQ#~> +Zi:&r!.j!XhQYl6hgsX8hSn>;hr*JQj5f=akND*olKdg(mdKZ8o(2MHpA"[fqtg3fr;HQjqtg-` +p\Fjhq"!tEn*]T0lg!d"ki_*ijQ#7Yi89+BJ_oJE$1?'.^#&2%SA!0t~> +6MpEAjk\M5a1o-gW1fZCMh?D!D.R$S:NHL8:F[S/'b:Z[nGe1f/2K=R8U:@9J8TOYKoD7cUT1W1 +_8t!ThraA%pAY/^!.imUgU5cGg=b-0e^W'qdF-Oof%8Q*gO\,%gHsTAbf.?,ZE'gnR$*A]Is?!L +AR8J:91;');#gPm)AE_l$kO*j+!iElO8o6F;-.(7CN4TJKo;(\StrBk\%]f(dt-8)gZ7)qmED$1 +g[34R]#oO~> +6MpEAjk\M5a1o-gW1fZCMh?D!D.R$S:NHL8:F[S/'b:Z[nGe1f/2K=R8U:@9J8TOYKoD7cUT1W1 +_8t!ThraA%pAY/X!.i^Pf!X'=e^W'qd*L"]bg"J\dF-Kpeq)Dpej@m2a2,BpY,@t_Q&^ZPI!'@@ +@pE&28OG[#:]LGl)AE\k$kF$i+!`Jqo>MR\?X]Zb!rlc@OPtf&YBglH,9u +f':AA[DdX~> +Zi:&r!.j!XhLXO7hLXPOhB(S4iSrnYjlYail0@R"m-X60nF?)@o_%qQs8Vunr;HR8r;6?dq"k$j +q=F1InF,f4mHa*'l0. +6MpB@jPAA3`kT!eW1fWBMM$7tCh6mQ:2p75:+7>)&dngGr;V$e.l'.O8T4Y/Li.BaKoD7cUT1T0 +_8t!ThraA%pAY/^!.imUgO\+1gO\,JgHsTAbJh3*Z)a[lR$*A]IX#jJA6r>78jkg$:B1>j(_R5] +"q)"Z*@*+fs8SKY:f^k3C2nHHKSttZStrBj\%T`'dXg/(gZ@/smED$1g[34R]%bti~> +6MpB@jPAA3`kT!eW1fWBMM$7tCh6mQ:2p75:+7>)&dngGr;V$e.l'.O8T4Y/Li.BaKoD7cUT1T0 +_8t!ThraA%pAY/X!.i^Peq)D'eq)E@ej@m2`kf6nXf%k^P`CNNHZa4>@U)o/8OGTu:&k5h(D.#Z +"ptnW*$Znbs8SHW:/kG+BPqp=JVT5KR\6R[Zamlkc%4Gsf&bHilH,9uf':AA[FWoZ~> +Zi:&r!.j!XhLXO7hLXPOhEg&WiSrnYjlYail0@R"m-X60nF?)@o_%qQs8Vunr;HWorVZQhq>1*j +q=O:LnaQ#8md9B,lKRNqk2k[aio/hQJ_kt7k2$L6c()K(m`h98hX9NV~> +6N$KBjPAA3`kT!eW1fWBMM$7tCh6mQ:N6@6:FRJ+'+>*Rs8R3d.l'.O8SeA+N,EfeKoD7cUT1W1 +_8t!ThraA&pAY/^!.imUgO\+1gO\,JgHsTAbJh3*Z)a[lR$!;\IX#jJA6r>68jkg$:B1>j(D$oU +!soMS*@#t2s-lAI:f^k3C2eBFKSknYStrBj\%T`&dXg/(gZI5umED$1g[34R]%cW5J,~> +6N$KBjPAA3`kT!eW1fWBMM$7tCh6mQ:N6@6:FRJ+'+>*Rs8R3d.l'.O8SeA+N,EfeKoD7cUT1W1 +_8t!ThraA&pAY/X!.i^Peq)D'eq)E@ej@m2`kf6nXf%k^P`:HMHZa4>@U)o.8OGTu:&k5h((U`S +!soJQ*$Te0s-l>G:/kG+BPhj;JVT5KR\6R[Zamlkc%4Gsf&kNklH,9uf':AA[FXX$J,~> +Zi:&r!.j!XhLXO7hLXPOhB(S4iSrnYjlYail0@R"m-X60nF?)@o_%qQs8Vunr;HR8r;6?frr;if +o^hYDnF,f4mHa*'l0. +6N$KBjk\M5a1o-gWM,cDMhHJ#D.[*U:icU9:b*e3(D.,fq>Ymj/MoOV8oan2L2V6aKoM=eUT:]3 +_9('VhrjG(pAY/^!.imUgO\+1gO\,JgHsTAbJh3*Z)a[lR$*A]IX#jJA6r>78jkj%:]LGk(_R5_ +#Rh=_*d.G]SNT>4:fgq5C2nHHKSttZStrBk\%]f(e:HA*gZR<"mED$1g[34R]%cVrdsq~> +6N$KBjk\M5a1o-gWM,cDMhHJ#D.[*U:icU9:b*e3(D.,fq>Ymj/MoOV8oan2L2V6aKoM=eUT:]3 +_9('VhrjG(pAY/X!.i^Peq)D'eq)E@ej@m2`kf6nXf%k^P`CNNHZa4>@U)o/8OGX!:B1>j(D.&] +#R_4\*H_8[S30,0:/tM-BPqp=JVT5KR\6R\Zb!rlc[jYuf&tTmlH,9uf':AA[FXWbc[Y~> +Zi:&r!.j!XhLXO7hLXPOhB(S4iSrnYjlYahl07L!m-X60nF?)@o_%qQs8Vumqu-F5qtg3hs82N_ +o^hYDnF,f4m-Es%l0. +6N-QDjkeS7a2#3iWhPuHN.l\'DJ*?Z;0)^:;(a7@*#KG,li2to0K)'_9Rd$AIrKU]L5qOiUoUi5 +_TC3Xi90S+pAY/^!.imUgO\+1gO\,JgHsWBbf.?,ZE'gnR$*A]IsH'NARAP;91;'*;#gPm)\s"q% +M9EoDuK^/3'Kc(;-7.8CN4TJKo;+]StrBk\%]f(e:HA*gZ[B$mED$1g[34R]%cVrR_""~> +6N-QDjkeS7a2#3iWhPuHN.l\'DJ*?Z;0)^:;(a7@*#KG,li2to0K)'_9Rd$AIrKU]L5qOiUoUi5 +_TC3Xi90S+pAY/X!.i^Peq)D'eq)E@ej@p3a2,BpY,@t_Q&^ZPI!0FB@pN,38OG[$:]LGl)ANen% +M0JqoANR\?X]Zb!rld"0c!f'(ZolH,9uf':AA[FXWbPdQ#~> +Zi:&r!.j!XhLXO7hLXPOhAbA1iSrnXjQ5OekiqBum-X60nF?)@o_%qPs8W)prVIW3qu6Wmp\+:P +oCDG@n*]T0lg!a!kND!hjQ#7Yi.9a9hWWf+m`h98hX8XY^#&2%SA!0t~> +6N-TFk24e;aMGHnX/)8NNJ;q,E+rcc<-A6@ +6N-TFk24e;aMGHnX/)8NNJ;q,E+rcc<-A6@ +Zi:&r!.j!XhLXO7hLXPOhA+r+i8WbVjQ5OekiqBum-X60nF?)@p](3g!<)Zl&bkb^mHj0(l0. +6N6]IkMY%@b/1ctXJMMSO,/@5Ebf2k +6N6]IkMY%@b/1ctXJMMSO,/@5Ebf2kuMOG\^?G]IV3?Wg<5p\t@\)]TsIp\tl2 +ASZ:1I=mE=QCO\JYI2!YaO5XjJ_',;&*h9$[FXWbPdT*-acs3aepm~> +Zi:&r!.j!XhLXO7hLXPNhA"l*iSrnYjlYail0@U$mI'E2nF?,Es8D`nrV-=(p[@P;m-Es$ki_*i +jQ#7Yi89+BJ_o\K%IVK2^#&2%SA!eIc()KHhLG~> +6N?iMl/C@FbJ_*%Y,@q\Oc"d=F`(i#>'g5K>s1/(0eP%0B`J*95!qn;=RlF!E-HbbN0B^)WN`kG +`m*#gj6H47pAY/^!.imUgO\+1gO\,IgD/5`aMPQsYGe1bQ&gcRI!0IDA6rAGp\t@b+s\J_p\tl5 +BlA-@Jr#GOS"Za_[(F/pcId^&J_TMF%IMB.]%cVrR_%8@baQ-BgOK~> +6N?iMl/C@FbJ_*%Y,@q\Oc"d=F`(i#>'g5K>s1/(0eP%0B`J*95!qn;=RlF!E-HbbN0B^)WN`kG +`m*#gj6H47pAY/X!.i^Peq)D'eq)E?eeQQS_nEL`X/)>RP)P-FH#mh9@U)r@p\t@a+X88\p\tl4 +B5M[6It`fCQ^snOYdV3]ajYglJ_'/<%I2'"[FXWbPdT*-acs44epm~> +Zi:&r!.j!XhLXO7hLXPNhAkG2iSrnYjlYail0@R"m-X60nF?)?oCV_Lp@n=Yq#C*gp)*JpoCMPC +nF,f4m-F!&l0. +63$cOlJp[Lc,RN-Yc4@dP`:EIG]IS2?@MqS@RE=@3&WWO38OVZ84cQUZN'`JF*`CnO-Z?4X0T:O +aNrGnjm;[5rrJPgJ_Pb1J_Pb1J_TVI6.O<'_S!=]Whc2POc+pCG]IV4@9Z`-8OPd)2DQp:.Ocer +.k`Y93^6#(:K:S+Ao)L4IY +63$cOlJp[Lc,RN-Yc4@dP`:EIG]IS2?@MqS@RE=@3&WWO38OVZ84cQUZN'`JF*`CnO-Z?4X0T:O +aNrGnjm;[5rrJ>aJ_#D'J_#D'J_'8?6.!fo^::JMVP'?@NJE+5F`1u(? +Zi:&rIt@$=mXaeWmXafmmf2bUnGi%Yo)J=]o`"O`pAamdq#C0gqYU0gqYp\Es38gdmXP~> +63$lTm,d*UccEr6Z`L!pQ]R&VI"pP8h;eP8hUpH18j5C$5&gH"fp7iEs8T->GCG4'P*qu@XgG^X +b0el!kO/3>rrJReBZ(L\[/?5SsYXuKRS/dCM$pW<)6+T5s74drA5NQ +3BTPp8PW2e?Y*tlGCG4'OdM`:X0T7M`m)ueidpKJmHa-Js3/^SmJkDSmHsqtbl$jY~> +63$lTm,d*UccEr6Z`L!pQ]R&VI"pP8h;eP8hUpH18j5C$5&gH"fp7iEs8T->GCG4'P*qu@XgG^X +b0el!kO/3>rrJ@6Ja.gOJa.gOJa2[g5j.%Wd`]SB\$;p-S +Zi:#3JWPGEJWPGEJWT5[5EhG&SY2dVWi`M3[^j&c_SsR +'`\1+mcWN]d`]SB[]cX'RZredJ<0I)!*9(f"%Ge4nbrJ'qlap\H\%!5Q(4VKYd_?cbgY>+l1"]F +rr?>UO+E@CO+E@CO6qunNerI@Ip0/>CE +4?l/%8ki/_=^>HHBP_[2H$t3sMh-q?O4fSE~> +'`\1+mcWN]d`]SB[]cX'RZredJ<0I)!*9(f"%Ge4nbrJ'qlap\H\%!5Q(4VKYd_?cbgY>+l1"]F +rr?5ROF`LEOF`LEOR8,NO,A[DIX60XDJEcm? +ZiB>X!$Qn[JO4oSJO4rT*Cih%='T0HCiOWFJVK&CQ'n2 +s8P:Xn`o,geBQ"J\[&94SsPRtKS,\YP)bESK7JDu;,'_Y9i"VkPHhTlGA_MKJ;0#GR@g@YZb!uo +cdpq5lh'iLlUh."JP(JcJP(JcJk@%R770@*4ZbY_2)?s?/Lr7t-6j]W*uu:=)&O,)(&\ge'c%Q$ +(`F>6*ZuXO,pt,l/MAh72)dNW4[25"77i&&JP,E(!$LT"J,~> +s8P:Xn`o,geBQ"J\[&94SsPRtKS,\YP)bESK7JDu;,'_Y9i"VkPHhTlGA_MKJ;0#GR@g@YZb!uo +cdpq5lh'iLlZW=PJTlZhJTlZhJp/5VE,96#@UEAC +ZiB>t!$Qo"JR3n6JR3q75%%r9DK9rJJ:r` +s8P:[oBbSqf?h[X]X=rAU77F/M1^5$F)G]'@9m&=<`N*u +s8P:[oBbSqf?h[X]X=rAU77F/M1^5$F)G]'@9m&=<`N*uJ]dH(!$U[NJ,~> +ZiB>l!$QnoJQ@>&JQ@>&4^2?+Bl8!9I"@!.NKTTrSt`*^Xg#1A]"Geo_SjC3`Pf^3_8!Xo\$WEC +WhuM^R[0+sMM6V0G]Re +s8O,=p@%5(g=4Be^q$bPVP'BBNei=:H$!t?BOkOY?2e(L?!^oJAS>n!FaAOjMN=$lTr5'#]>;S6 +e_K3Mnc&1\lXp2?JS0OHJS0OHJnH'8@piSG=B/6p9M.fD69dUo3&WQI0.e\(-mg5cq]d%*,pk#h +.kND/1Gq-Q4?l/#7nHBM;,gV#?!q2oB7X^aB)j(?B7K~> +s8O,=p@%5(g=4Be^q$bPVP'BBNei=:H$!t?BOkOY?2e(L?!^oJAS>n!FaAOjMN=$lTr5'#]>;S6 +e_K3Mnc&1\lb*9AJ\?WJJ\?WJK"X@\[]lg0Tq%I4N/3+8H$+(BB4G7P=&Vpi8k29:5s[b$5s[k+ +7nQHO;c[(.@V9IpFEr=eLQ%@]S"QXZYdf7tJ\CNc!$U[AJ,~> +ZiB@@!6B`@J^f8#J^f8#(Y.*gf\5'7h;7&Iio9"[jlYail0@R#m/HDPmelPRmf2\SlkJdNl0. +rr3u7o'>Dof?qaY]sk5GUn4! +rr3u7o'>Dof?qaY]sk5GUn4!5_PC=^#*=@V'7j +Ecue\Ko;%YS"Z^^ZamlkbgP2%l@J>RmI0EBaQr5D~> +JcC<$JcC<$JcC<$JcC<$[Jta~> +rr5+[p@%5(gXONi_7R+YWM?&PPE(KRJUMfeF)Z#6Chmg&D/XE8G'\RfL5M%VR\-FVYdV3^aj8Pn +j6H.5JcC<$JcC<$JcFO**<#QukMY(Cbf7H/Z`U.!S +rr5+[p@%5(gXONi_7R+YWM?&PPE(KRJUMfeF)Z#6Chmg&D/XE8G'\RfL5M%VR\-FVYdV3^aj8Pn +j6H.5JcC<$JcC<$JcFO**<#QukMY(Cbf7H/Z`U.!S +JcC<$JcC<$JcC<$JcC<$[Jta~> +rVnqTo'GJqf[A!`^q-nUW2#rPP`UfYK7JArG]n4OF)uGGG'J@_JV8i;P4eCrjD +mJ2>5JcC<$JcC<$gAa80na#8lf?qaZ^::JMVP0KFOc5*LJ:)WcFE2>=DJjB3EH?;KI"-d(N00Eq +TVecp\%T]%d+7%5lLFk)s+14%s*t~> +rVnqTo'GJqf[A!`^q-nUW2#rPP`UfYK7JArG]n4OF)uGGG'J@_JV8i;P4eCrjD +mJ2>5JcC<$JcC<$gAa80na#8lf?qaZ^::JMVP0KFOc5*LJ:)WcFE2>=DJjB3EH?;KI"-d(N00Eq +TVecp\%T]%d+7%5lLFk)s+14%s*t~> +JcC<$JcC<$JcC<$JcC<$[Jta~> +rVmi9p@.>,h:Brq`4rjhY,S4hR[0+sMMHk:IsueEHQ.?\IXm$(Ll7=XQ^aYEWirhA^rFUHf\Y]T +o)=4?JcC<$JcC<$gAa54p$V&&gXXWk_S!@_X/;S\Q]mGeLP(,*H[0j[G'8(SH$asiK8,8DP*_]4 +VQ6r0]Y_b8eCrmEmeMG6JcF=$J,~> +rVmi9p@.>,h:Brq`4rjhY,S4hR[0+sMMHk:IsueEHQ.?\IXm$(Ll7=XQ^aYEWirhA^rFUHf\Y]T +o)=4?JcC<$JcC<$gAa54p$V&&gXXWk_S!@_X/;S\Q]mGeLP(,*H[0j[G'8(SH$asiK8,8DP*_]4 +VQ6r0]Y_b8eCrmEmeMG6JcF=$J,~> +JcC<$JcC<$JcC<$JcC<$[Jta~> +r;R]5o^:o$gXXWl`4idhY,\=lS=#S(Nf/aLKnTDW)2*a"M2[LYQC4;jqXX"8hq?K'a2,EsZ)jjtSXGb*NJ`LFK7SN%rdG`7JV&Q1MN*a_R@U(LX0B%E +_8jgKg#(oWo7?pms4.."~> +r;R]5o^:o$gXXWl`4idhY,\=lS=#S(Nf/aLKnTDW)2*a"M2[LYQC4;jqXX"8hq?K'a2,EsZ)jjtSXGb*NJ`LFK7SN%rdG`7JV&Q1MN*a_R@U(LX0B%E +_8jgKg#(oWo7?pms4.."~> +JcC<$JcC<$JcC<$JcC<$[Jta~> +qu7Q0o'GMsg=4Hj`4idhYcF[sT:D:6PE:ibN;\YON/`m\PEqW,TVSNfZ*q6Z`QHQYgYq>_p4<6p +s+13$s+14%ru1n1nET)kf[A!a_7[7^Xf/%gS=#P'O,SsPL])rFLPUhHNffQnS"HISXKf4G_8a^H +fA5KOnG@e:JcF:#J,~> +qu7Q0o'GMsg=4Hj`4idhYcF[sT:D:6PE:ibN;\YON/`m\PEqW,TVSNfZ*q6Z`QHQYgYq>_p4<6p +s+13$s+14%ru1n1nET)kf[A!a_7[7^Xf/%gS=#P'O,SsPL])rFLPUhHNffQnS"HISXKf4G_8a^H +fA5KOnG@e:JcF:#J,~> +JcC<$JcC<$JcC<$JcC<$[Jta~> +qu8SRp@.D0hq?N)ai(s*[]us6VPBcSR[BD+P`q8oPE_?!R%'Y>USk,pZa[T_`QHQXgYh5\o_sFA +JcC<$JcC<$f)HWdp$_/*h:L&ua2,EtZ`^=*U7[sDQB[PoO8Y(UO-#NgQC+/5U8FrnZF@K_`llc\ +gu@PcpjrHrs4%(!~> +qu8SRp@.D0hq?N)ai(s*[]us6VPBcSR[BD+P`q8oPE_?!R%'Y>USk,pZa[T_`QHQXgYh5\o_sFA +JcC<$JcC<$f)HWdp$_/*h:L&ua2,EtZ`^=*U7[sDQB[PoO8Y(UO-#NgQC+/5U8FrnZF@K_`llc\ +gu@PcpjrHrs4%(!~> +JcC<$JcC<$JcC<$JcC<$[Jta~> +qYrDOp$_2,hq?N*b/M30\?rKBWMcPdTUq[CS!oe8S=Q7EU8=cfXg,:E]u%e4d+-n/jm2L:JcC<$ +JcC<$JcF7"2#Hn3ki1CLdEKYJ^:CYVXf81mTUhR?R$X,(Q'Rc(R@Bb?U8=ijYdCsS_T0mJf%f6I +mIl,2JcF4!J,~> +qYrDOp$_2,hq?N*b/M30\?rKBWMcPdTUq[CS!oe8S=Q7EU8=cfXg,:E]u%e4d+-n/jm2L:JcC<$ +JcC<$JcF7"2#Hn3ki1CLdEKYJ^:CYVXf81mTUhR?R$X,(Q'Rc(R@Bb?U8=ijYdCsS_T0mJf%f6I +mIl,2JcF4!J,~> +JcC<$JcC<$JcC<$JcC<$[Jta~> +q>V90p$_2,hqHW-bK%N8]=5/PY,eP"V50mcUApu'Uo(&hXKJh8\@fVqaNW&_gYh2ZnGRq +q>V90p$_2,hqHW-bK%N8]=5/PY,eP"V50mcUApu'Uo(&hXKJh8\@fVqaNW&_gYh2ZnGRq +JcC<$JcC<$JcC<$JcC<$[Jta~> +q#;--o^D),i7li2c,mrA^:Un_Z`pU7XK2<"'s"F +q#;--o^D),i7li2c,mrA^:Un_Z`pU7XK2<"'s"F +JcC<$JcC<$JcC<$JcC<$[Jta~> +p\ts*p$h;0iS<)8d*0SM_S +p\ts*p$h;0iS<)8d*0SM_S +JcC<$JcC<$JcC<$JcC<$[Jta~> +pAZ]Bp$h>2j58SBe'H7[`l,^.^:_(h]",A_]">Yi^r"%1b0A;_fA#6EkjA$AJcC<$JcC<$JcEso +/c##+l/^dWf@/'iaMl$3]t:hb[^ENM['d +pAZ]Bp$h>2j58SBe'H7[`l,^.^:_(h]",A_]">Yi^r"%1b0A;_fA#6EkjA$AJcC<$JcC<$JcEso +/c##+l/^dWf@/'iaMl$3]t:hb[^ENM['d +JcC<$JcC<$JcC<$JcC<$[Jta~> +p&>^)q"!k;jl,%Lf$i!kbK7fD`5BIk_>h@`_o0R8air&XeCWI3io]OppOW?qs+13$s+13ls!mp< +n*9&phV-Q0ccjMO`P]O,^:h4m]Y2"m^VRe*a2uQOdam+-iT9@mpOW?qs3CXp~> +p&>^)q"!k;jl,%Lf$i!kbK7fD`5BIk_>h@`_o0R8air&XeCWI3io]OppOW?qs+13$s+13ls!mp< +n*9&phV-Q0ccjMO`P]O,^:h4m]Y2"m^VRe*a2uQOdam+-iT9@mpOW?qs3CXp~> +JcC<$JcC<$JcC<$JcC<$[Jta~> +oD]F!o^M52jPeqKf[\Htccs\Wb5KEnb0/#RcdC4mf\>9Bjlu1&qLSZts+13$s+13jrt>;'na,K# +iSE5=eBuRcb/hWC`Vm^e`Q#s>b08/Ye(34.i8j+gnG7_9JcF!pJ,~> +oD]F!o^M52jPeqKf[\Htccs\Wb5KEnb0/#RcdC4mf\>9Bjlu1&qLSZts+13$s+13jrt>;'na,K# +iSE5=eBuRcb/hWC`Vm^e`Q#s>b08/Ye(34.i8j+gnG7_9JcF!pJ,~> +JcC<$JcC<$JcC<$JcC<$[Jta~> +o)B:#q"!n>kiLg]gtLE3e^MsprQu/(e'uq"f\>6?ioTFknFup5s+13$s+13$s2G!MrqQ*Rlf[?f +hV6]6eC)^icHZ=3&C&MHd*gCof\>6?jQGjso_j@@JcEsoJ,~> +o)B:#q"!n>kiLg]gtLE3e^MsprQu/(e'uq"f\>6?ioTFknFup5s+13$s+13$s2G!MrqQ*Rlf[?f +hV6]6eC)^icHZ=3&C&MHd*gCof\>6?jQGjso_j@@JcEsoJ,~> +JcC<$JcC<$JcC<$JcC<$[Jta~> +nG`srp[[e>l/q'di838DgABP3g=tH?i8Wh\lKn$6rIP!"s+13$s+13drt#)&o'Yi-jl54SgY(61 +e^aTI%Fs%^g"Y??ioT@hmdp=,s+13ms*t~> +nG`srp[[e>l/q'di838DgABP3g=tH?i8Wh\lKn$6rIP!"s+13$s+13drt#)&o'Yi-jl54SgY(61 +e^aTI%Fs%^g"Y??ioT@hmdp=,s+13ms*t~> +JcC<$JcC<$JcC<$JcC<$[Jta~> +mf+:*p[dnBlfmTojQ#7YiS`YQiSrnYk32*smd^&MJcC<$JcC<$JcEF`%/onjn*K<%jl>=WhVJ(a +$f0[siT'%_l0Rm1qgncus2b4j~> +mf+:*p[dnBlfmTojQ#7YiS`YQiSrnYk32*smd^&MJcC<$JcC<$JcEF`%/onjn*K<%jl>=WhVJ(a +$f0[siT'%_l0Rm1qgncus2b4j~> +JcC<$JcC<$JcC<$JcC<$[Jta~> +m/IFlqtK^Pn*TK-lKS<3s60gSlg4$-nb;q[JcC<$JcC<$JcE:\)ZBI&na>c/l0%3jjQ,@]jQ,Fb +kNM3tmd^#JJcC<$`rCP~> +m/IFlqtK^Pn*TK-lKS<3s60gSlg4$-nb;q[JcC<$JcC<$JcE:\)ZBI&na>c/l0%3jjQ,@]jQ,Fb +kNM3tmd^#JJcC<$`rCP~> +JcC<$JcC<$JcC<$JcC<$[Jta~> +kl1kbq=jOPnaZSI"nD0VpA+Z +kl1kbq=jOPnaZSI"nD0VpA+Z +JcC<$JcC<$JcC<$JcC<$[Jta~> +j8]/YrquirJcC<$JcC<$JcD_L%fZD'q=jUToCV_Mq"jmdJcC<$_#Jo~> +j8]/YrquirJcC<$JcC<$JcD_L%fZD'q=jUToCV_Mq"jmdJcC<$_#Jo~> +%%EndData +showpage +%%Trailer +end +%%EOF diff --git a/tags/lttv-0.11.3-23102008/doc/user/user_guide/docbook/lttv-numbered-5.png b/tags/lttv-0.11.3-23102008/doc/user/user_guide/docbook/lttv-numbered-5.png new file mode 100644 index 00000000..0f2e986c Binary files /dev/null and b/tags/lttv-0.11.3-23102008/doc/user/user_guide/docbook/lttv-numbered-5.png differ diff --git a/tags/lttv-0.11.3-23102008/doc/user/user_guide/docbook/user_guide.docbook b/tags/lttv-0.11.3-23102008/doc/user/user_guide/docbook/user_guide.docbook new file mode 100644 index 00000000..ed9c960d --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/user/user_guide/docbook/user_guide.docbook @@ -0,0 +1,571 @@ + + + + + + + +Linux Trace Toolkit Viewer User Guide + + +Mathieu +Desnoyers + + + +11/01/2006 +1.00.02 + + + +This document describes how to install Linux Trace +Toolkit Viewer and how to use it. + + + + + +Linux Trace Toolkit Viewer +Linux Trace Toolkit +tracing +Linux +visualization +operating system + + + + + +Introduction + +Linux Trace Toolkit (LTT) is a tracing tool that permits to get all the possible +execution information from the Linux Kernel. It is based on kernel +instrumentation and a high-speed relay file system to copy the information from +the kernel space to the user space. + + + +Linux Trace Toolkit Viewer (LTTV) is the second generation of visualization +tool. It is based on a trace format (the files where the data is recorded on +disk) written by the LTTng tracer. + + + +This document explains all the steps that are necessary in order to record a +trace with LTT and view it with LTTV. + + + + +Getting started + + +Installing LTTng and LTTV + +Follow the QUICKSTART guide found at +ltt.polymtl.ca. + + + + +At this point, LTTV is installed in the default directory. You may find the +lttv executable in /usr/local/bin and the librairies in /usr/local/lib. You will +also notice the presence of the convert executable in /usr/local/bin. This tool +will be used later to convert from the Linux Trace Toolkit trace format to the +LTTV format. + + + + + +Running the executable with basic libraries + +Starting the graphical mode with the basic viewer activated is as simple as : + + +$ lttv-gui + + +Using the text mode is very simple too. Look in /usr/local/lib/lttv/plugins for +the list of modules. You may use the --help switch to get basic help on the +command line parameters of every loaded modules. To simply output the events of +a trace in a text file, try the textDump module. The batchAnalysis module +permits to do batch mode analysis (state and statistics calculation ) on a +trace. + + +$ lttv -L /usr/local/lib/lttv/plugins -m textDump --help + + + + + +Using LTTV graphical interface + + +LTTV main window + +This section describes the main functionnalities that are provided by the LTTV +GUI and how to use them. + + +By default, when the lttv GUI starts with all the graphical modules loaded, +it loads the statistics viewer, the control flow viewer, and the detailed event +list inside a tab. Other viewers can be added later to this tab by interacting +with the main window. Let's describe the operations available on the window : + + + + + + + + + + +Linux Trace Toolkit Viewer GUI + + + + + +This toolbar allows you to navigate through the basic functionnalities of LTTV. +The first button opens a new window and the second one, a new tab. You can leave +your mouse over the buttons to read the information provided by the tooltips. + + + + +This notebook, containing different tabs, lets you select the "Trace Set" you +want to interact with. A trace set is an aggregation of traces, synchronised in +time. You may also want to use one tab per viewer by simply cloning the traceset +to a new tab. This way, you can have vertically stacked viewers in one tab, as +well as different viewers, independant from the time interval. Note that once +the Trace Set cloning is done, each trace set becomes completely independant. +For Traceset cloning, see the File Menu. + + + + +These buttons let you control the computation in progress on a trace. As +sometimes the computation may last for a while, you may want to stop it, restart +it from the beginning or simply to continue from where you stopped. This is +exactly what those three buttons offer you. + + + + +Buttons on the right side of the last spacer are semantically different from the +others. While the other buttons at the left side of the bar are built in the +lttv program and let you operate the basic functionnalities, the buttons at the +right side let you add a viewer to the active Tab. They belong to the +viewers themselves. The number of buttons that appears there should directly +depend on the number of viewer's modules loaded. + + + + +This is a tree representing the multiple statistics available for the current +traceset. This is shown by the guistatistics viewer. + + + + +This is the Y axis of the guicontrolflow viewer. It shows the process list of +the traced system. You may notice that it grows : it dynamically adds +process when they appear in the trace. + + + + +This is a (missing) time bar for the X axis. Maybe will it be used for viewer +specific buttons eventually. Work in progress. + + + + +The is the current time selected. The concept of current event and current time +selected is synchronised in a Tab for all the viewers. The control flow viewer +shows it a vertical white dotted line. You move this marker by clicking on the +background of the process state graph. This graph shows evolution of each +process's state through time. The meaning of the colors will be explained later. + + + + +This is the details event list. It shown the detailed information about each +event of the trace. It is synchronised with the current time and current event, +so selecting an event changes other viewer's current time and reciprocally. + + + + +You can enter the values of start time and end time you wish to see on the +screen here. It also supports pasting time as text input, simply by clicking of +the "Time Frame", "start" or "end:" fields. A valid entry consists of any +digital input separated by any quantity of non digital characters. For example : +"I start at 356247.124626 and stop at 724524.453455" would be a valid input +for the "Time Frame" field. + + + + +This horizontal scrollbar modifies the window of time shown by all the viewers +in the tab. It is linked with the fields below it (described at number 10 and +12). Another way to modify the time shown is to use the zoom buttons of the +toolbar (yes, the ones that looks like magnifying glasses). + + + + +This field works just like the "Time Frame" field. It modifies the current time +selected by the viewers. For example, changing its value will change the current +event selected by the detailed events list and the current time selected by the +control flow viewer. + + + + + + +Control Flow View Colors + + + + + + + + + +Control Flow View Color Legend + + + + +Here is a description of the colors used in the control flow view. Each color +represents a state of the process at a given time. + + + + + +White : this color is used for process from which state is not known. It may +happen when you seek quickly at a far time in the trace just after it has been +launched. At that moment, the precomputed state information is incomplete. The +"unknown" state is used to identify this. Note that the viewer gets refreshed +once the precomputation ends. + + + + +Green : This color is only used for process when they are running in user mode. +That includes execution of all the source code of an executable as well as the +libraries it uses. + + + + +Pale blue : A process is doing a system call to the kernel, and the mode is +switched from process limited rights to super user mode. Only code from the +kernel (including modules) should be run in that state. + + + + +Yellow : The kernel is running a trap that services a fault. The most frequent +trap is the memory page fault trap : it is called every time a page is missing +from physical memory. + + + + +Orange : IRQ servicing routine is running. It interrupts the currently running +process. As the IRQ does not change the currently running process (on some +architectures it uses the same stack as the process), the IRQ state is shown in +the state of the process. IRQ can be nested : a higher priority interrupt can +interrupt a lower priority interrupt. + + + + +Pink : SoftIRQ handler is running. A SoftIRQ is normally triggered by an +interrupt that whishes to have some work done very soon, but not "now". This is +especially useful, for example, to have the longest part of the network stack +traversal done : a too long computation in the interrupt handler would increase +the latency of the system. Therefore, doing the long part of the computation in +a softirq that will be run just after the IRQ handler exits will permits to do +this work while interrupts are enabled, without increasing the system latency. + + + + +Dark red : A process in that state is waiting for an input/output operation to +complete before it can continue its execution. + + + + +Dark yellow : A process is ready to run, but waiting to get the CPU (a schedule +in event). + + + + +Dark purple : A process in zombie state. This state happens when a process +exits and then waits for the parent to wait for it (wait() or waitpid()). + + + + +Dark green : A process has just been created by its parent and is waiting for +first scheduling. + + + + +Magenta : The process has exited, but still has the control of the CPU. It may +happend if it has some tasks to do in the exit system call. + + + + + + + +Using LTTV text modules + +The batch analysis module + +This batch analysis module can be invoked like this : + + +$ lttv -L path/to/lib/plugins -m batchAnalysis\ +-t trace1 -t trace2 ... + + +It permits to call any registered action to perform in batch mode on all the +trace set, which consists of the traces loaded on the command line. Actions that +are built in the batchAnalysis module are statistics computation. They can be +triggered by using the -s (--stats) switch. + + +However, the batchAnalysis module is mostly a backend for every other text +module that does batch computation over a complete trace set. + + + +The text dump module + + The goal of this module is to convert the binary data of the traces into +a formatted text file. + + +The text dump module is a good example of a usage of the batch analysis module +backend. In fact, the text dump module depends on it. You don't need to +explicitly load the batchAnalysis module though, as lttv offers a rich module +backend that deals with the dependencies, loading the module automatically if +needed. + + +The text dump module is invoked just like the batchAnalysis module. It adds more +options that can be specified in argument. You may specify the -o switch for the +output file name of the text dump. You can enable the output of the field names +(the identifier of the fields) with the -l switch. The -s switch, for process +states, is very useful to indicate the state in which the process is when the +event happens. + + +If you use the --help option on the textDump module, you will see all the detail +about the switches that can be used to show per cpu statistics and per process +statistics. You will notice that you can use both the switches for the +batchAnalysis module and those for textDump. You will also notice that the +options --process_state (from textDump) and --stats (from batchAnalysis) has the +same short name "-s". If you choose to invoke this option using the short name, +it will use the option of the last module loaded just before the -s switch. + + +For exemple, if you load the textDump module with -m textDump, it will first +load the batchAnalysis module, and then load itself. As it is the last module +loaded, the -s switch used after it will signify --process_stats. On the other +hand, if you choose to specify explicitly the loading of both modules like this +: + + +$ lttv -L path/to/lib/plugins -m batchAnalysis -s\ +-m textDump -s -t trace + + +The first "-s" will invoke batchAnalysis --stats and the second "-s" will invoke +textDump --process_state. The list of options generated by --help follows the +order of registration of the options by the modules, therefore the invocation +order of the modules. + + + + + + + diff --git a/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/Makefile.am b/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/Makefile.am new file mode 100644 index 00000000..f7ae2391 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = lttv-color-list.png lttv-numbered-5.png c115.html c20.html c25.html c42.html index.html x125.html x32.html x81.html diff --git a/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/c115.html b/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/c115.html new file mode 100644 index 00000000..b2f471fd --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/c115.html @@ -0,0 +1,169 @@ + +Using LTTV text modules

        Linux Trace Toolkit Viewer User Guide
        PrevNext

        Chapter 4. Using LTTV text modules

        4.1. The batch analysis module

        This batch analysis module can be invoked like this : +

        
$ lttv -L path/to/lib/plugins -m batchAnalysis\
        +-t trace1 -t trace2 ...
        +

        It permits to call any registered action to perform in batch mode on all the +trace set, which consists of the traces loaded on the command line. Actions that +are built in the batchAnalysis module are statistics computation. They can be +triggered by using the -s (--stats) switch. +

        However, the batchAnalysis module is mostly a backend for every other text +module that does batch computation over a complete trace set. +


        PrevHomeNext
        Control Flow View Colors The text dump module
        \ No newline at end of file diff --git a/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/c20.html b/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/c20.html new file mode 100644 index 00000000..312abbcd --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/c20.html @@ -0,0 +1,151 @@ + +Introduction
        Linux Trace Toolkit Viewer User Guide
        PrevNext

        Chapter 1. Introduction

        Linux Trace Toolkit (LTT) is a tracing tool that permits to get all the possible +execution information from the Linux Kernel. It is based on kernel +instrumentation and a high-speed relay file system to copy the information from +the kernel space to the user space. +

        Linux Trace Toolkit Viewer (LTTV) is the second generation of visualization +tool. It is based on a trace format (the files where the data is recorded on +disk) written by the LTTng tracer. +

        This document explains all the steps that are necessary in order to record a +trace with LTT and view it with LTTV. +


        PrevHomeNext
        Linux Trace Toolkit Viewer User Guide Getting started
        \ No newline at end of file diff --git a/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/c25.html b/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/c25.html new file mode 100644 index 00000000..8c36ae80 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/c25.html @@ -0,0 +1,160 @@ + +Getting started
        Linux Trace Toolkit Viewer User Guide
        PrevNext

        Chapter 2. Getting started

        2.1. Installing LTTng and LTTV

        Follow the QUICKSTART guide found at +ltt.polymtl.ca. +

        At this point, LTTV is installed in the default directory. You may find the +lttv executable in /usr/local/bin and the librairies in /usr/local/lib. You will +also notice the presence of the convert executable in /usr/local/bin. This tool +will be used later to convert from the Linux Trace Toolkit trace format to the +LTTV format. +


        PrevHomeNext
        Introduction Running the executable with basic libraries
        \ No newline at end of file diff --git a/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/c42.html b/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/c42.html new file mode 100644 index 00000000..c11c9152 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/c42.html @@ -0,0 +1,269 @@ + +Using LTTV graphical interface
        Linux Trace Toolkit Viewer User Guide
        PrevNext

        Chapter 3. Using LTTV graphical interface

        3.1. LTTV main window

        This section describes the main functionnalities that are provided by the LTTV +GUI and how to use them. +

        By default, when the lttv GUI starts with all the graphical modules loaded, +it loads the statistics viewer, the control flow viewer, and the detailed event +list inside a tab. Other viewers can be added later to this tab by interacting +with the main window. Let's describe the operations available on the window : +

        Linux Trace Toolkit Viewer GUI

        1. This toolbar allows you to navigate through the basic functionnalities of LTTV. +The first button opens a new window and the second one, a new tab. You can leave +your mouse over the buttons to read the information provided by the tooltips. +

        2. This notebook, containing different tabs, lets you select the "Trace Set" you +want to interact with. A trace set is an aggregation of traces, synchronised in +time. You may also want to use one tab per viewer by simply cloning the traceset +to a new tab. This way, you can have vertically stacked viewers in one tab, as +well as different viewers, independant from the time interval. Note that once +the Trace Set cloning is done, each trace set becomes completely independant. +For Traceset cloning, see the File Menu. +

        3. These buttons let you control the computation in progress on a trace. As +sometimes the computation may last for a while, you may want to stop it, restart +it from the beginning or simply to continue from where you stopped. This is +exactly what those three buttons offer you. +

        4. Buttons on the right side of the last spacer are semantically different from the +others. While the other buttons at the left side of the bar are built in the +lttv program and let you operate the basic functionnalities, the buttons at the +right side let you add a viewer to the active Tab. They belong to the +viewers themselves. The number of buttons that appears there should directly +depend on the number of viewer's modules loaded. +

        5. This is a tree representing the multiple statistics available for the current +traceset. This is shown by the guistatistics viewer. +

        6. This is the Y axis of the guicontrolflow viewer. It shows the process list of +the traced system. You may notice that it grows : it dynamically adds +process when they appear in the trace. +

        7. This is a (missing) time bar for the X axis. Maybe will it be used for viewer +specific buttons eventually. Work in progress. +

        8. The is the current time selected. The concept of current event and current time +selected is synchronised in a Tab for all the viewers. The control flow viewer +shows it a vertical white dotted line. You move this marker by clicking on the +background of the process state graph. This graph shows evolution of each +process's state through time. The meaning of the colors will be explained later. +

        9. This is the details event list. It shown the detailed information about each +event of the trace. It is synchronised with the current time and current event, +so selecting an event changes other viewer's current time and reciprocally. +

        10. You can enter the values of start time and end time you wish to see on the +screen here. It also supports pasting time as text input, simply by clicking of +the "Time Frame", "start" or "end:" fields. A valid entry consists of any +digital input separated by any quantity of non digital characters. For example : +"I start at 356247.124626 and stop at 724524.453455" would be a valid input +for the "Time Frame" field. +

        11. This horizontal scrollbar modifies the window of time shown by all the viewers +in the tab. It is linked with the fields below it (described at number 10 and +12). Another way to modify the time shown is to use the zoom buttons of the +toolbar (yes, the ones that looks like magnifying glasses). +

        12. This field works just like the "Time Frame" field. It modifies the current time +selected by the viewers. For example, changing its value will change the current +event selected by the detailed events list and the current time selected by the +control flow viewer. +


        PrevHomeNext
        Running the executable with basic libraries Control Flow View Colors
        \ No newline at end of file diff --git a/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/index.html b/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/index.html new file mode 100644 index 00000000..1661e62a --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/index.html @@ -0,0 +1,195 @@ + +Linux Trace Toolkit Viewer User Guide

          Next
          Introduction
        \ No newline at end of file diff --git a/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/lttv-color-list.png b/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/lttv-color-list.png new file mode 100644 index 00000000..98abddbe Binary files /dev/null and b/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/lttv-color-list.png differ diff --git a/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/lttv-numbered-5.png b/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/lttv-numbered-5.png new file mode 100644 index 00000000..0f2e986c Binary files /dev/null and b/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/lttv-numbered-5.png differ diff --git a/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/x125.html b/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/x125.html new file mode 100644 index 00000000..864f17d7 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/x125.html @@ -0,0 +1,185 @@ + +The text dump module
        Linux Trace Toolkit Viewer User Guide
        PrevChapter 4. Using LTTV text modules 

        4.2. The text dump module

        The goal of this module is to convert the binary data of the traces into +a formatted text file. +

        The text dump module is a good example of a usage of the batch analysis module +backend. In fact, the text dump module depends on it. You don't need to +explicitly load the batchAnalysis module though, as lttv offers a rich module +backend that deals with the dependencies, loading the module automatically if +needed. +

        The text dump module is invoked just like the batchAnalysis module. It adds more +options that can be specified in argument. You may specify the -o switch for the +output file name of the text dump. You can enable the output of the field names +(the identifier of the fields) with the -l switch. The -s switch, for process +states, is very useful to indicate the state in which the process is when the +event happens. +

        If you use the --help option on the textDump module, you will see all the detail +about the switches that can be used to show per cpu statistics and per process +statistics. You will notice that you can use both the switches for the +batchAnalysis module and those for textDump. You will also notice that the +options --process_state (from textDump) and --stats (from batchAnalysis) has the +same short name "-s". If you choose to invoke this option using the short name, +it will use the option of the last module loaded just before the -s switch. +

        For exemple, if you load the textDump module with -m textDump, it will first +load the batchAnalysis module, and then load itself. As it is the last module +loaded, the -s switch used after it will signify --process_stats. On the other +hand, if you choose to specify explicitly the loading of both modules like this +: +

        
$ lttv -L path/to/lib/plugins -m batchAnalysis -s\
        +-m textDump -s -t trace
        +

        The first "-s" will invoke batchAnalysis --stats and the second "-s" will invoke +textDump --process_state. The list of options generated by --help follows the +order of registration of the options by the modules, therefore the invocation +order of the modules. +


        PrevHome 
        Using LTTV text modulesUp 
        \ No newline at end of file diff --git a/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/x32.html b/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/x32.html new file mode 100644 index 00000000..174e8326 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/x32.html @@ -0,0 +1,175 @@ + +Running the executable with basic libraries
        Linux Trace Toolkit Viewer User Guide
        PrevChapter 2. Getting startedNext

        2.2. Running the executable with basic libraries

        Starting the graphical mode with the basic viewer activated is as simple as : +

        
$ lttv-gui
        +

        Using the text mode is very simple too. Look in /usr/local/lib/lttv/plugins for +the list of modules. You may use the --help switch to get basic help on the +command line parameters of every loaded modules. To simply output the events of +a trace in a text file, try the textDump module. The batchAnalysis module +permits to do batch mode analysis (state and statistics calculation ) on a +trace. +

        
$ lttv -L /usr/local/lib/lttv/plugins -m textDump --help
        +

        PrevHomeNext
        Getting startedUpUsing LTTV graphical interface
        \ No newline at end of file diff --git a/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/x81.html b/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/x81.html new file mode 100644 index 00000000..b2fd6878 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doc/user/user_guide/html/x81.html @@ -0,0 +1,244 @@ + +Control Flow View Colors
        Linux Trace Toolkit Viewer User Guide
        PrevChapter 3. Using LTTV graphical interfaceNext

        3.2. Control Flow View Colors

        Control Flow View Color Legend

        Here is a description of the colors used in the control flow view. Each color +represents a state of the process at a given time. +

        • White : this color is used for process from which state is not known. It may +happen when you seek quickly at a far time in the trace just after it has been +launched. At that moment, the precomputed state information is incomplete. The +"unknown" state is used to identify this. Note that the viewer gets refreshed +once the precomputation ends. +

        • Green : This color is only used for process when they are running in user mode. +That includes execution of all the source code of an executable as well as the +libraries it uses. +

        • Pale blue : A process is doing a system call to the kernel, and the mode is +switched from process limited rights to super user mode. Only code from the +kernel (including modules) should be run in that state. +

        • Yellow : The kernel is running a trap that services a fault. The most frequent +trap is the memory page fault trap : it is called every time a page is missing +from physical memory. +

        • Orange : IRQ servicing routine is running. It interrupts the currently running +process. As the IRQ does not change the currently running process (on some +architectures it uses the same stack as the process), the IRQ state is shown in +the state of the process. IRQ can be nested : a higher priority interrupt can +interrupt a lower priority interrupt. +

        • Pink : SoftIRQ handler is running. A SoftIRQ is normally triggered by an +interrupt that whishes to have some work done very soon, but not "now". This is +especially useful, for example, to have the longest part of the network stack +traversal done : a too long computation in the interrupt handler would increase +the latency of the system. Therefore, doing the long part of the computation in +a softirq that will be run just after the IRQ handler exits will permits to do +this work while interrupts are enabled, without increasing the system latency. +

        • Dark red : A process in that state is waiting for an input/output operation to +complete before it can continue its execution. +

        • Dark yellow : A process is ready to run, but waiting to get the CPU (a schedule +in event). +

        • Dark purple : A process in zombie state. This state happens when a process +exits and then waits for the parent to wait for it (wait() or waitpid()). +

        • Dark green : A process has just been created by its parent and is waiting for +first scheduling. +

        • Magenta : The process has exited, but still has the control of the CPU. It may +happend if it has some tasks to do in the exit system call. +


        PrevHomeNext
        Using LTTV graphical interfaceUpUsing LTTV text modules
        \ No newline at end of file diff --git a/tags/lttv-0.11.3-23102008/doc/user/user_guide/user_guide.dvi b/tags/lttv-0.11.3-23102008/doc/user/user_guide/user_guide.dvi new file mode 100644 index 00000000..4f20bf47 Binary files /dev/null and b/tags/lttv-0.11.3-23102008/doc/user/user_guide/user_guide.dvi differ diff --git a/tags/lttv-0.11.3-23102008/doxyfile b/tags/lttv-0.11.3-23102008/doxyfile new file mode 100644 index 00000000..9aa6785c --- /dev/null +++ b/tags/lttv-0.11.3-23102008/doxyfile @@ -0,0 +1,1219 @@ +# Doxyfile 1.4.2 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = Linux Trace Toolkit Viewer + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 0.4 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc/doxygen + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = YES + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, +# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, +# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, +# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, +# Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = NO + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = YES + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources +# only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. + +SHOW_DIRECTORIES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the progam writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = ./ltt/ ./lttv/ ./lttd/ +#INPUT = ./lttv/lttv/filter.c ./lttv/lttv/filter.h ./lttv/modules/gui/filter/filter.c ./lttv/modules/text/textFilter.c + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm + +FILE_PATTERNS = *.c *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = ./lttv/modules/examples/ + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 1 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = YES + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = YES + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = YES + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that a graph may be further truncated if the graph's +# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH +# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), +# the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/tags/lttv-0.11.3-23102008/ltt/Makefile.am b/tags/lttv-0.11.3-23102008/ltt/Makefile.am new file mode 100644 index 00000000..bb99f331 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/ltt/Makefile.am @@ -0,0 +1,25 @@ +# +# Makefile for LTT New generation user interface : plugins. +# +# Created by Mathieu Desnoyers on May 6, 2003 +# + +#libdir = ${lttlibdir} + +AM_CFLAGS = $(GLIB_CFLAGS) +LIBS += $(GLIB_LIBS) + +lib_LTLIBRARIES = liblttvtraceread.la +liblttvtraceread_la_SOURCES = tracefile.c marker.c event.c +noinst_HEADERS = ltt-private.h + +lttinclude_HEADERS = \ + compiler.h\ + marker.h\ + marker-field.h\ + ltt.h\ + time.h\ + trace.h\ + event.h\ + marker-desc.h\ + ltt-types.h diff --git a/tags/lttv-0.11.3-23102008/ltt/compiler.h b/tags/lttv-0.11.3-23102008/ltt/compiler.h new file mode 100644 index 00000000..2b496a88 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/ltt/compiler.h @@ -0,0 +1,53 @@ +/* This file is part of the Linux Trace Toolkit trace reading library + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License Version 2.1 as published by the Free Software Foundation. + * + * 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. + */ + +#ifndef COMPILER_H +#define COMPILER_H + +/* Fast prediction if likely branches */ +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) + + +/* + * Check at compile time that something is of a particular type. + * Always evaluates to 1 so you may use it easily in comparisons. + */ +#define typecheck(type,x) \ +({ type __dummy; \ + typeof(x) __dummy2; \ + (void)(&__dummy == &__dummy2); \ + 1; \ +}) + +/* Deal with 32 wrap correctly */ +#define guint32_after(a,b) \ + (typecheck(guint32, a) && \ + typecheck(guint32, b) && \ + ((gint32)(b) - (gint32)(a) < 0)) +#define guint32_before(a,b) guint32_after(b,a) + +#define guint32_after_eq(a,b) \ + (typecheck(guint32, a) && \ + typecheck(guint32, b) && \ + ((gint32)(b) - (gint32)(a) <= 0)) +#define guint32_before_eq(a,b) guint32_after_eq(b,a) + +#define __EXPORT __attribute__ ((visibility ("default"))) + +#endif //COMPILER_H diff --git a/tags/lttv-0.11.3-23102008/ltt/event.c b/tags/lttv-0.11.3-23102008/ltt/event.c new file mode 100644 index 00000000..42a62318 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/ltt/event.c @@ -0,0 +1,334 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2007 Mathieu Desnoyers + * + * Complete rewrite from the original version made by XangXiu Yang. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License Version 2.1 as published by the Free Software Foundation. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include +#include +#include + +/***************************************************************************** + *Function name + * ltt_event_position_get : get the event position data + *Input params + * e : an instance of an event type + * ep : a pointer to event's position structure + * tf : tracefile pointer + * block : current block + * offset : current offset + * tsc : current tsc + ****************************************************************************/ +void ltt_event_position_get(LttEventPosition *ep, LttTracefile **tf, + guint *block, guint *offset, guint64 *tsc) +{ + *tf = ep->tracefile; + *block = ep->block; + *offset = ep->offset; + *tsc = ep->tsc; +} + + +void ltt_event_position_set(LttEventPosition *ep, LttTracefile *tf, + guint block, guint offset, guint64 tsc) +{ + ep->tracefile = tf; + ep->block = block; + ep->offset = offset; + ep->tsc = tsc; +} + + +/***************************************************************************** + *Function name + * ltt_event_position : get the event's position + *Input params + * e : an instance of an event type + * ep : a pointer to event's position structure + ****************************************************************************/ + +void ltt_event_position(LttEvent *e, LttEventPosition *ep) +{ + ep->tracefile = e->tracefile; + ep->block = e->block; + ep->offset = e->offset; + ep->tsc = e->tsc; +} + +LttEventPosition * ltt_event_position_new() +{ + return g_new(LttEventPosition, 1); +} + + +/***************************************************************************** + * Function name + * ltt_event_position_compare : compare two positions + * A NULL value is infinite. + * Input params + * ep1 : a pointer to event's position structure + * ep2 : a pointer to event's position structure + * Return + * -1 is ep1 < ep2 + * 1 if ep1 > ep2 + * 0 if ep1 == ep2 + ****************************************************************************/ + + +gint ltt_event_position_compare(const LttEventPosition *ep1, + const LttEventPosition *ep2) +{ + if(ep1 == NULL && ep2 == NULL) + return 0; + if(ep1 != NULL && ep2 == NULL) + return -1; + if(ep1 == NULL && ep2 != NULL) + return 1; + + if(ep1->tracefile != ep2->tracefile) + g_error("ltt_event_position_compare on different tracefiles makes no sense"); + + if(ep1->block < ep2->block) + return -1; + if(ep1->block > ep2->block) + return 1; + if(ep1->offset < ep2->offset) + return -1; + if(ep1->offset > ep2->offset) + return 1; + return 0; +} + +/***************************************************************************** + * Function name + * ltt_event_position_copy : copy position + * Input params + * src : a pointer to event's position structure source + * dest : a pointer to event's position structure dest + * Return + * void + ****************************************************************************/ +void ltt_event_position_copy(LttEventPosition *dest, + const LttEventPosition *src) +{ + if(src == NULL) + dest = NULL; + else + *dest = *src; +} + + + +LttTracefile *ltt_event_position_tracefile(LttEventPosition *ep) +{ + return ep->tracefile; +} + +/***************************************************************************** + * These functions extract data from an event after architecture specific + * conversions + ****************************************************************************/ +guint32 ltt_event_get_unsigned(LttEvent *e, struct marker_field *f) +{ + gboolean reverse_byte_order; + + if(unlikely(f->attributes & LTT_ATTRIBUTE_NETWORK_BYTE_ORDER)) { + reverse_byte_order = (g_ntohs(0x1) != 0x1); + } else { + reverse_byte_order = LTT_GET_BO(e->tracefile); + } + + switch(f->size) { + case 1: + { + guint8 x = *(guint8 *)(e->data + f->offset); + return (guint32) x; + } + break; + case 2: + return (guint32)ltt_get_uint16(reverse_byte_order, e->data + f->offset); + break; + case 4: + return (guint32)ltt_get_uint32(reverse_byte_order, e->data + f->offset); + break; + case 8: + default: + g_critical("ltt_event_get_unsigned : field size %i unknown", f->size); + return 0; + break; + } +} + +gint32 ltt_event_get_int(LttEvent *e, struct marker_field *f) +{ + gboolean reverse_byte_order; + if(unlikely(f->attributes & LTT_ATTRIBUTE_NETWORK_BYTE_ORDER)) { + reverse_byte_order = (g_ntohs(0x1) != 0x1); + } else { + reverse_byte_order = LTT_GET_BO(e->tracefile); + } + + switch(f->size) { + case 1: + { + gint8 x = *(gint8 *)(e->data + f->offset); + return (gint32) x; + } + break; + case 2: + return (gint32)ltt_get_int16(reverse_byte_order, e->data + f->offset); + break; + case 4: + return (gint32)ltt_get_int32(reverse_byte_order, e->data + f->offset); + break; + case 8: + default: + g_critical("ltt_event_get_int : field size %i unknown", f->size); + return 0; + break; + } +} + +guint64 ltt_event_get_long_unsigned(LttEvent *e, struct marker_field *f) +{ + gboolean reverse_byte_order; + if(unlikely(f->attributes & LTT_ATTRIBUTE_NETWORK_BYTE_ORDER)) { + reverse_byte_order = (g_ntohs(0x1) != 0x1); + } else { + reverse_byte_order = LTT_GET_BO(e->tracefile); + } + + switch(f->size) { + case 1: + { + guint8 x = *(guint8 *)(e->data + f->offset); + return (guint64) x; + } + break; + case 2: + return (guint64)ltt_get_uint16(reverse_byte_order, e->data + f->offset); + break; + case 4: + return (guint64)ltt_get_uint32(reverse_byte_order, e->data + f->offset); + break; + case 8: + return ltt_get_uint64(reverse_byte_order, e->data + f->offset); + break; + default: + g_critical("ltt_event_get_long_unsigned : field size %i unknown", f->size); + return 0; + break; + } +} + +gint64 ltt_event_get_long_int(LttEvent *e, struct marker_field *f) +{ + gboolean reverse_byte_order; + if(unlikely(f->attributes & LTT_ATTRIBUTE_NETWORK_BYTE_ORDER)) { + reverse_byte_order = (g_ntohs(0x1) != 0x1); + } else { + reverse_byte_order = LTT_GET_BO(e->tracefile); + } + + switch(f->size) { + case 1: + { + gint8 x = *(gint8 *)(e->data + f->offset); + return (gint64) x; + } + break; + case 2: + return (gint64)ltt_get_int16(reverse_byte_order, e->data + f->offset); + break; + case 4: + return (gint64)ltt_get_int32(reverse_byte_order, e->data + f->offset); + break; + case 8: + return ltt_get_int64(reverse_byte_order, e->data + f->offset); + break; + default: + g_critical("ltt_event_get_long_int : field size %i unknown", f->size); + return 0; + break; + } +} + +#if 0 +float ltt_event_get_float(LttEvent *e, struct marker_field *f) +{ + gboolean reverse_byte_order; + if(unlikely(f->attributes & LTT_ATTRIBUTE_NETWORK_BYTE_ORDER)) { + reverse_byte_order = (g_ntohs(0x1) != 0x1); + } else { + g_assert(LTT_HAS_FLOAT(e->tracefile)); + reverse_byte_order = LTT_GET_FLOAT_BO(e->tracefile); + } + + g_assert(f->field_type.type_class == LTT_FLOAT && f->size == 4); + + if(reverse_byte_order == 0) return *(float *)(e->data + f->offset); + else{ + void *ptr = e->data + f->offset; + guint32 value = bswap_32(*(guint32*)ptr); + return *(float*)&value; + } +} + +double ltt_event_get_double(LttEvent *e, struct marker_field *f) +{ + gboolean reverse_byte_order; + if(unlikely(f->attributes & LTT_ATTRIBUTE_NETWORK_BYTE_ORDER)) { + reverse_byte_order = (g_ntohs(0x1) != 0x1); + } else { + g_assert(LTT_HAS_FLOAT(e->tracefile)); + reverse_byte_order = LTT_GET_FLOAT_BO(e->tracefile); + } + + if(f->size == 4) + return ltt_event_get_float(e, f); + + g_assert(f->field_type.type_class == LTT_FLOAT && f->size == 8); + + if(reverse_byte_order == 0) return *(double *)(e->data + f->offset); + else { + void *ptr = e->data + f->offset; + guint64 value = bswap_64(*(guint64*)ptr); + return *(double*)&value; + } +} +#endif + +/***************************************************************************** + * The string obtained is only valid until the next read from + * the same tracefile. + ****************************************************************************/ +char *ltt_event_get_string(LttEvent *e, struct marker_field *f) +{ + g_assert(f->type == LTT_TYPE_STRING); + + return (gchar*)g_strdup((gchar*)(e->data + f->offset)); +} + + diff --git a/tags/lttv-0.11.3-23102008/ltt/event.h b/tags/lttv-0.11.3-23102008/ltt/event.h new file mode 100644 index 00000000..31a49d36 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/ltt/event.h @@ -0,0 +1,111 @@ +#ifndef _LTT_EVENT_H +#define _LTT_EVENT_H + +#include +#include +#include +#include +#include +#include + +struct marker_field; + +/* + * Structure LttEvent and LttEventPosition must begin with the _exact_ same + * fields in the exact same order. LttEventPosition is a parent of LttEvent. + */ +struct LttEvent { + /* Begin of LttEventPosition fields */ + LttTracefile *tracefile; + unsigned int block; + unsigned int offset; + + /* Timekeeping */ + uint64_t tsc; /* Current timestamp counter */ + + /* End of LttEventPosition fields */ + guint32 timestamp; /* truncated timestamp */ + + guint16 event_id; + + LttTime event_time; + + void *data; /* event data */ + guint data_size; + guint event_size; /* event_size field of the header : + used to verify data_size from marker. */ + int count; /* the number of overflow of cycle count */ + gint64 overflow_nsec; /* precalculated nsec for overflows */ +}; + +struct LttEventPosition { + LttTracefile *tracefile; + unsigned int block; + unsigned int offset; + + /* Timekeeping */ + uint64_t tsc; /* Current timestamp counter */ +}; + +static inline guint16 ltt_event_id(struct LttEvent *event) +{ + return event->event_id; +} + +static inline LttTime ltt_event_time(struct LttEvent *event) +{ + return event->event_time; +} + +/* Obtain the position of the event within the tracefile. This + is used to seek back to this position later or to seek to another + position, computed relative to this position. The event position + structure is opaque and contains several fields, only two + of which are user accessible: block number and event index + within the block. */ + +void ltt_event_position(LttEvent *e, LttEventPosition *ep); + +LttEventPosition * ltt_event_position_new(); + +void ltt_event_position_get(LttEventPosition *ep, LttTracefile **tf, + guint *block, guint *offset, guint64 *tsc); + +void ltt_event_position_set(LttEventPosition *ep, LttTracefile *tf, + guint block, guint offset, guint64 tsc); + +gint ltt_event_position_compare(const LttEventPosition *ep1, + const LttEventPosition *ep2); + +void ltt_event_position_copy(LttEventPosition *dest, + const LttEventPosition *src); + +LttTracefile *ltt_event_position_tracefile(LttEventPosition *ep); + +/* These functions extract data from an event after architecture specific + * conversions. */ + +guint32 ltt_event_get_unsigned(LttEvent *e, struct marker_field *f); + +gint32 ltt_event_get_int(LttEvent *e, struct marker_field *f); + +guint64 ltt_event_get_long_unsigned(LttEvent *e, struct marker_field *f); + +gint64 ltt_event_get_long_int(LttEvent *e, struct marker_field *f); + +float ltt_event_get_float(LttEvent *e, struct marker_field *f); + +double ltt_event_get_double(LttEvent *e, struct marker_field *f); + + +/* The string obtained is only valid until the next read from + * the same tracefile. */ + +gchar *ltt_event_get_string(LttEvent *e, struct marker_field *f); + +static inline LttCycleCount ltt_event_cycle_count(const LttEvent *e) +{ + return e->tsc; +} + +#endif //_LTT_EVENT_H diff --git a/tags/lttv-0.11.3-23102008/ltt/ltt-private.h b/tags/lttv-0.11.3-23102008/ltt/ltt-private.h new file mode 100644 index 00000000..24e82f29 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/ltt/ltt-private.h @@ -0,0 +1,200 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Xiangxiu Yang + * 2006 Mathieu Desnoyers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License Version 2.1 as published by the Free Software Foundation. + * + * 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. + */ + +#ifndef LTT_PRIVATE_H +#define LTT_PRIVATE_H + +#include +#include +#include +#include +#include + +#ifndef max +#define max(a,b) ((a)>(b)?(a):(b)) +#endif + +#ifndef min +#define min(a,b) ((a)<(b)?(a):(b)) +#endif + + + +#define LTT_MAGIC_NUMBER 0x00D6B7ED +#define LTT_REV_MAGIC_NUMBER 0xEDB7D600 + +#define NSEC_PER_USEC 1000 + +/* Byte ordering */ +#define LTT_GET_BO(t) ((t)->reverse_bo) + +#define LTT_HAS_FLOAT(t) ((t)->float_word_order ! =0) +#define LTT_GET_FLOAT_BO(t) \ + (((t)->float_word_order == __BYTE_ORDER) ? 0 : 1) + +#define SEQUENCE_AVG_ELEMENTS 1000 + +/* + * offsetof taken from Linux kernel. + */ +#undef offsetof +#ifdef __compiler_offsetof +#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER) +#else +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +typedef guint8 uint8_t; +typedef guint16 uint16_t; +typedef guint32 uint32_t; +typedef guint64 uint64_t; + +/* Subbuffer header */ +struct ltt_subbuffer_header_2_1 { + uint64_t cycle_count_begin; /* Cycle count at subbuffer start */ + uint64_t cycle_count_end; /* Cycle count at subbuffer end */ + uint32_t magic_number; /* + * Trace magic number. + * contains endianness information. + */ + uint8_t major_version; + uint8_t minor_version; + uint8_t arch_size; /* Architecture pointer size */ + uint8_t alignment; /* LTT data alignment */ + uint64_t start_time_sec; /* NTP-corrected start time */ + uint64_t start_time_usec; + uint64_t start_freq; /* + * Frequency at trace start, + * used all along the trace. + */ + uint32_t freq_scale; /* Frequency scaling */ + uint32_t lost_size; /* Size unused at end of subbuffer */ + uint32_t buf_size; /* Size of this subbuffer */ + char header_end[0]; /* End of header */ +}; + +typedef struct ltt_subbuffer_header_2_1 ltt_subbuffer_header_t; + +/* + * Return header size without padding after the structure. Don't use packed + * structure because gcc generates inefficient code on some architectures + * (powerpc, mips..) + */ +static inline size_t ltt_subbuffer_header_size(void) +{ + return offsetof(ltt_subbuffer_header_t, header_end); +} + +enum field_status { FIELD_UNKNOWN, FIELD_VARIABLE, FIELD_FIXED }; + +typedef struct _LttBuffer { + void * head; + unsigned int index; + + struct { + LttTime timestamp; + uint64_t cycle_count; + uint64_t freq; /* Frequency in khz */ + } begin; + struct { + LttTime timestamp; + uint64_t cycle_count; + uint64_t freq; /* Frequency in khz */ + } end; + uint32_t lost_size; /* Size unused at the end of the buffer */ + + /* Timekeeping */ + uint64_t tsc; /* Current timestamp counter */ + uint64_t freq; /* Frequency in khz */ + guint32 cyc2ns_scale; +} LttBuffer; + +struct LttTracefile { + gboolean cpu_online; //is the cpu online ? + GQuark long_name; //tracefile complete filename + GQuark name; //tracefile name + guint cpu_num; //cpu number of the tracefile + guint tid; //Usertrace tid, else 0 + guint pgid; //Usertrace pgid, else 0 + guint64 creation; //Usertrace creation, else 0 + LttTrace * trace; //trace containing the tracefile + int fd; //file descriptor + off_t file_size; //file size + //unsigned block_size; //block_size + guint num_blocks; //number of blocks in the file + gboolean reverse_bo; //must we reverse byte order ? + gboolean float_word_order; //what is the byte order of floats ? + size_t alignment; //alignment of events in the tracefile. + // 0 or the architecture size in bytes. + + size_t buffer_header_size; + uint8_t tscbits; + uint8_t eventbits; + uint64_t tsc_mask; + uint64_t tsc_mask_next_bit; //next MSB after the mask + + /* Current event */ + LttEvent event; //Event currently accessible in the trace + + /* Current block */ + LttBuffer buffer; //current buffer + guint32 buf_size; /* The size of blocks */ +}; + +/* The characteristics of the system on which the trace was obtained + is described in a LttSystemDescription structure. */ + +struct LttSystemDescription { + gchar *description; + gchar *node_name; + gchar *domain_name; + unsigned nb_cpu; + LttArchSize size; + LttArchEndian endian; + gchar *kernel_name; + gchar *kernel_release; + gchar *kernel_version; + gchar *machine; + gchar *processor; + gchar *hardware_platform; + gchar *operating_system; + LttTime trace_start; + LttTime trace_end; +}; + +/* Calculate the offset needed to align the type. + * If alignment is 0, alignment is disactivated. + * else, the function returns the offset needed to + * align align_drift on the alignment value (should be + * the size of the architecture). */ +static inline unsigned int ltt_align(size_t align_drift, + size_t size_of_type, + size_t alignment) +{ + size_t align_offset = min(alignment, size_of_type); + + if(!alignment) + return 0; + + g_assert(size_of_type != 0); + return ((align_offset - align_drift) & (align_offset-1)); +} + + +#endif /* LTT_PRIVATE_H */ diff --git a/tags/lttv-0.11.3-23102008/ltt/ltt-types.h b/tags/lttv-0.11.3-23102008/ltt/ltt-types.h new file mode 100644 index 00000000..248cfab9 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/ltt/ltt-types.h @@ -0,0 +1,90 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2004-2005 Mathieu Desnoyers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License Version 2.1 as published by the Free Software Foundation. + * + * 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. + */ + +#ifndef LTT_TYPES_H +#define LTT_TYPES_H + +/* Set of functions to access the types portably, given the trace as parameter. + * */ + +#include +#include +#include + + +/***************************************************************************** + *Function name + * ltt_get_int64 : get a 64 bits integer number + *Input params + * ptr : pointer to the integer + *Return value + * gint64 : a 64 bits integer + * + * Takes care of endianness + * + ****************************************************************************/ + +static inline gint64 ltt_get_int64(gboolean reverse_byte_order, void *ptr) +{ + guint64 value = *(guint64*)ptr; + return (gint64) (reverse_byte_order ? GUINT64_SWAP_LE_BE(value): value); +} + + +static inline guint64 ltt_get_uint64(gboolean reverse_byte_order, void *ptr) +{ + guint64 value = *(guint64*)ptr; + return (guint64) (reverse_byte_order ? GUINT64_SWAP_LE_BE(value): value); +} + +static inline gint32 ltt_get_int32(gboolean reverse_byte_order, void *ptr) +{ + guint32 value = *(guint32*)ptr; + return (gint32) (reverse_byte_order ? GUINT32_SWAP_LE_BE(value): value); +} + +static inline guint32 ltt_get_uint32(gboolean reverse_byte_order, void *ptr) +{ + guint32 value = *(guint32*)ptr; + return (guint32) (reverse_byte_order ? GUINT32_SWAP_LE_BE(value): value); +} + +static inline gint16 ltt_get_int16(gboolean reverse_byte_order, void *ptr) +{ + guint16 value = *(guint16*)ptr; + return (gint16) (reverse_byte_order ? GUINT16_SWAP_LE_BE(value): value); +} + +static inline guint16 ltt_get_uint16(gboolean reverse_byte_order, void *ptr) +{ + guint16 value = *(guint16*)ptr; + return (guint16) (reverse_byte_order ? GUINT16_SWAP_LE_BE(value): value); +} + +static inline LttTime ltt_get_time(gboolean reverse_byte_order, void *ptr) +{ + LttTime output; + + output.tv_sec = ltt_get_uint32(reverse_byte_order, ptr); + ptr += sizeof(guint32); + output.tv_nsec = ltt_get_uint32(reverse_byte_order, ptr); + + return output; +} + +#endif // LTT_TYPES_H diff --git a/tags/lttv-0.11.3-23102008/ltt/ltt.h b/tags/lttv-0.11.3-23102008/ltt/ltt.h new file mode 100644 index 00000000..fd2155df --- /dev/null +++ b/tags/lttv-0.11.3-23102008/ltt/ltt.h @@ -0,0 +1,164 @@ +/* This file is part of the Linux Trace Toolkit trace reading library + * Copyright (C) 2003-2004 Michel Dagenais + * 2005 Mathieu Desnoyers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License Version 2.1 as published by the Free Software Foundation. + * + * 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. + */ + +#ifndef LTT_H +#define LTT_H + +#include +#include +#include + +/* A trace is associated with a tracing session run on a single, possibly + multi-cpu, system. It is defined as a pathname to a directory containing + all the relevant trace files. All the tracefiles for a trace were + generated in a single system for the same time period by the same + trace daemon. They simply contain different events. Typically control + tracefiles contain the important events (process creations and registering + tracing facilities) for all CPUs, and one file for each CPU contains all + the events for that CPU. All the tracefiles within the same trace directory + then use the exact same id numbers for event types. + + A tracefile (LttTracefile) contains a list of events (LttEvent) sorted + by time for each CPU; events from different CPUs may be slightly out of + order, especially using the (possibly drifting) cycle counters as + time unit. + + A facility is a list of event types (LttEventType), declared in a special + eventdefs file. A corresponding checksum differentiates different + facilities which would have the same name but a different content + (e.g., different versions). The files are stored within the trace + directory and are accessed automatically upon opening a trace. + The list of facilities (and associated checksum) used in a trace + must be known in order to properly decode the contained events. An event + is stored in the "facilities" control tracefile to denote each different + facility used. + + Event types (LttEventType) refer to data types (LttType) describing + their content. The data types supported are integer and unsigned integer + (of various length), enumerations (a special form of unsigned integer), + floating point (of various length), fixed size arrays, sequence + (variable sized arrays), structures and null terminated strings. + The elements of arrays and sequences, and the data members for + structures, may be of any nested data type (LttType). + + An LttField is a special object to denote a specific, possibly nested, + field within an event type. Suppose an event type socket_connect is a + structure containing two data members, source and destination, of type + socket_address. Type socket_address contains two unsigned integer + data members, ip and port. An LttField is different from a data type + structure member since it can denote a specific nested field, like the + source port, and store associated access information (byte offset within + the event data). The LttField objects are trace specific since the + contained information (byte offsets) may vary with the architecture + associated to the trace. */ + +#define PREALLOC_EVENTS 512 + +typedef struct LttTrace LttTrace; + +typedef struct LttTracefile LttTracefile; + +typedef struct LttSystemDescription LttSystemDescription; + +typedef struct LttEvent LttEvent; + +/* Checksums are used to differentiate facilities which have the same name + but differ. */ + +//typedef guint32 LttChecksum; + + +/* Events are usually stored with the easily obtained CPU clock cycle count, + ltt_cycle_count. This can be converted to the real time value, LttTime, + using linear interpolation between regularly sampled values (e.g. a few + times per second) of the real time clock with their corresponding + cycle count values. */ + + +typedef struct _TimeInterval{ + LttTime start_time; + LttTime end_time; +} TimeInterval; + + +typedef guint64 LttCycleCount; + +/* Event positions are used to seek within a tracefile based on + the block number and event position within the block. */ + +typedef struct LttEventPosition LttEventPosition; + + +/* Differences between architectures include word sizes, endianess, + alignment, floating point format and calling conventions. For a + packed binary trace, endianess and size matter, assuming that the + floating point format is standard (and is seldom used anyway). */ + +typedef enum _LttArchSize +{ LTT_LP32, LTT_ILP32, LTT_LP64, LTT_ILP64, LTT_UNKNOWN +} LttArchSize; + + +typedef enum _LttArchEndian +{ LTT_LITTLE_ENDIAN, LTT_BIG_ENDIAN +} LttArchEndian; + +typedef enum _LttTypeEnum +{ LTT_INT_FIXED, + LTT_UINT_FIXED, + LTT_POINTER, + LTT_CHAR, + LTT_UCHAR, + LTT_SHORT, + LTT_USHORT, + LTT_INT, + LTT_UINT, + LTT_LONG, + LTT_ULONG, + LTT_SIZE_T, + LTT_SSIZE_T, + LTT_OFF_T, + LTT_FLOAT, + LTT_STRING, + LTT_ENUM, + LTT_ARRAY, + LTT_SEQUENCE, + LTT_STRUCT, + LTT_UNION, + LTT_NONE +} LttTypeEnum; + + +/* Architecture types */ +#define LTT_ARCH_TYPE_I386 1 +#define LTT_ARCH_TYPE_PPC 2 +#define LTT_ARCH_TYPE_SH 3 +#define LTT_ARCH_TYPE_S390 4 +#define LTT_ARCH_TYPE_MIPS 5 +#define LTT_ARCH_TYPE_ARM 6 +#define LTT_ARCH_TYPE_PPC64 7 +#define LTT_ARCH_TYPE_X86_64 8 +#define LTT_ARCH_TYPE_C2 9 +#define LTT_ARCH_TYPE_POWERPC 10 +#define LTT_ARCH_TYPE_X86 11 + +/* Standard definitions for variants */ +#define LTT_ARCH_VARIANT_NONE 0 /* Main architecture implementation */ + +#endif // LTT_H diff --git a/tags/lttv-0.11.3-23102008/ltt/marker-desc.h b/tags/lttv-0.11.3-23102008/ltt/marker-desc.h new file mode 100644 index 00000000..741cfa67 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/ltt/marker-desc.h @@ -0,0 +1,24 @@ +#ifndef _LTT_MARKER_DESC_H +#define _LTT_MARKER_DESC_H + +/* + * Marker description support header. + * + * Mathieu Desnoyers, August 2007 + * License: LGPL. + */ + +#include +#include +#include + +//FIXME TEMP! +static inline GQuark ltt_enum_string_get(struct marker_field *f, + gulong value) +{ + char tmp[1024] = "ENUM-"; + sprintf(&tmp[sizeof("ENUM-") - 1], "%lu", value); + return g_quark_from_string(tmp); +} + +#endif //_LTT_MARKER_DESC_H diff --git a/tags/lttv-0.11.3-23102008/ltt/marker-field.h b/tags/lttv-0.11.3-23102008/ltt/marker-field.h new file mode 100644 index 00000000..252bd892 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/ltt/marker-field.h @@ -0,0 +1,49 @@ +#ifndef _LTT_MARKERS_FIELD_H +#define _LTT_MARKERS_FIELD_H + +/* + * Marker field support header. + * + * Mathieu Desnoyers, August 2007 + * License: LGPL. + */ + +#include + +enum ltt_type { + LTT_TYPE_SIGNED_INT, + LTT_TYPE_UNSIGNED_INT, + LTT_TYPE_POINTER, + LTT_TYPE_STRING, + LTT_TYPE_COMPACT, + LTT_TYPE_NONE, +}; + +struct marker_field { + GQuark name; + enum ltt_type type; + unsigned long offset; /* offset in the event data */ + unsigned long size; + unsigned long alignment; + unsigned long attributes; + int static_offset; /* boolean - private - is the field offset statically + * known with the preceding types ? */ + GString *fmt; +}; + +static inline GQuark marker_field_get_name(struct marker_field *field) +{ + return field->name; +} + +static inline enum ltt_type marker_field_get_type(struct marker_field *field) +{ + return field->type; +} + +static inline unsigned long marker_field_get_size(struct marker_field *field) +{ + return field->size; +} + +#endif //_LTT_MARKERS_FIELD_H diff --git a/tags/lttv-0.11.3-23102008/ltt/marker.c b/tags/lttv-0.11.3-23102008/ltt/marker.c new file mode 100644 index 00000000..3737f0aa --- /dev/null +++ b/tags/lttv-0.11.3-23102008/ltt/marker.c @@ -0,0 +1,531 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2007 Mathieu Desnoyers + * + * Complete rewrite from the original version made by XangXiu Yang. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License Version 2.1 as published by the Free Software Foundation. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_MARKERS_NUM 100 +#define DEFAULT_FIELDS_NUM 1 +#define MAX_NAME_LEN 1024 + +static inline const char *parse_trace_type(struct marker_info *info, + const char *fmt, + char *trace_size, enum ltt_type *trace_type, + unsigned long *attributes) +{ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + /* 'z' support added 23/7/1999 S.H. */ + /* 'z' changed to 'Z' --davidm 1/25/99 */ + /* 't' added for ptrdiff_t */ + + /* parse attributes. */ + repeat: + switch (*fmt) { + case 'n': + *attributes |= LTT_ATTRIBUTE_NETWORK_BYTE_ORDER; + ++fmt; + goto repeat; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || + *fmt =='Z' || *fmt == 'z' || *fmt == 't' || + *fmt == 'S' || *fmt == '1' || *fmt == '2' || + *fmt == '4' || *fmt == '8') { + qualifier = *fmt; + ++fmt; + if (qualifier == 'l' && *fmt == 'l') { + qualifier = 'L'; + ++fmt; + } + } + + switch (*fmt) { + case 'c': + *trace_type = LTT_TYPE_UNSIGNED_INT; + *trace_size = sizeof(char); + goto parse_end; + case 's': + *trace_type = LTT_TYPE_STRING; + goto parse_end; + case 'p': + *trace_type = LTT_TYPE_POINTER; + *trace_size = info->pointer_size; + goto parse_end; + case 'd': + case 'i': + *trace_type = LTT_TYPE_SIGNED_INT; + break; + case 'o': + case 'u': + case 'x': + case 'X': + *trace_type = LTT_TYPE_UNSIGNED_INT; + break; + default: + if (!*fmt) + --fmt; + goto parse_end; + } + switch (qualifier) { + case 'L': + *trace_size = sizeof(long long); + break; + case 'l': + *trace_size = info->long_size; + break; + case 'Z': + case 'z': + *trace_size = info->size_t_size; + break; + case 't': + *trace_size = info->pointer_size; + break; + case 'h': + *trace_size = sizeof(short); + break; + case '1': + *trace_size = sizeof(uint8_t); + break; + case '2': + *trace_size = sizeof(guint16); + break; + case '4': + *trace_size = sizeof(uint32_t); + break; + case '8': + *trace_size = sizeof(uint64_t); + break; + default: + *trace_size = info->int_size; + } + +parse_end: + return fmt; +} + +/* + * Restrictions: + * Field width and precision are *not* supported. + * %n not supported. + */ +__attribute__((no_instrument_function)) +static inline const char *parse_c_type(struct marker_info *info, + const char *fmt, + char *c_size, enum ltt_type *c_type, GString *field_fmt) +{ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + /* 'z' support added 23/7/1999 S.H. */ + /* 'z' changed to 'Z' --davidm 1/25/99 */ + /* 't' added for ptrdiff_t */ + + /* process flags : ignore standard print formats for now. */ + repeat: + switch (*fmt) { + case '-': + case '+': + case ' ': + case '#': + case '0': + g_string_append_c(field_fmt, *fmt); + ++fmt; + goto repeat; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || + *fmt =='Z' || *fmt == 'z' || *fmt == 't' || + *fmt == 'S') { + qualifier = *fmt; + ++fmt; + if (qualifier == 'l' && *fmt == 'l') { + qualifier = 'L'; + ++fmt; + } + } + + switch (*fmt) { + case 'c': + *c_type = LTT_TYPE_UNSIGNED_INT; + *c_size = sizeof(unsigned char); + g_string_append_c(field_fmt, *fmt); + goto parse_end; + case 's': + *c_type = LTT_TYPE_STRING; + goto parse_end; + case 'p': + *c_type = LTT_TYPE_POINTER; + *c_size = info->pointer_size; + goto parse_end; + case 'd': + case 'i': + *c_type = LTT_TYPE_SIGNED_INT; + g_string_append_c(field_fmt, 'l'); + g_string_append_c(field_fmt, 'l'); + g_string_append_c(field_fmt, *fmt); + break; + case 'o': + case 'u': + case 'x': + case 'X': + g_string_append_c(field_fmt, 'l'); + g_string_append_c(field_fmt, 'l'); + g_string_append_c(field_fmt, *fmt); + *c_type = LTT_TYPE_UNSIGNED_INT; + break; + default: + if (!*fmt) + --fmt; + goto parse_end; + } + switch (qualifier) { + case 'L': + *c_size = sizeof(long long); + break; + case 'l': + *c_size = info->long_size; + break; + case 'Z': + case 'z': + *c_size = info->size_t_size; + break; + case 't': + *c_size = info->pointer_size; + break; + case 'h': + *c_size = sizeof(short); + break; + default: + *c_size = info->int_size; + } + +parse_end: + return fmt; +} + +static inline long add_type(struct marker_info *info, + long offset, const char *name, + char trace_size, enum ltt_type trace_type, + char c_size, enum ltt_type c_type, unsigned long attributes, + unsigned int field_count, GString *field_fmt) +{ + struct marker_field *field; + char tmpname[MAX_NAME_LEN]; + + info->fields = g_array_set_size(info->fields, info->fields->len+1); + field = &g_array_index(info->fields, struct marker_field, + info->fields->len-1); + if (name) + field->name = g_quark_from_string(name); + else { + snprintf(tmpname, MAX_NAME_LEN-1, "field %u", field_count); + field->name = g_quark_from_string(tmpname); + } + field->type = trace_type; + field->fmt = g_string_new(field_fmt->str); + + switch (trace_type) { + case LTT_TYPE_SIGNED_INT: + case LTT_TYPE_UNSIGNED_INT: + case LTT_TYPE_POINTER: + field->size = trace_size; + field->alignment = trace_size; + info->largest_align = max((guint8)field->alignment, + (guint8)info->largest_align); + field->attributes = attributes; + if (offset == -1) { + field->offset = -1; + field->static_offset = 0; + return -1; + } else { + field->offset = offset + ltt_align(offset, field->alignment, + info->alignment); + field->static_offset = 1; + return field->offset + trace_size; + } + case LTT_TYPE_STRING: + field->offset = offset; + field->size = 0; /* Variable length, size is 0 */ + field->alignment = 1; + if (offset == -1) + field->static_offset = 0; + else + field->static_offset = 1; + return -1; + default: + g_error("Unexpected type"); + return 0; + } +} + +long marker_update_fields_offsets(struct marker_info *info, const char *data) +{ + struct marker_field *field; + unsigned int i; + long offset = 0; + + /* Find the last field with a static offset, then update from there. */ + for (i = info->fields->len - 1; i >= 0; i--) { + field = &g_array_index(info->fields, struct marker_field, i); + if (field->static_offset) { + offset = field->offset; + break; + } + } + + for (; i < info->fields->len; i++) { + field = &g_array_index(info->fields, struct marker_field, i); + + switch (field->type) { + case LTT_TYPE_SIGNED_INT: + case LTT_TYPE_UNSIGNED_INT: + case LTT_TYPE_POINTER: + field->offset = offset + ltt_align(offset, field->alignment, + info->alignment); + offset = field->offset + field->size; + break; + case LTT_TYPE_STRING: + field->offset = offset; + offset = offset + strlen(&data[offset]) + 1; + // not aligning on pointer size, breaking genevent backward compatibility. + break; + default: + g_error("Unexpected type"); + return -1; + } + } + return offset; +} + +static void format_parse(const char *fmt, struct marker_info *info) +{ + char trace_size = 0, c_size = 0; /* + * 0 (unset), 1, 2, 4, 8 bytes. + */ + enum ltt_type trace_type = LTT_TYPE_NONE, c_type = LTT_TYPE_NONE; + unsigned long attributes = 0; + long offset = 0; + const char *name_begin = NULL, *name_end = NULL; + char *name = NULL; + unsigned int field_count = 1; + GString *field_fmt = g_string_new(""); + + name_begin = fmt; + for (; *fmt ; ++fmt) { + switch (*fmt) { + case '#': + /* tracetypes (#) */ + ++fmt; /* skip first '#' */ + if (*fmt == '#') { /* Escaped ## */ + g_string_append_c(field_fmt, *fmt); + g_string_append_c(field_fmt, *fmt); + break; + } + attributes = 0; + fmt = parse_trace_type(info, fmt, &trace_size, &trace_type, + &attributes); + break; + case '%': + /* c types (%) */ + g_string_append_c(field_fmt, *fmt); + ++fmt; /* skip first '%' */ + if (*fmt == '%') { /* Escaped %% */ + g_string_append_c(field_fmt, *fmt); + break; + } + fmt = parse_c_type(info, fmt, &c_size, &c_type, field_fmt); + /* + * Output c types if no trace types has been + * specified. + */ + if (!trace_size) + trace_size = c_size; + if (trace_type == LTT_TYPE_NONE) + trace_type = c_type; + if (c_type == LTT_TYPE_STRING) + trace_type = LTT_TYPE_STRING; + /* perform trace write */ + offset = add_type(info, offset, name, trace_size, + trace_type, c_size, c_type, attributes, field_count++, + field_fmt); + trace_size = c_size = 0; + trace_type = c_size = LTT_TYPE_NONE; + g_string_truncate(field_fmt, 0); + attributes = 0; + name_begin = NULL; + if (name) { + g_free(name); + name = NULL; + } + break; + case ' ': + g_string_truncate(field_fmt, 0); + if (!name_end && name_begin) { + name_end = fmt; + if (name) + g_free(name); + name = g_new(char, name_end - name_begin + 1); + memcpy(name, name_begin, name_end - name_begin); + name[name_end - name_begin] = '\0'; + } + break; /* Skip white spaces */ + default: + g_string_append_c(field_fmt, *fmt); + if (!name_begin) { + name_begin = fmt; + name_end = NULL; + } + } + } + info->size = offset; + if (name) + g_free(name); + g_string_free(field_fmt, TRUE); +} + +int marker_parse_format(const char *format, struct marker_info *info) +{ + if (info->fields) + g_array_free(info->fields, TRUE); + info->fields = g_array_sized_new(FALSE, TRUE, + sizeof(struct marker_field), DEFAULT_FIELDS_NUM); + format_parse(format, info); + return 0; +} + +int marker_format_event(LttTrace *trace, GQuark name, const char *format) +{ + struct marker_info *info; + char *fquery; + char *fcopy; + + fquery = marker_get_format_from_name(trace, name); + if (fquery) { + if (strcmp(fquery, format) != 0) + g_error("Marker format mismatch \"%s\" vs \"%s\" for marker %s. " + "Kernel issue.", fquery, format, g_quark_to_string(name)); + else + return 0; /* Already exists. Nothing to do. */ + } + fcopy = g_new(char, strlen(format)+1); + strcpy(fcopy, format); + g_hash_table_insert(trace->markers_format_hash, (gpointer)(gulong)name, + (gpointer)fcopy); + + info = marker_get_info_from_name(trace, name); + for (; info != NULL; info = info->next) { + info->format = fcopy; + if (marker_parse_format(format, info)) + g_error("Error parsing marker format \"%s\" for marker \"%s\"", format, + g_quark_to_string(name)); + } + return 0; +} + +int marker_id_event(LttTrace *trace, GQuark name, guint16 id, + uint8_t int_size, uint8_t long_size, uint8_t pointer_size, + uint8_t size_t_size, uint8_t alignment) +{ + struct marker_info *info, *head; + int found = 0; + + if (trace->markers->len <= id) + trace->markers = g_array_set_size(trace->markers, + max(trace->markers->len * 2, id + 1)); + info = &g_array_index(trace->markers, struct marker_info, id); + info->name = name; + info->int_size = int_size; + info->long_size = long_size; + info->pointer_size = pointer_size; + info->size_t_size = size_t_size; + info->alignment = alignment; + info->fields = NULL; + info->next = NULL; + info->format = marker_get_format_from_name(trace, name); + info->largest_align = 1; + if (info->format && marker_parse_format(info->format, info)) + g_error("Error parsing marker format \"%s\" for marker \"%s\"", + info->format, g_quark_to_string(name)); + head = marker_get_info_from_name(trace, name); + if (!head) + g_hash_table_insert(trace->markers_hash, (gpointer)(gulong)name, + (gpointer)(gulong)id); + else { + struct marker_info *iter; + for (iter = head; iter != NULL; iter = iter->next) + if (iter->name == name) + found = 1; + if (!found) { + g_hash_table_replace(trace->markers_hash, (gpointer)(gulong)name, + (gpointer)(gulong)id); + info->next = head; + } + } + return 0; +} + +int allocate_marker_data(LttTrace *trace) +{ + /* Init array to 0 */ + trace->markers = g_array_sized_new(FALSE, TRUE, + sizeof(struct marker_info), DEFAULT_MARKERS_NUM); + if (!trace->markers) + return -ENOMEM; + trace->markers_hash = g_hash_table_new(g_direct_hash, g_direct_equal); + if (!trace->markers_hash) + return -ENOMEM; + trace->markers_format_hash = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL, g_free); + if (!trace->markers_hash) + return -ENOMEM; + return 0; +} + +void destroy_marker_data(LttTrace *trace) +{ + unsigned int i, j; + struct marker_info *info; + struct marker_field *field; + + for (i=0; imarkers->len; i++) { + info = &g_array_index(trace->markers, struct marker_info, i); + if (info->fields) { + for (j = 0; j < info->fields->len; j++) { + field = &g_array_index(info->fields, struct marker_field, j); + g_string_free(field->fmt, TRUE); + } + g_array_free(info->fields, TRUE); + } + } + g_array_free(trace->markers, TRUE); + g_hash_table_destroy(trace->markers_hash); + g_hash_table_destroy(trace->markers_format_hash); +} diff --git a/tags/lttv-0.11.3-23102008/ltt/marker.h b/tags/lttv-0.11.3-23102008/ltt/marker.h new file mode 100644 index 00000000..204448d5 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/ltt/marker.h @@ -0,0 +1,116 @@ +#ifndef _LTT_MARKERS_H +#define _LTT_MARKERS_H + +/* + * Marker support header. + * + * Mathieu Desnoyers, August 2007 + * License: LGPL. + */ + +#include +#include +#include +#include +#include + +#define LTT_ATTRIBUTE_NETWORK_BYTE_ORDER (1<<1) + +/* static ids 0-7 reserved for internal use. */ +#define MARKER_CORE_IDS 8 + +struct marker_info; + +struct marker_info { + GQuark name; + char *format; + long size; /* size if known statically, else -1 */ + guint8 largest_align; /* Size of the largest alignment needed in the + payload. */ + GArray *fields; /* Array of struct marker_field */ + guint8 int_size, long_size, pointer_size, size_t_size; + guint8 alignment; /* Size on which the architecture alignment must be + done. Useful to encapsulate x86_32 events on + x86_64 kernels. */ + struct marker_info *next; /* Linked list of markers with the same name */ +}; + +enum marker_id { + MARKER_ID_SET_MARKER_ID = 0, /* Static IDs available (range 0-7) */ + MARKER_ID_SET_MARKER_FORMAT, + MARKER_ID_DYNAMIC, /* Dynamic IDs (range: 8-65535) */ +}; + +static inline guint16 marker_get_id_from_info(LttTrace *trace, + struct marker_info *info) +{ + return ((unsigned long)info - (unsigned long)trace->markers->data) + / sizeof(struct marker_info); +} + +static inline struct marker_info *marker_get_info_from_id(LttTrace *trace, + guint16 id) +{ + if (unlikely(trace->markers->len <= id)) + return NULL; + return &g_array_index(trace->markers, struct marker_info, id); +} + +/* + * Returns the head of the marker info list for that name. + */ +static inline struct marker_info *marker_get_info_from_name(LttTrace *trace, + GQuark name) +{ + gpointer orig_key, value; + int res; + + res = g_hash_table_lookup_extended(trace->markers_hash, + (gconstpointer)(gulong)name, &orig_key, &value); + if (!res) + return NULL; + return marker_get_info_from_id(trace, (guint16)(gulong)value); +} + +static inline char *marker_get_format_from_name(LttTrace *trace, + GQuark name) +{ + gpointer orig_key, value; + int res; + + res = g_hash_table_lookup_extended(trace->markers_format_hash, + (gconstpointer)(gulong)name, &orig_key, &value); + if (!res) + return NULL; + return (char *)value; +} + +static inline struct marker_field *marker_get_field(struct marker_info *info, + guint i) +{ + return &g_array_index(info->fields, struct marker_field, i); +} + +static inline unsigned int marker_get_num_fields(struct marker_info *info) +{ + return info->fields->len; +} + +/* + * for_each_marker_field - iterate over fields of a marker + * @field: struct marker_field * to use as iterator + * @info: marker info pointer + */ +#define for_each_marker_field(field, info) \ + for (field = marker_get_field(info, 0); \ + field != marker_get_field(info, marker_get_num_fields(info)); \ + field++) + +int marker_format_event(LttTrace *trace, GQuark name, const char *format); +int marker_id_event(LttTrace *trace, GQuark name, guint16 id, + uint8_t int_size, uint8_t long_size, uint8_t pointer_size, + uint8_t size_t_size, uint8_t alignment); +int allocate_marker_data(LttTrace *trace); +void destroy_marker_data(LttTrace *trace); + +#endif //_LTT_MARKERS_H diff --git a/tags/lttv-0.11.3-23102008/ltt/time.h b/tags/lttv-0.11.3-23102008/ltt/time.h new file mode 100644 index 00000000..14c15d1d --- /dev/null +++ b/tags/lttv-0.11.3-23102008/ltt/time.h @@ -0,0 +1,250 @@ +/* This file is part of the Linux Trace Toolkit trace reading library + * Copyright (C) 2003-2004 Michel Dagenais + * 2005 Mathieu Desnoyers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License Version 2.1 as published by the Free Software Foundation. + * + * 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. + */ + +#ifndef LTT_TIME_H +#define LTT_TIME_H + +#include +#include +#include + +typedef struct _LttTime { + unsigned long tv_sec; + unsigned long tv_nsec; +} LttTime; + + +#define NANOSECONDS_PER_SECOND 1000000000 + +/* We give the DIV and MUL constants so we can always multiply, for a + * division as well as a multiplication of NANOSECONDS_PER_SECOND */ +/* 2^30/1.07374182400631629848 = 1000000000.0 */ +#define DOUBLE_SHIFT_CONST_DIV 1.07374182400631629848 +#define DOUBLE_SHIFT 30 + +/* 2^30*0.93132257461547851562 = 1000000000.0000000000 */ +#define DOUBLE_SHIFT_CONST_MUL 0.93132257461547851562 + + +/* 1953125 * 2^9 = NANOSECONDS_PER_SECOND */ +#define LTT_TIME_UINT_SHIFT_CONST 1953125 +#define LTT_TIME_UINT_SHIFT 9 + + +static const LttTime ltt_time_zero = { 0, 0 }; + +static const LttTime ltt_time_one = { 0, 1 }; + +static const LttTime ltt_time_infinite = { G_MAXUINT, NANOSECONDS_PER_SECOND }; + +static inline LttTime ltt_time_sub(LttTime t1, LttTime t2) +{ + LttTime res; + res.tv_sec = t1.tv_sec - t2.tv_sec; + res.tv_nsec = t1.tv_nsec - t2.tv_nsec; + /* unlikely : given equal chance to be anywhere in t1.tv_nsec, and + * higher probability of low value for t2.tv_sec, we will habitually + * not wrap. + */ + if(unlikely(t1.tv_nsec < t2.tv_nsec)) { + res.tv_sec--; + res.tv_nsec += NANOSECONDS_PER_SECOND; + } + return res; +} + + +static inline LttTime ltt_time_add(LttTime t1, LttTime t2) +{ + LttTime res; + res.tv_nsec = t1.tv_nsec + t2.tv_nsec; + res.tv_sec = t1.tv_sec + t2.tv_sec; + /* unlikely : given equal chance to be anywhere in t1.tv_nsec, and + * higher probability of low value for t2.tv_sec, we will habitually + * not wrap. + */ + if(unlikely(res.tv_nsec >= NANOSECONDS_PER_SECOND)) { + res.tv_sec++; + res.tv_nsec -= NANOSECONDS_PER_SECOND; + } + return res; +} + +/* Fastest comparison : t1 > t2 */ +static inline int ltt_time_compare(LttTime t1, LttTime t2) +{ + int ret=0; + if(likely(t1.tv_sec > t2.tv_sec)) ret = 1; + else if(unlikely(t1.tv_sec < t2.tv_sec)) ret = -1; + else if(likely(t1.tv_nsec > t2.tv_nsec)) ret = 1; + else if(unlikely(t1.tv_nsec < t2.tv_nsec)) ret = -1; + + return ret; +} + +#define LTT_TIME_MIN(a,b) ((ltt_time_compare((a),(b)) < 0) ? (a) : (b)) +#define LTT_TIME_MAX(a,b) ((ltt_time_compare((a),(b)) > 0) ? (a) : (b)) + +#define MAX_TV_SEC_TO_DOUBLE 0x7FFFFF +static inline double ltt_time_to_double(LttTime t1) +{ + /* We lose precision if tv_sec is > than (2^23)-1 + * + * Max values that fits in a double (53 bits precision on normalised + * mantissa): + * tv_nsec : NANOSECONDS_PER_SECONDS : 2^30 + * + * So we have 53-30 = 23 bits left for tv_sec. + * */ +#ifdef EXTRA_CHECK + g_assert(t1.tv_sec <= MAX_TV_SEC_TO_DOUBLE); + if(t1.tv_sec > MAX_TV_SEC_TO_DOUBLE) + g_warning("Precision loss in conversion LttTime to double"); +#endif //EXTRA_CHECK + return ((double)((guint64)t1.tv_sec< than (2^23)-1 + * + * Max values that fits in a double (53 bits precision on normalised + * mantissa): + * tv_nsec : NANOSECONDS_PER_SECONDS : 2^30 + * + * So we have 53-30 = 23 bits left for tv_sec. + * */ +#ifdef EXTRA_CHECK + g_assert(t1 <= MAX_TV_SEC_TO_DOUBLE); + if(t1 > MAX_TV_SEC_TO_DOUBLE) + g_warning("Conversion from non precise double to LttTime"); +#endif //EXTRA_CHECK + LttTime res; + //res.tv_sec = t1/(double)NANOSECONDS_PER_SECOND; + res.tv_sec = (guint64)(t1 * DOUBLE_SHIFT_CONST_DIV) >> DOUBLE_SHIFT; + res.tv_nsec = (t1 - (((guint64)res.tv_sec< than (2^62)-1 + * */ +#ifdef EXTRA_CHECK + g_assert(t1 <= MAX_TV_SEC_TO_UINT64); + if(t1 > MAX_TV_SEC_TO_UINT64) + g_warning("Conversion from uint64 to non precise LttTime"); +#endif //EXTRA_CHECK + LttTime res; + //if(unlikely(t1 >= NANOSECONDS_PER_SECOND)) { + if(likely(t1>>LTT_TIME_UINT_SHIFT >= LTT_TIME_UINT_SHIFT_CONST)) { + //res.tv_sec = t1/NANOSECONDS_PER_SECOND; + res.tv_sec = (t1>>LTT_TIME_UINT_SHIFT) + /LTT_TIME_UINT_SHIFT_CONST; // acceleration + res.tv_nsec = (t1 - res.tv_sec*NANOSECONDS_PER_SECOND); + } else { + res.tv_sec = 0; + res.tv_nsec = (guint32)t1; + } + return res; +} + +#endif // LTT_TIME_H diff --git a/tags/lttv-0.11.3-23102008/ltt/trace.h b/tags/lttv-0.11.3-23102008/ltt/trace.h new file mode 100644 index 00000000..2e031c22 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/ltt/trace.h @@ -0,0 +1,219 @@ +/* This file is part of the Linux Trace Toolkit trace reading library + * Copyright (C) 2003-2004 Michel Dagenais + * 2005 Mathieu Desnoyers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License Version 2.1 as published by the Free Software Foundation. + * + * 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. + */ + +#ifndef TRACE_H +#define TRACE_H + +#include +#include +#include +#include + +struct LttTrace { + GQuark pathname; //the pathname of the trace + //LttSystemDescription * system_description;//system description + + guint num_cpu; + + guint32 arch_type; + guint32 arch_variant; + guint8 arch_size; + guint8 ltt_major_version; + guint8 ltt_minor_version; + guint8 flight_recorder; + guint32 freq_scale; + uint64_t start_freq; + uint64_t start_tsc; + uint64_t start_monotonic; + LttTime start_time; + LttTime start_time_from_tsc; + + GData *tracefiles; //tracefiles groups + /* Support for markers */ + GArray *markers; //indexed by marker ID + GHashTable *markers_hash; //indexed by name hash + GHashTable *markers_format_hash; //indexed by name hash +}; + +static inline guint ltt_trace_get_num_cpu(LttTrace *t) +{ + return t->num_cpu; +} + +/* A trace is specified as a pathname to the directory containing all the + associated data (control tracefiles, per cpu tracefiles, event + descriptions...). + + When a trace is closed, all the associated facilities, types and fields + are released as well. + + return value is NULL if there is an error when opening the trace. + + */ + +LttTrace *ltt_trace_open(const gchar *pathname); + +/* copy reopens a trace + * + * return value NULL if error while opening the trace + */ +LttTrace *ltt_trace_copy(LttTrace *self); + +static inline GQuark ltt_trace_name(const LttTrace *t) +{ + return t->pathname; +} + + +void ltt_trace_close(LttTrace *t); + +LttSystemDescription *ltt_trace_system_description(LttTrace *t); + + +/* Get the start time and end time of the trace */ + +void ltt_trace_time_span_get(LttTrace *t, LttTime *start, LttTime *end); + + +/* Get the name of a tracefile */ + +static inline GQuark ltt_tracefile_name(const LttTracefile *tf) +{ + return tf->name; +} + +static inline GQuark ltt_tracefile_long_name(const LttTracefile *tf) +{ + return tf->long_name; +} + +/* get the cpu number of the tracefile */ + +static inline guint ltt_tracefile_cpu(LttTracefile *tf) +{ + return tf->cpu_num; +} + +/* For usertrace */ +static inline guint ltt_tracefile_tid(LttTracefile *tf) +{ + return tf->tid; +} + +static inline guint ltt_tracefile_pgid(LttTracefile *tf) +{ + return tf->pgid; +} + +static inline guint64 ltt_tracefile_creation(LttTracefile *tf) +{ + return tf->creation; +} + +static inline LttTrace *ltt_tracefile_get_trace(LttTracefile *tf) +{ + return tf->trace; +} + +/* Get the number of blocks in the tracefile */ + +static inline guint ltt_tracefile_block_number(LttTracefile *tf) +{ + return tf->num_blocks; +} + + +/* Seek to the first event of the trace with time larger or equal to time */ + +int ltt_tracefile_seek_time(LttTracefile *t, LttTime time); + +/* Seek to the first event with position equal or larger to ep */ + +int ltt_tracefile_seek_position(LttTracefile *t, + const LttEventPosition *ep); + +/* Read the next event */ + +int ltt_tracefile_read(LttTracefile *t); + +/* ltt_tracefile_read cut down in pieces */ +int ltt_tracefile_read_seek(LttTracefile *t); +int ltt_tracefile_read_update_event(LttTracefile *t); +int ltt_tracefile_read_op(LttTracefile *t); + +/* Get the current event of the tracefile : valid until the next read */ +LttEvent *ltt_tracefile_get_event(LttTracefile *tf); + +/* get the data type size and endian type of the local machine */ + +void getDataEndianType(LttArchSize * size, LttArchEndian * endian); + +/* get an integer number */ +gint64 get_int(gboolean reverse_byte_order, gint size, void *data); + +/* get the node name of the system */ + +gchar * ltt_trace_system_description_node_name (LttSystemDescription * s); + + +/* get the domain name of the system */ + +gchar * ltt_trace_system_description_domain_name (LttSystemDescription * s); + + +/* get the description of the system */ + +gchar * ltt_trace_system_description_description (LttSystemDescription * s); + + +/* get the NTP start time of the trace */ + +LttTime ltt_trace_start_time(LttTrace *t); + +/* get the monotonic start time of the trace */ + +LttTime ltt_trace_start_time_monotonic(LttTrace *t); + +void get_absolute_pathname(const gchar *pathname, gchar * abs_pathname); + +/* May return a NULL tracefile group */ +GData **ltt_trace_get_tracefiles_groups(LttTrace *trace); + +typedef void (*ForEachTraceFileFunc)(LttTracefile *tf, gpointer func_args); + +struct compute_tracefile_group_args { + ForEachTraceFileFunc func; + gpointer func_args; +}; + +void compute_tracefile_group(GQuark key_id, + GArray *group, + struct compute_tracefile_group_args *args); + + +gint64 ltt_get_int(gboolean reverse_byte_order, gint size, void *data); + +guint64 ltt_get_uint(gboolean reverse_byte_order, gint size, void *data); + +LttTime ltt_interpolate_time_from_tsc(LttTracefile *tf, guint64 tsc); + +/* Set to enable event debugging output */ +void ltt_event_debug(int state); + +#endif // TRACE_H diff --git a/tags/lttv-0.11.3-23102008/ltt/tracefile.c b/tags/lttv-0.11.3-23102008/ltt/tracefile.c new file mode 100644 index 00000000..1d411e3e --- /dev/null +++ b/tags/lttv-0.11.3-23102008/ltt/tracefile.c @@ -0,0 +1,2519 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2005 Mathieu Desnoyers + * + * Complete rewrite from the original version made by XangXiu Yang. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License Version 2.1 as published by the Free Software Foundation. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// For realpath +#include +#include + + +#include +#include "ltt-private.h" +#include +#include +#include +#include + +/* Tracefile names used in this file */ + +GQuark LTT_TRACEFILE_NAME_METADATA; + +#ifndef g_open +#define g_open open +#endif + + +#define __UNUSED__ __attribute__((__unused__)) + +#define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format) + +#ifndef g_debug +#define g_debug(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format) +#endif + +#define g_close close + +/* Those macros must be called from within a function where page_size is a known + * variable */ +#define PAGE_MASK (~(page_size-1)) +#define PAGE_ALIGN(addr) (((addr)+page_size-1)&PAGE_MASK) + +LttTrace *father_trace = NULL; + +/* set the offset of the fields belonging to the event, + need the information of the archecture */ +//void set_fields_offsets(LttTracefile *tf, LttEventType *event_type); +//size_t get_fields_offsets(LttTracefile *tf, LttEventType *event_type, void *data); + +#if 0 +/* get the size of the field type according to + * The facility size information. */ +static inline void preset_field_type_size(LttTracefile *tf, + LttEventType *event_type, + off_t offset_root, off_t offset_parent, + enum field_status *fixed_root, enum field_status *fixed_parent, + LttField *field); +#endif //0 + +/* map a fixed size or a block information from the file (fd) */ +static gint map_block(LttTracefile * tf, guint block_num); + +/* calculate nsec per cycles for current block */ +#if 0 +static guint32 calc_nsecs_per_cycle(LttTracefile * t); +static guint64 cycles_2_ns(LttTracefile *tf, guint64 cycles); +#endif //0 + +/* go to the next event */ +static int ltt_seek_next_event(LttTracefile *tf); + +static int open_tracefiles(LttTrace *trace, gchar *root_path, + gchar *relative_path); +static int ltt_process_metadata_tracefile(LttTracefile *tf); +static void ltt_tracefile_time_span_get(LttTracefile *tf, + LttTime *start, LttTime *end); +static void group_time_span_get(GQuark name, gpointer data, gpointer user_data); +static gint map_block(LttTracefile * tf, guint block_num); +static void ltt_update_event_size(LttTracefile *tf); + +/* Enable event debugging */ +static int a_event_debug = 0; + +void ltt_event_debug(int state) +{ + a_event_debug = state; +} + +/* trace can be NULL + * + * Return value : 0 success, 1 bad tracefile + */ +static int parse_trace_header(ltt_subbuffer_header_t *header, + LttTracefile *tf, LttTrace *t) +{ + if (header->magic_number == LTT_MAGIC_NUMBER) + tf->reverse_bo = 0; + else if(header->magic_number == LTT_REV_MAGIC_NUMBER) + tf->reverse_bo = 1; + else /* invalid magic number, bad tracefile ! */ + return 1; + + if(t) { + t->ltt_major_version = header->major_version; + t->ltt_minor_version = header->minor_version; + t->arch_size = header->arch_size; + } + tf->alignment = header->alignment; + + /* Get float byte order : might be different from int byte order + * (or is set to 0 if the trace has no float (kernel trace)) */ + tf->float_word_order = 0; + + switch(header->major_version) { + case 0: + case 1: + g_warning("Unsupported trace version : %hhu.%hhu", + header->major_version, header->minor_version); + return 1; + break; + case 2: + switch(header->minor_version) { + case 1: + { + struct ltt_subbuffer_header_2_1 *vheader = header; + tf->buffer_header_size = ltt_subbuffer_header_size(); + tf->tscbits = 27; + tf->eventbits = 5; + tf->tsc_mask = ((1ULL << tf->tscbits) - 1); + tf->tsc_mask_next_bit = (1ULL << tf->tscbits); + + if(t) { + t->start_freq = ltt_get_uint64(LTT_GET_BO(tf), + &vheader->start_freq); + t->freq_scale = ltt_get_uint32(LTT_GET_BO(tf), + &vheader->freq_scale); + if(father_trace) { + t->start_freq = father_trace->start_freq; + t->freq_scale = father_trace->freq_scale; + } else { + father_trace = t; + } + t->start_tsc = ltt_get_uint64(LTT_GET_BO(tf), + &vheader->cycle_count_begin); + t->start_monotonic = 0; + t->start_time.tv_sec = ltt_get_uint64(LTT_GET_BO(tf), + &vheader->start_time_sec); + t->start_time.tv_nsec = ltt_get_uint64(LTT_GET_BO(tf), + &vheader->start_time_usec); + t->start_time.tv_nsec *= 1000; /* microsec to nanosec */ + + t->start_time_from_tsc = ltt_time_from_uint64( + (double)t->start_tsc + * (1000000000.0 / tf->trace->freq_scale) + / (double)t->start_freq); + } + } + break; + default: + g_warning("Unsupported trace version : %hhu.%hhu", + header->major_version, header->minor_version); + return 1; + } + break; + default: + g_warning("Unsupported trace version : %hhu.%hhu", + header->major_version, header->minor_version); + return 1; + } + return 0; +} + + + +/***************************************************************************** + *Function name + * ltt_tracefile_open : open a trace file, construct a LttTracefile + *Input params + * t : the trace containing the tracefile + * fileName : path name of the trace file + * tf : the tracefile structure + *Return value + * : 0 for success, -1 otherwise. + ****************************************************************************/ + +static gint ltt_tracefile_open(LttTrace *t, gchar * fileName, LttTracefile *tf) +{ + struct stat lTDFStat; /* Trace data file status */ + ltt_subbuffer_header_t *header; + int page_size = getpagesize(); + + //open the file + tf->long_name = g_quark_from_string(fileName); + tf->trace = t; + tf->fd = open(fileName, O_RDONLY); + if(tf->fd < 0){ + g_warning("Unable to open input data file %s\n", fileName); + goto end; + } + + // Get the file's status + if(fstat(tf->fd, &lTDFStat) < 0){ + g_warning("Unable to get the status of the input data file %s\n", fileName); + goto close_file; + } + + // Is the file large enough to contain a trace + if(lTDFStat.st_size < + (off_t)(ltt_subbuffer_header_size())){ + g_print("The input data file %s does not contain a trace\n", fileName); + goto close_file; + } + + /* Temporarily map the buffer start header to get trace information */ + /* Multiple of pages aligned head */ + tf->buffer.head = mmap(0, + PAGE_ALIGN(ltt_subbuffer_header_size()), PROT_READ, + MAP_PRIVATE, tf->fd, 0); + if(tf->buffer.head == MAP_FAILED) { + perror("Error in allocating memory for buffer of tracefile"); + goto close_file; + } + g_assert( ( (gulong)tf->buffer.head&(8-1) ) == 0); // make sure it's aligned. + + header = (ltt_subbuffer_header_t *)tf->buffer.head; + + if(parse_trace_header(header, tf, NULL)) { + g_warning("parse_trace_header error"); + goto unmap_file; + } + + //store the size of the file + tf->file_size = lTDFStat.st_size; + tf->buf_size = ltt_get_uint32(LTT_GET_BO(tf), &header->buf_size); + tf->num_blocks = tf->file_size / tf->buf_size; + + if(munmap(tf->buffer.head, + PAGE_ALIGN(ltt_subbuffer_header_size()))) { + g_warning("unmap size : %u\n", + PAGE_ALIGN(ltt_subbuffer_header_size())); + perror("munmap error"); + g_assert(0); + } + tf->buffer.head = NULL; + + //read the first block + if(map_block(tf,0)) { + perror("Cannot map block for tracefile"); + goto close_file; + } + + return 0; + + /* Error */ +unmap_file: + if(munmap(tf->buffer.head, + PAGE_ALIGN(ltt_subbuffer_header_size()))) { + g_warning("unmap size : %u\n", + PAGE_ALIGN(ltt_subbuffer_header_size())); + perror("munmap error"); + g_assert(0); + } +close_file: + close(tf->fd); +end: + return -1; +} + + +/***************************************************************************** + *Function name + * ltt_tracefile_close: close a trace file, + *Input params + * t : tracefile which will be closed + ****************************************************************************/ + +static void ltt_tracefile_close(LttTracefile *t) +{ + int page_size = getpagesize(); + + if(t->buffer.head != NULL) + if(munmap(t->buffer.head, PAGE_ALIGN(t->buf_size))) { + g_warning("unmap size : %u\n", + PAGE_ALIGN(t->buf_size)); + perror("munmap error"); + g_assert(0); + } + + close(t->fd); +} + +/**************************************************************************** + * get_absolute_pathname + * + * return the unique pathname in the system + * + * MD : Fixed this function so it uses realpath, dealing well with + * forgotten cases (.. were not used correctly before). + * + ****************************************************************************/ +void get_absolute_pathname(const gchar *pathname, gchar * abs_pathname) +{ + abs_pathname[0] = '\0'; + + if (realpath(pathname, abs_pathname) != NULL) + return; + else + { + /* error, return the original path unmodified */ + strcpy(abs_pathname, pathname); + return; + } + return; +} + +/* Search for something like : .*_.* + * + * The left side is the name, the right side is the number. + */ + +static int get_tracefile_name_number(gchar *raw_name, + GQuark *name, + guint *num, + gulong *tid, + gulong *pgid, + guint64 *creation) +{ + guint raw_name_len = strlen(raw_name); + gchar char_name[PATH_MAX]; + int i; + int underscore_pos; + long int cpu_num; + gchar *endptr; + gchar *tmpptr; + + for(i=raw_name_len-1;i>=0;i--) { + if(raw_name[i] == '_') break; + } + if(i==-1) { /* Either not found or name length is 0 */ + /* This is a userspace tracefile */ + strncpy(char_name, raw_name, raw_name_len); + char_name[raw_name_len] = '\0'; + *name = g_quark_from_string(char_name); + *num = 0; /* unknown cpu */ + for(i=0;itracefiles; +} + + +void compute_tracefile_group(GQuark key_id, + GArray *group, + struct compute_tracefile_group_args *args) +{ + int i; + LttTracefile *tf; + + for(i=0; ilen; i++) { + tf = &g_array_index (group, LttTracefile, i); + if(tf->cpu_online) + args->func(tf, args->func_args); + } +} + + +static void ltt_tracefile_group_destroy(gpointer data) +{ + GArray *group = (GArray *)data; + int i; + LttTracefile *tf; + + for(i=0; ilen; i++) { + tf = &g_array_index (group, LttTracefile, i); + if(tf->cpu_online) + ltt_tracefile_close(tf); + } + g_array_free(group, TRUE); +} + +static gboolean ltt_tracefile_group_has_cpu_online(gpointer data) +{ + GArray *group = (GArray *)data; + int i; + LttTracefile *tf; + + for(i=0; ilen; i++) { + tf = &g_array_index (group, LttTracefile, i); + if(tf->cpu_online) + return 1; + } + return 0; +} + + +/* Open each tracefile under a specific directory. Put them in a + * GData : permits to access them using their tracefile group pathname. + * i.e. access control/modules tracefile group by index : + * "control/module". + * + * relative path is the path relative to the trace root + * root path is the full path + * + * A tracefile group is simply an array where all the per cpu tracefiles sit. + */ + +static int open_tracefiles(LttTrace *trace, gchar *root_path, gchar *relative_path) +{ + DIR *dir = opendir(root_path); + struct dirent *entry; + struct stat stat_buf; + int ret; + + gchar path[PATH_MAX]; + int path_len; + gchar *path_ptr; + + int rel_path_len; + gchar rel_path[PATH_MAX]; + gchar *rel_path_ptr; + LttTracefile tmp_tf; + + if(dir == NULL) { + perror(root_path); + return ENOENT; + } + + strncpy(path, root_path, PATH_MAX-1); + path_len = strlen(path); + path[path_len] = '/'; + path_len++; + path_ptr = path + path_len; + + strncpy(rel_path, relative_path, PATH_MAX-1); + rel_path_len = strlen(rel_path); + rel_path[rel_path_len] = '/'; + rel_path_len++; + rel_path_ptr = rel_path + rel_path_len; + + while((entry = readdir(dir)) != NULL) { + + if(entry->d_name[0] == '.') continue; + + strncpy(path_ptr, entry->d_name, PATH_MAX - path_len); + strncpy(rel_path_ptr, entry->d_name, PATH_MAX - rel_path_len); + + ret = stat(path, &stat_buf); + if(ret == -1) { + perror(path); + continue; + } + + g_debug("Tracefile file or directory : %s\n", path); + + // if(strcmp(rel_path, "/eventdefs") == 0) continue; + + if(S_ISDIR(stat_buf.st_mode)) { + + g_debug("Entering subdirectory...\n"); + ret = open_tracefiles(trace, path, rel_path); + if(ret < 0) continue; + } else if(S_ISREG(stat_buf.st_mode)) { + GQuark name; + guint num; + gulong tid, pgid; + guint64 creation; + GArray *group; + num = 0; + tid = pgid = 0; + creation = 0; + if(get_tracefile_name_number(rel_path, &name, &num, &tid, &pgid, &creation)) + continue; /* invalid name */ + + g_debug("Opening file.\n"); + if(ltt_tracefile_open(trace, path, &tmp_tf)) { + g_info("Error opening tracefile %s", path); + + continue; /* error opening the tracefile : bad magic number ? */ + } + + g_debug("Tracefile name is %s and number is %u", + g_quark_to_string(name), num); + + tmp_tf.cpu_online = 1; + tmp_tf.cpu_num = num; + tmp_tf.name = name; + tmp_tf.tid = tid; + tmp_tf.pgid = pgid; + tmp_tf.creation = creation; + group = g_datalist_id_get_data(&trace->tracefiles, name); + if(group == NULL) { + /* Elements are automatically cleared when the array is allocated. + * It makes the cpu_online variable set to 0 : cpu offline, by default. + */ + group = g_array_sized_new (FALSE, TRUE, sizeof(LttTracefile), 10); + g_datalist_id_set_data_full(&trace->tracefiles, name, + group, ltt_tracefile_group_destroy); + } + + /* Add the per cpu tracefile to the named group */ + unsigned int old_len = group->len; + if(num+1 > old_len) + group = g_array_set_size(group, num+1); + g_array_index (group, LttTracefile, num) = tmp_tf; + g_array_index (group, LttTracefile, num).event.tracefile = + &g_array_index (group, LttTracefile, num); + } + } + + closedir(dir); + + return 0; +} + + +/* Presumes the tracefile is already seeked at the beginning. It makes sense, + * because it must be done just after the opening */ +static int ltt_process_metadata_tracefile(LttTracefile *tf) +{ + int err; + guint i; + + while(1) { + err = ltt_tracefile_read_seek(tf); + if(err == EPERM) goto seek_error; + else if(err == ERANGE) break; /* End of tracefile */ + + err = ltt_tracefile_read_update_event(tf); + if(err) goto update_error; + + /* The rules are : + * It contains only core events : + * 0 : set_marker_id + * 1 : set_marker_format + */ + if(tf->event.event_id >= MARKER_CORE_IDS) { + /* Should only contain core events */ + g_warning("Error in processing metadata file %s, " + "should not contain event id %u.", g_quark_to_string(tf->name), + tf->event.event_id); + err = EPERM; + goto event_id_error; + } else { + char *pos; + const char *marker_name, *format; + uint16_t id; + guint8 int_size, long_size, pointer_size, size_t_size, alignment; + + switch((enum marker_id)tf->event.event_id) { + case MARKER_ID_SET_MARKER_ID: + marker_name = pos = tf->event.data; + g_debug("Doing MARKER_ID_SET_MARKER_ID of marker %s", marker_name); + pos += strlen(marker_name) + 1; + pos += ltt_align((size_t)pos, sizeof(guint16), tf->alignment); + id = ltt_get_uint16(LTT_GET_BO(tf), pos); + g_debug("In MARKER_ID_SET_MARKER_ID of marker %s id %hu", + marker_name, id); + pos += sizeof(guint16); + int_size = *(guint8*)pos; + pos += sizeof(guint8); + long_size = *(guint8*)pos; + pos += sizeof(guint8); + pointer_size = *(guint8*)pos; + pos += sizeof(guint8); + size_t_size = *(guint8*)pos; + pos += sizeof(guint8); + alignment = *(guint8*)pos; + pos += sizeof(guint8); + marker_id_event(tf->trace, g_quark_from_string(marker_name), + id, int_size, long_size, + pointer_size, size_t_size, alignment); + break; + case MARKER_ID_SET_MARKER_FORMAT: + marker_name = pos = tf->event.data; + g_debug("Doing MARKER_ID_SET_MARKER_FORMAT of marker %s", + marker_name); + pos += strlen(marker_name) + 1; + format = pos; + pos += strlen(format) + 1; + marker_format_event(tf->trace, g_quark_from_string(marker_name), + format); + /* get information from dictionary TODO */ + break; + default: + g_warning("Error in processing metadata file %s, " + "unknown event id %hhu.", + g_quark_to_string(tf->name), + tf->event.event_id); + err = EPERM; + goto event_id_error; + } + } + } + return 0; + + /* Error handling */ +event_id_error: +update_error: +seek_error: + g_warning("An error occured in metadata tracefile parsing"); + return err; +} + +/* + * Open a trace and return its LttTrace handle. + * + * pathname must be the directory of the trace + */ + +LttTrace *ltt_trace_open(const gchar *pathname) +{ + gchar abs_path[PATH_MAX]; + LttTrace * t; + LttTracefile *tf; + GArray *group; + int i, ret; + ltt_subbuffer_header_t *header; + DIR *dir; + struct dirent *entry; + guint control_found = 0; + struct stat stat_buf; + gchar path[PATH_MAX]; + + t = g_new(LttTrace, 1); + if(!t) goto alloc_error; + + get_absolute_pathname(pathname, abs_path); + t->pathname = g_quark_from_string(abs_path); + + g_datalist_init(&t->tracefiles); + + /* Test to see if it looks like a trace */ + dir = opendir(abs_path); + if(dir == NULL) { + perror(abs_path); + goto open_error; + } + while((entry = readdir(dir)) != NULL) { + strcpy(path, abs_path); + strcat(path, "/"); + strcat(path, entry->d_name); + ret = stat(path, &stat_buf); + if(ret == -1) { + perror(path); + continue; + } + if(S_ISDIR(stat_buf.st_mode)) { + if(strcmp(entry->d_name, "control") == 0) { + control_found = 1; + } + } + } + closedir(dir); + + if(!control_found) goto find_error; + + /* Open all the tracefiles */ + if(open_tracefiles(t, abs_path, "")) { + g_warning("Error opening tracefile %s", abs_path); + goto find_error; + } + + /* Parse each trace control/metadata_N files : get runtime fac. info */ + group = g_datalist_id_get_data(&t->tracefiles, LTT_TRACEFILE_NAME_METADATA); + if(group == NULL) { + g_error("Trace %s has no metadata tracefile", abs_path); + g_assert(0); + goto metadata_error; + } + + /* + * Get the trace information for the control/metadata_0 tracefile. + * Getting a correct trace start_time and start_tsc is insured by the fact + * that no subbuffers are supposed to be lost in the metadata channel. + * Therefore, the first subbuffer contains the start_tsc timestamp in its + * buffer header. + */ + g_assert(group->len > 0); + tf = &g_array_index (group, LttTracefile, 0); + header = (ltt_subbuffer_header_t *)tf->buffer.head; + ret = parse_trace_header(header, tf, t); + g_assert(!ret); + + t->num_cpu = group->len; + + ret = allocate_marker_data(t); + if (ret) + g_error("Error in allocating marker data"); + + for(i=0; ilen; i++) { + tf = &g_array_index (group, LttTracefile, i); + if (tf->cpu_online) + if(ltt_process_metadata_tracefile(tf)) + goto metadata_error; + } + + return t; + + /* Error handling */ +metadata_error: + destroy_marker_data(t); +find_error: + g_datalist_clear(&t->tracefiles); +open_error: + g_free(t); +alloc_error: + return NULL; + +} + +/* Open another, completely independant, instance of a trace. + * + * A read on this new instance will read the first event of the trace. + * + * When we copy a trace, we want all the opening actions to happen again : + * the trace will be reopened and totally independant from the original. + * That's why we call ltt_trace_open. + */ +LttTrace *ltt_trace_copy(LttTrace *self) +{ + return ltt_trace_open(g_quark_to_string(self->pathname)); +} + +/* + * Close a trace + */ + +void ltt_trace_close(LttTrace *t) +{ + g_datalist_clear(&t->tracefiles); + g_free(t); +} + + +/***************************************************************************** + * Get the start time and end time of the trace + ****************************************************************************/ + +void ltt_tracefile_time_span_get(LttTracefile *tf, + LttTime *start, LttTime *end) +{ + int err; + + err = map_block(tf, 0); + if(unlikely(err)) { + g_error("Can not map block"); + *start = ltt_time_infinite; + } else + *start = tf->buffer.begin.timestamp; + + err = map_block(tf, tf->num_blocks - 1); /* Last block */ + if(unlikely(err)) { + g_error("Can not map block"); + *end = ltt_time_zero; + } else + *end = tf->buffer.end.timestamp; +} + +struct tracefile_time_span_get_args { + LttTrace *t; + LttTime *start; + LttTime *end; +}; + +static void group_time_span_get(GQuark name, gpointer data, gpointer user_data) +{ + struct tracefile_time_span_get_args *args = + (struct tracefile_time_span_get_args*)user_data; + + GArray *group = (GArray *)data; + int i; + LttTracefile *tf; + LttTime tmp_start; + LttTime tmp_end; + + for(i=0; ilen; i++) { + tf = &g_array_index (group, LttTracefile, i); + if(tf->cpu_online) { + ltt_tracefile_time_span_get(tf, &tmp_start, &tmp_end); + if(ltt_time_compare(*args->start, tmp_start)>0) *args->start = tmp_start; + if(ltt_time_compare(*args->end, tmp_end)<0) *args->end = tmp_end; + } + } +} + +/* return the start and end time of a trace */ + +void ltt_trace_time_span_get(LttTrace *t, LttTime *start, LttTime *end) +{ + LttTime min_start = ltt_time_infinite; + LttTime max_end = ltt_time_zero; + struct tracefile_time_span_get_args args = { t, &min_start, &max_end }; + + g_datalist_foreach(&t->tracefiles, &group_time_span_get, &args); + + if(start != NULL) *start = min_start; + if(end != NULL) *end = max_end; + +} + + +/* Seek to the first event in a tracefile that has a time equal or greater than + * the time passed in parameter. + * + * If the time parameter is outside the tracefile time span, seek to the first + * event or if after, return ERANGE. + * + * If the time parameter is before the first event, we have to seek specially to + * there. + * + * If the time is after the end of the trace, return ERANGE. + * + * Do a binary search to find the right block, then a sequential search in the + * block to find the event. + * + * In the special case where the time requested fits inside a block that has no + * event corresponding to the requested time, the first event of the next block + * will be seeked. + * + * IMPORTANT NOTE : // FIXME everywhere... + * + * You MUST NOT do a ltt_tracefile_read right after a ltt_tracefile_seek_time : + * you will jump over an event if you do. + * + * Return value : 0 : no error, the tf->event can be used + * ERANGE : time if after the last event of the trace + * otherwise : this is an error. + * + * */ + +int ltt_tracefile_seek_time(LttTracefile *tf, LttTime time) +{ + int ret = 0; + int err; + unsigned int block_num, high, low; + + /* seek at the beginning of trace */ + err = map_block(tf, 0); /* First block */ + if(unlikely(err)) { + g_error("Can not map block"); + goto fail; + } + + /* If the time is lower or equal the beginning of the trace, + * go to the first event. */ + if(ltt_time_compare(time, tf->buffer.begin.timestamp) <= 0) { + ret = ltt_tracefile_read(tf); + if(ret == ERANGE) goto range; + else if (ret) goto fail; + goto found; /* There is either no event in the trace or the event points + to the first event in the trace */ + } + + err = map_block(tf, tf->num_blocks - 1); /* Last block */ + if(unlikely(err)) { + g_error("Can not map block"); + goto fail; + } + + /* If the time is after the end of the trace, return ERANGE. */ + if(ltt_time_compare(time, tf->buffer.end.timestamp) > 0) { + goto range; + } + + /* Binary search the block */ + high = tf->num_blocks - 1; + low = 0; + + while(1) { + block_num = ((high-low) / 2) + low; + + err = map_block(tf, block_num); + if(unlikely(err)) { + g_error("Can not map block"); + goto fail; + } + if(high == low) { + /* We cannot divide anymore : this is what would happen if the time + * requested was exactly between two consecutive buffers'end and start + * timestamps. This is also what would happend if we didn't deal with out + * of span cases prior in this function. */ + /* The event is right in the buffer! + * (or in the next buffer first event) */ + while(1) { + ret = ltt_tracefile_read(tf); + if(ret == ERANGE) goto range; /* ERANGE or EPERM */ + else if(ret) goto fail; + + if(ltt_time_compare(time, tf->event.event_time) <= 0) + goto found; + } + + } else if(ltt_time_compare(time, tf->buffer.begin.timestamp) < 0) { + /* go to lower part */ + high = block_num - 1; + } else if(ltt_time_compare(time, tf->buffer.end.timestamp) > 0) { + /* go to higher part */ + low = block_num + 1; + } else {/* The event is right in the buffer! + (or in the next buffer first event) */ + while(1) { + ret = ltt_tracefile_read(tf); + if(ret == ERANGE) goto range; /* ERANGE or EPERM */ + else if(ret) goto fail; + + if(ltt_time_compare(time, tf->event.event_time) <= 0) + break; + } + goto found; + } + } + +found: + return 0; +range: + return ERANGE; + + /* Error handling */ +fail: + g_error("ltt_tracefile_seek_time failed on tracefile %s", + g_quark_to_string(tf->name)); + return EPERM; +} + +/* Seek to a position indicated by an LttEventPosition + */ + +int ltt_tracefile_seek_position(LttTracefile *tf, const LttEventPosition *ep) +{ + int err; + + if(ep->tracefile != tf) { + goto fail; + } + + err = map_block(tf, ep->block); + if(unlikely(err)) { + g_error("Can not map block"); + goto fail; + } + + tf->event.offset = ep->offset; + + /* Put back the event real tsc */ + tf->event.tsc = ep->tsc; + tf->buffer.tsc = ep->tsc; + + err = ltt_tracefile_read_update_event(tf); + if(err) goto fail; + + /* deactivate this, as it does nothing for now + err = ltt_tracefile_read_op(tf); + if(err) goto fail; + */ + + return 0; + +fail: + g_error("ltt_tracefile_seek_time failed on tracefile %s", + g_quark_to_string(tf->name)); + return 1; +} + +/* Given a TSC value, return the LttTime (seconds,nanoseconds) it + * corresponds to. + */ + +LttTime ltt_interpolate_time_from_tsc(LttTracefile *tf, guint64 tsc) +{ + LttTime time; + + if(tsc > tf->trace->start_tsc) { + time = ltt_time_from_uint64( + (double)(tsc - tf->trace->start_tsc) + * (1000000000.0 / tf->trace->freq_scale) + / (double)tf->trace->start_freq); + time = ltt_time_add(tf->trace->start_time_from_tsc, time); + } else { + time = ltt_time_from_uint64( + (double)(tf->trace->start_tsc - tsc) + * (1000000000.0 / tf->trace->freq_scale) + / (double)tf->trace->start_freq); + time = ltt_time_sub(tf->trace->start_time_from_tsc, time); + } + return time; +} + +/* Calculate the real event time based on the buffer boundaries */ +LttTime ltt_interpolate_time(LttTracefile *tf, LttEvent *event) +{ + return ltt_interpolate_time_from_tsc(tf, tf->buffer.tsc); +} + + +/* Get the current event of the tracefile : valid until the next read */ +LttEvent *ltt_tracefile_get_event(LttTracefile *tf) +{ + return &tf->event; +} + + + +/***************************************************************************** + *Function name + * ltt_tracefile_read : Read the next event in the tracefile + *Input params + * t : tracefile + *Return value + * + * Returns 0 if an event can be used in tf->event. + * Returns ERANGE on end of trace. The event in tf->event still can be used + * (if the last block was not empty). + * Returns EPERM on error. + * + * This function does make the tracefile event structure point to the event + * currently pointed to by the tf->event. + * + * Note : you must call a ltt_tracefile_seek to the beginning of the trace to + * reinitialize it after an error if you want results to be coherent. + * It would be the case if a end of trace last buffer has no event : the end + * of trace wouldn't be returned, but an error. + * We make the assumption there is at least one event per buffer. + ****************************************************************************/ + +int ltt_tracefile_read(LttTracefile *tf) +{ + int err; + + err = ltt_tracefile_read_seek(tf); + if(err) return err; + err = ltt_tracefile_read_update_event(tf); + if(err) return err; + + /* deactivate this, as it does nothing for now + err = ltt_tracefile_read_op(tf); + if(err) return err; + */ + + return 0; +} + +int ltt_tracefile_read_seek(LttTracefile *tf) +{ + int err; + + /* Get next buffer until we finally have an event, or end of trace */ + while(1) { + err = ltt_seek_next_event(tf); + if(unlikely(err == ENOPROTOOPT)) { + return EPERM; + } + + /* Are we at the end of the buffer ? */ + if(err == ERANGE) { + if(unlikely(tf->buffer.index == tf->num_blocks-1)){ /* end of trace ? */ + return ERANGE; + } else { + /* get next block */ + err = map_block(tf, tf->buffer.index + 1); + if(unlikely(err)) { + g_error("Can not map block"); + return EPERM; + } + } + } else break; /* We found an event ! */ + } + + return 0; +} + +/* do an operation when reading a new event */ + +/* This function does nothing for now */ +#if 0 +int ltt_tracefile_read_op(LttTracefile *tf) +{ + LttEvent *event; + + event = &tf->event; + + /* do event specific operation */ + + /* nothing */ + + return 0; +} +#endif + +static void print_debug_event_header(LttEvent *ev, void *start_pos, void *end_pos) +{ + unsigned int offset = 0; + int i, j; + + g_printf("Event header (tracefile %s offset %llx):\n", + g_quark_to_string(ev->tracefile->long_name), + ((uint64_t)ev->tracefile->buffer.index * ev->tracefile->buf_size) + + (long)start_pos - (long)ev->tracefile->buffer.head); + + while (offset < (long)end_pos - (long)start_pos) { + g_printf("%8lx", (long)start_pos - (long)ev->tracefile->buffer.head + offset); + g_printf(" "); + + for (i = 0; i < 4 ; i++) { + for (j = 0; j < 4; j++) { + if (offset + ((i * 4) + j) < + (long)end_pos - (long)start_pos) + g_printf("%02hhX", + ((char*)start_pos)[offset + ((i * 4) + j)]); + else + g_printf(" "); + g_printf(" "); + } + if (i < 4) + g_printf(" "); + } + offset+=16; + g_printf("\n"); + } +} + + +/* same as ltt_tracefile_read, but does not seek to the next event nor call + * event specific operation. */ +int ltt_tracefile_read_update_event(LttTracefile *tf) +{ + void * pos; + LttEvent *event; + void *pos_aligned; + + event = &tf->event; + pos = tf->buffer.head + event->offset; + + /* Read event header */ + + /* Align the head */ + pos += ltt_align((size_t)pos, sizeof(guint32), tf->alignment); + pos_aligned = pos; + + event->timestamp = ltt_get_uint32(LTT_GET_BO(tf), pos); + event->event_id = event->timestamp >> tf->tscbits; + event->timestamp = event->timestamp & tf->tsc_mask; + pos += sizeof(guint32); + + switch (event->event_id) { + case 29: /* LTT_RFLAG_ID_SIZE_TSC */ + event->event_id = ltt_get_uint16(LTT_GET_BO(tf), pos); + pos += sizeof(guint16); + event->event_size = ltt_get_uint16(LTT_GET_BO(tf), pos); + pos += sizeof(guint16); + if (event->event_size == 0xFFFF) { + event->event_size = ltt_get_uint32(LTT_GET_BO(tf), pos); + pos += sizeof(guint32); + } + pos += ltt_align((size_t)pos, sizeof(guint64), tf->alignment); + tf->buffer.tsc = ltt_get_uint64(LTT_GET_BO(tf), pos); + pos += sizeof(guint64); + break; + case 30: /* LTT_RFLAG_ID_SIZE */ + event->event_id = ltt_get_uint16(LTT_GET_BO(tf), pos); + pos += sizeof(guint16); + event->event_size = ltt_get_uint16(LTT_GET_BO(tf), pos); + pos += sizeof(guint16); + if (event->event_size == 0xFFFF) { + event->event_size = ltt_get_uint32(LTT_GET_BO(tf), pos); + pos += sizeof(guint32); + } + break; + case 31: /* LTT_RFLAG_ID */ + event->event_id = ltt_get_uint16(LTT_GET_BO(tf), pos); + pos += sizeof(guint16); + event->event_size = G_MAXUINT; + break; + default: + event->event_size = G_MAXUINT; + break; + } + + if (likely(event->event_id != 29)) { + /* No extended timestamp */ + if (event->timestamp < (tf->buffer.tsc & tf->tsc_mask)) + tf->buffer.tsc = ((tf->buffer.tsc & ~tf->tsc_mask) /* overflow */ + + tf->tsc_mask_next_bit) + | (guint64)event->timestamp; + else + tf->buffer.tsc = (tf->buffer.tsc & ~tf->tsc_mask) /* no overflow */ + | (guint64)event->timestamp; + } + event->tsc = tf->buffer.tsc; + + event->event_time = ltt_interpolate_time(tf, event); + + if (a_event_debug) + print_debug_event_header(event, pos_aligned, pos); + + event->data = pos; + + /* + * Let ltt_update_event_size update event->data according to the largest + * alignment within the payload. + * Get the data size and update the event fields with the current + * information. */ + ltt_update_event_size(tf); + + return 0; +} + + +/**************************************************************************** + *Function name + * map_block : map a block from the file + *Input Params + * lttdes : ltt trace file + * whichBlock : the block which will be read + *return value + * 0 : success + * EINVAL : lseek fail + * EIO : can not read from the file + ****************************************************************************/ + +static gint map_block(LttTracefile * tf, guint block_num) +{ + int page_size = getpagesize(); + ltt_subbuffer_header_t *header; + + g_assert(block_num < tf->num_blocks); + + if(tf->buffer.head != NULL) { + if(munmap(tf->buffer.head, PAGE_ALIGN(tf->buf_size))) { + g_warning("unmap size : %u\n", + PAGE_ALIGN(tf->buf_size)); + perror("munmap error"); + g_assert(0); + } + } + + /* Multiple of pages aligned head */ + tf->buffer.head = mmap(0, + PAGE_ALIGN(tf->buf_size), + PROT_READ, MAP_PRIVATE, tf->fd, + PAGE_ALIGN((off_t)tf->buf_size * (off_t)block_num)); + + if(tf->buffer.head == MAP_FAILED) { + perror("Error in allocating memory for buffer of tracefile"); + g_assert(0); + goto map_error; + } + g_assert( ( (gulong)tf->buffer.head&(8-1) ) == 0); // make sure it's aligned. + + + tf->buffer.index = block_num; + + header = (ltt_subbuffer_header_t *)tf->buffer.head; + + tf->buffer.begin.cycle_count = ltt_get_uint64(LTT_GET_BO(tf), + &header->cycle_count_begin); + tf->buffer.begin.freq = tf->trace->start_freq; + + tf->buffer.begin.timestamp = ltt_interpolate_time_from_tsc(tf, + tf->buffer.begin.cycle_count); + tf->buffer.end.cycle_count = ltt_get_uint64(LTT_GET_BO(tf), + &header->cycle_count_end); + tf->buffer.end.freq = tf->trace->start_freq; + + tf->buffer.lost_size = ltt_get_uint32(LTT_GET_BO(tf), + &header->lost_size); + tf->buffer.end.timestamp = ltt_interpolate_time_from_tsc(tf, + tf->buffer.end.cycle_count); + tf->buffer.tsc = tf->buffer.begin.cycle_count; + tf->event.tsc = tf->buffer.tsc; + tf->buffer.freq = tf->buffer.begin.freq; + + /* FIXME + * eventually support variable buffer size : will need a partial pre-read of + * the headers to create an index when we open the trace... eventually. */ + g_assert(tf->buf_size == ltt_get_uint32(LTT_GET_BO(tf), + &header->buf_size)); + + /* Make the current event point to the beginning of the buffer : + * it means that the event read must get the first event. */ + tf->event.tracefile = tf; + tf->event.block = block_num; + tf->event.offset = 0; + + return 0; + +map_error: + return -errno; +} + +static void print_debug_event_data(LttEvent *ev) +{ + unsigned int offset = 0; + int i, j; + + if (!max(ev->event_size, ev->data_size)) + return; + + g_printf("Event data (tracefile %s offset %llx):\n", + g_quark_to_string(ev->tracefile->long_name), + ((uint64_t)ev->tracefile->buffer.index * ev->tracefile->buf_size) + + (long)ev->data - (long)ev->tracefile->buffer.head); + + while (offset < max(ev->event_size, ev->data_size)) { + g_printf("%8lx", (long)ev->data + offset + - (long)ev->tracefile->buffer.head); + g_printf(" "); + + for (i = 0; i < 4 ; i++) { + for (j = 0; j < 4; j++) { + if (offset + ((i * 4) + j) < max(ev->event_size, ev->data_size)) + g_printf("%02hhX", ((char*)ev->data)[offset + ((i * 4) + j)]); + else + g_printf(" "); + g_printf(" "); + } + if (i < 4) + g_printf(" "); + } + + g_printf(" "); + + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + if (offset + ((i * 4) + j) < max(ev->event_size, ev->data_size)) { + if (isprint(((char*)ev->data)[offset + ((i * 4) + j)])) + g_printf("%c", ((char*)ev->data)[offset + ((i * 4) + j)]); + else + g_printf("."); + } else + g_printf(" "); + } + } + offset+=16; + g_printf("\n"); + } +} + +/* It will update the fields offsets too */ +void ltt_update_event_size(LttTracefile *tf) +{ + off_t size = 0; + char *tscdata; + struct marker_info *info; + + switch((enum marker_id)tf->event.event_id) { + case MARKER_ID_SET_MARKER_ID: + size = strlen((char*)tf->event.data) + 1; + g_debug("marker %s id set", (char*)tf->event.data); + size += ltt_align(size, sizeof(guint16), tf->alignment); + size += sizeof(guint16); + size += sizeof(guint8); + size += sizeof(guint8); + size += sizeof(guint8); + size += sizeof(guint8); + size += sizeof(guint8); + break; + case MARKER_ID_SET_MARKER_FORMAT: + g_debug("marker %s format set", (char*)tf->event.data); + size = strlen((char*)tf->event.data) + 1; + size += strlen((char*)tf->event.data + size) + 1; + break; + } + + info = marker_get_info_from_id(tf->trace, tf->event.event_id); + + if (tf->event.event_id >= MARKER_CORE_IDS) + g_assert(info != NULL); + + /* Do not update field offsets of core markers when initially reading the + * metadata tracefile when the infos about these markers do not exist yet. + */ + if (likely(info && info->fields)) { + /* alignment */ + tf->event.data += ltt_align((off_t)(unsigned long)tf->event.data, + info->largest_align, + info->alignment); + /* size, dynamically computed */ + if (info->size != -1) + size = info->size; + else + size = marker_update_fields_offsets(marker_get_info_from_id(tf->trace, + tf->event.event_id), tf->event.data); + } + + tf->event.data_size = size; + + /* Check consistency between kernel and LTTV structure sizes */ + if(tf->event.event_size == G_MAXUINT) { + /* Event size too big to fit in the event size field */ + tf->event.event_size = tf->event.data_size; + } + + if (a_event_debug) + print_debug_event_data(&tf->event); + + /* Having a marker load or marker format event out of the metadata + * tracefiles is a serious bug. */ + switch((enum marker_id)tf->event.event_id) { + case MARKER_ID_SET_MARKER_ID: + case MARKER_ID_SET_MARKER_FORMAT: + if (tf->name != g_quark_from_string("/control/metadata")) + g_error("Trace inconsistency : metadata event found in data " + "tracefile %s", g_quark_to_string(tf->long_name)); + } + + if (tf->event.data_size != tf->event.event_size) { + struct marker_info *info = marker_get_info_from_id(tf->trace, + tf->event.event_id); + g_error("Kernel/LTTV event size differs for event %s: kernel %u, LTTV %u", + g_quark_to_string(info->name), + tf->event.event_size, tf->event.data_size); + exit(-1); + } +} + + +/* Take the tf current event offset and use the event id to figure out where is + * the next event offset. + * + * This is an internal function not aiming at being used elsewhere : it will + * not jump over the current block limits. Please consider using + * ltt_tracefile_read to do this. + * + * Returns 0 on success + * ERANGE if we are at the end of the buffer. + * ENOPROTOOPT if an error occured when getting the current event size. + */ +static int ltt_seek_next_event(LttTracefile *tf) +{ + int ret = 0; + void *pos; + + /* seek over the buffer header if we are at the buffer start */ + if(tf->event.offset == 0) { + tf->event.offset += tf->buffer_header_size; + + if(tf->event.offset == tf->buf_size - tf->buffer.lost_size) { + ret = ERANGE; + } + goto found; + } + + pos = tf->event.data; + + if(tf->event.data_size < 0) goto error; + + pos += (size_t)tf->event.data_size; + + tf->event.offset = pos - tf->buffer.head; + + if(tf->event.offset == tf->buf_size - tf->buffer.lost_size) { + ret = ERANGE; + goto found; + } + g_assert(tf->event.offset < tf->buf_size - tf->buffer.lost_size); + +found: + return ret; + +error: + g_error("Error in ltt_seek_next_event for tracefile %s", + g_quark_to_string(tf->name)); + return ENOPROTOOPT; +} + +#if 0 +/***************************************************************************** + *Function name + * set_fields_offsets : set the precomputable offset of the fields + *Input params + * tracefile : opened trace file + * event_type : the event type + ****************************************************************************/ + +void set_fields_offsets(LttTracefile *tf, LttEventType *event_type) +{ + LttField *field = event_type->root_field; + enum field_status fixed_root = FIELD_FIXED, fixed_parent = FIELD_FIXED; + + if(likely(field)) + preset_field_type_size(tf, event_type, 0, 0, + &fixed_root, &fixed_parent, + field); + +} +#endif //0 + + +/***************************************************************************** + *Function name + * get_alignment : Get the alignment needed for a field. + *Input params + * field : field + * + * returns : The size on which it must be aligned. + * + ****************************************************************************/ +#if 0 +off_t get_alignment(LttField *field) +{ + LttType *type = &field->field_type; + + switch(type->type_class) { + case LTT_INT_FIXED: + case LTT_UINT_FIXED: + case LTT_POINTER: + case LTT_CHAR: + case LTT_UCHAR: + case LTT_SHORT: + case LTT_USHORT: + case LTT_INT: + case LTT_UINT: + case LTT_LONG: + case LTT_ULONG: + case LTT_SIZE_T: + case LTT_SSIZE_T: + case LTT_OFF_T: + case LTT_FLOAT: + case LTT_ENUM: + /* Align offset on type size */ + g_assert(field->field_size != 0); + return field->field_size; + break; + case LTT_STRING: + return 1; + break; + case LTT_ARRAY: + g_assert(type->fields->len == 1); + { + LttField *child = &g_array_index(type->fields, LttField, 0); + return get_alignment(child); + } + break; + case LTT_SEQUENCE: + g_assert(type->fields->len == 2); + { + off_t localign = 1; + LttField *child = &g_array_index(type->fields, LttField, 0); + + localign = max(localign, get_alignment(child)); + + child = &g_array_index(type->fields, LttField, 1); + localign = max(localign, get_alignment(child)); + + return localign; + } + break; + case LTT_STRUCT: + case LTT_UNION: + { + guint i; + off_t localign = 1; + + for(i=0; ifields->len; i++) { + LttField *child = &g_array_index(type->fields, LttField, i); + localign = max(localign, get_alignment(child)); + } + return localign; + } + break; + case LTT_NONE: + default: + g_error("get_alignment : unknown type"); + return -1; + } +} + +#endif //0 + +/***************************************************************************** + *Function name + * field_compute_static_size : Determine the size of fields known by their + * sole definition. Unions, arrays and struct sizes might be known, but + * the parser does not give that information. + *Input params + * tf : tracefile + * field : field + * + ****************************************************************************/ +#if 0 +void field_compute_static_size(LttFacility *fac, LttField *field) +{ + LttType *type = &field->field_type; + + switch(type->type_class) { + case LTT_INT_FIXED: + case LTT_UINT_FIXED: + case LTT_POINTER: + case LTT_CHAR: + case LTT_UCHAR: + case LTT_SHORT: + case LTT_USHORT: + case LTT_INT: + case LTT_UINT: + case LTT_LONG: + case LTT_ULONG: + case LTT_SIZE_T: + case LTT_SSIZE_T: + case LTT_OFF_T: + case LTT_FLOAT: + case LTT_ENUM: + case LTT_STRING: + /* nothing to do */ + break; + case LTT_ARRAY: + /* note this : array type size is the number of elements in the array, + * while array field size of the length of the array in bytes */ + g_assert(type->fields->len == 1); + { + LttField *child = &g_array_index(type->fields, LttField, 0); + field_compute_static_size(fac, child); + + if(child->field_size != 0) { + field->field_size = type->size * child->field_size; + field->dynamic_offsets = g_array_sized_new(FALSE, TRUE, + sizeof(off_t), type->size); + } else { + field->field_size = 0; + } + } + break; + case LTT_SEQUENCE: + g_assert(type->fields->len == 2); + { + off_t local_offset = 0; + LttField *child = &g_array_index(type->fields, LttField, 1); + field_compute_static_size(fac, child); + field->field_size = 0; + type->size = 0; + if(child->field_size != 0) { + field->dynamic_offsets = g_array_sized_new(FALSE, TRUE, + sizeof(off_t), SEQUENCE_AVG_ELEMENTS); + } + } + break; + case LTT_STRUCT: + case LTT_UNION: + { + guint i; + for(i=0;ifields->len;i++) { + LttField *child = &g_array_index(type->fields, LttField, i); + field_compute_static_size(fac, child); + if(child->field_size != 0) { + type->size += ltt_align(type->size, get_alignment(child), + fac->alignment); + type->size += child->field_size; + } else { + /* As soon as we find a child with variable size, we have + * a variable size */ + type->size = 0; + break; + } + } + field->field_size = type->size; + } + break; + default: + g_error("field_static_size : unknown type"); + } + +} +#endif //0 + + +/***************************************************************************** + *Function name + * precompute_fields_offsets : set the precomputable offset of the fields + *Input params + * fac : facility + * field : the field + * offset : pointer to the current offset, must be incremented + * + * return : 1 : found a variable length field, stop the processing. + * 0 otherwise. + ****************************************************************************/ + +#if 0 +gint precompute_fields_offsets(LttFacility *fac, LttField *field, off_t *offset, gint is_compact) +{ + LttType *type = &field->field_type; + + if(unlikely(is_compact)) { + g_assert(field->field_size != 0); + /* FIXME THIS IS A HUUUUUGE hack : + * offset is between the compact_data field in struct LttEvent + * and the address of the field root in the memory map. + * ark. Both will stay at the same addresses while the event + * is readable, so it's ok. + */ + field->offset_root = 0; + field->fixed_root = FIELD_FIXED; + return 0; + } + + switch(type->type_class) { + case LTT_INT_FIXED: + case LTT_UINT_FIXED: + case LTT_POINTER: + case LTT_CHAR: + case LTT_UCHAR: + case LTT_SHORT: + case LTT_USHORT: + case LTT_INT: + case LTT_UINT: + case LTT_LONG: + case LTT_ULONG: + case LTT_SIZE_T: + case LTT_SSIZE_T: + case LTT_OFF_T: + case LTT_FLOAT: + case LTT_ENUM: + g_assert(field->field_size != 0); + /* Align offset on type size */ + *offset += ltt_align(*offset, get_alignment(field), + fac->alignment); + /* remember offset */ + field->offset_root = *offset; + field->fixed_root = FIELD_FIXED; + /* Increment offset */ + *offset += field->field_size; + return 0; + break; + case LTT_STRING: + field->offset_root = *offset; + field->fixed_root = FIELD_FIXED; + return 1; + break; + case LTT_ARRAY: + g_assert(type->fields->len == 1); + { + LttField *child = &g_array_index(type->fields, LttField, 0); + + *offset += ltt_align(*offset, get_alignment(field), + fac->alignment); + + /* remember offset */ + field->offset_root = *offset; + field->array_offset = *offset; + field->fixed_root = FIELD_FIXED; + + /* Let the child be variable */ + //precompute_fields_offsets(tf, child, offset); + + if(field->field_size != 0) { + /* Increment offset */ + /* field_size is the array size in bytes */ + *offset += field->field_size; + return 0; + } else { + return 1; + } + } + break; + case LTT_SEQUENCE: + g_assert(type->fields->len == 2); + { + LttField *child; + guint ret; + + *offset += ltt_align(*offset, get_alignment(field), + fac->alignment); + + /* remember offset */ + field->offset_root = *offset; + field->fixed_root = FIELD_FIXED; + + child = &g_array_index(type->fields, LttField, 0); + ret = precompute_fields_offsets(fac, child, offset, is_compact); + g_assert(ret == 0); /* Seq len cannot have variable len */ + + child = &g_array_index(type->fields, LttField, 1); + *offset += ltt_align(*offset, get_alignment(child), + fac->alignment); + field->array_offset = *offset; + /* Let the child be variable. */ + //ret = precompute_fields_offsets(fac, child, offset); + + /* Cannot precompute fields offsets of sequence members, and has + * variable length. */ + return 1; + } + break; + case LTT_STRUCT: + { + LttField *child; + guint i; + gint ret=0; + + *offset += ltt_align(*offset, get_alignment(field), + fac->alignment); + /* remember offset */ + field->offset_root = *offset; + field->fixed_root = FIELD_FIXED; + + for(i=0; i< type->fields->len; i++) { + child = &g_array_index(type->fields, LttField, i); + ret = precompute_fields_offsets(fac, child, offset, is_compact); + + if(ret) break; + } + return ret; + } + break; + case LTT_UNION: + { + LttField *child; + guint i; + gint ret=0; + + *offset += ltt_align(*offset, get_alignment(field), + fac->alignment); + /* remember offset */ + field->offset_root = *offset; + field->fixed_root = FIELD_FIXED; + + for(i=0; i< type->fields->len; i++) { + *offset = field->offset_root; + child = &g_array_index(type->fields, LttField, i); + ret = precompute_fields_offsets(fac, child, offset, is_compact); + + if(ret) break; + } + *offset = field->offset_root + field->field_size; + return ret; + } + + break; + case LTT_NONE: + default: + g_error("precompute_fields_offsets : unknown type"); + return 1; + } + +} + +#endif //0 + +#if 0 +/***************************************************************************** + *Function name + * precompute_offsets : set the precomputable offset of an event type + *Input params + * tf : tracefile + * event : event type + * + ****************************************************************************/ +void precompute_offsets(LttFacility *fac, LttEventType *event) +{ + guint i; + off_t offset = 0; + gint ret; + + /* First, compute the size of fixed size fields. Will determine size for + * arrays, struct and unions, which is not done by the parser */ + for(i=0; ifields->len; i++) { + LttField *field = &g_array_index(event->fields, LttField, i); + field_compute_static_size(fac, field); + } + + /* Precompute all known offsets */ + for(i=0; ifields->len; i++) { + LttField *field = &g_array_index(event->fields, LttField, i); + if(event->has_compact_data && i == 0) + ret = precompute_fields_offsets(fac, field, &offset, 1); + else + ret = precompute_fields_offsets(fac, field, &offset, 0); + if(ret) break; + } +} +#endif //0 + + + +/***************************************************************************** + *Function name + * preset_field_type_size : set the fixed sizes of the field type + *Input params + * tf : tracefile + * event_type : event type + * offset_root : offset from the root + * offset_parent : offset from the parent + * fixed_root : Do we know a fixed offset to the root ? + * fixed_parent : Do we know a fixed offset to the parent ? + * field : field + ****************************************************************************/ + + + +// preset the fixed size offsets. Calculate them just like genevent-new : an +// increment of a *to value that represents the offset from the start of the +// event data. +// The preset information is : offsets up to (and including) the first element +// of variable size. All subsequent fields must be flagged "VARIABLE OFFSET". +#if 0 +void preset_field_type_size(LttTracefile *tf, LttEventType *event_type, + off_t offset_root, off_t offset_parent, + enum field_status *fixed_root, enum field_status *fixed_parent, + LttField *field) +{ + enum field_status local_fixed_root, local_fixed_parent; + guint i; + LttType *type; + + g_assert(field->fixed_root == FIELD_UNKNOWN); + g_assert(field->fixed_parent == FIELD_UNKNOWN); + g_assert(field->fixed_size == FIELD_UNKNOWN); + + type = field->field_type; + + field->fixed_root = *fixed_root; + if(field->fixed_root == FIELD_FIXED) + field->offset_root = offset_root; + else + field->offset_root = 0; + + field->fixed_parent = *fixed_parent; + if(field->fixed_parent == FIELD_FIXED) + field->offset_parent = offset_parent; + else + field->offset_parent = 0; + + size_t current_root_offset; + size_t current_offset; + enum field_status current_child_status, final_child_status; + size_t max_size; + + switch(type->type_class) { + case LTT_INT_FIXED: + case LTT_UINT_FIXED: + case LTT_CHAR: + case LTT_UCHAR: + case LTT_SHORT: + case LTT_USHORT: + case LTT_INT: + case LTT_UINT: + case LTT_FLOAT: + case LTT_ENUM: + field->field_size = ltt_type_size(tf->trace, type); + field->fixed_size = FIELD_FIXED; + break; + case LTT_POINTER: + field->field_size = (off_t)event_type->facility->pointer_size; + field->fixed_size = FIELD_FIXED; + break; + case LTT_LONG: + case LTT_ULONG: + field->field_size = (off_t)event_type->facility->long_size; + field->fixed_size = FIELD_FIXED; + break; + case LTT_SIZE_T: + case LTT_SSIZE_T: + case LTT_OFF_T: + field->field_size = (off_t)event_type->facility->size_t_size; + field->fixed_size = FIELD_FIXED; + break; + case LTT_SEQUENCE: + local_fixed_root = FIELD_VARIABLE; + local_fixed_parent = FIELD_VARIABLE; + preset_field_type_size(tf, event_type, + 0, 0, + &local_fixed_root, &local_fixed_parent, + field->child[0]); + field->fixed_size = FIELD_VARIABLE; + field->field_size = 0; + *fixed_root = FIELD_VARIABLE; + *fixed_parent = FIELD_VARIABLE; + break; + case LTT_STRING: + field->fixed_size = FIELD_VARIABLE; + field->field_size = 0; + *fixed_root = FIELD_VARIABLE; + *fixed_parent = FIELD_VARIABLE; + break; + case LTT_ARRAY: + local_fixed_root = FIELD_VARIABLE; + local_fixed_parent = FIELD_VARIABLE; + preset_field_type_size(tf, event_type, + 0, 0, + &local_fixed_root, &local_fixed_parent, + field->child[0]); + field->fixed_size = field->child[0]->fixed_size; + if(field->fixed_size == FIELD_FIXED) { + field->field_size = type->element_number * field->child[0]->field_size; + } else { + field->field_size = 0; + *fixed_root = FIELD_VARIABLE; + *fixed_parent = FIELD_VARIABLE; + } + break; + case LTT_STRUCT: + current_root_offset = field->offset_root; + current_offset = 0; + current_child_status = FIELD_FIXED; + for(i=0;ielement_number;i++) { + preset_field_type_size(tf, event_type, + current_root_offset, current_offset, + fixed_root, ¤t_child_status, + field->child[i]); + if(current_child_status == FIELD_FIXED) { + current_root_offset += field->child[i]->field_size; + current_offset += field->child[i]->field_size; + } else { + current_root_offset = 0; + current_offset = 0; + } + } + if(current_child_status != FIELD_FIXED) { + *fixed_parent = current_child_status; + field->field_size = 0; + field->fixed_size = current_child_status; + } else { + field->field_size = current_offset; + field->fixed_size = FIELD_FIXED; + } + break; + case LTT_UNION: + current_root_offset = field->offset_root; + current_offset = 0; + max_size = 0; + final_child_status = FIELD_FIXED; + for(i=0;ielement_number;i++) { + enum field_status current_root_child_status = FIELD_FIXED; + enum field_status current_child_status = FIELD_FIXED; + preset_field_type_size(tf, event_type, + current_root_offset, current_offset, + ¤t_root_child_status, ¤t_child_status, + field->child[i]); + if(current_child_status != FIELD_FIXED) + final_child_status = current_child_status; + else + max_size = max(max_size, field->child[i]->field_size); + } + if(final_child_status != FIELD_FIXED) { + g_error("LTTV does not support variable size fields in unions."); + /* This will stop the application. */ + *fixed_root = final_child_status; + *fixed_parent = final_child_status; + field->field_size = 0; + field->fixed_size = current_child_status; + } else { + field->field_size = max_size; + field->fixed_size = FIELD_FIXED; + } + break; + case LTT_NONE: + g_error("unexpected type NONE"); + break; + } + +} +#endif //0 + +/***************************************************************************** + *Function name + * check_fields_compatibility : Check for compatibility between two fields : + * do they use the same inner structure ? + *Input params + * event_type1 : event type + * event_type2 : event type + * field1 : field + * field2 : field + *Returns : 0 if identical + * 1 if not. + ****************************************************************************/ +// this function checks for equality of field types. Therefore, it does not use +// per se offsets. For instance, an aligned version of a structure is +// compatible with an unaligned version of the same structure. +#if 0 +gint check_fields_compatibility(LttEventType *event_type1, + LttEventType *event_type2, + LttField *field1, LttField *field2) +{ + guint different = 0; + LttType *type1; + LttType *type2; + + if(field1 == NULL) { + if(field2 == NULL) goto end; + else { + different = 1; + goto end; + } + } else if(field2 == NULL) { + different = 1; + goto end; + } + + type1 = &field1->field_type; + type2 = &field2->field_type; + + if(type1->type_class != type2->type_class) { + different = 1; + goto end; + } + if(type1->network != type2->network) { + different = 1; + goto end; + } + + switch(type1->type_class) { + case LTT_INT_FIXED: + case LTT_UINT_FIXED: + case LTT_POINTER: + case LTT_CHAR: + case LTT_UCHAR: + case LTT_SHORT: + case LTT_USHORT: + case LTT_INT: + case LTT_UINT: + case LTT_LONG: + case LTT_ULONG: + case LTT_SIZE_T: + case LTT_SSIZE_T: + case LTT_OFF_T: + case LTT_FLOAT: + case LTT_ENUM: + if(field1->field_size != field2->field_size) + different = 1; + break; + case LTT_STRING: + break; + case LTT_ARRAY: + { + LttField *child1 = &g_array_index(type1->fields, LttField, 0); + LttField *child2 = &g_array_index(type2->fields, LttField, 0); + + if(type1->size != type2->size) + different = 1; + if(check_fields_compatibility(event_type1, event_type2, child1, child2)) + different = 1; + } + break; + case LTT_SEQUENCE: + { + LttField *child1 = &g_array_index(type1->fields, LttField, 1); + LttField *child2 = &g_array_index(type2->fields, LttField, 1); + + if(check_fields_compatibility(event_type1, event_type2, child1, child2)) + different = 1; + } + break; + case LTT_STRUCT: + case LTT_UNION: + { + LttField *child; + guint i; + + if(type1->fields->len != type2->fields->len) { + different = 1; + goto end; + } + + for(i=0; i< type1->fields->len; i++) { + LttField *child1; + LttField *child2; + child1 = &g_array_index(type1->fields, LttField, i); + child2 = &g_array_index(type2->fields, LttField, i); + different = check_fields_compatibility(event_type1, + event_type2, child1, child2); + + if(different) break; + } + } + break; + case LTT_NONE: + default: + g_error("check_fields_compatibility : unknown type"); + } + +end: + return different; +} +#endif //0 + +#if 0 +gint check_fields_compatibility(LttEventType *event_type1, + LttEventType *event_type2, + LttField *field1, LttField *field2) +{ + guint different = 0; + guint i; + LttType *type1; + LttType *type2; + + if(field1 == NULL) { + if(field2 == NULL) goto end; + else { + different = 1; + goto end; + } + } else if(field2 == NULL) { + different = 1; + goto end; + } + + g_assert(field1->fixed_root != FIELD_UNKNOWN); + g_assert(field2->fixed_root != FIELD_UNKNOWN); + g_assert(field1->fixed_parent != FIELD_UNKNOWN); + g_assert(field2->fixed_parent != FIELD_UNKNOWN); + g_assert(field1->fixed_size != FIELD_UNKNOWN); + g_assert(field2->fixed_size != FIELD_UNKNOWN); + + type1 = field1->field_type; + type2 = field2->field_type; + + if(type1->type_class != type2->type_class) { + different = 1; + goto end; + } + if(type1->element_name != type2->element_name) { + different = 1; + goto end; + } + + switch(type1->type_class) { + case LTT_INT_FIXED: + case LTT_UINT_FIXED: + case LTT_POINTER: + case LTT_CHAR: + case LTT_UCHAR: + case LTT_SHORT: + case LTT_USHORT: + case LTT_INT: + case LTT_UINT: + case LTT_FLOAT: + case LTT_POINTER: + case LTT_LONG: + case LTT_ULONG: + case LTT_SIZE_T: + case LTT_SSIZE_T: + case LTT_OFF_T: + if(field1->field_size != field2->field_size) { + different = 1; + goto end; + } + break; + case LTT_ENUM: + if(type1->element_number != type2->element_number) { + different = 1; + goto end; + } + for(i=0;ielement_number;i++) { + if(type1->enum_strings[i] != type2->enum_strings[i]) { + different = 1; + goto end; + } + } + break; + case LTT_SEQUENCE: + /* Two elements : size and child */ + g_assert(type1->element_number != type2->element_number); + for(i=0;ielement_number;i++) { + if(check_fields_compatibility(event_type1, event_type2, + field1->child[0], field2->child[0])) { + different = 1; + goto end; + } + } + break; + case LTT_STRING: + break; + case LTT_ARRAY: + if(field1->field_size != field2->field_size) { + different = 1; + goto end; + } + /* Two elements : size and child */ + g_assert(type1->element_number != type2->element_number); + for(i=0;ielement_number;i++) { + if(check_fields_compatibility(event_type1, event_type2, + field1->child[0], field2->child[0])) { + different = 1; + goto end; + } + } + break; + case LTT_STRUCT: + case LTT_UNION: + if(type1->element_number != type2->element_number) { + different = 1; + break; + } + for(i=0;ielement_number;i++) { + if(check_fields_compatibility(event_type1, event_type2, + field1->child[0], field2->child[0])) { + different = 1; + goto end; + } + } + break; + } +end: + return different; +} +#endif //0 + + +/***************************************************************************** + *Function name + * ltt_get_int : get an integer number + *Input params + * reverse_byte_order: must we reverse the byte order ? + * size : the size of the integer + * ptr : the data pointer + *Return value + * gint64 : a 64 bits integer + ****************************************************************************/ + +gint64 ltt_get_int(gboolean reverse_byte_order, gint size, void *data) +{ + gint64 val; + + switch(size) { + case 1: val = *((gint8*)data); break; + case 2: val = ltt_get_int16(reverse_byte_order, data); break; + case 4: val = ltt_get_int32(reverse_byte_order, data); break; + case 8: val = ltt_get_int64(reverse_byte_order, data); break; + default: val = ltt_get_int64(reverse_byte_order, data); + g_critical("get_int : integer size %d unknown", size); + break; + } + + return val; +} + +/***************************************************************************** + *Function name + * ltt_get_uint : get an unsigned integer number + *Input params + * reverse_byte_order: must we reverse the byte order ? + * size : the size of the integer + * ptr : the data pointer + *Return value + * guint64 : a 64 bits unsigned integer + ****************************************************************************/ + +guint64 ltt_get_uint(gboolean reverse_byte_order, gint size, void *data) +{ + guint64 val; + + switch(size) { + case 1: val = *((gint8*)data); break; + case 2: val = ltt_get_uint16(reverse_byte_order, data); break; + case 4: val = ltt_get_uint32(reverse_byte_order, data); break; + case 8: val = ltt_get_uint64(reverse_byte_order, data); break; + default: val = ltt_get_uint64(reverse_byte_order, data); + g_critical("get_uint : unsigned integer size %d unknown", + size); + break; + } + + return val; +} + + +/* get the node name of the system */ + +char * ltt_trace_system_description_node_name (LttSystemDescription * s) +{ + return s->node_name; +} + + +/* get the domain name of the system */ + +char * ltt_trace_system_description_domain_name (LttSystemDescription * s) +{ + return s->domain_name; +} + + +/* get the description of the system */ + +char * ltt_trace_system_description_description (LttSystemDescription * s) +{ + return s->description; +} + + +/* get the NTP corrected start time of the trace */ +LttTime ltt_trace_start_time(LttTrace *t) +{ + return t->start_time; +} + +/* get the monotonic start time of the trace */ +LttTime ltt_trace_start_time_monotonic(LttTrace *t) +{ + return t->start_time_from_tsc; +} + +static LttTracefile *ltt_tracefile_new() +{ + LttTracefile *tf; + tf = g_new(LttTracefile, 1); + tf->event.tracefile = tf; + return tf; +} + +static void ltt_tracefile_destroy(LttTracefile *tf) +{ + g_free(tf); +} + +static void ltt_tracefile_copy(LttTracefile *dest, const LttTracefile *src) +{ + *dest = *src; +} + +/* Before library loading... */ + +static __attribute__((constructor)) void init(void) +{ + LTT_TRACEFILE_NAME_METADATA = g_quark_from_string("/control/metadata"); +} diff --git a/tags/lttv-0.11.3-23102008/lttv/Makefile.am b/tags/lttv-0.11.3-23102008/lttv/Makefile.am new file mode 100644 index 00000000..8e473bb2 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/Makefile.am @@ -0,0 +1,5 @@ +# WARNING : modules must be done at the end, so modules can dynamically link +# themselves to libraries compiled here but not installed in the system. +SUBDIRS = lttv modules + +modules_CFLAGS = -fvisibility=hidden diff --git a/tags/lttv-0.11.3-23102008/lttv/README b/tags/lttv-0.11.3-23102008/lttv/README new file mode 100644 index 00000000..e38ef070 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/README @@ -0,0 +1,21 @@ +Linux Trace Toolkit Visualizer + +* Getting started + +Use ./autogen.sh in the top level directory. It will create the Makefile +for you. Then, you can use make and make install to install this user +tool. + +Loading it from the command line is then as simple as : + +lttv -L modules-path -m module1 -m module2 ... + +So, to load the graphical interface with a detailed events list, it +would be (if the installation prefix is /usr, for instance) + +lttv -L /usr/lib/lttv/plugins -m lttvwindow -m guievents + +* Tree structure +lttv: main program composed of the program itself including helper modules. +modules: text and graphical viewing and analysis tools. + diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/Makefile.am b/tags/lttv-0.11.3-23102008/lttv/lttv/Makefile.am new file mode 100644 index 00000000..f96f418f --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/Makefile.am @@ -0,0 +1,66 @@ +AM_CFLAGS = $(GLIB_CFLAGS) +LIBS += $(POPT_LIBS) $(GLIB_LIBS) -lgobject-2.0 -L${top_builddir}/ltt\ + -llttvtraceread + +bin_PROGRAMS = lttv.real + +bin_SCRIPTS = lttv lttv-gui +CLEANFILES = $(bin_SCRIPTS) +EXTRA_DIST = lttv.sh lttv-gui.sh + +lttv: lttv.sh + rm -f lttv + echo "#!"$(BASH) > lttv + cat $(srcdir)/lttv.sh >> lttv + chmod ugo+x lttv + +lttv-gui: lttv-gui.sh + rm -f lttv-gui + echo "#!"$(BASH) > lttv-gui + cat $(srcdir)/lttv-gui.sh >> lttv-gui + chmod ugo+x lttv-gui + + +INCLUDES = \ + -DPACKAGE_PLUGIN_DIR=\""$(lttvplugindir)"\" \ + @PACKAGE_CFLAGS@ \ + $(DEFAULT_INCLUDES) + +libdir = ${lttvplugindir} + +lttvinclude_HEADERS = \ + attribute.h\ + hook.h\ + iattribute.h\ + lttv.h\ + module.h\ + option.h\ + state.h\ + stats.h\ + tracecontext.h\ + traceset.h\ + filter.h\ + print.h + +#noinst_HEADERS = \ +# filter.h + +lttv_real_SOURCES = batchtest.c main.c module.c option.c \ + hook.c attribute.c \ + iattribute.c state.c stats.c \ + tracecontext.c traceset.c filter.c print.c + +#man_MANS = lttv.1 +#EXTRA_DIST = lttv.1 + +#install-data-hook: +# cd $(DESTDIR)$(mandir)/man1 && \ +# $(LN_S) -f lttv.1 lttv-gui.1 \ +# $(LN_S) -f lttv.1 lttv.real.1 + +lttv_real_LDFLAGS = -export-dynamic + +if LTTVSTATIC + lttv_real_LDFLAGS += -profile -static +endif + diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/attribute.c b/tags/lttv-0.11.3-23102008/lttv/lttv/attribute.c new file mode 100644 index 00000000..eab857be --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/attribute.c @@ -0,0 +1,668 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +typedef union _AttributeValue { + int dv_int; + unsigned dv_uint; + long dv_long; + unsigned long dv_ulong; + float dv_float; + double dv_double; + LttTime dv_time; + gpointer dv_pointer; + char *dv_string; + GObject *dv_gobject; +} AttributeValue; + + +typedef struct _Attribute { + LttvAttributeName name; + LttvAttributeType type; + AttributeValue value; + gboolean is_named; +} Attribute; + + +static __inline__ LttvAttributeValue address_of_value(LttvAttributeType t, + AttributeValue *v) +{ + LttvAttributeValue va; + + switch(t) { + case LTTV_INT: va.v_int = &v->dv_int; break; + case LTTV_UINT: va.v_uint = &v->dv_uint; break; + case LTTV_LONG: va.v_long = &v->dv_long; break; + case LTTV_ULONG: va.v_ulong = &v->dv_ulong; break; + case LTTV_FLOAT: va.v_float = &v->dv_float; break; + case LTTV_DOUBLE: va.v_double = &v->dv_double; break; + case LTTV_TIME: va.v_time = &v->dv_time; break; + case LTTV_POINTER: va.v_pointer = &v->dv_pointer; break; + case LTTV_STRING: va.v_string = &v->dv_string; break; + case LTTV_GOBJECT: va.v_gobject = &v->dv_gobject; break; + case LTTV_NONE: break; + } + return va; +} + + +AttributeValue init_value(LttvAttributeType t) +{ + AttributeValue v; + + switch(t) { + case LTTV_INT: v.dv_int = 0; break; + case LTTV_UINT: v.dv_uint = 0; break; + case LTTV_LONG: v.dv_long = 0; break; + case LTTV_ULONG: v.dv_ulong = 0; break; + case LTTV_FLOAT: v.dv_float = 0; break; + case LTTV_DOUBLE: v.dv_double = 0; break; + case LTTV_TIME: v.dv_time.tv_sec = 0; v.dv_time.tv_nsec = 0; break; + case LTTV_POINTER: v.dv_pointer = NULL; break; + case LTTV_STRING: v.dv_string = NULL; break; + case LTTV_GOBJECT: v.dv_gobject = NULL; break; + case LTTV_NONE: break; + } + return v; +} + + +unsigned int +lttv_attribute_get_number(LttvAttribute *self) +{ + return self->attributes->len; +} + +gboolean +lttv_attribute_named(LttvAttribute *self, gboolean *homogeneous) +{ + *homogeneous = FALSE; + return TRUE; +} + +LttvAttributeType +lttv_attribute_get(LttvAttribute *self, unsigned i, LttvAttributeName *name, + LttvAttributeValue *v, gboolean *is_named) +{ + Attribute *a; + + a = &g_array_index(self->attributes, Attribute, i); + *name = a->name; + *v = address_of_value(a->type, &(a->value)); + *is_named = a->is_named; + return a->type; +} + + +LttvAttributeType +lttv_attribute_get_by_name(LttvAttribute *self, LttvAttributeName name, + LttvAttributeValue *v) +{ + Attribute *a; + + unsigned i; + + gpointer p; + + p = g_hash_table_lookup(self->names, GUINT_TO_POINTER(name)); + if(p == NULL) return LTTV_NONE; + + i = GPOINTER_TO_UINT(p); + i--; + a = &g_array_index(self->attributes, Attribute, i); + *v = address_of_value(a->type, &(a->value)); + return a->type; +} + + +LttvAttributeValue +lttv_attribute_add(LttvAttribute *self, LttvAttributeName name, + LttvAttributeType t) +{ + unsigned i; + + Attribute a, *pa; + + i = (unsigned)g_hash_table_lookup(self->names, GUINT_TO_POINTER(name)); + if(i != 0) g_error("duplicate entry in attribute table"); + + a.name = name; + a.is_named = 1; + a.type = t; + a.value = init_value(t); + g_array_append_val(self->attributes, a); + i = self->attributes->len - 1; + pa = &g_array_index(self->attributes, Attribute, i); + g_hash_table_insert(self->names, GUINT_TO_POINTER(name), + GUINT_TO_POINTER(i + 1)); + return address_of_value(t, &(pa->value)); +} + +LttvAttributeValue +lttv_attribute_add_unnamed(LttvAttribute *self, LttvAttributeName name, + LttvAttributeType t) +{ + unsigned i; + + Attribute a, *pa; + + i = (unsigned)g_hash_table_lookup(self->names, GUINT_TO_POINTER(name)); + if(i != 0) g_error("duplicate entry in attribute table"); + + a.name = name; + a.is_named = 0; + a.type = t; + a.value = init_value(t); + g_array_append_val(self->attributes, a); + i = self->attributes->len - 1; + pa = &g_array_index(self->attributes, Attribute, i); + g_hash_table_insert(self->names, GUINT_TO_POINTER(name), + GUINT_TO_POINTER(i + 1)); + return address_of_value(t, &(pa->value)); +} + + +/* Remove an attribute */ + +void +lttv_attribute_remove(LttvAttribute *self, unsigned i) +{ + Attribute *a; + + a = &g_array_index(self->attributes, Attribute, i); + + /* If the element is a gobject, unreference it. */ + if(a->type == LTTV_GOBJECT && a->value.dv_gobject != NULL) + g_object_unref(a->value.dv_gobject); + + /* Remove the array element and its entry in the name index */ + + g_hash_table_remove(self->names, GUINT_TO_POINTER(a->name)); + g_array_remove_index_fast(self->attributes, i); + + /* The element used to replace the removed element has its index entry + all wrong now. Reinsert it with its new position. */ + + if(likely(self->attributes->len != i)){ + g_hash_table_remove(self->names, GUINT_TO_POINTER(a->name)); + g_hash_table_insert(self->names, GUINT_TO_POINTER(a->name), GUINT_TO_POINTER(i + 1)); + } +} + +void +lttv_attribute_remove_by_name(LttvAttribute *self, LttvAttributeName name) +{ + unsigned i; + + i = (unsigned)g_hash_table_lookup(self->names, GUINT_TO_POINTER(name)); + if(unlikely(i == 0)) g_error("remove by name non existent attribute"); + + lttv_attribute_remove(self, i - 1); +} + +/* Create an empty iattribute object and add it as an attribute under the + specified name, or return an existing iattribute attribute. If an + attribute of that name already exists but is not a GObject supporting the + iattribute interface, return NULL. */ + +/*CHECK*/LttvAttribute* +lttv_attribute_find_subdir(LttvAttribute *self, LttvAttributeName name) +{ + unsigned i; + + Attribute a; + + LttvAttribute *new; + + i = (unsigned)g_hash_table_lookup(self->names, GUINT_TO_POINTER(name)); + if(likely(i != 0)) { + a = g_array_index(self->attributes, Attribute, i - 1); + if(likely(a.type == LTTV_GOBJECT && LTTV_IS_IATTRIBUTE(a.value.dv_gobject))) { + return LTTV_ATTRIBUTE(a.value.dv_gobject); + } + else return NULL; + } + new = g_object_new(LTTV_ATTRIBUTE_TYPE, NULL); + *(lttv_attribute_add(self, name, LTTV_GOBJECT).v_gobject) = G_OBJECT(new); + return (LttvAttribute *)new; +} + +/*CHECK*/LttvAttribute* +lttv_attribute_find_subdir_unnamed(LttvAttribute *self, LttvAttributeName name) +{ + unsigned i; + + Attribute a; + + LttvAttribute *new; + + i = (unsigned)g_hash_table_lookup(self->names, GUINT_TO_POINTER(name)); + if(likely(i != 0)) { + a = g_array_index(self->attributes, Attribute, i - 1); + if(likely(a.type == LTTV_GOBJECT && LTTV_IS_IATTRIBUTE(a.value.dv_gobject))) { + return LTTV_ATTRIBUTE(a.value.dv_gobject); + } + else return NULL; + } + new = g_object_new(LTTV_ATTRIBUTE_TYPE, NULL); + *(lttv_attribute_add_unnamed(self, name, LTTV_GOBJECT).v_gobject) + = G_OBJECT(new); + return (LttvAttribute *)new; +} + +gboolean +lttv_attribute_find(LttvAttribute *self, LttvAttributeName name, + LttvAttributeType t, LttvAttributeValue *v) +{ + unsigned i; + + Attribute *a; + + i = (unsigned)g_hash_table_lookup(self->names, GUINT_TO_POINTER(name)); + if(likely(i != 0)) { + a = &g_array_index(self->attributes, Attribute, i - 1); + if(unlikely(a->type != t)) return FALSE; + *v = address_of_value(t, &(a->value)); + return TRUE; + } + + *v = lttv_attribute_add(self, name, t); + return TRUE; +} + +gboolean +lttv_attribute_find_unnamed(LttvAttribute *self, LttvAttributeName name, + LttvAttributeType t, LttvAttributeValue *v) +{ + unsigned i; + + Attribute *a; + + i = (unsigned)g_hash_table_lookup(self->names, GUINT_TO_POINTER(name)); + if(likely(i != 0)) { + a = &g_array_index(self->attributes, Attribute, i - 1); + if(unlikely(a->type != t)) return FALSE; + *v = address_of_value(t, &(a->value)); + return TRUE; + } + + *v = lttv_attribute_add_unnamed(self, name, t); + return TRUE; +} + + +/*void lttv_attribute_recursive_free(LttvAttribute *self) +{ + int i, nb; + + Attribute *a; + + nb = self->attributes->len; + + for(i = 0 ; i < nb ; i++) { + a = &g_array_index(self->attributes, Attribute, i); + if(a->type == LTTV_GOBJECT && LTTV_IS_ATTRIBUTE(a->value.dv_gobject)) { + lttv_attribute_recursive_free((LttvAttribute *)(a->value.dv_gobject)); + } + } + g_object_unref(self); +}*/ + + +void lttv_attribute_recursive_add(LttvAttribute *dest, LttvAttribute *src) +{ + int i, nb; + + Attribute *a; + + LttvAttributeValue value; + + nb = src->attributes->len; + + for(i = 0 ; i < nb ; i++) { + a = &g_array_index(src->attributes, Attribute, i); + if(a->type == LTTV_GOBJECT && LTTV_IS_ATTRIBUTE(a->value.dv_gobject)) { + if(a->is_named) + lttv_attribute_recursive_add( + /*CHECK*/(LttvAttribute *)lttv_attribute_find_subdir(dest, a->name), + (LttvAttribute *)(a->value.dv_gobject)); + else + lttv_attribute_recursive_add( + /*CHECK*/(LttvAttribute *)lttv_attribute_find_subdir_unnamed( + dest, a->name), (LttvAttribute *)(a->value.dv_gobject)); + } + else { + if(a->is_named) + g_assert(lttv_attribute_find(dest, a->name, a->type, &value)); + else + g_assert(lttv_attribute_find_unnamed(dest, a->name, a->type, &value)); + switch(a->type) { + case LTTV_INT: + *value.v_int += a->value.dv_int; + break; + case LTTV_UINT: + *value.v_uint += a->value.dv_uint; + break; + case LTTV_LONG: + *value.v_long += a->value.dv_long; + break; + case LTTV_ULONG: + *value.v_ulong += a->value.dv_ulong; + break; + case LTTV_FLOAT: + *value.v_float += a->value.dv_float; + break; + case LTTV_DOUBLE: + *value.v_double += a->value.dv_double; + break; + case LTTV_TIME: + *value.v_time = ltt_time_add(*value.v_time, a->value.dv_time); + break; + case LTTV_POINTER: + break; + case LTTV_STRING: + break; + case LTTV_GOBJECT: + break; + case LTTV_NONE: + break; + } + } + } +} + + +static void +print_indent(FILE *fp, int pos) +{ + int i; + + for(i = 0 ; i < pos ; i++) putc(' ', fp); +} + + +void +lttv_attribute_write_xml(LttvAttribute *self, FILE *fp, int pos, int indent) +{ + int i, nb; + + Attribute *a; + + nb = self->attributes->len; + + fprintf(fp,"\n"); + for(i = 0 ; i < nb ; i++) { + a = &g_array_index(self->attributes, Attribute, i); + print_indent(fp, pos); + fprintf(fp, "name)); + if(a->type == LTTV_GOBJECT && LTTV_IS_ATTRIBUTE(a->value.dv_gobject)) { + fprintf(fp, "TYPE=ATTRS>"); + lttv_attribute_write_xml((LttvAttribute *)(a->value.dv_gobject), fp, + pos + indent, indent); + } + else { + switch(a->type) { + case LTTV_INT: + fprintf(fp, "TYPE=INT VALUE=%d/>\n", a->value.dv_int); + break; + case LTTV_UINT: + fprintf(fp, "TYPE=UINT VALUE=%u/>\n", a->value.dv_uint); + break; + case LTTV_LONG: + fprintf(fp, "TYPE=LONG VALUE=%ld/>\n", a->value.dv_long); + break; + case LTTV_ULONG: + fprintf(fp, "TYPE=ULONG VALUE=%lu/>\n", a->value.dv_ulong); + break; + case LTTV_FLOAT: + fprintf(fp, "TYPE=FLOAT VALUE=%f/>\n", a->value.dv_float); + break; + case LTTV_DOUBLE: + fprintf(fp, "TYPE=DOUBLE VALUE=%f/>\n", a->value.dv_double); + break; + case LTTV_TIME: + fprintf(fp, "TYPE=TIME SEC=%lu NSEC=%lu/>\n", a->value.dv_time.tv_sec, + a->value.dv_time.tv_nsec); + break; + case LTTV_POINTER: + fprintf(fp, "TYPE=POINTER VALUE=%p/>\n", a->value.dv_pointer); + break; + case LTTV_STRING: + fprintf(fp, "TYPE=STRING VALUE=\"%s\"/>\n", a->value.dv_string); + break; + case LTTV_GOBJECT: + fprintf(fp, "TYPE=GOBJECT VALUE=%p/>\n", a->value.dv_gobject); + break; + case LTTV_NONE: + fprintf(fp, "TYPE=NONE/>\n"); + break; + } + } + } + print_indent(fp, pos); + fprintf(fp,"\n"); +} + + +void +lttv_attribute_read_xml(LttvAttribute *self, FILE *fp) +{ + int res; + + char buffer[256], type[10]; + + LttvAttributeName name; + + LttvAttributeValue value; + + LttvAttribute *subtree; + + fscanf(fp,""); + while(1) { + res = fscanf(fp, "]", buffer, type); + g_assert(res == 2); + name = g_quark_from_string(buffer); + if(strcmp(type, "ATTRS") == 0) { + fscanf(fp, ">"); + subtree = lttv_attribute_find_subdir(self, name); + lttv_attribute_read_xml(subtree, fp); + } + else if(strcmp(type, "INT") == 0) { + value = lttv_attribute_add(self, name, LTTV_INT); + res = fscanf(fp, " VALUE=%d/>", value.v_int); + g_assert(res == 1); + } + else if(strcmp(type, "UINT") == 0) { + value = lttv_attribute_add(self, name, LTTV_UINT); + res = fscanf(fp, " VALUE=%u/>", value.v_uint); + g_assert(res == 1); + } + else if(strcmp(type, "LONG") == 0) { + value = lttv_attribute_add(self, name, LTTV_LONG); + res = fscanf(fp, " VALUE=%ld/>", value.v_long); + g_assert(res == 1); + } + else if(strcmp(type, "ULONG") == 0) { + value = lttv_attribute_add(self, name, LTTV_ULONG); + res = fscanf(fp, " VALUE=%lu/>", value.v_ulong); + g_assert(res == 1); + } + else if(strcmp(type, "FLOAT") == 0) { + float d; + value = lttv_attribute_add(self, name, LTTV_FLOAT); + res = fscanf(fp, " VALUE=%f/>", &d); + *(value.v_float) = d; + g_assert(res == 1); + } + else if(strcmp(type, "DOUBLE") == 0) { + value = lttv_attribute_add(self, name, LTTV_DOUBLE); + res = fscanf(fp, " VALUE=%lf/>", value.v_double); + g_assert(res == 1); + } + else if(strcmp(type, "TIME") == 0) { + value = lttv_attribute_add(self, name, LTTV_TIME); + res = fscanf(fp, " SEC=%lu NSEC=%lu/>", &(value.v_time->tv_sec), + &(value.v_time->tv_nsec)); + g_assert(res == 2); + } + else if(strcmp(type, "POINTER") == 0) { + value = lttv_attribute_add(self, name, LTTV_POINTER); + res = fscanf(fp, " VALUE=%p/>", value.v_pointer); + g_error("Cannot read a pointer"); + } + else if(strcmp(type, "STRING") == 0) { + value = lttv_attribute_add(self, name, LTTV_STRING); + res = fscanf(fp, " VALUE=\"%256[^\"]\"/>", buffer); + *(value.v_string) = g_strdup(buffer); + g_assert(res == 1); + } + else if(strcmp(type, "GOBJECT") == 0) { + value = lttv_attribute_add(self, name, LTTV_GOBJECT); + res = fscanf(fp, " VALUE=%p/>", value.v_gobject); + g_error("Cannot read a pointer"); + } + else if(strcmp(type, "NONE") == 0) { + value = lttv_attribute_add(self, name, LTTV_NONE); + fscanf(fp, "/>"); + } + else g_error("Unknown type to read"); + } + fscanf(fp,""); +} + +static LttvAttribute * +new_attribute (LttvAttribute *self) +{ + return g_object_new(LTTV_ATTRIBUTE_TYPE, NULL); +} + + +static void +attribute_interface_init (gpointer g_iface, gpointer iface_data) +{ + LttvIAttributeClass *klass = (LttvIAttributeClass *)g_iface; + + klass->new_attribute = (LttvIAttribute* (*) (LttvIAttribute *self)) + new_attribute; + + klass->get_number = (unsigned int (*) (LttvIAttribute *self)) + lttv_attribute_get_number; + + klass->named = (gboolean (*) (LttvIAttribute *self, gboolean *homogeneous)) + lttv_attribute_named; + + klass->get = (LttvAttributeType (*) (LttvIAttribute *self, unsigned i, + LttvAttributeName *name, LttvAttributeValue *v, gboolean *is_named)) + lttv_attribute_get; + + klass->get_by_name = (LttvAttributeType (*) (LttvIAttribute *self, + LttvAttributeName name, LttvAttributeValue *v)) + lttv_attribute_get_by_name; + + klass->add = (LttvAttributeValue (*) (LttvIAttribute *self, + LttvAttributeName name, LttvAttributeType t)) lttv_attribute_add; + + klass->add_unnamed = (LttvAttributeValue (*) (LttvIAttribute *self, + LttvAttributeName name, LttvAttributeType t)) lttv_attribute_add_unnamed; + + klass->remove = (void (*) (LttvIAttribute *self, unsigned i)) + lttv_attribute_remove; + + klass->remove_by_name = (void (*) (LttvIAttribute *self, + LttvAttributeName name)) lttv_attribute_remove_by_name; + + klass->find_subdir = (LttvIAttribute* (*) (LttvIAttribute *self, + LttvAttributeName name)) lttv_attribute_find_subdir; + + klass->find_subdir = (LttvIAttribute* (*) (LttvIAttribute *self, + LttvAttributeName name)) lttv_attribute_find_subdir_unnamed; +} + +static void +attribute_instance_init (GTypeInstance *instance, gpointer g_class) +{ + LttvAttribute *self = (LttvAttribute *)instance; + self->names = g_hash_table_new(g_direct_hash, + g_direct_equal); + self->attributes = g_array_new(FALSE, FALSE, sizeof(Attribute)); +} + + +static void +attribute_finalize (LttvAttribute *self) +{ + guint i; + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "attribute_finalize()"); + + for(i=0;iattributes->len;i++) { + lttv_attribute_remove(self, i); + } + + g_hash_table_destroy(self->names); + g_array_free(self->attributes, TRUE); +} + + +static void +attribute_class_init (LttvAttributeClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + + gobject_class->finalize = (void (*)(GObject *self))attribute_finalize; +} + +GType +lttv_attribute_get_type (void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (LttvAttributeClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) attribute_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (LttvAttribute), + 0, /* n_preallocs */ + (GInstanceInitFunc) attribute_instance_init, /* instance_init */ + NULL /* value handling */ + }; + + static const GInterfaceInfo iattribute_info = { + (GInterfaceInitFunc) attribute_interface_init, /* interface_init */ + NULL, /* interface_finalize */ + NULL /* interface_data */ + }; + + type = g_type_register_static (G_TYPE_OBJECT, "LttvAttributeType", &info, + 0); + g_type_add_interface_static (type, LTTV_IATTRIBUTE_TYPE, &iattribute_info); + } + return type; +} + + diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/attribute.h b/tags/lttv-0.11.3-23102008/lttv/lttv/attribute.h new file mode 100644 index 00000000..498d567c --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/attribute.h @@ -0,0 +1,131 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 ATTRIBUTE_H +#define ATTRIBUTE_H + +/* FIXME : unnamed attributes not implemented */ + +#include +#include +#include + +#define LTTV_ATTRIBUTE_TYPE (lttv_attribute_get_type ()) +#define LTTV_ATTRIBUTE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LTTV_ATTRIBUTE_TYPE, LttvAttribute)) +#define LTTV_ATTRIBUTE_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), LTTV_ATTRIBUTE_TYPE, LttvAttributeClass)) +#define LTTV_IS_ATTRIBUTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LTTV_ATTRIBUTE_TYPE)) +#define LTTV_IS_ATTRIBUTE_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), LTTV_ATTRIBUTE_TYPE)) +#define LTTV_ATTRIBUTE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), LTTV_ATTRIBUTE_TYPE, LttvAttributeClass)) + + +typedef struct _LttvAttribute LttvAttribute; +typedef struct _LttvAttributeClass LttvAttributeClass; + +struct _LttvAttribute { + GObject parent; + + /* private members */ + GHashTable *names; + GArray *attributes; +}; + +struct _LttvAttributeClass { + GObjectClass parent; + +}; + +GType lttv_attribute_get_type (void); + + +/* The functions exported in the IAttribute interface are also available + directly. */ + + +/* Total number of attributes */ + +unsigned int lttv_attribute_get_number(LttvAttribute *self); + + +/* Container type. Named (fields in struct or elements in a hash table) + or unnamed (elements in an array) attributes, homogeneous type or not. */ + +gboolean lttv_attribute_named(LttvAttribute *self, gboolean *homogeneous); + + +/* Get the i th attribute along with its type and a pointer to its value. */ + +LttvAttributeType lttv_attribute_get(LttvAttribute *self, unsigned i, + LttvAttributeName *name, LttvAttributeValue *v, gboolean *is_named); + + +/* Get the named attribute in the table along with its type and a pointer to + its value. If the named attribute does not exist, the type is LTTV_NONE. */ + +LttvAttributeType lttv_attribute_get_by_name(LttvAttribute *self, + LttvAttributeName name, LttvAttributeValue *v); + + +/* Add an attribute, which must not exist. The name is an empty string for + containers with unnamed attributes. */ + +LttvAttributeValue lttv_attribute_add(LttvAttribute *self, + LttvAttributeName name, LttvAttributeType t); + +LttvAttributeValue lttv_attribute_add_unnamed(LttvAttribute *self, + LttvAttributeName name, LttvAttributeType t); + +/* Remove an attribute */ + +void lttv_attribute_remove(LttvAttribute *self, unsigned i); + +void lttv_attribute_remove_by_name(LttvAttribute *self, + LttvAttributeName name); + + +/* Create an empty iattribute object and add it as an attribute under the + specified name, or return an existing iattribute attribute. If an + attribute of that name already exists but is not a GObject supporting the + iattribute interface, return NULL. */ + +LttvAttribute* lttv_attribute_find_subdir(LttvAttribute *self, + LttvAttributeName name); + +LttvAttribute* lttv_attribute_find_subdir_unnamed(LttvAttribute *self, + LttvAttributeName name); + + +gboolean lttv_attribute_find(LttvAttribute *self, LttvAttributeName name, + LttvAttributeType t, LttvAttributeValue *v); + + +/* Free recursively a tree of attributes. All contained gobject of type + LttvAttribute are freed (unreferenced) recursively. */ + +// Now done by default. +// void lttv_attribute_recursive_free(LttvAttribute *self); + +/* Add items from a tree of attributes to another tree. */ + +void lttv_attribute_recursive_add(LttvAttribute *dest, LttvAttribute *src); + +void +lttv_attribute_write_xml(LttvAttribute *self, FILE *fp, int pos, int indent); + +void lttv_attribute_read_xml(LttvAttribute *self, FILE *fp); + +#endif // ATTRIBUTE_H diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/batchtest.c b/tags/lttv-0.11.3-23102008/lttv/lttv/batchtest.c new file mode 100644 index 00000000..bc68f373 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/batchtest.c @@ -0,0 +1,1006 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +/* This module inserts a hook in the program main loop. This hook processes + all the events in the main tracefile while testing the speed and + functionality of the state and stats computations. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define __UNUSED__ __attribute__((__unused__)) + +static LttvTraceset *traceset; + +static LttvHooks + *before_traceset, + *after_traceset, + *before_trace, + *after_trace, + *before_tracefile, + *after_tracefile, + //*before_event, + //*after_event, + *event_hook, + *main_hooks; + +static char *a_trace; + +static char *a_dump_tracefiles; + +static char *a_save_sample; + +static int + a_sample_interval, + a_sample_number, + a_seek_number, + a_save_interval; + +static gboolean + a_trace_event, + a_save_state_copy, + a_test1, + a_test2, + a_test3, + a_test4, + a_test5, + a_test6, + a_test7, + a_test8, + a_test9, + a_test10, + a_test_all; + +static GQuark QUARK_BLOCK_START, + QUARK_BLOCK_END; + +LttEventPosition *a_event_position; + +typedef struct _save_state { + guint count; + FILE *fp; + guint interval; + guint position; + guint size; + LttTime *write_time; + guint version; +} SaveState; + + +static void lttv_trace_option(void __UNUSED__ *hook_data) +{ + LttTrace *trace; + + trace = ltt_trace_open(a_trace); + if(trace == NULL) { + g_critical("cannot open trace %s", a_trace); + } else { + lttv_traceset_add(traceset, lttv_trace_new(trace)); + } +} + +static double get_time() +{ + GTimeVal gt; + + g_get_current_time(>); + return gt.tv_sec + (double)gt.tv_usec / (double)1000000.0; +} + +static double run_one_test(LttvTracesetState *ts, LttTime start, LttTime end) +{ + double t0, t1; + + unsigned int i; + + //lttv_traceset_context_add_hooks(&ts->parent, + //before_traceset, after_traceset, NULL, before_trace, after_trace, + //NULL, before_tracefile, after_tracefile, NULL, before_event, after_event); + lttv_process_traceset_begin(&ts->parent, + before_traceset, + before_trace, + before_tracefile, + event_hook, + NULL); + + for(i = 0 ; i < lttv_traceset_number(traceset) ; i++) { + ((LttvTraceState *)(ts->parent.traces[i]))->save_interval =a_save_interval; + } + + t0 = get_time(); + lttv_state_traceset_seek_time_closest(ts, start); + //lttv_process_traceset(&ts->parent, end, G_MAXULONG); + lttv_process_traceset_middle(&ts->parent, + end, + G_MAXULONG, + NULL); + t1 = get_time(); + + //lttv_traceset_context_remove_hooks(&ts->parent, + //before_traceset, after_traceset, NULL, before_trace, after_trace, + //NULL, before_tracefile, after_tracefile, NULL, before_event, after_event); + lttv_process_traceset_end(&ts->parent, + after_traceset, + after_trace, + after_tracefile, + event_hook, + NULL); + + return t1 - t0; +} + + +gboolean trace_event(void __UNUSED__ *hook_data, void *call_data) +{ + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + guint nb_block, offset; + + guint64 tsc; + + LttTracefile *tf; + LttEvent *e = ltt_tracefile_get_event(tfs->parent.tf); + ltt_event_position(e, a_event_position); + ltt_event_position_get(a_event_position, &tf, &nb_block, &offset, &tsc); + fprintf(stderr,"Event %s %lu.%09lu [%u 0x%x tsc %llu]\n", + g_quark_to_string(marker_get_info_from_id(ltt_tracefile_get_trace(tf), + ltt_event_id(e))->name), + tfs->parent.timestamp.tv_sec, tfs->parent.timestamp.tv_nsec, + nb_block, offset, tsc); + return FALSE; +} + +static LttTime count_previous_time = { 0, 0 }; + +gboolean count_event(void *hook_data, void __UNUSED__ *call_data) +{ + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + LttTracefile *tracefile = tfs->parent.tf; + guint nb_block, offset; + LttTracefile *tf_pos; + guint64 tsc; + LttEvent * event = ltt_tracefile_get_event(tracefile); + LttTime time; + guint *pcount = (guint *)hook_data; + + (*pcount)++; + + time = ltt_event_time(event); + ltt_event_position(event, a_event_position); + ltt_event_position_get(a_event_position, &tf_pos, &nb_block, &offset, &tsc); + + if(ltt_time_compare(time, count_previous_time) < 0) { + g_warning("Time decreasing trace %s tracefile %s cpu %u position %u/0x%x", + g_quark_to_string(ltt_trace_name(ltt_tracefile_get_trace(tracefile))), + g_quark_to_string(ltt_tracefile_name(tracefile)), + tfs->cpu, nb_block, offset); + g_warning("last time %lu.%lu vs current %lu.%lu", + count_previous_time.tv_sec, count_previous_time.tv_nsec, + time.tv_sec, time.tv_nsec); + } + count_previous_time = time; + + + + return FALSE; +} + + +gboolean save_state_copy_event(void *hook_data, void *call_data) +{ + SaveState __UNUSED__ *save_state = (SaveState *)hook_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttvTraceState *ts = (LttvTraceState *)tfs->parent.t_context; + + LttEvent *e = ltt_tracefile_get_event(tfs->parent.tf); + + GString *filename; + + FILE *fp; + + LttTrace *trace = ((LttvTracefileContext *)tfs)->t_context->t; + + if(ts->nb_event == 0 && + marker_get_info_from_id(trace, e->event_id)->name + == QUARK_BLOCK_START) { + if(a_save_sample != NULL) { + filename = g_string_new(""); + g_string_printf(filename, "%s.copy.%lu.%09lu.xml", a_save_sample, + tfs->parent.timestamp.tv_sec, tfs->parent.timestamp.tv_nsec); + fp = fopen(filename->str, "w"); + if(fp == NULL) g_error("Cannot open %s", filename->str); + g_string_free(filename, TRUE); + lttv_state_write(ts, tfs->parent.timestamp, fp); + fclose(fp); + } //else lttv_state_write(ts, tfs->parent.timestamp, save_state->fp); + } + return FALSE; +} + + +gboolean save_state_event(void *hook_data, void *call_data) +{ + SaveState *save_state = (SaveState *)hook_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttvTraceState *ts = (LttvTraceState *)tfs->parent.t_context; + + GString *filename; + + FILE *fp; + + (save_state->count)++; + if(save_state->count % save_state->interval == 0 && + save_state->position < save_state->size) { + if(a_save_sample != NULL) { + filename = g_string_new(""); + g_string_printf(filename, "%s.%u.xml.%u", a_save_sample, + save_state->position, save_state->version); + fp = fopen(filename->str, "w"); + if(fp == NULL) g_error("Cannot open %s", filename->str); + g_string_free(filename, TRUE); + lttv_state_write(ts, tfs->parent.timestamp, fp); + fclose(fp); + } //else lttv_state_write(ts, tfs->parent.timestamp, save_state->fp); + + save_state->write_time[save_state->position] = tfs->parent.timestamp; + save_state->position++; + } + return FALSE; +} + + +static void sanitize_name(gchar *name) +{ + while(*name != '\0') { + if(*name == '/') *name = '_'; + name++; + } + +} + + +static void compute_tracefile(LttTracefile *tracefile, void *hook_data) +{ + GString *filename; + guint nb_equal, nb_block, offset; + guint64 tsc; + FILE *fp; + LttTime time, previous_time; + LttEvent *event = ltt_tracefile_get_event(tracefile); + //LttEventType *event_type; + struct marker_info *minfo; + int err; + gchar mod_name[PATH_MAX]; + + /* start_count is always initialized in this function _if_ there is always + * a block_start before a block_end. + */ + //long long unsigned cycle_count, start_count=0, delta_cycle; + + + filename = g_string_new(""); + strcpy(mod_name, g_quark_to_string(ltt_tracefile_name(tracefile))); + + sanitize_name(mod_name); + + g_warning("test %s test", g_quark_to_string(ltt_tracefile_name(tracefile))); + g_string_printf(filename, "%s.%s.%u.trace", a_dump_tracefiles, + mod_name, ltt_tracefile_cpu(tracefile)); + fp = fopen(filename->str, "w"); + if(fp == NULL) g_error("Cannot open %s", filename->str); + g_string_free(filename, TRUE); + err = ltt_tracefile_seek_time(tracefile, ltt_time_zero); + if(err) goto close; + + previous_time = ltt_time_zero; + nb_equal = 0; + + do { + LttTracefile *tf_pos; + //event_type = ltt_event_eventtype(event); + minfo = marker_get_info_from_id(ltt_tracefile_get_trace(tracefile), + ltt_event_id(event)); + time = ltt_event_time(event); + ltt_event_position(event, a_event_position); + ltt_event_position_get(a_event_position, &tf_pos, &nb_block, &offset, &tsc); + //fprintf(fp,"%s.%s: %llu %lu.%09lu position %u/%u\n", + fprintf(fp, "%s: %llu %lu.%09lu position %u/%u, tracefile %s\n", + g_quark_to_string(minfo->name), + tsc, (unsigned long)time.tv_sec, + (unsigned long)time.tv_nsec, + nb_block, offset, + g_quark_to_string(ltt_tracefile_name(tracefile))); + + if(ltt_time_compare(time, previous_time) < 0) { + g_warning("Time decreasing trace %s tracefile %s cpu %u position %u/0x%x", + g_quark_to_string(ltt_trace_name(ltt_tracefile_get_trace(tracefile))), + g_quark_to_string(ltt_tracefile_name(tracefile)), + ltt_tracefile_cpu(tracefile), nb_block, offset); + g_warning("last time %lu.%lu vs current %lu.%lu", + previous_time.tv_sec, previous_time.tv_nsec, + time.tv_sec, time.tv_nsec); + } + +#if 0 //FIXME + if(ltt_eventtype_name(event_type) == QUARK_BLOCK_START) { + start_count = cycle_count; + start_time = time; + } + else if(ltt_eventtype_name(event_type) == QUARK_BLOCK_END) { + delta_cycle = cycle_count - start_count; + end_nsec_sec = (long long unsigned)time.tv_sec * (long long unsigned)1000000000; + end_nsec_nsec = time.tv_nsec; + end_nsec = end_nsec_sec + end_nsec_nsec; + start_nsec = (long long unsigned)start_time.tv_sec * (long long unsigned)1000000000 + (long long unsigned)start_time.tv_nsec; + delta_nsec = end_nsec - start_nsec; + cycle_per_nsec = (double)delta_cycle / (double)delta_nsec; + nsec_per_cycle = (double)delta_nsec / (double)delta_cycle; + added_nsec = (double)delta_cycle * nsec_per_cycle; + interpolated_nsec = start_nsec + added_nsec; + added_nsec2 = (double)delta_cycle / cycle_per_nsec; + interpolated_nsec2 = start_nsec + added_nsec2; + + fprintf(fp,"Time: start_count %llu, end_count %llu, delta_cycle %llu, start_nsec %llu, end_nsec_sec %llu, end_nsec_nsec %llu, end_nsec %llu, delta_nsec %llu, cycle_per_nsec %.25f, nsec_per_cycle %.25f, added_nsec %llu, added_nsec2 %llu, interpolated_nsec %llu, interpolated_nsec2 %llu\n", start_count, cycle_count, delta_cycle, start_nsec, end_nsec_sec, end_nsec_nsec, end_nsec, delta_nsec, cycle_per_nsec, nsec_per_cycle, added_nsec, added_nsec2, interpolated_nsec, interpolated_nsec2); + } + else { +#endif //0 + if(ltt_time_compare(time, previous_time) == 0) + nb_equal++; + else if(nb_equal > 0) { + g_warning("Consecutive %d events with time %lu.%09lu", + nb_equal + 1, previous_time.tv_sec, previous_time.tv_nsec); + nb_equal = 0; + } + previous_time = time; + //} + } while((!ltt_tracefile_read(tracefile))); + +close: + fclose(fp); +} + +static gboolean process_traceset(void __UNUSED__ *hook_data, + void __UNUSED__ *call_data) +{ + GString *filename; + LttvTracesetStats *tscs; + + LttvTracesetState *ts; + + LttvTracesetContext *tc; + + FILE *fp; + + double t; + + //guint count, nb_control, nb_tracefile, nb_block, nb_event; + //guint i, j, count, nb_control, nb_tracefile, nb_block, nb_event, nb_equal; + guint i, j, count; + + LttTrace *trace; + + LttTime max_time = { G_MAXULONG, G_MAXULONG }; + + a_event_position = ltt_event_position_new(); + + GData **tracefiles_groups; + + struct compute_tracefile_group_args args; + + args.func = compute_tracefile; + args.func_args = NULL; + + if(a_dump_tracefiles != NULL) { + for(i = 0 ; i < lttv_traceset_number(traceset) ; i++) { + trace = lttv_trace(lttv_traceset_get(traceset, i)); + tracefiles_groups = ltt_trace_get_tracefiles_groups(trace); + + g_datalist_foreach(tracefiles_groups, + (GDataForeachFunc)compute_tracefile_group, + &args); + + } + } + + tscs = g_object_new(LTTV_TRACESET_STATS_TYPE, NULL); + ts = &tscs->parent; + tc = &tscs->parent.parent; + + lttv_context_init(tc, traceset); + + /* For each case compute and print the elapsed time. + The first case is simply to run through all events with a + simple counter. */ + + if(a_test1 || a_test_all) { + count = 0; + lttv_hooks_add(event_hook, count_event, &count, LTTV_PRIO_DEFAULT); + t = run_one_test(ts, ltt_time_zero, max_time); + lttv_hooks_remove_data(event_hook, count_event, &count); + g_message( + "Processing trace while counting events (%u events in %g seconds)", + count, t); + } + + /* Run through all events computing the state. */ + + if(a_test2 || a_test_all) { + lttv_state_add_event_hooks(ts); + t = run_one_test(ts, ltt_time_zero, max_time); + lttv_state_remove_event_hooks(ts); + g_message("Processing trace while updating state (%g seconds)", t); + } + + /* Run through all events computing the state and writing it out + periodically. */ + + SaveState save_state; + + save_state.interval = a_sample_interval; + save_state.size = a_sample_number; + save_state.fp = stderr; + save_state.write_time = g_new(LttTime, a_sample_number); + + + if(a_test3 || a_test_all) { + for(i = 0 ; i < 2 ; i++) { + save_state.count = 0; + save_state.position = 0; + save_state.version = i; + lttv_state_add_event_hooks(ts); + lttv_hooks_add(event_hook, save_state_event, &save_state, + LTTV_PRIO_DEFAULT); + t = run_one_test(ts, ltt_time_zero, max_time); + lttv_state_remove_event_hooks(ts); + lttv_hooks_remove_data(event_hook, save_state_event, &save_state); + g_warning("Processing while updating/writing state (%g seconds)", t); + } + } + + /* Run through all events computing the stats. */ + + if(a_test4 || a_test_all) { + if(lttv_profile_memory) { + g_message("Memory summary before computing stats"); + g_mem_profile(); + } + + lttv_stats_add_event_hooks(tscs); + t = run_one_test(ts, ltt_time_zero, max_time); + lttv_stats_remove_event_hooks(tscs); + g_message("Processing trace while counting stats (%g seconds)", t); + + if(lttv_profile_memory) { + g_message("Memory summary after computing stats"); + g_mem_profile(); + } + + lttv_stats_sum_traceset(tscs, ltt_time_infinite); + + if(lttv_profile_memory) { + g_message("Memory summary after summing stats"); + g_mem_profile(); + } + + lttv_context_fini(tc); + lttv_context_init(tc, traceset); + + if(lttv_profile_memory) { + g_message("Memory summary after cleaning up the stats"); + g_mem_profile(); + } + } + + /* Run through all events computing the state and stats. */ + + if(a_test5 || a_test_all) { + if(lttv_profile_memory) { + g_message("Memory summary before computing state and stats"); + g_mem_profile(); + } + + lttv_state_add_event_hooks(ts); + lttv_stats_add_event_hooks(tscs); + t = run_one_test(ts, ltt_time_zero, max_time); + lttv_state_remove_event_hooks(ts); + lttv_stats_remove_event_hooks(tscs); + g_message( + "Processing trace while counting state and stats (%g seconds)", t); + + if(lttv_profile_memory) { + g_message("Memory summary after computing and state and stats"); + g_mem_profile(); + } + + lttv_context_fini(tc); + lttv_context_init(tc, traceset); + + if(lttv_profile_memory) { + g_message("Memory summary after cleaning up the stats"); + g_mem_profile(); + } + } + + /* Run through all events computing and saving the state. */ + + if(a_trace_event) lttv_hooks_add(event_hook, trace_event, NULL, + LTTV_PRIO_DEFAULT); + + if(a_test6 || a_test_all) { + if(lttv_profile_memory) { + g_message("Memory summary before computing and saving state"); + g_mem_profile(); + } + + lttv_state_add_event_hooks(ts); + lttv_state_save_add_event_hooks(ts); + if(a_save_state_copy) + lttv_hooks_add(event_hook, save_state_copy_event, &save_state, + LTTV_PRIO_DEFAULT); + t = run_one_test(ts, ltt_time_zero, max_time); + lttv_state_remove_event_hooks(ts); + lttv_state_save_remove_event_hooks(ts); + if(a_save_state_copy) + lttv_hooks_remove_data(event_hook,save_state_copy_event, &save_state); + + g_message("Processing trace while updating/saving state (%g seconds)", t); + + if(lttv_profile_memory) { + g_message("Memory summary after computing/saving state"); + g_mem_profile(); + } + } + + /* Seek a few times to each saved position */ + + if((a_test7 && a_test3) || a_test_all) { + g_assert(a_seek_number >= 0); + for(i = 0 ; i < (guint)a_seek_number ; i++) { + gint reverse_j; /* just to make sure j is unsigned */ + for(reverse_j = save_state.position - 1 ; reverse_j >= 0 ; reverse_j--) { + j = (guint)reverse_j; + lttv_state_add_event_hooks(ts); + t = run_one_test(ts, save_state.write_time[j], + save_state.write_time[j]); + lttv_state_remove_event_hooks(ts); + g_message("Seeking to %lu.%lu (%g seconds)", + save_state.write_time[j].tv_sec, save_state.write_time[j].tv_nsec, + t); + + if(a_save_sample != NULL) { + filename = g_string_new(""); + g_string_printf(filename, "%s.%d.xml.bak%d", a_save_sample, j, i); + fp = fopen(filename->str, "w"); + if(fp == NULL) g_error("Cannot open %s", filename->str); + g_string_free(filename, TRUE); + lttv_state_write((LttvTraceState *)tc->traces[0], + save_state.write_time[j], fp); + fclose(fp); + } + //else lttv_state_write((LttvTraceState *)tc->traces[0], + // save_state.write_time[j], save_state.fp); + } + } + } + + /* Seek at specified interval, using states computed in 6, making + * sure that there is no more than the number of events between + * state save interval to read before getting there. + */ + + if((a_test8 && a_test6) || a_test_all) { + g_message("Running test 8 : check save interval"); + LttTime time = tc->time_span.start_time; + LttTime interval; + interval.tv_sec = 0; + interval.tv_nsec = 175674987; + guint count; + + while(ltt_time_compare(time, tc->time_span.end_time) < 0) { + //g_message("Seeking at time %u.%u", time.tv_sec, time.tv_nsec); + lttv_process_traceset_seek_time(&ts->parent, ltt_time_zero); + lttv_state_traceset_seek_time_closest(ts, time); + /* We add no hook to the traceset, not necessary */ + count = lttv_process_traceset_middle(&ts->parent, + time, G_MAXUINT, NULL); + g_info("Number of events to jump over : %u", count); + + if(count > LTTV_STATE_SAVE_INTERVAL) + g_warning("Oops! Save interval is %u and it took %u events to seek to a time %lu.%lu supposed to be closer from the last saved state.", + LTTV_STATE_SAVE_INTERVAL, count, time.tv_sec, time.tv_nsec); + time = ltt_time_add(time, interval); + } + + } + + if(a_test9 || a_test_all) { + double t0, t1; + /* Run seek_forward and seek_backward test */ + guint count; + LttvTracesetContext *tsc = &ts->parent; + LttvTracesetContextPosition *saved_pos = + lttv_traceset_context_position_new(tsc); + g_message("Running test 9 : seek_forward and seek_backward"); + lttv_process_traceset_seek_time(tsc, ltt_time_zero); + + count = lttv_process_traceset_seek_n_forward(tsc, 500, NULL, NULL, NULL, NULL, NULL, NULL); + g_assert(count == 500); + lttv_traceset_context_position_save(tsc, saved_pos); + t0 = get_time(); + count = lttv_process_traceset_seek_n_forward(tsc, 150000, NULL, NULL, NULL, NULL, NULL, NULL); + t1 = get_time(); + g_message("Seek forward 150000 events in %g seconds", t1 - t0); + g_assert(count == 150000); + t0 = get_time(); + count = lttv_process_traceset_seek_n_backward(tsc, 150000, + seek_back_default_offset, lttv_process_traceset_seek_time, NULL, + NULL, NULL, NULL, NULL, NULL); + t1 = get_time(); + g_message("Seek backward 150000 events in %g seconds", t1 - t0); + g_assert(count == 150000); + if(lttv_traceset_context_ctx_pos_compare(tsc, saved_pos)) { + g_warning("Problem with seek_n ! Positions differ. (1)"); + } + + lttv_process_traceset_seek_n_forward(tsc, 500, NULL, NULL, NULL, NULL, NULL, NULL); + lttv_traceset_context_position_save(tsc, saved_pos); + lttv_process_traceset_seek_n_forward(tsc, 15000, NULL, NULL, NULL, NULL, NULL, NULL); + lttv_process_traceset_seek_n_backward(tsc, 15005, + seek_back_default_offset, lttv_process_traceset_seek_time, NULL, + NULL, NULL, NULL, NULL, NULL); + lttv_process_traceset_seek_n_forward(tsc, 5, NULL, NULL, NULL, NULL, NULL, NULL); + if(lttv_traceset_context_ctx_pos_compare(tsc, saved_pos)) { + g_warning("Problem with seek_n ! Positions differ. (2)"); + } + + lttv_process_traceset_seek_time(tsc, ltt_time_infinite); + + count = lttv_process_traceset_seek_n_forward(tsc, 15000, NULL, NULL, NULL, NULL, NULL, NULL); + if(count > 0) + g_warning("Problem with seek_n ! Forward at end of traceset."); + + lttv_process_traceset_seek_time(tsc, ltt_time_infinite); + + lttv_traceset_context_position_save(tsc, saved_pos); + t0 = get_time(); + lttv_process_traceset_seek_n_backward(tsc, 300, + seek_back_default_offset, lttv_process_traceset_seek_time, NULL, + NULL, NULL, NULL, NULL, NULL); + t1 = get_time(); + g_message("Seek backward 300 events in %g seconds", t1 - t0); + count = lttv_process_traceset_seek_n_forward(tsc, 299, NULL, NULL, NULL, NULL, NULL, NULL); + count = lttv_process_traceset_seek_n_forward(tsc, 1, NULL, NULL, NULL, NULL, NULL, NULL); + + if(lttv_traceset_context_ctx_pos_compare(tsc, saved_pos)) { + g_warning("Problem with seek_n ! Positions differ. (4)"); + } + + lttv_traceset_context_position_save(tsc, saved_pos); + t0 = get_time(); + lttv_process_traceset_seek_n_backward(tsc, 10, + seek_back_default_offset, lttv_process_traceset_seek_time, NULL, + NULL, NULL, NULL, NULL, NULL); + t1 = get_time(); + g_message("Seek backward 10 events in %g seconds", t1 - t0); + t0 = get_time(); + count = lttv_process_traceset_seek_n_forward(tsc, 10, NULL, NULL, NULL, NULL, NULL, NULL); + t1 = get_time(); + g_message("Seek forward 10 events in %g seconds", t1 - t0); + + + /* try a volountary error */ + lttv_process_traceset_seek_time(tsc, ltt_time_infinite); + + lttv_traceset_context_position_save(tsc, saved_pos); + lttv_process_traceset_seek_n_backward(tsc, 301, + seek_back_default_offset, lttv_process_traceset_seek_time, NULL, + NULL, NULL, NULL, NULL, NULL); + count = lttv_process_traceset_seek_n_forward(tsc, 299, NULL, NULL, NULL, NULL, NULL, NULL); + count = lttv_process_traceset_seek_n_forward(tsc, 1, NULL, NULL, NULL, NULL, NULL, NULL); + + if(lttv_traceset_context_ctx_pos_compare(tsc, saved_pos) == 0) { + g_warning("Problem with seek_n ! Positions _should_ differ. (5)"); + } + + /* Try a seek by closest time : Hint : try this one with and without states + * computed. */ + lttv_process_traceset_seek_time(tsc, ltt_time_zero); + count = lttv_process_traceset_seek_n_forward(tsc, 200000, NULL, NULL, NULL, NULL, NULL, NULL); + lttv_traceset_context_position_save(tsc, saved_pos); + t0 = get_time(); + lttv_process_traceset_seek_n_backward(tsc, 100301, + seek_back_default_offset, + (seek_time_fct)lttv_state_traceset_seek_time_closest, NULL, + NULL, NULL, NULL, NULL, NULL); + t1 = get_time(); + g_message("Seek backward 100301 events (with seek closest) in %g seconds", + t1 - t0); + count = lttv_process_traceset_seek_n_forward(tsc, 100301, NULL, NULL, NULL, NULL, NULL, NULL); + + if(lttv_traceset_context_ctx_pos_compare(tsc, saved_pos)) { + g_warning("Problem with seek_n with state seek time! Positions differ. (6)"); + } + + lttv_traceset_context_position_destroy(saved_pos); + } + + if(a_test10 || a_test_all) { + g_message("Running test 10 : check seek traceset context position"); + LttvTracesetContext *tsc = &ts->parent; + LttvTracesetContextPosition *saved_pos = + lttv_traceset_context_position_new(tsc); + + lttv_process_traceset_seek_time(tsc, ltt_time_zero); + lttv_process_traceset_seek_n_forward(tsc, 200000, NULL, NULL, NULL, NULL, NULL, NULL); + lttv_traceset_context_position_save(tsc, saved_pos); + if(lttv_traceset_context_ctx_pos_compare(tsc, saved_pos) != 0) + g_critical("Error in seek position. (1)"); + + lttv_process_traceset_seek_time(tsc, ltt_time_infinite); + lttv_process_traceset_seek_n_backward(tsc, 500, + seek_back_default_offset, lttv_process_traceset_seek_time, NULL, + NULL, NULL, NULL, NULL, NULL); + lttv_traceset_context_position_save(tsc, saved_pos); + + if(lttv_traceset_context_ctx_pos_compare(tsc, saved_pos) != 0) + g_critical("Error in seek position. (2)"); + + lttv_traceset_context_position_destroy(saved_pos); + } + + if(a_trace_event) lttv_hooks_remove_data(event_hook, trace_event, NULL); + + g_free(save_state.write_time); + g_free(a_event_position); + lttv_context_fini(tc); + g_object_unref(tscs); + + if(lttv_profile_memory) { + g_message("Memory summary at the end of batchtest"); + g_mem_profile(); + } + + g_info("BatchTest end process traceset"); + return 0; +} + + +static void init() +{ + LttvAttributeValue value; + + LttvIAttribute *attributes = LTTV_IATTRIBUTE(lttv_global_attributes()); + + g_info("Init batchtest.c"); + + /* Init GQuarks */ + QUARK_BLOCK_START = g_quark_from_string("block_start"); + QUARK_BLOCK_END = g_quark_from_string("block_end"); + + + lttv_option_add("trace", 't', + "add a trace to the trace set to analyse", + "pathname of the directory containing the trace", + LTTV_OPT_STRING, &a_trace, lttv_trace_option, NULL); + + a_trace_event = FALSE; + + a_dump_tracefiles = NULL; + lttv_option_add("dump-tracefiles", 'D', + "Write event by event the content of tracefiles", + "basename for the files where to dump events", + LTTV_OPT_STRING, &a_dump_tracefiles, NULL, NULL); + + a_save_sample = NULL; + lttv_option_add("save-sample", 's', + "Save state samples to multiple files", + "basename for the files containing the state samples", + LTTV_OPT_STRING, &a_save_sample, NULL, NULL); + + a_save_state_copy = FALSE; + lttv_option_add("save-state-copy", 'S', "Write the state saved for seeking", + "", LTTV_OPT_NONE, &a_save_state_copy, NULL, NULL); + + a_save_interval = 100000; + lttv_option_add("save-interval", 'i', + "Interval between saving state", + "number of events before a block start triggers saving state", + LTTV_OPT_INT, &a_save_interval, NULL, NULL); + + a_sample_interval = 100000; + lttv_option_add("sample-interval", 'S', + "Interval between sampling state", + "number of events before sampling and writing state", + LTTV_OPT_INT, &a_sample_interval, NULL, NULL); + + a_sample_number = 20; + lttv_option_add("sample-number", 'N', + "Number of state samples", + "maximum number", + LTTV_OPT_INT, &a_sample_number, NULL, NULL); + + a_seek_number = 200; + lttv_option_add("seek-number", 'K', + "Number of seek", + "number", + LTTV_OPT_INT, &a_seek_number, NULL, NULL); + + a_test1 = FALSE; + lttv_option_add("test1", '1', "Test just counting events", "", + LTTV_OPT_NONE, &a_test1, NULL, NULL); + + a_test2 = FALSE; + lttv_option_add("test2", '2', "Test computing the state", "", + LTTV_OPT_NONE, &a_test2, NULL, NULL); + + a_test3 = FALSE; + lttv_option_add("test3", '3', "Test computing the state, writing out a few", + "", LTTV_OPT_NONE, &a_test3, NULL, NULL); + + a_test4 = FALSE; + lttv_option_add("test4", '4', "Test computing the stats", "", + LTTV_OPT_NONE, &a_test4, NULL, NULL); + + a_test5 = FALSE; + lttv_option_add("test5", '5', "Test computing the state and stats", "", + LTTV_OPT_NONE, &a_test5, NULL, NULL); + + a_test6 = FALSE; + lttv_option_add("test6", '6', "Test computing and saving the state", "", + LTTV_OPT_NONE, &a_test6, NULL, NULL); + + a_test7 = FALSE; + lttv_option_add("test7", '7', "Test seeking to positions written out in 3", + "", LTTV_OPT_NONE, &a_test7, NULL, NULL); + + a_test8 = FALSE; + lttv_option_add("test8", '8', "Test seeking to positions using saved states computed at 6 : check if number of events fits", + "", LTTV_OPT_NONE, &a_test8, NULL, NULL); + + a_test9 = FALSE; + lttv_option_add("test9", '9', "Test seeking backward/forward positions", + "", LTTV_OPT_NONE, &a_test9, NULL, NULL); + + a_test10 = FALSE; + lttv_option_add("test10", ' ', "Test seeking traceset by position", + "", LTTV_OPT_NONE, &a_test10, NULL, NULL); + + + + a_test_all = FALSE; + lttv_option_add("testall", 'a', "Run all tests ", "", + LTTV_OPT_NONE, &a_test_all, NULL, NULL); + + traceset = lttv_traceset_new(); + + before_traceset = lttv_hooks_new(); + after_traceset = lttv_hooks_new(); + before_trace = lttv_hooks_new(); + after_trace = lttv_hooks_new(); + before_tracefile = lttv_hooks_new(); + after_tracefile = lttv_hooks_new(); + //before_event = lttv_hooks_new(); + //after_event = lttv_hooks_new(); + event_hook = lttv_hooks_new(); + + + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/traceset/before", + LTTV_POINTER, &value)); + *(value.v_pointer) = before_traceset; + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/traceset/after", + LTTV_POINTER, &value)); + *(value.v_pointer) = after_traceset; + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/trace/before", + LTTV_POINTER, &value)); + *(value.v_pointer) = before_trace; + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/trace/after", + LTTV_POINTER, &value)); + *(value.v_pointer) = after_trace; + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/tracefile/before", + LTTV_POINTER, &value)); + *(value.v_pointer) = before_tracefile; + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/tracefile/after", + LTTV_POINTER, &value)); + *(value.v_pointer) = after_tracefile; + //g_assert(lttv_iattribute_find_by_path(attributes, "hooks/event/before", + // LTTV_POINTER, &value)); + //*(value.v_pointer) = before_event; + //g_assert(lttv_iattribute_find_by_path(attributes, "hooks/event/after", + // LTTV_POINTER, &value)); + //*(value.v_pointer) = after_event; + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/event", + LTTV_POINTER, &value)); + *(value.v_pointer) = event_hook; + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/main/before", + LTTV_POINTER, &value)); + g_assert((main_hooks = *(value.v_pointer)) != NULL); + lttv_hooks_add(main_hooks, process_traceset, NULL, LTTV_PRIO_DEFAULT); +} + + +static void destroy() +{ + guint i, nb; + + LttvTrace *trace; + + g_info("Destroy batchAnalysis.c"); + + lttv_option_remove("trace"); + lttv_option_remove("dump-tracefiles"); + lttv_option_remove("save-sample"); + lttv_option_remove("save-state-copy"); + lttv_option_remove("sample-interval"); + lttv_option_remove("sample-number"); + lttv_option_remove("seek-number"); + lttv_option_remove("save-interval"); + lttv_option_remove("test1"); + lttv_option_remove("test2"); + lttv_option_remove("test3"); + lttv_option_remove("test4"); + lttv_option_remove("test5"); + lttv_option_remove("test6"); + lttv_option_remove("test7"); + lttv_option_remove("test8"); + lttv_option_remove("test9"); + lttv_option_remove("test10"); + lttv_option_remove("testall"); + + lttv_hooks_destroy(before_traceset); + lttv_hooks_destroy(after_traceset); + lttv_hooks_destroy(before_trace); + lttv_hooks_destroy(after_trace); + lttv_hooks_destroy(before_tracefile); + lttv_hooks_destroy(after_tracefile); + //lttv_hooks_destroy(before_event); + //lttv_hooks_destroy(after_event); + lttv_hooks_destroy(event_hook); + lttv_hooks_remove_data(main_hooks, process_traceset, NULL); + + nb = lttv_traceset_number(traceset); + for(i = 0 ; i < nb ; i++) { + trace = lttv_traceset_get(traceset, i); + lttv_traceset_remove(traceset,i); + ltt_trace_close(lttv_trace(trace)); + lttv_trace_destroy(trace); + } + + lttv_traceset_destroy(traceset); +} + + +LTTV_MODULE("batchtest", "Batch processing of a trace for tests", \ + "Run through a trace calling all the registered hooks for tests", \ + init, destroy, "state", "stats", "option" ) diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/contextmacros.h b/tags/lttv-0.11.3-23102008/lttv/lttv/contextmacros.h new file mode 100644 index 00000000..b6276e98 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/contextmacros.h @@ -0,0 +1,376 @@ +/* This file is part of the Linux Trace Toolkit trace reading library + * Copyright (C) 2004 Mathieu Desnoyers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License Version 2.1 as published by the Free Software Foundation. + * + * 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. + */ + +/* This is a header that contains macro helpers to give an object-oriented + * access to the Trace context, state and statistics. + */ + + +#include +#include +#include +#include + +/************ TracesetContext get methods ***************/ + +/* LTTV_TRACESET_CONTEXT_GET_TRACE_CONTEXT + * + * Input : LttvTracesetContext *tsc + * int trace number (0 .. num_traces -1) + * returns : (LttvTraceContext *) + */ +#define LTTV_TRACESET_CONTEXT_GET_TRACE_CONTEXT(tsc, trace_num)\ + (tsc->traces[trace_num]) + +/* LTTV_TRACESET_CONTEXT_GET_NUM_TRACES + * + * Input : LttvTracesetContext *tsc + * returns : (guint) the number of traces + */ +#define LTTV_TRACESET_CONTEXT_GET_NUM_TRACES(tsc)\ + (lttv_traceset_number(tsc->ts)) + +/* LTTV_TRACESET_CONTEXT_GET_TRACESET_STATS + * + * Input : LttvTracesetContext *tsc + * returns : (LttvTracesetStats*) + */ +#define LTTV_TRACESET_CONTEXT_GET_TRACESET_STATS(tsc)\ + ((LttvTracesetStats*)tsc) + + +/************ TraceContext get methods ***************/ + +/* LTTV_TRACE_CONTEXT_GET_TRACESET_CONTEXT + * + * Input : LttvTraceContext *tc + * returns : (LttvTracesetContext *) + */ +#define LTTV_TRACE_CONTEXT_GET_TRACESET_CONTEXT(tc)\ + (tc->ts_context) + +/* LTTV_TRACE_CONTEXT_GET_TRACE_INDEX + * + * Input : LttvTraceContext *tc + * returns : (guint) trace context index in its traceset context. + */ +#define LTTV_TRACE_CONTEXT_GET_TRACE_INDEX(tc)\ + (tc->index) + + + +/* LTTV_TRACE_CONTEXT_GET_TRACE_STATE + * + * Input : LttvTraceContext *tc + * returns : (LttvTraceState *) + */ +#define LTTV_TRACE_CONTEXT_GET_TRACE_STATE(tc)\ + ((LttvTraceState*)tc) + +/* LTTV_TRACE_CONTEXT_GET_TRACE_STATS + * + * Input : LttvTraceContext *tc + * returns : (LttvTraceStats *) + */ +#define LTTV_TRACE_CONTEXT_GET_TRACE_STATS(tc)\ + ((LttvTraceStats*)tc) + +/* LTTV_TRACE_CONTEXT_GET_CPU_TRACEFILE_CONTEXT + * + * Input : LttvTraceContext *tc + * guint cpu_index (0 .. number_cpu-1) + * returns : (LttvTracefileContext*) + */ +#define LTTV_TRACE_CONTEXT_GET_CPU_TRACEFILE_CONTEXT(tc, cpu_index)\ + ( tc->tracefiles[cpu_index + ltt_trace_control_tracefile_number(tc->t)] ) + +/* LTTV_TRACE_CONTEXT_GET_NUMBER_CPU + * + * input : LttvTraceContext *tc + * returns : (guint) number_cpu + */ +#define LTTV_TRACE_CONTEXT_GET_NUMBER_CPU(tc)\ + ( ltt_trace_per_cpu_tracefile_number(tc->t) ) + + +/* LTTV_TRACE_CONTEXT_GET_CONTROL_TRACEFILE_CONTEXT + * + * Input : LttvTraceContext *tc + * guint control_index (0 .. number_control-1) + * returns : (LttvTracefileContext*) + */ +#define LTTV_TRACE_CONTEXT_GET_CONTROL_TRACEFILE_CONTEXT(tc, control_index)\ + (tc->tracefiles[control_index]) + +/* LTTV_TRACE_CONTEXT_GET_NUMBER_CONTROL + * + * Input : LttvTraceContext *tc + * returns : (guint) number_control + */ +#define LTTV_TRACE_CONTEXT_GET_NUMBER_CONTROL(tc)\ + ( ltt_trace_control_tracefile_number(tc->t) ) + +/* LTTV_TRACE_CONTEXT_GET_TRACE + * + * Input : LttvTraceContext *tc + * returns : (LttvTrace*) + * + * NOTE : see traceset.h for LttvTrace methods + */ +#define LTTV_TRACE_CONTEXT_GET_TRACE(tc)\ + (tc->vt) + + + + + +/************ TracefileContext get methods ***************/ + +/* LTTV_TRACEFILE_CONTEXT_GET_TRACE_CONTEXT + * + * Input : LttvTracefileContext *tfc + * returns : (LttvTraceContext*) + */ +#define LTTV_TRACEFILE_CONTEXT_GET_TRACE_CONTEXT(tfc)\ + (tfc->t_context) + +/* LTTV_TRACEFILE_CONTEXT_GET_EVENT + * + * Input : LttvTracefileContext *tfc + * returns : (LttEvent *) + */ +#define LTTV_TRACEFILE_CONTEXT_GET_EVENT(tfc)\ + (tfc->e) + +/* LTTV_TRACEFILE_CONTEXT_GET_TRACEFILE_STATE + * + * Input : LttvTracefileContext *tfc + * returns : (LttvTracefileState *) + */ +#define LTTV_TRACEFILE_CONTEXT_GET_TRACEFILE_STATE(tfc)\ + ((LttvTracefileState*)tfc) + +/* LTTV_TRACEFILE_CONTEXT_GET_TRACEFILE_STATS + * + * Input : LttvTracefileContext *tfc + * returns : (LttvTracefileStats *) + */ +#define LTTV_TRACEFILE_CONTEXT_GET_TRACEFILE_STATS(tfc)\ + ((LttvTracefileStats*)tfc) + +/* LTTV_TRACEFILE_CONTEXT_GET_TRACEFILE_INDEX + * + * Returns the tracefile index. + * + * It checks if it's a control tracefile or a cpu tracefile and returns the + * cpu_index or control_index, depending of the case. + * + * Input : LttvTracefileContext *tfc + * returns : (guint) cpu_index or control_index. + */ +#define LTTV_TRACEFILE_CONTEXT_GET_TRACEFILE_INDEX(tfc)\ + (tfc->control?\ + tfc->index:\ + tfc->index-ltt_trace_control_tracefile_number(tfc->t_context->t)) + + + +/************ TraceState get methods ***************/ + +/* LTTV_TRACE_STATE_GET_TRACE_CONTEXT + * + * Input : LttvTraceState *tse + * returns : (LttvTraceContext*) + * + */ +#define LTTV_TRACE_STATE_GET_TRACE_CONTEXT(tse)\ + ((LttvTraceContext*)tse) + +/* LTTV_TRACE_STATE_GET_EVENTTYPE_NAME + * + * Input : LttvTraceState *tse + * guint eventtype_number + * returns : (GQuark) + * + * NOTE : use g_quark_to_string to convert a GQuark into a static char * + */ +#define LTTV_TRACE_STATE_GET_EVENTTYPE_NAME(tse, eventtype_number)\ + (tse->eventtype_names[eventtype_number]) + +/* LTTV_TRACE_STATE_GET_SYSCALL_NAME + * + * Input : LttvTraceState *tse + * guint syscall_number + * returns : (GQuark) + * + * NOTE : use g_quark_to_string to convert a GQuark into a static char * + */ +#define LTTV_TRACE_STATE_GET_SYSCALL_NAME(tse, syscall_number)\ + (tse->syscall_names[syscall_number]) + +/* LTTV_TRACE_STATE_GET_TRAP_NAME + * + * Input : LttvTraceState *tse + * guint trap_number + * returns : (GQuark) + * + * NOTE : use g_quark_to_string to convert a GQuark into a static char * + */ +#define LTTV_TRACE_STATE_GET_TRAP_NAME(tse, trap_number)\ + (tse->trap_names[trap_number]) + +/* LTTV_TRACE_STATE_GET_IRQ_NAME + * + * Input : LttvTraceState *tse + * guint irq_number + * returns : (GQuark) + * + * NOTE : use g_quark_to_string to convert a GQuark into a static char * + */ +#define LTTV_TRACE_STATE_GET_IRQ_NAME(tse, irq_number)\ + (tse->irq_names[irq_number]) + + +/* LTTV_TRACE_STATE_GET_PROCESS_STATE + * + * Input : LttvTraceState *tse + * guint pid + * guint cpu_index (0 .. number_cpu-1) + * returns : (LttvProcessState *) + * + * NOTE : if pid is 0, the special process corresponding to the CPU that + * corresponds to the tracefile will be returned. + * if pid is different than 0, the process returned may be running + * on any cpu of the trace. + */ +#define LTTV_TRACE_STATE_GET_PROCESS_STATE(tse, pid, cpu_index)\ + (lttv_state_find_process( \ + (LttvTraceFileState*)tse->parent->tracefiles[\ + cpu_index+\ + ltt_trace_control_tracefile_number((LttvTraceContext*)tse->t)], pid)) + + +/* LTTV_TRACE_STATE_GET_NUMBER_CPU + * + * input : LttvTraceState *tse + * returns : (guint) number_cpu + */ +#define LTTV_TRACE_STATE_GET_NUMBER_CPU(tse)\ + ( ltt_trace_per_cpu_tracefile_number((LttvTraceState*)tse->t) ) + + + + +/************ TracefileState get methods ***************/ + +/* LTTV_TRACEFILE_STATE_GET_TRACEFILE_CONTEXT + * + * Input : LttvTracefileState *tfse + * returns : (LttvTracefileContext*) + * + */ +#define LTTV_TRACEFILE_STATE_GET_TRACEFILE_CONTEXT(tfse)\ + ((LttvTracefileContext*)tfse) + + +/* LTTV_TRACEFILE_STATE_GET_CURRENT_PROCESS_STATE + * + * Returns the state of the current process. + * + * Input : LttvTracefileState *tfse + * returns : (LttvProcessState *) + */ +#define LTTV_TRACEFILE_STATE_GET_CURRENT_PROCESS_STATE(tfse)\ + (tfse->process) + +/* LTTV_TRACEFILE_STATE_GET_CPU_NAME + * + * Input : LttvTracefileState *tfse + * returns : (GQuark) + * + * NOTE : use g_quark_to_string to convert a GQuark into a static char * + */ +#define LTTV_TRACEFILE_STATE_GET_CPU_NAME(tfse)\ + (tfse->cpu_name) + + +/* LTTV_TRACEFILE_STATE_GET_PROCESS_STATE + * + * Input : LttvTracefileState *tfse + * guint pid + * returns : (LttvProcessState *) + * + * NOTE : if pid is 0, the special process corresponding to the CPU that + * corresponds to the tracefile will be returned. + * if pid is different than 0, the process returned may be running + * on any cpu of the trace. + */ +#define LTTV_TRACEFILE_STATE_GET_PROCESS_STATE(tfse, pid)\ + (lttv_state_find_process(tfse, pid)) + + + + + +/************ ProcessState get methods ***************/ +/* Use direct access to LttvProcessState members for other attributes */ +/* see struct _LttvProcessState definition in state.h */ + +/* LTTV_PROCESS_STATE_GET_CURRENT_EXECUTION_STATE + * + * Input : LttvProcessState *pse + * returns : (LttvExecutionState*) + */ +#define LTTV_PROCESS_STATE_GET_EXECUTION_STATE(pse)\ + (pse->state) + +/* LTTV_PROCESS_STATE_GET_NESTED_EXECUTION_STATE + * + * Input : LttvProcessState *pse + * guint nest_number (0 to num_nest-1) + * returns : (LttvExecutionState*) + */ +#define LTTV_PROCESS_STATE_GET_NESTED_EXECUTION_STATE(pse, num_nest)\ + (&g_array_index(pse->execution_stack,LttvExecutionState,num_nest)) + + +/* LTTV_PROCESS_STATE_GET_NUM_NESTED_EXECUTION_STATES + * + * Returns the number of nested execution states currently on the stack. + * + * Input : LttvProcessState *pse + * returns : (guint) + */ +#define LTTV_PROCESS_STATE_GET_NUM_NESTED_EXECUTION_STATES(pse)\ + (pse->execution_stack->len) + + +/************ ExecutionState get methods ***************/ +/* Use direct access to LttvExecutionState members to access attributes */ +/* see struct _LttvExecutionState definition in state.h */ + + + + + + +/* Statistics */ + + + + diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/filter.c b/tags/lttv-0.11.3-23102008/lttv/lttv/filter.c new file mode 100644 index 00000000..8ed06022 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/filter.c @@ -0,0 +1,2161 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2005 Michel Dagenais and Simon Bouvier-Zappa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +/*! \file lttv/lttv/filter.c + * \brief Defines the core filter of application + * + * consist in AND, OR and NOT nested expressions, forming a tree with + * simple relations as leaves. The simple relations test if a field + * in an event is equal, not equal, smaller, smaller or equal, larger, or + * larger or equal to a specified value. + * + * Fields specified in a simple expression can take following + * values + * + * \verbatim + * LttvTracefileContext{} + * |->event\ + * | |->name (String, converted to GQuark) + * | |->facility (String, converted to GQuark) + * | |->category (String, not yet implemented) + * | |->time (LttTime) + * | |->tsc (LttCycleCount --> uint64) + * | |->target_pid (target PID of the event) + * | |->fields + * | |->"facility_name + * | |->"event name" + * | |->"field name" + * | |->"sub-field name" + * | |->... + * | |->"leaf-field name" (field type) + * |->tracefile + * | |->name (String, converted to GQuark) + * |->trace + * | |->name (String, converted to GQuark) + * |->state + * |->pid (guint) + * |->ppid (guint) + * |->creation_time (LttTime) + * |->insertion_time (LttTime) + * |->process_name (String, converted to GQuark) + * |->thread_brand (String, converted to GQuark) + * |->execution_mode (LttvExecutionMode) + * |->execution_submode (LttvExecutionSubmode) + * |->process_status (LttvProcessStatus) + * |->cpu (guint) + * \endverbatim + */ + +/* + * \todo + * - refine switch of expression in multiple uses functions + * - remove the idle expressions in the tree + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +//#define TEST +#ifdef TEST +#include +#include +#endif + +#include +#include +#include +#include +#include + +/** + * @fn LttvSimpleExpression* lttv_simple_expression_new() + * + * Constructor for LttvSimpleExpression + * @return pointer to new LttvSimpleExpression + */ +LttvSimpleExpression* +lttv_simple_expression_new() { + + LttvSimpleExpression* se = g_new(LttvSimpleExpression,1); + + se->field = LTTV_FILTER_UNDEFINED; + se->op = NULL; + se->offset = 0; + + return se; +} + +/* + * Keeps the array order. + */ +static inline gpointer ltt_g_ptr_array_remove_index_slow(GPtrArray *fp, + int index) +{ + gpointer ptr; + int i; + + if (fp->len == 0) + return NULL; + + ptr = g_ptr_array_index(fp, index); + for (i = index; i < fp->len - 1; i++) { + g_ptr_array_index(fp, i) = g_ptr_array_index(fp, i + 1); + } + g_ptr_array_remove_index(fp, fp->len - 1); + return ptr; +} + +/** + * @fn gboolean lttv_simple_expression_assign_field(GPtrArray*,LttvSimpleExpression*) + * + * Parse through filtering field hierarchy as specified + * by user. This function compares each value to + * predetermined quarks + * @param fp The field path list + * @param se current simple expression + * @return success/failure of operation + */ +gboolean +lttv_simple_expression_assign_field(GPtrArray* fp, LttvSimpleExpression* se) { + + GString* f = NULL; + + if(fp->len < 2) return FALSE; + g_assert((f=ltt_g_ptr_array_remove_index_slow(fp,0))); + + + /* + * Parse through the specified + * hardcoded fields. + * + * Take note however that the + * 'event' subfields might change + * depending on values specified + * in core.xml file. Hence, if + * none of the subfields in the + * array match the hardcoded + * subfields, it will be considered + * as a dynamic field + */ + + if(!g_strcasecmp(f->str,"trace") ) { + /* + * Possible values: + * trace.name + */ + g_string_free(f,TRUE); + f=ltt_g_ptr_array_remove_index_slow(fp,0); + if(!g_strcasecmp(f->str,"name")) { + se->field = LTTV_FILTER_TRACE_NAME; + } + } else if(!g_strcasecmp(f->str,"traceset") ) { + /* + * FIXME: not yet implemented ! + */ + } else if(!g_strcasecmp(f->str,"tracefile") ) { + /* + * Possible values: + * tracefile.name + */ + g_string_free(f,TRUE); + f=ltt_g_ptr_array_remove_index_slow(fp,0); + if(!g_strcasecmp(f->str,"name")) { + se->field = LTTV_FILTER_TRACEFILE_NAME; + } + } else if(!g_strcasecmp(f->str,"state") ) { + /* + * Possible values: + * state.pid + * state.ppid + * state.creation_time + * state.insertion_time + * state.process_name + * state.thread_brand + * state.execution_mode + * state.execution_submode + * state.process_status + * state.cpu + */ + g_string_free(f,TRUE); + f=ltt_g_ptr_array_remove_index_slow(fp,0); + if(!g_strcasecmp(f->str,"pid") ) { + se->field = LTTV_FILTER_STATE_PID; + } + else if(!g_strcasecmp(f->str,"ppid") ) { + se->field = LTTV_FILTER_STATE_PPID; + } + else if(!g_strcasecmp(f->str,"creation_time") ) { + se->field = LTTV_FILTER_STATE_CT; + } + else if(!g_strcasecmp(f->str,"insertion_time") ) { + se->field = LTTV_FILTER_STATE_IT; + } + else if(!g_strcasecmp(f->str,"process_name") ) { + se->field = LTTV_FILTER_STATE_P_NAME; + } + else if(!g_strcasecmp(f->str,"thread_brand") ) { + se->field = LTTV_FILTER_STATE_T_BRAND; + } + else if(!g_strcasecmp(f->str,"execution_mode") ) { + se->field = LTTV_FILTER_STATE_EX_MODE; + } + else if(!g_strcasecmp(f->str,"execution_submode") ) { + se->field = LTTV_FILTER_STATE_EX_SUBMODE; + } + else if(!g_strcasecmp(f->str,"process_status") ) { + se->field = LTTV_FILTER_STATE_P_STATUS; + } + else if(!g_strcasecmp(f->str,"cpu") ) { + se->field = LTTV_FILTER_STATE_CPU; + } + } else if(!g_strcasecmp(f->str,"event") ) { + /* + * Possible values: + * event.name + * event.category + * event.time + * event.tsc + * event.target_pid + * event.field + */ + g_string_free(f,TRUE); + f=ltt_g_ptr_array_remove_index_slow(fp,0); + + if(!g_strcasecmp(f->str,"name") ) { + se->field = LTTV_FILTER_EVENT_NAME; + } + else if(!g_strcasecmp(f->str,"category") ) { + /* + * FIXME: Category not yet functional in lttv + */ + se->field = LTTV_FILTER_EVENT_CATEGORY; + } + else if(!g_strcasecmp(f->str,"time") ) { + se->field = LTTV_FILTER_EVENT_TIME; + } + else if(!g_strcasecmp(f->str,"tsc") ) { + se->field = LTTV_FILTER_EVENT_TSC; + } + else if(!g_strcasecmp(f->str,"target_pid") ) { + se->field = LTTV_FILTER_EVENT_TARGET_PID; + } + else if(!g_strcasecmp(f->str,"field") ) { + se->field = LTTV_FILTER_EVENT_FIELD; + g_string_free(f,TRUE); + f=ltt_g_ptr_array_remove_index_slow(fp,0); + + } else { + //g_string_free(f,TRUE); + //f=ltt_g_ptr_array_remove_index_slow(fp,0); + g_warning("Unknown event filter subtype %s", f->str); + } + } else { + g_string_free(f,TRUE); + f=ltt_g_ptr_array_remove_index_slow(fp,0); + + g_warning("Unrecognized field in filter string"); + } + + /* free memory for last string */ + g_string_free(f,TRUE); + + /* array should be empty */ + g_assert(fp->len == 0); + + if(se->field == LTTV_FILTER_UNDEFINED) { + g_warning("The specified field was not recognized !"); + return FALSE; + } + return TRUE; +} + +/** + * @fn gboolean lttv_simple_expression_assign_operator(LttvSimpleExpression*,LttvExpressionOp) + * + * Sets the function pointer for the current + * Simple Expression + * @param se current simple expression + * @param op current operator + * @return success/failure of operation + */ +gboolean +lttv_simple_expression_assign_operator(LttvSimpleExpression* se, LttvExpressionOp op) { + + switch(se->field) { + /* + * string + */ + case LTTV_FILTER_TRACE_NAME: + case LTTV_FILTER_TRACEFILE_NAME: + case LTTV_FILTER_STATE_P_NAME: + case LTTV_FILTER_STATE_T_BRAND: + case LTTV_FILTER_EVENT_NAME: + case LTTV_FILTER_STATE_EX_MODE: + case LTTV_FILTER_STATE_EX_SUBMODE: + case LTTV_FILTER_STATE_P_STATUS: + switch(op) { + case LTTV_FIELD_EQ: + se->op = lttv_apply_op_eq_quark; + break; + case LTTV_FIELD_NE: + se->op = lttv_apply_op_ne_quark; + break; + default: + g_warning("Error encountered in operator assignment = or != expected"); + return FALSE; + } + break; + /* + * integer + */ + case LTTV_FILTER_EVENT_TSC: + switch(op) { + case LTTV_FIELD_EQ: + se->op = lttv_apply_op_eq_uint64; + break; + case LTTV_FIELD_NE: + se->op = lttv_apply_op_ne_uint64; + break; + case LTTV_FIELD_LT: + se->op = lttv_apply_op_lt_uint64; + break; + case LTTV_FIELD_LE: + se->op = lttv_apply_op_le_uint64; + break; + case LTTV_FIELD_GT: + se->op = lttv_apply_op_gt_uint64; + break; + case LTTV_FIELD_GE: + se->op = lttv_apply_op_ge_uint64; + break; + default: + g_warning("Error encountered in operator assignment"); + return FALSE; + } + break; + /* + * unsigned integers + */ + case LTTV_FILTER_STATE_CPU: + case LTTV_FILTER_STATE_PID: + case LTTV_FILTER_STATE_PPID: + case LTTV_FILTER_EVENT_TARGET_PID: + switch(op) { + case LTTV_FIELD_EQ: + se->op = lttv_apply_op_eq_uint; + break; + case LTTV_FIELD_NE: + se->op = lttv_apply_op_ne_uint; + break; + case LTTV_FIELD_LT: + se->op = lttv_apply_op_lt_uint; + break; + case LTTV_FIELD_LE: + se->op = lttv_apply_op_le_uint; + break; + case LTTV_FIELD_GT: + se->op = lttv_apply_op_gt_uint; + break; + case LTTV_FIELD_GE: + se->op = lttv_apply_op_ge_uint; + break; + default: + g_warning("Error encountered in operator assignment"); + return FALSE; + } + break; + + /* + * Enums + * Entered as string, converted to enum + * + * can only be compared with 'equal' or 'not equal' operators + * + * unsigned int of 16 bits are used here since enums + * should not go over 2^16-1 values + */ +// case /*NOTHING*/: +// switch(op) { +// case LTTV_FIELD_EQ: +// se->op = lttv_apply_op_eq_uint16; +// break; +// case LTTV_FIELD_NE: +// se->op = lttv_apply_op_ne_uint16; +// break; +// default: +// g_warning("Error encountered in operator assignment = or != expected"); +// return FALSE; +// } +// break; + /* + * Ltttime + */ + case LTTV_FILTER_STATE_CT: + case LTTV_FILTER_STATE_IT: + case LTTV_FILTER_EVENT_TIME: + switch(op) { + case LTTV_FIELD_EQ: + se->op = lttv_apply_op_eq_ltttime; + break; + case LTTV_FIELD_NE: + se->op = lttv_apply_op_ne_ltttime; + break; + case LTTV_FIELD_LT: + se->op = lttv_apply_op_lt_ltttime; + break; + case LTTV_FIELD_LE: + se->op = lttv_apply_op_le_ltttime; + break; + case LTTV_FIELD_GT: + se->op = lttv_apply_op_gt_ltttime; + break; + case LTTV_FIELD_GE: + se->op = lttv_apply_op_ge_ltttime; + break; + default: + g_warning("Error encountered in operator assignment"); + return FALSE; + } + break; + default: + g_warning("Error encountered in operator assignation ! Field type:%i",se->field); + return FALSE; + } + + return TRUE; + +} + +/** + * @fn gboolean lttv_simple_expression_assign_value(LttvSimpleExpression*,char*) + * + * Assign the value field to the current LttvSimpleExpression + * @param se pointer to the current LttvSimpleExpression + * @param value string value for simple expression + */ +gboolean +lttv_simple_expression_assign_value(LttvSimpleExpression* se, char* value) { + + unsigned i; + gboolean is_double = FALSE; + LttTime t = ltt_time_zero; + GString* v; + guint string_len; + + switch(se->field) { + /* + * Strings + * entered as strings, converted to Quarks + */ + case LTTV_FILTER_TRACE_NAME: + case LTTV_FILTER_TRACEFILE_NAME: + case LTTV_FILTER_STATE_P_NAME: + case LTTV_FILTER_STATE_T_BRAND: + case LTTV_FILTER_EVENT_NAME: + case LTTV_FILTER_STATE_EX_MODE: + case LTTV_FILTER_STATE_EX_SUBMODE: + case LTTV_FILTER_STATE_P_STATUS: + // se->value.v_string = value; + se->value.v_quark = g_quark_from_string(value); + g_free(value); + break; + /* + * integer -- supposed to be uint64 + */ + case LTTV_FILTER_EVENT_TSC: + se->value.v_uint64 = atoi(value); + g_free(value); + break; + /* + * unsigned integers + */ + case LTTV_FILTER_STATE_PID: + case LTTV_FILTER_STATE_PPID: + case LTTV_FILTER_STATE_CPU: + case LTTV_FILTER_EVENT_TARGET_PID: + se->value.v_uint = atoi(value); + g_free(value); + break; + /* + * LttTime + */ + case LTTV_FILTER_STATE_CT: + case LTTV_FILTER_STATE_IT: + case LTTV_FILTER_EVENT_TIME: + //se->value.v_double = atof(value); + /* + * parsing logic could be optimised, + * but as for now, simpler this way + */ + v = g_string_new(""); + string_len = strlen(value); + for(i=0;istr); + g_string_free(v,TRUE); + v = g_string_new(""); + } else v = g_string_append_c(v,value[i]); + } + /* number can be integer or double */ + if(is_double) t.tv_nsec = atoi(v->str); + else { + t.tv_sec = atoi(v->str); + t.tv_nsec = 0; + } + + g_string_free(v,TRUE); + + se->value.v_ltttime = t; + g_free(value); + break; + default: + g_warning("Error encountered in value assignation ! Field type = %i",se->field); + g_free(value); + return FALSE; + } + + return TRUE; + +} + +/** + * @fn void lttv_simple_expression_destroy(LttvSimpleExpression*) + * + * Disallocate memory for the current + * simple expression + * @param se pointer to the current LttvSimpleExpression + */ +void +lttv_simple_expression_destroy(LttvSimpleExpression* se) { + + // g_free(se->value); +// switch(se->field) { +// case LTTV_FILTER_TRACE_NAME: +// case LTTV_FILTER_TRACEFILE_NAME: +// case LTTV_FILTER_STATE_P_NAME: +// case LTTV_FILTER_EVENT_NAME: +// g_free(se->value.v_string); +// break; +// } + g_free(se); + +} + +/** + * @fn gint lttv_struct_type(gint) + * + * Finds the structure type depending + * on the fields in parameters + * @params ft Field of the current structure + * @return LttvStructType enum or -1 for error + */ +gint +lttv_struct_type(gint ft) { + + switch(ft) { + case LTTV_FILTER_TRACE_NAME: + return LTTV_FILTER_TRACE; + break; + case LTTV_FILTER_TRACEFILE_NAME: + return LTTV_FILTER_TRACEFILE; + break; + case LTTV_FILTER_STATE_PID: + case LTTV_FILTER_STATE_PPID: + case LTTV_FILTER_STATE_CT: + case LTTV_FILTER_STATE_IT: + case LTTV_FILTER_STATE_P_NAME: + case LTTV_FILTER_STATE_T_BRAND: + case LTTV_FILTER_STATE_EX_MODE: + case LTTV_FILTER_STATE_EX_SUBMODE: + case LTTV_FILTER_STATE_P_STATUS: + case LTTV_FILTER_STATE_CPU: + return LTTV_FILTER_STATE; + break; + case LTTV_FILTER_EVENT_NAME: + case LTTV_FILTER_EVENT_CATEGORY: + case LTTV_FILTER_EVENT_TIME: + case LTTV_FILTER_EVENT_TSC: + case LTTV_FILTER_EVENT_TARGET_PID: + case LTTV_FILTER_EVENT_FIELD: + return LTTV_FILTER_EVENT; + break; + default: + return -1; + } +} + +/** + * @fn gboolean lttv_apply_op_eq_uint(gpointer,LttvFieldValue) + * + * Applies the 'equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_eq_uint(const gpointer v1, LttvFieldValue v2) { + + guint* r = (guint*) v1; + return (*r == v2.v_uint); + +} + +/** + * @fn gboolean lttv_apply_op_eq_uint64(gpointer,LttvFieldValue) + * + * Applies the 'equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_eq_uint64(const gpointer v1, LttvFieldValue v2) { + + guint64* r = (guint64*) v1; + return (*r == v2.v_uint64); + +} + +/** + * @fn gboolean lttv_apply_op_eq_uint32(gpointer,LttvFieldValue) + * + * Applies the 'equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_eq_uint32(const gpointer v1, LttvFieldValue v2) { + guint32* r = (guint32*) v1; + return (*r == v2.v_uint32); +} + +/** + * @fn gboolean lttv_apply_op_eq_uint16(gpointer,LttvFieldValue) + * + * Applies the 'equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_eq_uint16(const gpointer v1, LttvFieldValue v2) { + guint16* r = (guint16*) v1; + return (*r == v2.v_uint16); +} + +/** + * @fn gboolean lttv_apply_op_eq_double(gpointer,LttvFieldValue) + * + * Applies the 'equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_eq_double(const gpointer v1, LttvFieldValue v2) { + double* r = (double*) v1; + return (*r == v2.v_double); +} + +/** + * @fn gboolean lttv_apply_op_eq_string(gpointer,LttvFieldValue) + * + * Applies the 'equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_eq_string(const gpointer v1, LttvFieldValue v2) { + char* r = (char*) v1; + return (!g_strcasecmp(r,v2.v_string)); +} + +/** + * @fn gboolean lttv_apply_op_eq_quark(gpointer,LttvFieldValue) + * + * Applies the 'equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_eq_quark(const gpointer v1, LttvFieldValue v2) { + GQuark* r = (GQuark*) v1; + return (*r == v2.v_quark); +} + +/** + * @fn gboolean lttv_apply_op_eq_ltttime(gpointer,LttvFieldValue) + * + * Applies the 'equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_eq_ltttime(const gpointer v1, LttvFieldValue v2) { + LttTime* r = (LttTime*) v1; + return ltt_time_compare(*r, v2.v_ltttime)==0?1:0; +} + +/** + * @fn gboolean lttv_apply_op_ne_uint(gpointer,LttvFieldValue) + * + * Applies the 'not equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_ne_uint(const gpointer v1, LttvFieldValue v2) { + guint* r = (guint*) v1; + return (*r != v2.v_uint); +} + +/** + * @fn gboolean lttv_apply_op_ne_uint64(gpointer,LttvFieldValue) + * + * Applies the 'not equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_ne_uint64(const gpointer v1, LttvFieldValue v2) { + guint64* r = (guint64*) v1; + return (*r != v2.v_uint64); +} + +/** + * @fn gboolean lttv_apply_op_ne_uint32(gpointer,LttvFieldValue) + * + * Applies the 'not equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_ne_uint32(const gpointer v1, LttvFieldValue v2) { + guint32* r = (guint32*) v1; + return (*r != v2.v_uint32); +} + +/** + * @fn gboolean lttv_apply_op_ne_uint16(gpointer,LttvFieldValue) + * + * Applies the 'not equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_ne_uint16(const gpointer v1, LttvFieldValue v2) { + guint16* r = (guint16*) v1; + return (*r != v2.v_uint16); +} + +/** + * @fn gboolean lttv_apply_op_ne_double(gpointer,LttvFieldValue) + * + * Applies the 'not equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_ne_double(const gpointer v1, LttvFieldValue v2) { + double* r = (double*) v1; + return (*r != v2.v_double); +} + +/** + * @fn gboolean lttv_apply_op_ne_string(gpointer,LttvFieldValue) + * + * Applies the 'not equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_ne_string(const gpointer v1, LttvFieldValue v2) { + char* r = (char*) v1; + return (g_strcasecmp(r,v2.v_string)); +} + +/** + * @fn gboolean lttv_apply_op_ne_quark(gpointer,LttvFieldValue) + * + * Applies the 'not equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_ne_quark(const gpointer v1, LttvFieldValue v2) { + GQuark* r = (GQuark*) v1; + return (*r != v2.v_quark); +} + + +/** + * @fn gboolean lttv_apply_op_ne_ltttime(gpointer,LttvFieldValue) + * + * Applies the 'not equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_ne_ltttime(const gpointer v1, LttvFieldValue v2) { + LttTime* r = (LttTime*) v1; + return ltt_time_compare(*r, v2.v_ltttime)!=0?1:0; +} + +/** + * @fn gboolean lttv_apply_op_lt_uint(gpointer,LttvFieldValue) + * + * Applies the 'lower than' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_lt_uint(const gpointer v1, LttvFieldValue v2) { + guint* r = (guint*) v1; + return (*r < v2.v_uint); +} + +/** + * @fn gboolean lttv_apply_op_lt_uint64(gpointer,LttvFieldValue) + * + * Applies the 'lower than' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_lt_uint64(const gpointer v1, LttvFieldValue v2) { + guint64* r = (guint64*) v1; + return (*r < v2.v_uint64); +} + +/** + * @fn gboolean lttv_apply_op_lt_uint32(gpointer,LttvFieldValue) + * + * Applies the 'lower than' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_lt_uint32(const gpointer v1, LttvFieldValue v2) { + guint32* r = (guint32*) v1; + return (*r < v2.v_uint32); +} + +/** + * @fn gboolean lttv_apply_op_lt_uint16(gpointer,LttvFieldValue) + * + * Applies the 'lower than' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_lt_uint16(const gpointer v1, LttvFieldValue v2) { + guint16* r = (guint16*) v1; + return (*r < v2.v_uint16); +} + +/** + * @fn gboolean lttv_apply_op_lt_double(gpointer,LttvFieldValue) + * + * Applies the 'lower than' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_lt_double(const gpointer v1, LttvFieldValue v2) { + double* r = (double*) v1; + return (*r < v2.v_double); +} + +/** + * @fn gboolean lttv_apply_op_lt_ltttime(gpointer,LttvFieldValue) + * + * Applies the 'lower than' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_lt_ltttime(const gpointer v1, LttvFieldValue v2) { + LttTime* r = (LttTime*) v1; +// return ((r->tv_sec < v2.v_ltttime.tv_sec) || ((r->tv_sec == v2.v_ltttime.tv_sec) && (r->tv_nsec < v2.v_ltttime.tv_nsec))); + return ltt_time_compare(*r, v2.v_ltttime)==-1?1:0; +} + +/** + * @fn gboolean lttv_apply_op_le_uint(gpointer,LttvFieldValue) + * + * Applies the 'lower or equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_le_uint(const gpointer v1, LttvFieldValue v2) { + guint* r = (guint*) v1; + return (*r <= v2.v_uint); +} + +/** + * @fn gboolean lttv_apply_op_le_uint64(gpointer,LttvFieldValue) + * + * Applies the 'lower or equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_le_uint64(const gpointer v1, LttvFieldValue v2) { + guint64* r = (guint64*) v1; + return (*r <= v2.v_uint64); +} + +/** + * @fn gboolean lttv_apply_op_le_uint32(gpointer,LttvFieldValue) + * + * Applies the 'lower or equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_le_uint32(const gpointer v1, LttvFieldValue v2) { + guint32* r = (guint32*) v1; + return (*r <= v2.v_uint32); +} + +/** + * @fn gboolean lttv_apply_op_le_uint16(gpointer,LttvFieldValue) + * + * Applies the 'lower or equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_le_uint16(const gpointer v1, LttvFieldValue v2) { + guint16* r = (guint16*) v1; + return (*r <= v2.v_uint16); +} + +/** + * @fn gboolean lttv_apply_op_le_double(gpointer,LttvFieldValue) + * + * Applies the 'lower or equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_le_double(const gpointer v1, LttvFieldValue v2) { + double* r = (double*) v1; + return (*r <= v2.v_double); +} + +/** + * @fn gboolean lttv_apply_op_le_ltttime(gpointer,LttvFieldValue) + * + * Applies the 'lower or equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_le_ltttime(const gpointer v1, LttvFieldValue v2) { + LttTime* r = (LttTime*) v1; +// return ((r->tv_sec < v2.v_ltttime.tv_sec) || ((r->tv_sec == v2.v_ltttime.tv_sec) && (r->tv_nsec <= v2.v_ltttime.tv_nsec))); + return ltt_time_compare(*r, v2.v_ltttime)<1?1:0; +} + + +/** + * @fn gboolean lttv_apply_op_gt_uint(gpointer,LttvFieldValue) + * + * Applies the 'greater than' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_gt_uint(const gpointer v1, LttvFieldValue v2) { + guint* r = (guint*) v1; + return (*r > v2.v_uint); +} + +/** + * @fn gboolean lttv_apply_op_gt_uint64(gpointer,LttvFieldValue) + * + * Applies the 'greater than' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_gt_uint64(const gpointer v1, LttvFieldValue v2) { + guint64* r = (guint64*) v1; + return (*r > v2.v_uint64); +} + +/** + * @fn gboolean lttv_apply_op_gt_uint32(gpointer,LttvFieldValue) + * + * Applies the 'greater than' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_gt_uint32(const gpointer v1, LttvFieldValue v2) { + guint32* r = (guint32*) v1; + return (*r > v2.v_uint32); +} + +/** + * @fn gboolean lttv_apply_op_gt_uint16(gpointer,LttvFieldValue) + * + * Applies the 'greater than' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_gt_uint16(const gpointer v1, LttvFieldValue v2) { + guint16* r = (guint16*) v1; + return (*r > v2.v_uint16); +} + +/** + * @fn gboolean lttv_apply_op_gt_double(gpointer,LttvFieldValue) + * + * Applies the 'greater than' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_gt_double(const gpointer v1, LttvFieldValue v2) { + double* r = (double*) v1; + return (*r > v2.v_double); +} + +/** + * @fn gboolean lttv_apply_op_gt_ltttime(gpointer,LttvFieldValue) + * + * Applies the 'greater than' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_gt_ltttime(const gpointer v1, LttvFieldValue v2) { + LttTime* r = (LttTime*) v1; +// return ((r->tv_sec > v2.v_ltttime.tv_sec) || ((r->tv_sec == v2.v_ltttime.tv_sec) && (r->tv_nsec > v2.v_ltttime.tv_nsec))); + return ltt_time_compare(*r, v2.v_ltttime)==1?1:0; +} + +/** + * @fn gboolean lttv_apply_op_ge_uint(gpointer,LttvFieldValue) + * + * Applies the 'greater or equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_ge_uint(const gpointer v1, LttvFieldValue v2) { + guint* r = (guint*) v1; + return (*r >= v2.v_uint); +} + +/** + * @fn gboolean lttv_apply_op_ge_uint64(gpointer,LttvFieldValue) + * + * Applies the 'greater or equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_ge_uint64(const gpointer v1, LttvFieldValue v2) { + guint64* r = (guint64*) v1; + return (*r >= v2.v_uint64); +} + +/** + * @fn gboolean lttv_apply_op_ge_uint32(gpointer,LttvFieldValue) + * + * Applies the 'greater or equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_ge_uint32(const gpointer v1, LttvFieldValue v2) { + guint32* r = (guint32*) v1; + return (*r >= v2.v_uint32); +} + +/** + * @fn gboolean lttv_apply_op_ge_uint16(gpointer,LttvFieldValue) + * + * Applies the 'greater or equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_ge_uint16(const gpointer v1, LttvFieldValue v2) { + guint16* r = (guint16*) v1; + return (*r >= v2.v_uint16); +} + +/** + * @fn gboolean lttv_apply_op_ge_double(gpointer,LttvFieldValue) + * + * Applies the 'greater or equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_ge_double(const gpointer v1, LttvFieldValue v2) { + double* r = (double*) v1; + return (*r >= v2.v_double); +} + +/** + * @fn gboolean lttv_apply_op_ge_ltttime(gpointer,LttvFieldValue) + * + * Applies the 'greater or equal' operator to the + * specified structure and value + * @param v1 left member of comparison + * @param v2 right member of comparison + * @return success/failure of operation + */ +gboolean lttv_apply_op_ge_ltttime(const gpointer v1, LttvFieldValue v2) { + LttTime* r = (LttTime*) v1; +// return ((r->tv_sec > v2.v_ltttime.tv_sec) || ((r->tv_sec == v2.v_ltttime.tv_sec) && (r->tv_nsec >= v2.v_ltttime.tv_nsec))); + return ltt_time_compare(*r, v2.v_ltttime)>-1?1:0; +} + + + +/** + * Makes a copy of the current filter tree + * @param tree pointer to the current tree + * @return new copy of the filter tree + */ +LttvFilterTree* +lttv_filter_tree_clone(const LttvFilterTree* tree) { + + LttvFilterTree* newtree = lttv_filter_tree_new(); + + newtree->node = tree->node; + + newtree->left = tree->left; + if(newtree->left == LTTV_TREE_NODE) { + newtree->l_child.t = lttv_filter_tree_clone(tree->l_child.t); + } else if(newtree->left == LTTV_TREE_LEAF) { + newtree->l_child.leaf = lttv_simple_expression_new(); + newtree->l_child.leaf->field = tree->l_child.leaf->field; + newtree->l_child.leaf->offset = tree->l_child.leaf->offset; + newtree->l_child.leaf->op = tree->l_child.leaf->op; + /* FIXME: special case for string copy ! */ + newtree->l_child.leaf->value = tree->l_child.leaf->value; + } + + newtree->right = tree->right; + if(newtree->right == LTTV_TREE_NODE) { + newtree->r_child.t = lttv_filter_tree_clone(tree->r_child.t); + } else if(newtree->right == LTTV_TREE_LEAF) { + newtree->r_child.leaf = lttv_simple_expression_new(); + newtree->r_child.leaf->field = tree->r_child.leaf->field; + newtree->r_child.leaf->offset = tree->r_child.leaf->offset; + newtree->r_child.leaf->op = tree->r_child.leaf->op; + newtree->r_child.leaf->value = tree->r_child.leaf->value; + } + + return newtree; + +} + +/** + * Makes a copy of the current filter + * @param filter pointer to the current filter + * @return new copy of the filter + */ +LttvFilter* +lttv_filter_clone(const LttvFilter* filter) { + + if(!filter) return NULL; + + LttvFilter* newfilter = g_new(LttvFilter,1); + + strcpy(newfilter->expression,filter->expression); + + newfilter->head = lttv_filter_tree_clone(filter->head); + + return newfilter; + +} + + +/** + * @fn LttvFilter* lttv_filter_new() + * + * Creates a new LttvFilter + * @return the current LttvFilter or NULL if error + */ +LttvFilter* +lttv_filter_new() { + + LttvFilter* filter = g_new(LttvFilter,1); + filter->expression = NULL; + filter->head = NULL; + + return filter; + +} + +/** + * @fn gboolean lttv_filter_update(LttvFilter*) + * + * Updates the current LttvFilter by building + * its tree based upon the expression string + * @param filter pointer to the current LttvFilter + * @return Failure/Success of operation + */ +gboolean +lttv_filter_update(LttvFilter* filter) { + +// g_print("filter::lttv_filter_new()\n"); /* debug */ + + if(filter->expression == NULL) return FALSE; + + int + i, + p_nesting=0, /* parenthesis nesting value */ + not=0; + guint expression_len; + + /* trees */ + LttvFilterTree + *tree = lttv_filter_tree_new(), /* main tree */ + *subtree = NULL, /* buffer for subtrees */ + *t1, /* buffer #1 */ + *t2, /* buffer #2 */ + *t3; /* buffer #3 */ + + /* + * the filter + * If the tree already exists, + * destroy it and build a new one + */ + if(filter->head != NULL) lttv_filter_tree_destroy(filter->head); + filter->head = NULL; /* will be assigned at the end */ + + /* + * Tree Stack + * each element of the list + * is a sub tree created + * by the use of parenthesis in the + * global expression. The final tree + * will be the one left at the root of + * the list + */ + GPtrArray *tree_stack = g_ptr_array_new(); + g_ptr_array_add( tree_stack,(gpointer) tree ); + + /* temporary values */ + GString *a_field_component = g_string_new(""); + GString *a_string_spaces = g_string_new(""); + GPtrArray *a_field_path = g_ptr_array_new(); + + /* simple expression buffer */ + LttvSimpleExpression* a_simple_expression = lttv_simple_expression_new(); + + gint nest_quotes = 0; + + /* + * Parse entire expression and construct + * the binary tree. There are two steps + * in browsing that string + * 1. finding boolean ops " &,|,^,! " and parenthesis " {,(,[,],),} " + * 2. finding simple expressions + * - field path ( separated by dots ) + * - op ( >, <, =, >=, <=, !=) + * - value ( integer, string ... ) + * To spare computing time, the whole + * string is parsed in this loop for a + * O(n) complexity order. + * + * When encountering logical op &,|,^ + * 1. parse the last value if any + * 2. create a new tree + * 3. add the expression (simple exp, or exp (subtree)) to the tree + * 4. concatenate this tree with the current tree on top of the stack + * When encountering math ops >,>=,<,<=,=,!= + * 1. add to op to the simple expression + * 2. concatenate last field component to field path + * When encountering concatening ops . + * 1. concatenate last field component to field path + * When encountering opening parenthesis (,{,[ + * 1. create a new subtree on top of tree stack + * When encountering closing parenthesis ),},] + * 1. add the expression on right child of the current tree + * 2. the subtree is completed, allocate a new subtree + * 3. pop the tree value from the tree stack + */ + +#ifdef TEST + struct timeval starttime; + struct timeval endtime; + gettimeofday(&starttime, NULL); +#endif + + expression_len = strlen(filter->expression); + for(i=0;iexpression[i]); + if(nest_quotes) { + switch(filter->expression[i]) { + case '\\' : + if(filter->expression[i+1] == '\"') { + i++; + } + break; + case '\"': + nest_quotes = 0; + i++; + break; + } + if(a_string_spaces->len != 0) { + a_field_component = g_string_append( + a_field_component, a_string_spaces->str); + a_string_spaces = g_string_set_size(a_string_spaces, 0); + } + a_field_component = g_string_append_c(a_field_component, + filter->expression[i]); + continue; + } + + switch(filter->expression[i]) { + /* + * logical operators + */ + case '&': /* and */ + + /* get current tree in tree stack */ + t1 = (LttvFilterTree*)g_ptr_array_index(tree_stack,tree_stack->len-1); + + /* get current node at absolute right */ + while(t1->right != LTTV_TREE_IDLE) { + g_assert(t1->right == LTTV_TREE_NODE); + t1 = t1->r_child.t; + } + t2 = lttv_filter_tree_new(); + t2->node = LTTV_LOGICAL_AND; + t1->right = LTTV_TREE_NODE; + t1->r_child.t = t2; + if(not) { /* add not operator to tree */ + t3 = lttv_filter_tree_new(); + t3->node = LTTV_LOGICAL_NOT; + t2->left = LTTV_TREE_NODE; + t2->l_child.t = t3; + t2 = t3; + not = 0; + } + if(subtree != NULL) { /* append subtree to current tree */ + t2->left = LTTV_TREE_NODE; + t2->l_child.t = subtree; + subtree = NULL; + } else { /* append a simple expression */ + lttv_simple_expression_assign_value(a_simple_expression,g_string_free(a_field_component,FALSE)); + a_field_component = g_string_new(""); + g_string_free(a_string_spaces, TRUE); + a_string_spaces = g_string_new(""); + t2->left = LTTV_TREE_LEAF; + t2->l_child.leaf = a_simple_expression; + a_simple_expression = lttv_simple_expression_new(); + } + break; + + case '|': /* or */ + + t1 = (LttvFilterTree*)g_ptr_array_index(tree_stack,tree_stack->len-1); + while(t1->right != LTTV_TREE_IDLE) { + g_assert(t1->right == LTTV_TREE_NODE); + t1 = t1->r_child.t; + } + t2 = lttv_filter_tree_new(); + t2->node = LTTV_LOGICAL_OR; + t1->right = LTTV_TREE_NODE; + t1->r_child.t = t2; + if(not) { // add not operator to tree + t3 = lttv_filter_tree_new(); + t3->node = LTTV_LOGICAL_NOT; + t2->left = LTTV_TREE_NODE; + t2->l_child.t = t3; + t2 = t3; + not = 0; + } + if(subtree != NULL) { /* append subtree to current tree */ + t2->left = LTTV_TREE_NODE; + t2->l_child.t = subtree; + subtree = NULL; + } else { /* append a simple expression */ + lttv_simple_expression_assign_value(a_simple_expression,g_string_free(a_field_component,FALSE)); + a_field_component = g_string_new(""); + g_string_free(a_string_spaces, TRUE); + a_string_spaces = g_string_new(""); + t2->left = LTTV_TREE_LEAF; + t2->l_child.leaf = a_simple_expression; + a_simple_expression = lttv_simple_expression_new(); + } + break; + + case '^': /* xor */ + + t1 = (LttvFilterTree*)g_ptr_array_index(tree_stack,tree_stack->len-1); + while(t1->right != LTTV_TREE_IDLE) { + g_assert(t1->right == LTTV_TREE_NODE); + t1 = t1->r_child.t; + } + t2 = lttv_filter_tree_new(); + t2->node = LTTV_LOGICAL_XOR; + t1->right = LTTV_TREE_NODE; + t1->r_child.t = t2; + if(not) { // add not operator to tree + t3 = lttv_filter_tree_new(); + t3->node = LTTV_LOGICAL_NOT; + t2->left = LTTV_TREE_NODE; + t2->l_child.t = t3; + t2 = t3; + not = 0; + } + if(subtree != NULL) { /* append subtree to current tree */ + t2->left = LTTV_TREE_NODE; + t2->l_child.t = subtree; + subtree = NULL; + } else { /* append a simple expression */ + lttv_simple_expression_assign_value(a_simple_expression,g_string_free(a_field_component,FALSE)); + a_field_component = g_string_new(""); + g_string_free(a_string_spaces, TRUE); + a_string_spaces = g_string_new(""); + t2->left = LTTV_TREE_LEAF; + t2->l_child.leaf = a_simple_expression; + a_simple_expression = lttv_simple_expression_new(); + } + break; + + case '!': /* not, or not equal (math op) */ + + if(filter->expression[i+1] == '=') { /* != */ + g_ptr_array_add( a_field_path,(gpointer) a_field_component ); + lttv_simple_expression_assign_field(a_field_path,a_simple_expression); + a_field_component = g_string_new(""); + g_string_free(a_string_spaces, TRUE); + a_string_spaces = g_string_new(""); + lttv_simple_expression_assign_operator(a_simple_expression,LTTV_FIELD_NE); + i++; + } else { /* ! */ + not=1; + } + break; + + case '(': /* start of parenthesis */ + case '[': + case '{': + + p_nesting++; /* incrementing parenthesis nesting value */ + t1 = lttv_filter_tree_new(); + if(not) { /* add not operator to tree */ + t3 = lttv_filter_tree_new(); + t3->node = LTTV_LOGICAL_NOT; + t1->right = LTTV_TREE_NODE; + t1->r_child.t = t3; + not = 0; + } + g_ptr_array_add( tree_stack,(gpointer) t1 ); + break; + + case ')': /* end of parenthesis */ + case ']': + case '}': + + p_nesting--; /* decrementing parenthesis nesting value */ + if(p_nesting<0 || tree_stack->len<2) { + g_warning("Wrong filtering options, the string\n\"%s\"\n\ + is not valid due to parenthesis incorrect use",filter->expression); + return FALSE; + } + + /* there must at least be the root tree left in the array */ + g_assert(tree_stack->len>0); + + t1 = g_ptr_array_index(tree_stack,tree_stack->len-1); + while(t1->right != LTTV_TREE_IDLE) { + t1 = t1->r_child.t; + } + if(not) { // add not operator to tree + g_print("ici"); + t3 = lttv_filter_tree_new(); + t3->node = LTTV_LOGICAL_NOT; + t1->right = LTTV_TREE_NODE; + t1->r_child.t = t3; + t1 = t3; + not = 0; + } + if(subtree != NULL) { /* append subtree to current tree */ + t1->right = LTTV_TREE_NODE; + t1->r_child.t = subtree; + subtree = g_ptr_array_index(tree_stack,tree_stack->len-1); + g_ptr_array_remove_index(tree_stack,tree_stack->len-1); + } else { /* assign subtree as current tree */ + lttv_simple_expression_assign_value(a_simple_expression,g_string_free(a_field_component,FALSE)); + a_field_component = g_string_new(""); + g_string_free(a_string_spaces, TRUE); + a_string_spaces = g_string_new(""); + t1->right = LTTV_TREE_LEAF; + t1->r_child.leaf = a_simple_expression; + a_simple_expression = lttv_simple_expression_new(); + subtree = g_ptr_array_remove_index(tree_stack,tree_stack->len-1); + } + break; + + /* + * mathematic operators + */ + case '<': /* lower, lower or equal */ + + g_ptr_array_add( a_field_path,(gpointer) a_field_component ); + lttv_simple_expression_assign_field(a_field_path,a_simple_expression); + a_field_component = g_string_new(""); + g_string_free(a_string_spaces, TRUE); + a_string_spaces = g_string_new(""); + if(filter->expression[i+1] == '=') { /* <= */ + i++; + lttv_simple_expression_assign_operator(a_simple_expression,LTTV_FIELD_LE); + } else lttv_simple_expression_assign_operator(a_simple_expression,LTTV_FIELD_LT); + break; + + case '>': /* higher, higher or equal */ + + g_ptr_array_add( a_field_path,(gpointer) a_field_component ); + lttv_simple_expression_assign_field(a_field_path,a_simple_expression); + a_field_component = g_string_new(""); + g_string_free(a_string_spaces, TRUE); + a_string_spaces = g_string_new(""); + if(filter->expression[i+1] == '=') { /* >= */ + i++; + lttv_simple_expression_assign_operator(a_simple_expression,LTTV_FIELD_GE); + } else lttv_simple_expression_assign_operator(a_simple_expression,LTTV_FIELD_GT); + break; + + case '=': /* equal */ + + g_ptr_array_add( a_field_path,(gpointer) a_field_component ); + lttv_simple_expression_assign_field(a_field_path,a_simple_expression); + a_field_component = g_string_new(""); + g_string_free(a_string_spaces, TRUE); + a_string_spaces = g_string_new(""); + lttv_simple_expression_assign_operator(a_simple_expression,LTTV_FIELD_EQ); + break; + + /* + * Field concatening caracter + */ + case '.': /* dot */ + + /* + * divide field expression into elements + * in a_field_path array. + * + * A dot can also be present in double values + */ + if(a_simple_expression->field == LTTV_FILTER_UNDEFINED) { + g_ptr_array_add( a_field_path,(gpointer) a_field_component ); + a_field_component = g_string_new(""); + g_string_free(a_string_spaces, TRUE); + a_string_spaces = g_string_new(""); + } else { + /* Operator found, we are in the value field */ + g_string_append_c(a_field_component, filter->expression[i]); + } + break; + case ' ': /* keep spaces that are within a field component */ + if(a_field_component->len == 0) break; /* ignore */ + else + a_string_spaces = g_string_append_c(a_string_spaces, + filter->expression[i]); + + case '\n': /* ignore */ + break; + case '\"': + nest_quotes?(nest_quotes=0):(nest_quotes=1); + break; + default: /* concatening current string */ + if(a_string_spaces->len != 0) { + a_field_component = g_string_append( + a_field_component, a_string_spaces->str); + a_string_spaces = g_string_set_size(a_string_spaces, 0); + } + a_field_component = g_string_append_c(a_field_component, + filter->expression[i]); + } + } + + /* + * Preliminary check to see + * if tree was constructed correctly + */ + if( p_nesting>0 ) { + g_warning("Wrong filtering options, the string\n\"%s\"\n\ + is not valid due to parenthesis incorrect use",filter->expression); + return FALSE; + } + + if(tree_stack->len != 1) /* only root tree should remain */ + return FALSE; + + /* + * processing last element of expression + */ + t1 = g_ptr_array_index(tree_stack,tree_stack->len-1); + while(t1->right != LTTV_TREE_IDLE) { + g_assert(t1->right == LTTV_TREE_NODE); + t1 = t1->r_child.t; + } + if(not) { // add not operator to tree + t3 = lttv_filter_tree_new(); + t3->node = LTTV_LOGICAL_NOT; + t1->right = LTTV_TREE_NODE; + t1->r_child.t = t3; + t1 = t3; + not = 0; + } + if(subtree != NULL) { /* add the subtree */ + t1->right = LTTV_TREE_NODE; + t1->r_child.t = subtree; + subtree = NULL; + } else { /* add a leaf */ + lttv_simple_expression_assign_value(a_simple_expression,g_string_free(a_field_component,FALSE)); + a_field_component = NULL; + g_string_free(a_string_spaces, TRUE); + a_string_spaces = NULL; + t1->right = LTTV_TREE_LEAF; + t1->r_child.leaf = a_simple_expression; + a_simple_expression = NULL; + } + + + /* free the pointer array */ + g_assert(a_field_path->len == 0); + g_ptr_array_free(a_field_path,TRUE); + + /* free the tree stack -- but keep the root tree */ + filter->head = ltt_g_ptr_array_remove_index_slow(tree_stack,0); + g_ptr_array_free(tree_stack,TRUE); + + /* free the field buffer if allocated */ + if(a_field_component != NULL) g_string_free(a_field_component,TRUE); + if(a_string_spaces != NULL) g_string_free(a_string_spaces, TRUE); + + /* free the simple expression buffer if allocated */ + if(a_simple_expression != NULL) lttv_simple_expression_destroy(a_simple_expression); + + g_assert(filter->head != NULL); /* tree should exist */ + g_assert(subtree == NULL); /* remaining subtree should be included in main tree */ + +#ifdef TEST + gettimeofday(&endtime, NULL); + + /* Calcul du temps de l'algorithme */ + double time1 = starttime.tv_sec + (starttime.tv_usec/1000000.0); + double time2 = endtime.tv_sec + (endtime.tv_usec/1000000.0); +// g_print("Tree build took %.10f ms for strlen of %i\n",(time2-time1)*1000,strlen(filter->expression)); + g_print("%.10f %i\n",(time2-time1)*1000,strlen(filter->expression)); +#endif + + /* debug */ + g_debug("+++++++++++++++ BEGIN PRINT ++++++++++++++++\n"); + lttv_print_tree(filter->head,0) ; + g_debug("+++++++++++++++ END PRINT ++++++++++++++++++\n"); + + /* success */ + return TRUE; + +} + +/** + * @fn void lttv_filter_destroy(LttvFilter*) + * + * Destroy the current LttvFilter + * @param filter pointer to the current LttvFilter + */ +void +lttv_filter_destroy(LttvFilter* filter) { + + if(!filter) return; + + if(filter->expression) + g_free(filter->expression); + if(filter->head) + lttv_filter_tree_destroy(filter->head); + g_free(filter); + +} + +/** + * @fn LttvFilterTree* lttv_filter_tree_new() + * + * Assign a new tree for the current expression + * or sub expression + * @return pointer of LttvFilterTree + */ +LttvFilterTree* +lttv_filter_tree_new() { + LttvFilterTree* tree; + + tree = g_new(LttvFilterTree,1); + tree->node = 0; //g_new(lttv_expression,1); + tree->left = LTTV_TREE_IDLE; + tree->right = LTTV_TREE_IDLE; + tree->r_child.t = NULL; + tree->l_child.t = NULL; + + return tree; +} + +/** + * @fn void lttv_filter_append_expression(LttvFilter*,char*) + * + * Append a new expression to the expression + * defined in the current filter + * @param filter pointer to the current LttvFilter + * @param expression string that must be appended + * @return Success/Failure of operation + */ +gboolean +lttv_filter_append_expression(LttvFilter* filter, const char *expression) { + + if(expression == NULL) return FALSE; + if(filter == NULL) return FALSE; + if(expression[0] == '\0') return FALSE; /* Empty expression */ + + GString* s = g_string_new(""); + if(filter->expression != NULL) { + s = g_string_append(s,filter->expression); + s = g_string_append_c(s,'&'); + } + s = g_string_append(s,expression); + + g_free(filter->expression); + filter->expression = g_string_free(s,FALSE); + + /* TRUE if construction of tree proceeded without errors */ + return lttv_filter_update(filter); + +} + +/** + * @fn void lttv_filter_clear_expression(LttvFilter*) + * + * Clear the filter expression from the + * current filter and sets its pointer to NULL + * @param filter pointer to the current LttvFilter + */ +void +lttv_filter_clear_expression(LttvFilter* filter) { + + if(filter->expression != NULL) { + g_free(filter->expression); + filter->expression = NULL; + } + +} + +/** + * @fn void lttv_filter_tree_destroy(LttvFilterTree*) + * + * Destroys the tree and his sub-trees + * @param tree Tree which must be destroyed + */ +void +lttv_filter_tree_destroy(LttvFilterTree* tree) { + + if(tree == NULL) return; + + if(tree->left == LTTV_TREE_LEAF) lttv_simple_expression_destroy(tree->l_child.leaf); + else if(tree->left == LTTV_TREE_NODE) lttv_filter_tree_destroy(tree->l_child.t); + + if(tree->right == LTTV_TREE_LEAF) lttv_simple_expression_destroy(tree->r_child.leaf); + else if(tree->right == LTTV_TREE_NODE) lttv_filter_tree_destroy(tree->r_child.t); + +// g_free(tree->node); + g_free(tree); +} + +/** + * Global parsing function for the current + * LttvFilterTree + * @param t pointer to the current LttvFilterTree + * @param event current LttEvent, NULL if not used + * @param tracefile current LttTracefile, NULL if not used + * @param trace current LttTrace, NULL if not used + * @param state current LttvProcessState, NULL if not used + * @param context current LttvTracefileContext, NULL if not used + * @return response of filter + */ +gboolean +lttv_filter_tree_parse( + const LttvFilterTree* t, + const LttEvent* event, + const LttTracefile* tracefile, + const LttTrace* trace, + const LttvTracefileContext* context, + const LttvProcessState* state, + const LttvTraceContext* tc + /*,...*/) +{ + + /* + * Each tree is parsed in inorder. + * This way, it's possible to apply the left filter of the + * tree, then decide whether or not the right branch should + * be parsed depending on the linking logical operator + * + * Each node consists in a + * 1. logical operator + * 2. left child ( node or simple expression ) + * 3. right child ( node or simple expression ) + * + * When the child is a simple expression, we must + * before all determine if the expression refers to + * a structure which is whithin observation ( not NULL ). + * -If so, the expression is evaluated. + * -If not, the result is set to TRUE since this particular + * operation does not interfere with the lttv structure + * + * The result of each simple expression will directly + * affect the next branch. This way, depending on + * the linking logical operator, the parser will decide + * to explore or not the next branch. + * 1. AND OPERATOR + * -If result of left branch is 0 / FALSE + * then don't explore right branch and return 0; + * -If result of left branch is 1 / TRUE then explore + * 2. OR OPERATOR + * -If result of left branch is 1 / TRUE + * then don't explore right branch and return 1; + * -If result of left branch is 0 / FALSE then explore + * 3. XOR OPERATOR + * -Result of left branch will not affect exploration of + * right branch + */ + + gboolean lresult = FALSE, rresult = FALSE; + + LttvTraceState *ts; + LttvTracefileState *tfs = (LttvTracefileState*)context; + if(tc) + ts = (LttvTraceState*)tc; + else if(context) + ts = (LttvTraceState*)context->t_context; + + if(tfs) { + guint cpu = tfs->cpu; + if(ts) + state = ts->running_process[cpu]; + } + + /* + * Parse left branch + */ + if(t->left == LTTV_TREE_NODE) { + lresult = lttv_filter_tree_parse(t->l_child.t,event,tracefile,trace,context,NULL,NULL); + } + else if(t->left == LTTV_TREE_LEAF) { + lresult = lttv_filter_tree_parse_branch(t->l_child.leaf,event,tracefile,trace,state,context); + } + + /* + * Parse linking operator + * make a cutoff if possible + */ + if((t->node & LTTV_LOGICAL_OR) && lresult == TRUE) return TRUE; + if((t->node & LTTV_LOGICAL_AND) && lresult == FALSE) return FALSE; + + /* + * Parse right branch + */ + if(t->right == LTTV_TREE_NODE) { + rresult = lttv_filter_tree_parse(t->r_child.t,event,tracefile,trace,context,NULL,NULL); + } + else if(t->right == LTTV_TREE_LEAF) { + rresult = lttv_filter_tree_parse_branch(t->r_child.leaf,event,tracefile,trace,state,context); + } + + + /* + * Apply and return the + * logical link between the + * two operation + */ + switch(t->node) { + case LTTV_LOGICAL_OR: return (lresult | rresult); + case LTTV_LOGICAL_AND: return (lresult & rresult); + case LTTV_LOGICAL_NOT: + return (t->left==LTTV_TREE_LEAF)?!lresult:((t->right==LTTV_TREE_LEAF)?!rresult:TRUE); + case LTTV_LOGICAL_XOR: return (lresult ^ rresult); + case 0: return (rresult); + default: + /* + * This case should never be + * parsed, if so, this subtree + * is cancelled ! + */ + return TRUE; + } + +} + +/** + * This function parses a particular branch of the tree + * @param se pointer to the current LttvSimpleExpression + * @param event current LttEvent, NULL if not used + * @param tracefile current LttTracefile, NULL if not used + * @param trace current LttTrace, NULL if not used + * @param state current LttvProcessState, NULL if not used + * @param context current LttvTracefileContext, NULL if not used + * @return response of filter + */ +gboolean +lttv_filter_tree_parse_branch( + const LttvSimpleExpression* se, + const LttEvent* event, + const LttTracefile* tracefile, + const LttTrace* trace, + const LttvProcessState* state, + const LttvTracefileContext* context) { + + LttvFieldValue v; + v = se->value; + switch(se->field) { + case LTTV_FILTER_TRACE_NAME: + if(trace == NULL) return TRUE; + else { + GQuark quark = ltt_trace_name(trace); + return se->op((gpointer)&quark,v); + } + break; + case LTTV_FILTER_TRACEFILE_NAME: + if(tracefile == NULL) return TRUE; + else { + GQuark quark = ltt_tracefile_name(tracefile); + return se->op((gpointer)&quark,v); + } + break; + case LTTV_FILTER_STATE_PID: + if(state == NULL) return TRUE; + else return se->op((gpointer)&state->pid,v); + break; + case LTTV_FILTER_STATE_PPID: + if(state == NULL) return TRUE; + else return se->op((gpointer)&state->ppid,v); + break; + case LTTV_FILTER_STATE_CT: + if(state == NULL) return TRUE; + else { + return se->op((gpointer)&state->creation_time,v); + } + break; + case LTTV_FILTER_STATE_IT: + if(state == NULL) return TRUE; + else { + return se->op((gpointer)&state->insertion_time,v); + } + break; + case LTTV_FILTER_STATE_P_NAME: + if(state == NULL) return TRUE; + else { + GQuark quark = state->name; + return se->op((gpointer)&quark,v); + } + break; + case LTTV_FILTER_STATE_T_BRAND: + if(state == NULL) return TRUE; + else { + GQuark quark = state->brand; + return se->op((gpointer)&quark,v); + } + break; + case LTTV_FILTER_STATE_EX_MODE: + if(state == NULL) return TRUE; + else return se->op((gpointer)&state->state->t,v); + break; + case LTTV_FILTER_STATE_EX_SUBMODE: + if(state == NULL) return TRUE; + else return se->op((gpointer)&state->state->n,v); + break; + case LTTV_FILTER_STATE_P_STATUS: + if(state == NULL) return TRUE; + else return se->op((gpointer)&state->state->s,v); + break; + case LTTV_FILTER_STATE_CPU: + if(state == NULL) return TRUE; + else { + return se->op((gpointer)&state->cpu,v); + } + break; + case LTTV_FILTER_EVENT_NAME: + if(event == NULL) return TRUE; + else { + struct marker_info *info; + info = marker_get_info_from_id((LttTrace *)trace, event->event_id); + g_assert(info != NULL); + GQuark quark = info->name; + return se->op((gpointer)&quark,v); + } + break; + case LTTV_FILTER_EVENT_CATEGORY: + /* + * TODO: Not yet implemented + */ + return TRUE; + break; + case LTTV_FILTER_EVENT_TIME: + if(event == NULL) return TRUE; + else { + LttTime time = ltt_event_time(event); + return se->op((gpointer)&time,v); + } + break; + case LTTV_FILTER_EVENT_TSC: + if(event == NULL) return TRUE; + else { + LttCycleCount count = ltt_event_cycle_count(event); + return se->op((gpointer)&count,v); + } + break; + case LTTV_FILTER_EVENT_TARGET_PID: + if(context == NULL) return TRUE; + else { + guint target_pid = + lttv_state_get_target_pid((LttvTracefileState*)context); + return se->op((gpointer)&target_pid,v); + } + break; + case LTTV_FILTER_EVENT_FIELD: + /* + * TODO: Use the offset to + * find the dynamic field + * in the event struct + */ + return TRUE; + default: + /* + * This case should never be + * parsed, if so, the whole + * filtering is cancelled + */ + g_warning("Error while parsing the filter tree"); + return TRUE; + } + + /* should never get here */ + return TRUE; + +} + + + +/** + * Debug function. Prints tree memory allocation. + * @param t the pointer to the current LttvFilterTree + */ +void +lttv_print_tree(const LttvFilterTree* t, const int count) { + + g_debug("node:%p lchild:%p rchild:%p depth:%i\n",t, //t->l_child.t,t->r_child.t); + (t->left==LTTV_TREE_NODE)?t->l_child.t:NULL, + (t->right==LTTV_TREE_NODE)?t->r_child.t:NULL, + count); + g_debug("logic operator: %s\n",(t->node&1)?"OR":((t->node&2)?"AND":((t->node&4)?"NOT":((t->node&8)?"XOR":"IDLE")))); + g_debug("|-> left branch %p is a %s\n",t->l_child.t,(t->left==LTTV_TREE_NODE)?"NODE":((t->left==LTTV_TREE_LEAF)?"LEAF":"IDLE")); + if(t->left == LTTV_TREE_LEAF) { + g_debug("| |-> field type number: %i\n",t->l_child.leaf->field); + g_debug("| |-> offset is: %i\n",t->l_child.leaf->offset); + g_debug("| |-> operator function is: %p\n",t->l_child.leaf->op); + } + g_debug("|-> right branch %p is a %s\n",t->r_child.t,(t->right==LTTV_TREE_NODE)?"NODE":((t->right==LTTV_TREE_LEAF)?"LEAF":"IDLE")); + if(t->right == LTTV_TREE_LEAF) { + g_debug("| |-> field type number: %i\n",t->r_child.leaf->field); + g_debug("| |-> offset is: %i\n",t->r_child.leaf->offset); + g_debug("| |-> operator function is: %p\n",t->r_child.leaf->op); + } + + if(t->left == LTTV_TREE_NODE) lttv_print_tree(t->l_child.t,count+1); + if(t->right == LTTV_TREE_NODE) lttv_print_tree(t->r_child.t,count+1); +} + +/** + * @fn static void module_init() + * + * Initializes the filter module and specific values + */ +static void module_init() +{ + +} + +/** + * Destroys the filter module and specific values + */ +static void module_destroy() +{ + +} + + +LTTV_MODULE("filter", "Filters traceset and events", \ + "Filters traceset and events specifically to user input", \ + module_init, module_destroy) + + + diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/filter.h b/tags/lttv-0.11.3-23102008/lttv/lttv/filter.h new file mode 100644 index 00000000..49d97981 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/filter.h @@ -0,0 +1,361 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2005 Michel Dagenais and Simon Bouvier-Zappa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 FILTER_H +#define FILTER_H + +/*! \file lttv/lttv/filter.h + * \brief Defines the core filter of application + * + * A filter expression consists in nested AND, OR and NOT expressions + * involving boolean relation (>, >=, =, !=, <, <=) between event fields and + * specific values. It is compiled into an efficient data structure which + * is used in functions to check if a given event or tracefile satisfies the + * filter. + * + * The grammar for filters is: + * + * filter = expression + * + * expression = "(" expression ")" | "!" expression | + * expression "&&" expression | expression "||" expression | + * simpleExpression + * + * simpleExpression = fieldPath op value + * + * fieldPath = fieldComponent [ "." fieldPath ] + * + * fieldComponent = name [ "[" integer "]" ] + * + * value = integer | double | string + */ + + +#include +#include +#include +#include +#include +#include +#include + +/* structures prototypes */ +typedef enum _LttvStructType LttvStructType; +typedef enum _LttvFieldType LttvFieldType; +typedef enum _LttvExpressionOp LttvExpressionOp; +typedef enum _LttvTreeElement LttvTreeElement; +typedef enum _LttvLogicalOp LttvLogicalOp; + +typedef union _LttvFieldValue LttvFieldValue; + +typedef struct _LttvSimpleExpression LttvSimpleExpression; +typedef struct _LttvFilterTree LttvFilterTree; + +#ifndef LTTVFILTER_TYPE_DEFINED +typedef struct _LttvFilter LttvFilter; +#define LTTVFILTER_TYPE_DEFINED +#endif + +/** + * @enum _LttvStructType + * @brief The lttv structures + * + * the LttvStructType enumerates + * the possible structures for the + * lttv core filter + */ +enum _LttvStructType { + LTTV_FILTER_TRACE, /**< trace (LttTrace) */ + LTTV_FILTER_TRACESET, /**< traceset */ + LTTV_FILTER_TRACEFILE, /**< tracefile (LttTracefile) */ + LTTV_FILTER_EVENT, /**< event (LttEvent) */ + LTTV_FILTER_STATE /**< state (LttvProcessState) */ +}; + +/** + * @enum _LttvFieldType + * @brief Possible fields for the structures + * + * the LttvFieldType enum consists on + * all the hardcoded structures and + * their appropriate fields on which + * filters can be applied. + */ +enum _LttvFieldType { + LTTV_FILTER_TRACE_NAME, /**< trace.name (char*) */ + LTTV_FILTER_TRACEFILE_NAME, /**< tracefile.name (char*) */ + LTTV_FILTER_STATE_PID, /**< state.pid (guint) */ + LTTV_FILTER_STATE_PPID, /**< state.ppid (guint) */ + LTTV_FILTER_STATE_CT, /**< state.creation_time (double) */ + LTTV_FILTER_STATE_IT, /**< state.insertion_time (double) */ + LTTV_FILTER_STATE_P_NAME, /**< state.process_name (char*) */ + LTTV_FILTER_STATE_T_BRAND, /**< state.thread_brand (char*) */ + LTTV_FILTER_STATE_EX_MODE, /**< state.execution_mode (LttvExecutionMode) */ + LTTV_FILTER_STATE_EX_SUBMODE, /**< state.execution_submode (LttvExecutionSubmode) */ + LTTV_FILTER_STATE_P_STATUS, /**< state.process_status (LttvProcessStatus) */ + LTTV_FILTER_STATE_CPU, /**< state.cpu (?last_cpu?) */ + LTTV_FILTER_EVENT_NAME, /**< event.name (char*) */ + LTTV_FILTER_EVENT_CATEGORY, /**< FIXME: not implemented */ + LTTV_FILTER_EVENT_TIME, /**< event.time (double) */ + LTTV_FILTER_EVENT_TSC, /**< event.tsc (double) */ + LTTV_FILTER_EVENT_TARGET_PID, /**< event.target_pid (guint) */ + LTTV_FILTER_EVENT_FIELD, /**< dynamic field, specified in facility */ + LTTV_FILTER_UNDEFINED /**< undefined field */ +}; + +/** + * @enum _LttvExpressionOp + * @brief Contains possible operators + * + * This enumeration defines the + * possible operator used to compare + * right and left member in simple + * expression + */ +enum _LttvExpressionOp +{ + LTTV_FIELD_EQ, /**< equal */ + LTTV_FIELD_NE, /**< not equal */ + LTTV_FIELD_LT, /**< lower than */ + LTTV_FIELD_LE, /**< lower or equal */ + LTTV_FIELD_GT, /**< greater than */ + LTTV_FIELD_GE /**< greater or equal */ +}; + +/** + * @union _LttvFieldValue + * @brief Contains possible field values + * + * This particular union defines the + * possible set of values taken by the + * right member of a simple expression. + * It is used for comparison whithin the + * 'operators' functions + */ +union _LttvFieldValue { + GQuark v_quark; /**< GQuark */ + guint64 v_uint64; /**< unsigned int of 64 bytes */ + guint32 v_uint32; /**< unsigned int of 32 bytes */ + guint16 v_uint16; /**< unsigned int of 16 bytes */ + guint16 v_uint; /**< unsigned int */ + double v_double; /**< double */ + char* v_string; /**< string */ + LttTime v_ltttime; /**< LttTime */ +}; + +/** + * @enum _LttvTreeElement + * @brief element types for the tree nodes + * + * LttvTreeElement defines the possible + * types of nodes which build the LttvFilterTree. + */ +enum _LttvTreeElement { + LTTV_TREE_IDLE, /**< this node does nothing */ + LTTV_TREE_NODE, /**< this node contains a logical operator */ + LTTV_TREE_LEAF /**< this node is a leaf and contains a simple expression */ +}; + + +/** + * @struct _LttvSimpleExpression + * @brief simple expression structure + * + * An LttvSimpleExpression is the base + * of all filtering operations. It also + * populates the leaves of the + * LttvFilterTree. Each expression + * consists basically in a structure + * field, an operator and a specific + * value. + */ +struct _LttvSimpleExpression +{ + gint field; /**< left member of simple expression */ + gint offset; /**< offset used for dynamic fields */ + gboolean (*op)(gpointer,LttvFieldValue); /**< operator of simple expression */ + LttvFieldValue value; /**< right member of simple expression */ +}; + +/** + * @enum _LttvLogicalOp + * @brief logical operators + * + * Contains the possible values taken + * by logical operator used to link + * simple expression. Values are + * AND, OR, XOR or NOT + */ +enum _LttvLogicalOp { + LTTV_LOGICAL_OR = 1, /**< OR (1) */ + LTTV_LOGICAL_AND = 1<<1, /**< AND (2) */ + LTTV_LOGICAL_NOT = 1<<2, /**< NOT (4) */ + LTTV_LOGICAL_XOR = 1<<3 /**< XOR (8) */ +}; + +/** + * @struct _LttvFilterTree + * @brief The filtering tree + * + * The filtering tree is used to represent the + * expression string in its entire hierarchy + * composed of simple expressions and logical + * operators + */ +struct _LttvFilterTree { + int node; /**< value of LttvLogicalOp */ + LttvTreeElement left; /**< nature of left branch (node/leaf) */ + LttvTreeElement right; /**< nature of right branch (node/leaf) */ + union { + LttvFilterTree* t; + LttvSimpleExpression* leaf; + } l_child; /**< left branch of tree */ + union { + LttvFilterTree* t; + LttvSimpleExpression* leaf; + } r_child; /**< right branch of tree */ +}; + +/** + * @struct _LttvFilter + * @brief The filter + * + * Contains a binary tree of filtering options along + * with the expression itself. + */ +struct _LttvFilter { + char *expression; /**< filtering expression string */ + LttvFilterTree *head; /**< tree associated to expression */ +}; + +/* + * Simple Expression + */ +LttvSimpleExpression* lttv_simple_expression_new(); + +gboolean lttv_simple_expression_assign_field(GPtrArray* fp, LttvSimpleExpression* se); + +gboolean lttv_simple_expression_assign_operator(LttvSimpleExpression* se, LttvExpressionOp op); + +gboolean lttv_simple_expression_assign_value(LttvSimpleExpression* se, char* value); + +void lttv_simple_expression_destroy(LttvSimpleExpression* se); + + +/* + * Logical operators functions + */ + +gboolean lttv_apply_op_eq_uint(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_eq_uint64(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_eq_uint32(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_eq_uint16(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_eq_double(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_eq_string(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_eq_quark(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_eq_ltttime(const gpointer v1, LttvFieldValue v2); + +gboolean lttv_apply_op_ne_uint(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_ne_uint64(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_ne_uint32(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_ne_uint16(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_ne_double(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_ne_string(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_ne_quark(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_ne_ltttime(const gpointer v1, LttvFieldValue v2); + +gboolean lttv_apply_op_lt_uint(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_lt_uint64(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_lt_uint32(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_lt_uint16(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_lt_double(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_lt_ltttime(const gpointer v1, LttvFieldValue v2); + +gboolean lttv_apply_op_le_uint(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_le_uint64(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_le_uint32(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_le_uint16(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_le_double(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_le_ltttime(const gpointer v1, LttvFieldValue v2); + +gboolean lttv_apply_op_gt_uint(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_gt_uint64(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_gt_uint32(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_gt_uint16(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_gt_double(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_gt_ltttime(const gpointer v1, LttvFieldValue v2); + +gboolean lttv_apply_op_ge_uint(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_ge_uint64(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_ge_uint32(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_ge_uint16(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_ge_double(const gpointer v1, LttvFieldValue v2); +gboolean lttv_apply_op_ge_ltttime(const gpointer v1, LttvFieldValue v2); + +/* + * Cloning + */ + +LttvFilterTree* lttv_filter_tree_clone(const LttvFilterTree* tree); + +LttvFilter* lttv_filter_clone(const LttvFilter* filter); + +/* + * LttvFilter + */ +LttvFilter *lttv_filter_new(); + +gboolean lttv_filter_update(LttvFilter* filter); + +void lttv_filter_destroy(LttvFilter* filter); + +gboolean lttv_filter_append_expression(LttvFilter* filter, const char *expression); + +void lttv_filter_clear_expression(LttvFilter* filter); + +/* + * LttvFilterTree + */ +LttvFilterTree* lttv_filter_tree_new(); + +void lttv_filter_tree_destroy(LttvFilterTree* tree); + +gboolean lttv_filter_tree_parse( + const LttvFilterTree* t, + const LttEvent* event, + const LttTracefile* tracefile, + const LttTrace* trace, + const LttvTracefileContext* context, + const LttvProcessState* pstate, + const LttvTraceContext* tc); + +gboolean lttv_filter_tree_parse_branch( + const LttvSimpleExpression* se, + const LttEvent* event, + const LttTracefile* tracefile, + const LttTrace* trace, + const LttvProcessState* state, + const LttvTracefileContext* context); + +/* + * Debug functions + */ +void lttv_print_tree(const LttvFilterTree* t, const int count); + +#endif // FILTER_H + diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/hook.c b/tags/lttv-0.11.3-23102008/lttv/lttv/hook.c new file mode 100644 index 00000000..6578aeba --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/hook.c @@ -0,0 +1,460 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +typedef struct _LttvHookClosure { + LttvHook hook; + void *hook_data; + LttvHookPrio prio; + guint ref_count; +} LttvHookClosure; + +gint lttv_hooks_prio_compare(LttvHookClosure *a, LttvHookClosure *b) +{ + gint ret=0; + if(a->prio < b->prio) ret = -1; + else if(a->prio > b->prio) ret = 1; + return ret; +} + + +LttvHooks *lttv_hooks_new() +{ + return g_array_new(FALSE, FALSE, sizeof(LttvHookClosure)); +} + + +void lttv_hooks_destroy(LttvHooks *h) +{ + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "lttv_hooks_destroy()"); + g_array_free(h, TRUE); +} + + +void lttv_hooks_add(LttvHooks *h, LttvHook f, void *hook_data, LttvHookPrio p) +{ + LttvHookClosure *c, new_c; + guint i; + + if(unlikely(h == NULL))g_error("Null hook added"); + + new_c.hook = f; + new_c.hook_data = hook_data; + new_c.prio = p; + new_c.ref_count = 1; + + /* Preliminary check for duplication */ + /* only hook and hook data is checked */ + for(i = 0; i < h->len; i++) { + c = &g_array_index(h, LttvHookClosure, i); + if(new_c.hook == c->hook && new_c.hook_data == c->hook_data) { + g_assert(new_c.prio == c->prio); + c->ref_count++; + return; + } + } + + + for(i = 0; i < h->len; i++) { + c = &g_array_index(h, LttvHookClosure, i); + if(new_c.prio < c->prio) { + g_array_insert_val(h,i,new_c); + return; + } + } + if(i == h->len) + g_array_append_val(h,new_c); +} + +/* lttv_hooks_add_list + * + * Adds a sorted list into another sorted list. + * + * Note : h->len is modified, but only incremented. This assures + * its coherence through the function. + * + * j is an index to the element following the last one added in the + * destination array. + */ +void lttv_hooks_add_list(LttvHooks *h, const LttvHooks *list) +{ + guint i,j,k; + LttvHookClosure *c; + const LttvHookClosure *new_c; + + if(unlikely(list == NULL)) return; + + for(i = 0, j = 0 ; i < list->len; i++) { + new_c = &g_array_index(list, LttvHookClosure, i); + gboolean found=FALSE; + + /* Preliminary check for duplication */ + /* only hook and hook data is checked, not priority */ + for(k = 0; k < h->len; k++) { + c = &g_array_index(h, LttvHookClosure, k); + if(new_c->hook == c->hook && new_c->hook_data == c->hook_data) { + /* Found another identical entry : increment its ref_count and + * jump over the source index */ + g_assert(new_c->prio == c->prio); + found=TRUE; + c->ref_count++; + break; + } + } + + if(!found) { + /* If not found, add it to the destination array */ + while(j < h->len) { + c = &g_array_index(h, LttvHookClosure, j); + if(new_c->prio < c->prio) { + g_array_insert_val(h,j,*new_c); + j++; + break; + } + else j++; + } + if(j == h->len) { + g_array_append_val(h,*new_c); + j++; + } + } + } +} + + +void *lttv_hooks_remove(LttvHooks *h, LttvHook f) +{ + unsigned i; + + void *hook_data; + + LttvHookClosure *c; + + for(i = 0 ; i < h->len ; i++) { + c = &g_array_index(h, LttvHookClosure, i); + if(c->hook == f) { + if(c->ref_count == 1) { + hook_data = c->hook_data; + lttv_hooks_remove_by_position(h, i); + return hook_data; + } else { + g_assert(c->ref_count != 0); + c->ref_count--; + return NULL; /* We do not want anyone to free a hook_data + still referenced */ + } + } + } + return NULL; +} + + +void lttv_hooks_remove_data(LttvHooks *h, LttvHook f, void *hook_data) +{ + unsigned i; + + LttvHookClosure *c; + + for(i = 0 ; i < h->len ; i++) { + c = &g_array_index(h, LttvHookClosure, i); + if(c->hook == f && c->hook_data == hook_data) { + if(c->ref_count == 1) { + lttv_hooks_remove_by_position(h, i); + return; + } else { + g_assert(c->ref_count != 0); + c->ref_count--; + return; + } + } + } +} + + +void lttv_hooks_remove_list(LttvHooks *h, LttvHooks *list) +{ + guint i, j; + + LttvHookClosure *c, *c_list; + + if(list == NULL) return; + for(i = 0, j = 0 ; i < h->len && j < list->len ;) { + c = &g_array_index(h, LttvHookClosure, i); + c_list = &g_array_index(list, LttvHookClosure, j); + if(c->hook == c_list->hook && c->hook_data == c_list->hook_data) { + if(c->ref_count == 1) { + lttv_hooks_remove_by_position(h, i); + } else { + g_assert(c->ref_count != 0); + c->ref_count--; + } + j++; + } + else i++; + } + + /* Normally the hooks in h are ordered as in list. If this is not the case, + try harder here. */ + + if(unlikely(j < list->len)) { + for(; j < list->len ; j++) { + c_list = &g_array_index(list, LttvHookClosure, j); + lttv_hooks_remove_data(h, c_list->hook, c_list->hook_data); + } + } +} + + +unsigned lttv_hooks_number(LttvHooks *h) +{ + return h->len; +} + + +void lttv_hooks_get(LttvHooks *h, unsigned i, LttvHook *f, void **hook_data, + LttvHookPrio *p) +{ + LttvHookClosure *c; + + if(unlikely(i >= h->len)) + { + *f = NULL; + *hook_data = NULL; + *p = 0; + return; + } + + c = &g_array_index(h, LttvHookClosure, i); + *f = c->hook; + *hook_data = c->hook_data; + *p = c->prio; +} + + +void lttv_hooks_remove_by_position(LttvHooks *h, unsigned i) +{ + g_array_remove_index(h, i); +} + +gboolean lttv_hooks_call(LttvHooks *h, void *call_data) +{ + gboolean ret, sum_ret = FALSE; + + LttvHookClosure *c; + + guint i; + + if(likely(h != NULL)) { + for(i = 0 ; i < h->len ; i++) { + c = &g_array_index(h, LttvHookClosure, i); + ret = c->hook(c->hook_data,call_data); + sum_ret = sum_ret || ret; + } + } + return sum_ret; +} + + +gboolean lttv_hooks_call_check(LttvHooks *h, void *call_data) +{ + LttvHookClosure *c; + + guint i; + + for(i = 0 ; i < h->len ; i++) { + c = &g_array_index(h, LttvHookClosure, i); + if(unlikely(c->hook(c->hook_data,call_data))) return TRUE; + } + return FALSE; +} + +/* Optimised for h1 == NULL, h2 != NULL. This is the case + * for optimised computation (with specific by id hooks, but + * no main hooks). + * + * The second case that should occur the most often is + * h1 != NULL , h2 == NULL. + */ +gint lttv_hooks_call_merge(LttvHooks *h1, void *call_data1, + LttvHooks *h2, void *call_data2) +{ + gint ret, sum_ret = 0; + + LttvHookClosure *c1, *c2; + + guint i, j; + + if(unlikely(h1 != NULL)) { + if(unlikely(h2 != NULL)) { + for(i = 0, j = 0 ; i < h1->len && j < h2->len ;) { + c1 = &g_array_index(h1, LttvHookClosure, i); + c2 = &g_array_index(h2, LttvHookClosure, j); + if(c1->prio <= c2->prio) { + ret = c1->hook(c1->hook_data,call_data1); + sum_ret = sum_ret | ret; + i++; + } + else { + ret = c2->hook(c2->hook_data,call_data2); + sum_ret = sum_ret | ret; + j++; + } + } + /* Finish the last list with hooks left */ + for(;i < h1->len; i++) { + c1 = &g_array_index(h1, LttvHookClosure, i); + ret = c1->hook(c1->hook_data,call_data1); + sum_ret = sum_ret | ret; + } + for(;j < h2->len; j++) { + c2 = &g_array_index(h2, LttvHookClosure, j); + ret = c2->hook(c2->hook_data,call_data2); + sum_ret = sum_ret | ret; + } + } else { /* h1 != NULL && h2 == NULL */ + for(i = 0 ; i < h1->len ; i++) { + c1 = &g_array_index(h1, LttvHookClosure, i); + ret = c1->hook(c1->hook_data,call_data1); + sum_ret = sum_ret | ret; + } + } + } else if(likely(h2 != NULL)) { /* h1 == NULL && h2 != NULL */ + for(j = 0 ; j < h2->len ; j++) { + c2 = &g_array_index(h2, LttvHookClosure, j); + ret = c2->hook(c2->hook_data,call_data2); + sum_ret = sum_ret | ret; + } + } + + return sum_ret; +} + +gboolean lttv_hooks_call_check_merge(LttvHooks *h1, void *call_data1, + LttvHooks *h2, void *call_data2) +{ + LttvHookClosure *c1, *c2; + + guint i, j; + + if(unlikely(h1 != NULL)) { + if(unlikely(h2 != NULL)) { + for(i = 0, j = 0 ; i < h1->len && j < h2->len ;) { + c1 = &g_array_index(h1, LttvHookClosure, i); + c2 = &g_array_index(h2, LttvHookClosure, j); + if(c1->prio <= c2->prio) { + if(c1->hook(c1->hook_data,call_data1)) return TRUE; + i++; + } + else { + if(c2->hook(c2->hook_data,call_data2)) return TRUE; + j++; + } + } + /* Finish the last list with hooks left */ + for(;i < h1->len; i++) { + c1 = &g_array_index(h1, LttvHookClosure, i); + if(c1->hook(c1->hook_data,call_data1)) return TRUE; + } + for(;j < h2->len; j++) { + c2 = &g_array_index(h2, LttvHookClosure, j); + if(c2->hook(c2->hook_data,call_data2)) return TRUE; + } + } else { /* h2 == NULL && h1 != NULL */ + for(i = 0 ; i < h1->len ; i++) { + c1 = &g_array_index(h1, LttvHookClosure, i); + if(c1->hook(c1->hook_data,call_data1)) return TRUE; + } + } + } else if(likely(h2 != NULL)) { /* h1 == NULL && h2 != NULL */ + for(j = 0 ; j < h2->len ; j++) { + c2 = &g_array_index(h2, LttvHookClosure, j); + if(c2->hook(c2->hook_data,call_data2)) return TRUE; + } + } + + return FALSE; + +} + +/* Two pointer arrays : + * * one indexed by id for quick search : + * size : max id + * typically 4 bytes * 256 facilities * 10 events = 10kbytes + * * another array that keeps a list of used numbers (for later deletion) + * size : number of ids used. + */ + +LttvHooksById *lttv_hooks_by_id_new() +{ + LttvHooksById *h = g_new(LttvHooksById, 1); + h->index = g_ptr_array_sized_new(PREALLOC_EVENTS); + h->array = g_array_sized_new(FALSE, FALSE, sizeof(guint), 50); + return h; +} + + +void lttv_hooks_by_id_destroy(LttvHooksById *h) +{ + guint i; + + for(i = 0 ; i < h->array->len ; i++) { + guint index = g_array_index(h->array, guint, i); + if(h->index->pdata[index] != NULL) { /* hook may have been removed */ + lttv_hooks_destroy(h->index->pdata[index]); + h->index->pdata[index] = NULL; /* Must be there in case of + multiple addition of the same index */ + } + } + g_ptr_array_free(h->index, TRUE); + g_array_free(h->array, TRUE); +} + +/* Optimised for searching an existing hook */ +LttvHooks *lttv_hooks_by_id_find(LttvHooksById *h, unsigned id) +{ + if(unlikely(h->index->len <= id)) g_ptr_array_set_size(h->index, id + 1); + if(unlikely(h->index->pdata[id] == NULL)) { + h->index->pdata[id] = lttv_hooks_new(); + g_array_append_val(h->array, id); + } + return h->index->pdata[id]; +} + + +unsigned lttv_hooks_by_id_max_id(LttvHooksById *h) +{ + return h->index->len; +} + +/* We don't bother removing the used slot array id : lttv_hooks_by_id_destroy is + * almost never called and is able to deal with used slot repetition. */ +void lttv_hooks_by_id_remove(LttvHooksById *h, unsigned id) +{ + if(likely(id < h->index->len && h->index->pdata[id] != NULL)) { + lttv_hooks_destroy((LttvHooks *)h->index->pdata[id]); + h->index->pdata[id] = NULL; + } +} + diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/hook.h b/tags/lttv-0.11.3-23102008/lttv/lttv/hook.h new file mode 100644 index 00000000..325d2186 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/hook.h @@ -0,0 +1,164 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * 25/05/2004 Mathieu Desnoyers : Hook priorities + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 HOOK_H +#define HOOK_H + +#include +#include + +/* A hook is a function to call with the supplied hook data, and with + call site specific data (e.g., hooks for events are called with a + pointer to the current event). */ + +typedef gboolean (*LttvHook)(void *hook_data, void *call_data); + + +/* A list of hooks allows registering hooks to be called later. */ + +typedef GArray LttvHooks; + +/* A priority associated with each hook, from -19 (high prio) to 20 (low prio) + * 0 being the default priority. + * + * Priority ordering is done in the lttv_hooks_add and lttv_hooks_add_list + * functions. Hook removal does not change list order. + */ + +#define LTTV_PRIO_DEFAULT 50 +#define LTTV_PRIO_HIGH 0 +#define LTTV_PRIO_LOW 99 + +typedef gint LttvHookPrio; + +/* Create and destroy a list of hooks */ + +LttvHooks *lttv_hooks_new(); + +void lttv_hooks_destroy(LttvHooks *h); + + +/* Add a hook and its hook data to the list */ + +void lttv_hooks_add(LttvHooks *h, LttvHook f, void *hook_data, LttvHookPrio p); + + +/* Add a list of hooks to the list h */ + +void lttv_hooks_add_list(LttvHooks *h, const LttvHooks *list); + + +/* Remove a hook from the list. Return the hook data. */ + +void *lttv_hooks_remove(LttvHooks *h, LttvHook f); + + +/* Remove a hook from the list checking that the hook data match. */ + +void lttv_hooks_remove_data(LttvHooks *h, LttvHook f, void *hook_data); + + +/* Remove a list of hooks from the hooks list in h. */ + +void lttv_hooks_remove_list(LttvHooks *h, LttvHooks *list); + + +/* Return the number of hooks in the list */ + +unsigned lttv_hooks_number(LttvHooks *h); + + +/* Return the hook at the specified position in the list. + * *f and *hook_data are NULL if no hook exists at that position. */ + +void lttv_hooks_get(LttvHooks *h, unsigned i, LttvHook *f, void **hook_data, + LttvHookPrio *p); + + +/* Remove the specified hook. The position of the following hooks may change */ +/* The hook is removed from the list event if its ref_count is higher than 1 */ + +void lttv_hooks_remove_by_position(LttvHooks *h, unsigned i); + + +/* Call all the hooks in the list, each with its hook data, + with the specified call data, in priority order. Return TRUE if one hook + returned TRUE. */ + +gboolean lttv_hooks_call(LttvHooks *h, void *call_data); + + +/* Call the hooks in the list in priority order until one returns true, + * in which case TRUE is returned. */ + +gboolean lttv_hooks_call_check(LttvHooks *h, void *call_data); + + +/* Call hooks from two lists in priority order. If priority is the same, + * hooks from h1 are called first. */ + +gboolean lttv_hooks_call_merge(LttvHooks *h1, void *call_data1, + LttvHooks *h2, void *call_data2); + +gboolean lttv_hooks_call_check_merge(LttvHooks *h1, void *call_data1, + LttvHooks *h2, void *call_data2); + +/* Sometimes different hooks need to be called based on the case. The + case is represented by an unsigned integer id */ + +typedef struct _LttvHooksById { + GPtrArray *index; + GArray *array; +} LttvHooksById; + +/* Create and destroy a hooks by id list */ + +LttvHooksById *lttv_hooks_by_id_new(); + +void lttv_hooks_by_id_destroy(LttvHooksById *h); + + +/* Obtain the hooks for a given id, creating a list if needed */ + +LttvHooks *lttv_hooks_by_id_find(LttvHooksById *h, unsigned id); + + +/* Return an id larger than any for which a list exists. */ + +unsigned lttv_hooks_by_id_max_id(LttvHooksById *h); + + +/* Get the list of hooks for an id, NULL if none exists */ + +static inline LttvHooks *lttv_hooks_by_id_get(LttvHooksById *h, unsigned id) +{ + LttvHooks *ret; + if(likely(id < h->index->len)) ret = h->index->pdata[id]; + else ret = NULL; + + return ret; +} + + +/* Remove the list of hooks associated with an id */ + +void lttv_hooks_by_id_remove(LttvHooksById *h, unsigned id); + +#endif // HOOK_H diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/iattribute.c b/tags/lttv-0.11.3-23102008/lttv/lttv/iattribute.c new file mode 100644 index 00000000..0271dddb --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/iattribute.c @@ -0,0 +1,307 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +static void +lttv_iattribute_base_init (gpointer klass) +{ + static gboolean initialized = FALSE; + + if (!initialized) { + initialized = TRUE; + } +} + + +GType +lttv_iattribute_get_type (void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (LttvIAttributeClass), + lttv_iattribute_base_init, /* base_init */ + NULL, /* base_finalize */ + NULL, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL /* instance_init */ + }; + type = g_type_register_static (G_TYPE_INTERFACE, "LttvIAttribute", + &info, 0); + } + return type; +} + + +unsigned int lttv_iattribute_get_number(LttvIAttribute *self) +{ + return LTTV_IATTRIBUTE_GET_CLASS (self)->get_number (self); +} + + +gboolean lttv_iattribute_named(LttvIAttribute *self, gboolean *homogeneous) +{ + return LTTV_IATTRIBUTE_GET_CLASS (self)->named (self, homogeneous); +} + + +LttvAttributeType lttv_iattribute_get(LttvIAttribute *self, unsigned i, + LttvAttributeName *name, LttvAttributeValue *v, gboolean *is_named) +{ + return LTTV_IATTRIBUTE_GET_CLASS (self)->get (self, i, name, v, is_named); +} + + +LttvAttributeType lttv_iattribute_get_by_name(LttvIAttribute *self, + LttvAttributeName name, LttvAttributeValue *v) +{ + return LTTV_IATTRIBUTE_GET_CLASS (self)->get_by_name (self, name, v); +} + + +LttvAttributeValue lttv_iattribute_add(LttvIAttribute *self, + LttvAttributeName name, LttvAttributeType t) +{ + return LTTV_IATTRIBUTE_GET_CLASS (self)->add (self, name, t); +} + +LttvAttributeValue lttv_iattribute_add_unnamed(LttvIAttribute *self, + LttvAttributeName name, LttvAttributeType t) +{ + return LTTV_IATTRIBUTE_GET_CLASS (self)->add_unnamed (self, name, t); +} + +void lttv_iattribute_remove(LttvIAttribute *self, unsigned i) +{ + return LTTV_IATTRIBUTE_GET_CLASS (self)->remove (self, i); +} + + +void lttv_iattribute_remove_by_name(LttvIAttribute *self, + LttvAttributeName name) +{ + return LTTV_IATTRIBUTE_GET_CLASS (self)->remove_by_name (self, name); +} + +LttvIAttribute* lttv_iattribute_find_subdir(LttvIAttribute *self, + LttvAttributeName name) +{ + return LTTV_IATTRIBUTE_GET_CLASS (self)->find_subdir (self, name); +} + +LttvIAttribute* lttv_iattribute_find_subdir_unnamed(LttvIAttribute *self, + LttvAttributeName name) +{ + return LTTV_IATTRIBUTE_GET_CLASS (self)->find_subdir_unnamed (self, name); +} + + + +/* Find the named attribute in the table, which must be of the specified type. + If it does not exist, it is created with a default value of 0 (NULL for + pointer types). Since the address of the value is obtained, it may be + changed easily afterwards. The function returns false when the attribute + exists but is of incorrect type. */ + +gboolean lttv_iattribute_find(LttvIAttribute *self, LttvAttributeName name, + LttvAttributeType t, LttvAttributeValue *v) +{ + LttvAttributeType found_type; + + found_type = lttv_iattribute_get_by_name(self, name, v); + if(found_type == t) return TRUE; + + if(found_type == LTTV_NONE) { + *v = lttv_iattribute_add(self, name, t); + return TRUE; + } + + return FALSE; +} + + +/* Trees of attribute tables may be accessed using a hierarchical path with + components separated by /, like in filesystems */ + +gboolean lttv_iattribute_find_by_path(LttvIAttribute *self, char *path, + LttvAttributeType t, LttvAttributeValue *v) +{ + LttvIAttribute *node = self; + + LttvAttributeType found_type; + + LttvAttributeName name; + + gchar **components, **cursor; + + components = g_strsplit(path, "\"", G_MAXINT); + + if(components == NULL || *components == NULL) { + g_strfreev(components); + return FALSE; + } + + for(cursor = components;;) { + name = g_quark_from_string(*cursor); + cursor++; + + if(*cursor == NULL) { + g_strfreev(components); + return lttv_iattribute_find(node, name, t, v); + } + else { + found_type = lttv_iattribute_get_by_name(node, name, v); + if(found_type == LTTV_NONE) { + node = lttv_iattribute_find_subdir(node, name); + } + else if(found_type == LTTV_GOBJECT && + LTTV_IS_IATTRIBUTE(*(v->v_gobject))) { + node = LTTV_IATTRIBUTE(*(v->v_gobject)); + } + else { + g_strfreev(components); + return FALSE; + } + } + } +} + + +/* Shallow and deep copies */ + +LttvIAttribute *lttv_iattribute_shallow_copy(LttvIAttribute *self) +{ + LttvIAttribute *copy; + + LttvAttributeType t; + + LttvAttributeValue v, v_copy; + + LttvAttributeName name; + + gboolean is_named; + + int i; + + int nb_attributes = lttv_iattribute_get_number(self); + + copy = LTTV_IATTRIBUTE_GET_CLASS(self)->new_attribute(NULL); + + for(i = 0 ; i < nb_attributes ; i++) { + t = lttv_iattribute_get(self, i, &name, &v, &is_named); + if(is_named) + v_copy = lttv_iattribute_add(copy, name, t); + else + v_copy = lttv_iattribute_add_unnamed(copy, name, t); + lttv_iattribute_copy_value(t, v_copy, v); + } + return copy; +} + +LttvIAttribute *lttv_iattribute_deep_copy(LttvIAttribute *self) +{ + LttvIAttribute *copy, *child; + + LttvAttributeType t; + + LttvAttributeValue v, v_copy; + + LttvAttributeName name; + + gboolean is_named; + + int i; + + int nb_attributes = lttv_iattribute_get_number(self); + + copy = LTTV_IATTRIBUTE_GET_CLASS(self)->new_attribute(NULL); + + for(i = 0 ; i < nb_attributes ; i++) { + t = lttv_iattribute_get(self, i, &name, &v, &is_named); + if(is_named) + v_copy = lttv_iattribute_add(copy, name, t); + else + v_copy = lttv_iattribute_add_unnamed(copy, name, t); + if(t == LTTV_GOBJECT && LTTV_IS_IATTRIBUTE(*(v.v_gobject))) { + child = LTTV_IATTRIBUTE(*(v.v_gobject)); + *(v_copy.v_gobject) = G_OBJECT(lttv_iattribute_deep_copy(child)); + } + else lttv_iattribute_copy_value(t, v_copy, v); + } + return copy; +} + +void lttv_iattribute_copy_value(LttvAttributeType t, LttvAttributeValue dest, + LttvAttributeValue src) +{ + switch(t) { + case LTTV_INT: + *(dest.v_int) = *(src.v_int); + break; + + case LTTV_UINT: + *(dest.v_uint) = *(src.v_uint); + break; + + case LTTV_LONG: + *(dest.v_long) = *(src.v_long); + break; + + case LTTV_ULONG: + *(dest.v_ulong) = *(src.v_ulong); + break; + + case LTTV_FLOAT: + *(dest.v_float) = *(src.v_float); + break; + + case LTTV_DOUBLE: + *(dest.v_double) = *(src.v_double); + break; + + case LTTV_TIME: + *(dest.v_time) = *(src.v_time); + break; + + case LTTV_POINTER: + *(dest.v_pointer) = *(src.v_pointer); + break; + + case LTTV_STRING: + *(dest.v_string) = *(src.v_string); + break; + + case LTTV_GOBJECT: + *(dest.v_gobject) = *(src.v_gobject); + if(*(dest.v_gobject) != NULL) g_object_ref(*(dest.v_gobject)); + break; + + case LTTV_NONE: + break; + } +} + + diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/iattribute.h b/tags/lttv-0.11.3-23102008/lttv/lttv/iattribute.h new file mode 100644 index 00000000..cbda045f --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/iattribute.h @@ -0,0 +1,195 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +/* FIXME : unnamed attributes not implemented */ + +#ifndef IATTRIBUTE_H +#define IATTRIBUTE_H + + +#include +#include + +/* The content of a data structure may be seen as an array of pairs of + attribute name and value. This simple model allows generic navigation + and access functions over a wide range of structures. The names are + represented by unique integer identifiers, GQuarks. */ + +/* Please note that adding a value of type gobject that is non null does not + * increment the reference count of this object : the actual reference to + * the object is "given" to the attribute tree. When the gobject value + * is removed, the object is unreferenced. A value copy through + * lttv_iattribute_copy_value does increase the reference count of the + * gobject. */ + +typedef GQuark LttvAttributeName; + +typedef enum _LttvAttributeType { + LTTV_INT, LTTV_UINT, LTTV_LONG, LTTV_ULONG, LTTV_FLOAT, LTTV_DOUBLE, + LTTV_TIME, LTTV_POINTER, LTTV_STRING, LTTV_GOBJECT, LTTV_NONE +} LttvAttributeType; + +typedef union LttvAttributeValue { + int *v_int; + unsigned *v_uint; + long *v_long; + unsigned long *v_ulong; + float *v_float; + double *v_double; + LttTime *v_time; + gpointer *v_pointer; + char **v_string; + GObject **v_gobject; +} LttvAttributeValue; + + +/* GObject interface type macros */ + +#define LTTV_IATTRIBUTE_TYPE (lttv_iattribute_get_type ()) +#define LTTV_IATTRIBUTE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LTTV_IATTRIBUTE_TYPE, LttvIAttribute)) +#define LTTV_IATTRIBUTE_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), LTTV_IATTRIBUTE_TYPE, LttvIAttributeClass)) +#define LTTV_IS_IATTRIBUTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LTTV_IATTRIBUTE_TYPE)) +#define LTTV_IS_IATTRIBUTE_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), LTTV_IATTRIBUTE_TYPE)) +#define LTTV_IATTRIBUTE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), LTTV_IATTRIBUTE_TYPE, LttvIAttributeClass)) + + +typedef struct _LttvIattribute LttvIAttribute; /* dummy object */ +typedef struct _LttvIAttributeClass LttvIAttributeClass; + + +struct _LttvIAttributeClass { + GTypeInterface parent; + + LttvIAttribute* (*new_attribute) (LttvIAttribute *self); + + unsigned int (*get_number) (LttvIAttribute *self); + + gboolean (*named) (LttvIAttribute *self, gboolean *homogeneous); + + LttvAttributeType (*get) (LttvIAttribute *self, unsigned i, + LttvAttributeName *name, LttvAttributeValue *v, gboolean *is_named); + + LttvAttributeType (*get_by_name) (LttvIAttribute *self, + LttvAttributeName name, LttvAttributeValue *v); + + LttvAttributeValue (*add) (LttvIAttribute *self, LttvAttributeName name, + LttvAttributeType t); + + LttvAttributeValue (*add_unnamed) (LttvIAttribute *self, + LttvAttributeName name, + LttvAttributeType t); + + void (*remove) (LttvIAttribute *self, unsigned i); + + void (*remove_by_name) (LttvIAttribute *self, + LttvAttributeName name); + + LttvIAttribute* (*find_subdir) (LttvIAttribute *self, + LttvAttributeName name); + + LttvIAttribute* (*find_subdir_unnamed) (LttvIAttribute *self, + LttvAttributeName name); + +}; + + +GType lttv_iattribute_get_type(void); + + +/* Total number of attributes */ + +unsigned int lttv_iattribute_get_number(LttvIAttribute *self); + + +/* Container type. Named (fields in struct or elements in a hash table) + or unnamed (elements in an array) attributes, homogeneous type or not. */ + +gboolean lttv_iattribute_named(LttvIAttribute *self, gboolean *homogeneous); + + +/* Get the i th attribute along with its type and a pointer to its value. */ + +LttvAttributeType lttv_iattribute_get(LttvIAttribute *self, unsigned i, + LttvAttributeName *name, LttvAttributeValue *v, gboolean *is_named); + + +/* Get the named attribute in the table along with its type and a pointer to + its value. If the named attribute does not exist, the type is LTTV_NONE. */ + +LttvAttributeType lttv_iattribute_get_by_name(LttvIAttribute *self, + LttvAttributeName name, LttvAttributeValue *v); + + +/* Add an attribute, which must not exist. The name is an empty string for + containers with unnamed attributes. Its value is initialized to 0 or NULL + and its pointer returned. */ + +LttvAttributeValue lttv_iattribute_add(LttvIAttribute *self, + LttvAttributeName name, LttvAttributeType t); + +LttvAttributeValue lttv_iattribute_add_unnamed(LttvIAttribute *self, + LttvAttributeName name, LttvAttributeType t); +/* Remove an attribute */ + +void lttv_iattribute_remove(LttvIAttribute *self, unsigned i); + +void lttv_iattribute_remove_by_name(LttvIAttribute *self, + LttvAttributeName name); + + +/* Create an empty iattribute object and add it as an attribute under the + specified name, or return an existing iattribute attribute. If an + attribute of that name already exists but is not a GObject supporting the + iattribute interface, return NULL. */ + +LttvIAttribute* lttv_iattribute_find_subdir(LttvIAttribute *self, + LttvAttributeName name); + +LttvIAttribute* lttv_iattribute_find_subdir_unnamed(LttvIAttribute *self, + LttvAttributeName name); + +/* The remaining utility functions are not part of the LttvIAttribute + interface but operate on objects implementing it. */ + +/* Find the named attribute in the table, which must be of the specified type. + If it does not exist, it is created with a default value of 0 (NULL for + pointer types). Since the address of the value is obtained, it may be + changed easily afterwards. The function returns false when the attribute + exists but is of incorrect type. */ + +gboolean lttv_iattribute_find(LttvIAttribute *self, LttvAttributeName name, + LttvAttributeType t, LttvAttributeValue *v); + + +/* Trees of attribute tables may be accessed using a hierarchical path with + components separated by /, like in filesystems */ + +gboolean lttv_iattribute_find_by_path(LttvIAttribute *self, char *path, + LttvAttributeType t, LttvAttributeValue *v); + + +/* Shallow and deep copies */ + +void lttv_iattribute_copy_value(LttvAttributeType t, LttvAttributeValue dest, + LttvAttributeValue src); + +LttvIAttribute *lttv_iattribute_shallow_copy(LttvIAttribute *self); + +LttvIAttribute *lttv_iattribute_deep_copy(LttvIAttribute *self); + +#endif // IATTRIBUTE_H diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/lttv-gui.sh b/tags/lttv-0.11.3-23102008/lttv/lttv/lttv-gui.sh new file mode 100644 index 00000000..2de28dee --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/lttv-gui.sh @@ -0,0 +1,10 @@ +# -* sh *- + +# This is a simple script that starts lttv with default GUI modules +# Mathieu Desnoyers 15-09-2005 + +LTTV_CMD=`echo $0 | sed 's/-gui$//'` + +$LTTV_CMD.real -m guievents -m guifilter -m guicontrolflow -m resourceview \ + -m guistatistics -m guitracecontrol $* + diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/lttv.h b/tags/lttv-0.11.3-23102008/lttv/lttv/lttv.h new file mode 100644 index 00000000..b0dd43ba --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/lttv.h @@ -0,0 +1,63 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 LTTV_H +#define LTTV_H + +#include + +/* The modules in the visualizer communicate with the main module and + with each other through attributes. There is a global set of attributes */ + +LttvAttribute *lttv_global_attributes(); + +extern gboolean lttv_profile_memory; + +extern int lttv_argc; + +extern char **lttv_argv; + +/* A number of global attributes are initialized before modules are + loaded, for example hooks lists. More global attributes are defined + in individual mudules to store information or to communicate with other + modules (GUI windows, menus...). + + The hooks lists (lttv_hooks) are initialized in the main module and may be + used by other modules. Each corresponds to a specific location in the main + module processing loop. The attribute key and typical usage for each + is indicated. + + /hooks/options/before + Good place to define new command line options to be parsed. + + /hooks/options/after + Read the values set by the command line options. + + /hooks/main/before + + /hooks/main/after + +*/ + +#define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format) + +#ifndef g_debug +#define g_debug(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format) +#endif + +#endif // LTTV_H diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/lttv.sh b/tags/lttv-0.11.3-23102008/lttv/lttv/lttv.sh new file mode 100644 index 00000000..a7d7dc3a --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/lttv.sh @@ -0,0 +1,14 @@ +# -* sh *- + +# This is a simple script that starts lttv with no modules : +# For batch mode. +# Mathieu Desnoyers 15-09-2005 + +if [ x"$*" = x"" ]; then + echo "This is a wrapper around $0.real for convenience purposes" + echo "What you really want is maybe the lttv-gui command ?" + echo + $0.real --help +else + $0.real $* +fi diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/main.c b/tags/lttv-0.11.3-23102008/lttv/lttv/main.c new file mode 100644 index 00000000..eca23438 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/main.c @@ -0,0 +1,317 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* The main program maintains a few central data structures and relies + on modules for the rest. These data structures may be accessed by modules + through an exported API */ + +static LttvIAttribute *attributes; + +static LttvHooks + *before_options, + *after_options, + *before_main, + *after_main; + +static char + *a_module, + *a_module_path; + +static gboolean + a_verbose, + a_debug, + a_fatal; + +gboolean lttv_profile_memory; + +int lttv_argc; + +char **lttv_argv; + +static void lttv_module_option(void *hook_data); + +static void lttv_module_path_option(void *hook_data); + +static void lttv_verbose(void *hook_data); + +static void lttv_debug(void *hook_data); + +static void lttv_event_debug(void *hook_data); + +static void lttv_fatal(void *hook_data); + +static void lttv_help(void *hook_data); + +/* This is the handler to specify when we dont need all the debugging + messages. It receives the message and does nothing. */ + +void ignore_and_drop_message(const gchar *log_domain, GLogLevelFlags log_level, + const gchar *message, gpointer user_data) +{ +} + + +/* Since everything is done in modules, the main program only takes care + of the infrastructure. */ + +int main(int argc, char **argv) +{ + + int i; + + char + *profile_memory_short_option = "-M", + *profile_memory_long_option = "--memory"; + + gboolean profile_memory = FALSE; + + LttvAttributeValue value; + + lttv_argc = argc; + lttv_argv = argv; + + /* Before anything else, check if memory profiling is requested */ + + for(i = 1 ; i < argc ; i++) { + if(*(argv[i]) != '-') break; + if(strcmp(argv[i], profile_memory_short_option) == 0 || + strcmp(argv[i], profile_memory_long_option) == 0) { + g_mem_set_vtable(glib_mem_profiler_table); + g_message("Memory summary before main"); + g_mem_profile(); + profile_memory = TRUE; + break; + } + } + + + /* Initialize glib and by default ignore info and debug messages */ + + g_type_init(); + //g_type_init_with_debug_flags (G_TYPE_DEBUG_OBJECTS | G_TYPE_DEBUG_SIGNALS); + g_log_set_handler(NULL, G_LOG_LEVEL_INFO, ignore_and_drop_message, NULL); + g_log_set_handler(NULL, G_LOG_LEVEL_DEBUG, ignore_and_drop_message, NULL); + + + /* Have an attributes subtree to store hooks to be registered by modules. */ + + attributes = LTTV_IATTRIBUTE(g_object_new(LTTV_ATTRIBUTE_TYPE, NULL)); + + before_options = lttv_hooks_new(); + after_options = lttv_hooks_new(); + before_main = lttv_hooks_new(); + after_main = lttv_hooks_new(); + + + /* Create a number of hooks lists */ + + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/options/before", + LTTV_POINTER, &value)); + *(value.v_pointer) = before_options; + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/options/after", + LTTV_POINTER, &value)); + *(value.v_pointer) = after_options; + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/main/before", + LTTV_POINTER, &value)); + *(value.v_pointer) = before_main; + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/main/after", + LTTV_POINTER, &value)); + *(value.v_pointer) = after_main; + + + /* Initialize the command line options processing */ + + GError *error = NULL; + + LttvModule *module_module = lttv_module_require("module", &error); + if(error != NULL) g_error("%s", error->message); + LttvModule *module_option = lttv_module_require("option", &error); + if(error != NULL) g_error("%s", error->message); + + /* Initialize the module loading */ + + lttv_library_path_add(PACKAGE_PLUGIN_DIR); + + + /* Add some built-in options */ + + lttv_option_add("module",'m', "load a module", "name of module to load", + LTTV_OPT_STRING, &a_module, lttv_module_option, NULL); + + lttv_option_add("modules-path", 'L', + "add a directory to the module search path", + "directory to add to the path", LTTV_OPT_STRING, &a_module_path, + lttv_module_path_option, NULL); + + lttv_option_add("help",'h', "basic help", "none", + LTTV_OPT_NONE, NULL, lttv_help, NULL); + + a_verbose = FALSE; + lttv_option_add("verbose",'v', "print information messages", "none", + LTTV_OPT_NONE, NULL, lttv_verbose, NULL); + + a_debug = FALSE; + lttv_option_add("debug",'d', "print debugging messages", "none", + LTTV_OPT_NONE, NULL, lttv_debug, NULL); + + /* use --edebug, -e conflicts with filter. Problem with option parsing when we + * reparse the options with different number of arguments. */ + lttv_option_add("edebug",'e', "print event debugging", "none", + LTTV_OPT_NONE, NULL, lttv_event_debug, NULL); + + a_fatal = FALSE; + lttv_option_add("fatal",'f', "make critical messages fatal", + "none", + LTTV_OPT_NONE, NULL, lttv_fatal, NULL); + + lttv_profile_memory = FALSE; + lttv_option_add(profile_memory_long_option + 2, + profile_memory_short_option[1], "print memory information", "none", + LTTV_OPT_NONE, <tv_profile_memory, NULL, NULL); + + + /* Process the options */ + + lttv_hooks_call(before_options, NULL); + lttv_option_parse(argc, argv); + lttv_hooks_call(after_options, NULL); + + + /* Memory profiling to be useful must be activated as early as possible */ + + if(profile_memory != lttv_profile_memory) + g_error("Memory profiling options must appear before other options"); + + + /* Do the main work */ + + lttv_hooks_call(before_main, NULL); + lttv_hooks_call(after_main, NULL); + + + /* Clean up everything */ + + lttv_module_release(module_option); + lttv_module_release(module_module); + + lttv_hooks_destroy(before_options); + lttv_hooks_destroy(after_options); + lttv_hooks_destroy(before_main); + lttv_hooks_destroy(after_main); + g_object_unref(attributes); + + if(profile_memory) { + g_message("Memory summary after main"); + g_mem_profile(); + } + return 0; +} + + +LttvAttribute *lttv_global_attributes() +{ + return (LttvAttribute*)attributes; +} + + +void lttv_module_option(void *hook_data) +{ + GError *error = NULL; + + lttv_module_require(a_module, &error); + if(error != NULL) g_error("%s", error->message); +} + + +void lttv_module_path_option(void *hook_data) +{ + lttv_library_path_add(a_module_path); +} + + +void lttv_verbose(void *hook_data) +{ + g_log_set_handler(NULL, G_LOG_LEVEL_INFO, g_log_default_handler, NULL); + g_info("Logging set to include INFO level messages"); +} + +void lttv_debug(void *hook_data) +{ + g_log_set_handler(NULL, G_LOG_LEVEL_DEBUG, g_log_default_handler, NULL); + g_info("Logging set to include DEBUG level messages"); +} + +void lttv_event_debug(void *hook_data) +{ + ltt_event_debug(1); + g_info("Output event detailed debug"); +} + +void lttv_fatal(void *hook_data) +{ + g_log_set_always_fatal(G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL); + //g_log_set_always_fatal(G_LOG_LEVEL_CRITICAL); + g_info("Critical log from glib will abort execution"); +} + +void lttv_help(void *hook_data) +{ + printf("Linux Trace Toolkit Visualizer " VERSION "\n"); + printf("\n"); + lttv_option_show_help(); + printf("\n"); +} + +/* + +- Define formally traceset/trace in the GUI for the user and decide how + trace/traceset sharing goes in the application. + +- Use appropriately the new functions in time.h + +- remove the separate tracefiles (control/per cpu) arrays/loops in context. + +- split processTrace into context.c and processTrace.c + +- check spelling conventions. + +- get all the copyright notices. + +- remove all the warnings. + +- get all the .h files properly doxygen commented to produce useful documents. + +- have an intro/architecture document. + +- write a tutorial */ diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/module.c b/tags/lttv-0.11.3-23102008/lttv/lttv/module.c new file mode 100644 index 00000000..198e1385 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/module.c @@ -0,0 +1,610 @@ + +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + + +/* module.c : Implementation of the module loading/unloading mechanism. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +struct _LttvLibrary +{ + LttvLibraryInfo info; + GPtrArray *modules; + GModule *gm; + guint locked_loaded; +}; + + +struct _LttvModule +{ + LttvModuleInfo info; + char **prerequisites_names; + GPtrArray *prerequisites; +}; + + +/* Modules are searched by name. However, a library may be loaded which + provides a module with the same name as an existing one. A stack of + modules is thus maintained for each name. + + Libraries correspond to glib modules. The g_module function is + responsible for loading each library only once. */ + +static GHashTable *modules_by_name = NULL; + +static GPtrArray *libraries = NULL; + +static GHashTable *libraries_by_g_module = NULL; + +static GPtrArray *library_paths = NULL; + +static gboolean initialized = FALSE; + +static gboolean destroyed = TRUE; + +static struct _LttvModuleDescription *builtin_chain = NULL; + +static struct _LttvModuleDescription *module_chain = NULL; + +static struct _LttvModuleDescription **module_next = &module_chain; + +static GQuark lttv_module_error; + +static void init(); + +static void finish_destroy(); + +static void module_release(LttvModule *m); + + +static LttvLibrary *library_add(char *name, char *path, GModule *gm) +{ + LttvLibrary *l; + + LttvModule *m; + + struct _LttvModuleDescription *link; + + GPtrArray *modules; + + l = g_new(LttvLibrary, 1); + l->modules = g_ptr_array_new(); + l->gm = gm; + l->locked_loaded = 0; + l->info.name = g_strdup(name); + l->info.path = g_strdup(path); + l->info.load_count = 0; + + g_ptr_array_add(libraries, l); + g_hash_table_insert(libraries_by_g_module, gm, l); + + *module_next = NULL; + for(link = module_chain; link != NULL; link = link->next) { + m = g_new(LttvModule, 1); + g_ptr_array_add(l->modules, m); + + modules = g_hash_table_lookup(modules_by_name, link->name); + if(modules == NULL) { + modules = g_ptr_array_new(); + g_hash_table_insert(modules_by_name, g_strdup(link->name), modules); + } + g_ptr_array_add(modules, m); + + m->prerequisites_names = link->prerequisites; + m->prerequisites = g_ptr_array_new(); + m->info.name = link->name; + m->info.short_description = link->short_description; + m->info.description = link->description; + m->info.init = link->init; + m->info.destroy = link->destroy; + m->info.library = l; + m->info.require_count = 0; + m->info.use_count = 0; + m->info.prerequisites_number = link->prerequisites_number; + } + return l; +} + + +static void library_remove(LttvLibrary *l) +{ + LttvModule *m; + + GPtrArray *modules; + GPtrArray **modules_ptr = &modules; /* for strict aliasing */ + guint i; + + char *key; + char **key_ptr = &key; /* for strict aliasing */ + + for(i = 0 ; i < l->modules->len ; i++) { + m = (LttvModule *)(l->modules->pdata[i]); + + g_hash_table_lookup_extended(modules_by_name, m->info.name, + (gpointer *)key_ptr, (gpointer *)modules_ptr); + g_assert(modules != NULL); + g_ptr_array_remove(modules, m); + if(modules->len == 0) { + g_hash_table_remove(modules_by_name, m->info.name); + g_ptr_array_free(modules, TRUE); + g_free(key); + } + + g_ptr_array_free(m->prerequisites, TRUE); + g_free(m); + } + + g_ptr_array_remove(libraries, l); + g_hash_table_remove(libraries_by_g_module, l->gm); + g_ptr_array_free(l->modules, TRUE); + g_free(l->info.name); + g_free(l->info.path); + g_free(l); +} + + +static LttvLibrary *library_load(char *name, GError **error) +{ + GModule *gm = NULL; + + int i, nb; + + /* path is always initialized, checked */ + char *path = NULL, *pathname; + + LttvLibrary *l; + + GString *messages = g_string_new(""); + + /* insure that module.c is initialized */ + + init(); + + /* Try to find the library along all the user specified paths */ + + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Load library %s", name); + nb = lttv_library_path_number(); + for(i = 0 ; i <= nb ; i++) { + if(i < nb) path = lttv_library_path_get(i); + else path = NULL; + + pathname = g_module_build_path(path ,name); + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Try path %s", pathname); + module_chain = NULL; + module_next = &module_chain; + gm = g_module_open(pathname,0); + g_free(pathname); + + if(gm != NULL) break; + + messages = g_string_append(messages, g_module_error()); + messages = g_string_append(messages, "\n"); + g_log(G_LOG_DOMAIN,G_LOG_LEVEL_INFO,"Trial failed, %s", g_module_error()); + } + + /* Module cannot be found */ + + if(gm == NULL) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Failed to load %s", name); + g_set_error(error, lttv_module_error, LTTV_MODULE_NOT_FOUND, + "Cannot load library %s: %s", name, messages->str); + g_string_free(messages, TRUE); + return NULL; + } + g_string_free(messages, TRUE); + + /* Check if the library was already loaded */ + + l = g_hash_table_lookup(libraries_by_g_module, gm); + + /* This library was not already loaded */ + + if(l == NULL) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Library %s (%s) loaded", name, + g_module_name(gm)); + l = library_add(name, path, gm); + } + return l; +} + + +LttvLibrary *lttv_library_load(char *name, GError **error) +{ + LttvLibrary *l = library_load(name, error); + if(l != NULL) l->info.load_count++; + return l; +} + +/* Returns < 0 if still in use, 0 if freed */ +static gint library_unload(LttvLibrary *l) +{ + guint i; + + GModule *gm; + + LttvModule *m; + + if(l->locked_loaded > 0) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Unload library %s: locked loaded", + l->info.name); + return 1; + } + + if(l->info.load_count > 0) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Unload library %s: load count %d", + l->info.name, l->info.load_count); + return l->info.load_count; + } + + /* Check if all its modules have been released */ + + for(i = 0 ; i < l->modules->len ; i++) { + m = (LttvModule *)(l->modules->pdata[i]); + if(m->info.use_count > 0) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO,"Unload library %s: module %s used", + l->info.name, m->info.name); + return 1; + } + } + + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Unload library %s: close the GModule", + l->info.name); + gm = l->gm; + library_remove(l); + if(gm != NULL) g_module_close(gm); + + /* insure that module.c will be finalized */ + + finish_destroy(); + return 0; +} + + +gint lttv_library_unload(LttvLibrary *l) +{ + /* In the case where we wait for a module to release, the load count is 0 + * and should not be decremented. */ + if(l->info.load_count != 0) { + l->info.load_count--; + return l->info.load_count; + } else { + library_unload(l); + return 0; + } +} + + +static void library_lock_loaded(LttvLibrary *l) +{ + l->locked_loaded++; +} + + +static gint library_unlock_loaded(LttvLibrary *l) +{ + l->locked_loaded--; + return library_unload(l); +} + + +static LttvModule *module_require(char *name, GError **error) +{ + GError *tmp_error = NULL; + + guint i, j; + + LttvModule *m, *required; + + LttvLibrary *l = NULL; + + GPtrArray *modules; + + /* Insure that module.c is initialized */ + + init(); + + /* Check if the module is already loaded */ + + modules = g_hash_table_lookup(modules_by_name, name); + + /* Try to load a library having the module name */ + + if(modules == NULL) { + l = library_load(name, error); + if(l == NULL) return NULL; + else library_lock_loaded(l); + + /* A library was found, does it contain the named module */ + + modules = g_hash_table_lookup(modules_by_name, name); + if(modules == NULL) { + g_set_error(error, lttv_module_error, LTTV_MODULE_NOT_FOUND, + "Module %s not found in library %s", name, l->info.name); + library_unlock_loaded(l); + return NULL; + } + } + m = (LttvModule *)(modules->pdata[modules->len - 1]); + + /* We have the module */ + + m->info.use_count++; + + /* First use of the module. Initialize after getting the prerequisites */ + + if(m->info.use_count == 1) { + for(i = 0 ; i < m->info.prerequisites_number ; i++) { + required = module_require(m->prerequisites_names[i], &tmp_error); + + /* A prerequisite could not be found, undo everything and fail */ + + if(required == NULL) { + for(j = 0 ; j < m->prerequisites->len ; j++) { + module_release((LttvModule *)(m->prerequisites->pdata[j])); + } + g_ptr_array_set_size(m->prerequisites, 0); + if(l != NULL) library_unlock_loaded(l); + g_set_error(error, lttv_module_error, LTTV_MODULE_NOT_FOUND, + "Cannot find prerequisite for module %s: %s", name, + tmp_error->message); + g_clear_error(&tmp_error); + return NULL; + } + g_ptr_array_add(m->prerequisites, required); + } + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Module %s: init()", m->info.name); + m->info.init(); + } + + /* Decrement the load count of the library. It will not really be + unloaded since it contains a currently used module. */ + + if(l != NULL) library_unlock_loaded(l); + + return(m); +} + + +/* The require_count for a module is the number of explicit calls to + lttv_module_require, while the use_count also counts the number of times + a module is needed as a prerequisite. */ + +LttvModule *lttv_module_require(char *name, GError **error) +{ + LttvModule *m = module_require(name, error); + if(m != NULL) m->info.require_count++; + return(m); +} + + +static void module_release(LttvModule *m) +{ + guint i; + + library_lock_loaded(m->info.library); + + m->info.use_count--; + if(m->info.use_count == 0) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Module %s: destroy()",m->info.name); + m->info.destroy(); + for(i = 0 ; i < m->prerequisites->len ; i++) { + module_release((LttvModule *)(m->prerequisites->pdata[i])); + } + g_ptr_array_set_size(m->prerequisites, 0); + } + library_unlock_loaded(m->info.library); +} + + +void lttv_module_release(LttvModule *m) +{ + m->info.require_count--; + module_release(m); +} + + +void lttv_module_info(LttvModule *m, LttvModuleInfo *info) +{ + *info = m->info; +} + + +unsigned lttv_module_prerequisite_number(LttvModule *m) +{ + return m->prerequisites->len; +} + + +LttvModule *lttv_module_prerequisite_get(LttvModule *m, unsigned i) +{ + return (LttvModule *)(m->prerequisites->pdata[i]); +} + + +void lttv_library_info(LttvLibrary *l, LttvLibraryInfo *info) +{ + *info = l->info; +} + + +unsigned lttv_library_module_number(LttvLibrary *l) +{ + return l->modules->len; +} + + +LttvModule *lttv_library_module_get(LttvLibrary *l, unsigned i) +{ + return (LttvModule *)(l->modules->pdata[i]); +} + + +unsigned lttv_library_number() +{ + return libraries->len; +} + + +LttvLibrary *lttv_library_get(unsigned i) +{ + return (LttvLibrary *)(libraries->pdata[i]); +} + + +void lttv_library_path_add(const char *name) +{ + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Add library path %s", name); + g_ptr_array_add(library_paths,(char*)g_strdup(name)); +} + + +void lttv_library_path_remove(const char *name) +{ + guint i; + + for(i = 0 ; i < library_paths->len ; i++) { + if(g_str_equal(name, library_paths->pdata[i])) { + g_free(library_paths->pdata[i]); + g_ptr_array_remove_index(library_paths,i); + return; + } + } +} + + +unsigned lttv_library_path_number() +{ + return library_paths->len; +} + + +char *lttv_library_path_get(unsigned i) +{ + return (char *)(library_paths->pdata[library_paths->len - i - 1]); +} + + +void lttv_module_register(struct _LttvModuleDescription *d) +{ + *module_next = d; + module_next = &(d->next); +} + + +static void init() +{ + if(initialized) return; + g_assert(destroyed); + + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Init module.c"); + + initialized = TRUE; + destroyed = FALSE; + lttv_module_error = g_quark_from_string("LTTV_MODULE_ERROR"); + modules_by_name = g_hash_table_new(g_str_hash, g_str_equal); + libraries = g_ptr_array_new(); + libraries_by_g_module = g_hash_table_new(g_direct_hash, g_direct_equal); + library_paths = g_ptr_array_new(); + + if(builtin_chain == NULL) builtin_chain = module_chain; + module_chain = builtin_chain; + library_add("builtin", NULL, NULL); +} + + +static void finish_destroy() +{ + guint i; + + if(initialized) return; + g_assert(!destroyed); + + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Finish destroy module.c"); + g_hash_table_destroy(modules_by_name); + g_ptr_array_free(libraries, TRUE); + g_hash_table_destroy(libraries_by_g_module); + for(i = 0 ; i < library_paths->len ; i++) { + g_free(library_paths->pdata[i]); + } + g_ptr_array_free(library_paths, TRUE); + destroyed = TRUE; +} + + +static void destroy() +{ + guint i, j, nb; + + LttvLibrary *l, **locked_libraries; + + LttvModule *m; + + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Destroy module.c"); + + /* Unload all libraries */ + + nb = libraries->len; + locked_libraries = g_new(LttvLibrary *, nb); + + for(i = 0 ; i < nb ; i++) { + //g_assert(nb == libraries->len); + l = (LttvLibrary *)(libraries->pdata[i]); + locked_libraries[i] = l; + library_lock_loaded(l); + for(j = 0 ; j < l->modules->len ; j++) { + m = (LttvModule *)(l->modules->pdata[j]); + while(m->info.require_count > 0) lttv_module_release(m); + } + if(library_unlock_loaded(l) > 0) + while(lttv_library_unload(l) > 0); + + /* If the number of librairies loaded have changed, restart from the + * beginning */ + if(nb != libraries->len) { + i = 0; + nb = libraries->len; + } + + } + + for(i = 0 ; i < nb ; i++) { + l = locked_libraries[i]; + library_unlock_loaded(l); + } + g_free(locked_libraries); + + /* The library containing module.c may be locked by our caller */ + + g_assert(libraries->len <= 1); + + initialized = FALSE; + + exit(0); +} + +LTTV_MODULE("module", "Modules in libraries", \ + "Load libraries, list, require and initialize contained modules", \ + init, destroy) + diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/module.h b/tags/lttv-0.11.3-23102008/lttv/lttv/module.h new file mode 100644 index 00000000..67931405 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/module.h @@ -0,0 +1,208 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 MODULES_H +#define MODULES_H + +#include + +/* A module contains some functionality which becomes available atfer it is + initialized and before it is destroyed. A module is characterized by a name, + a description (short and long), a list of names of other modules on which + it depends, and an initialization and a destruction function. + + A library contains one or more modules and may be loaded dynamically. + The modules contained in a library are automatically registered through + constructors which are called when the library is loaded. For modules + directly linked with the main program (builtin), the constructors are + called before the main program starts. (However, neither malloc nor glib + functions are used during the registration process). + + The library loading path is a set of directories, where requested + libraries and modules are searched for. +*/ + +typedef struct _LttvModule LttvModule; + +typedef struct _LttvLibrary LttvLibrary; + +typedef void (*LttvModuleInit)(); + +typedef void (*LttvModuleDestroy)(); + +typedef struct _LttvModuleInfo +{ + char *name; + char *short_description; + char *description; + LttvModuleInit init; + LttvModuleDestroy destroy; + LttvLibrary *library; + unsigned require_count; + unsigned use_count; + unsigned prerequisites_number; +} LttvModuleInfo; + + +typedef struct _LttvLibraryInfo +{ + char *name; + char *path; + unsigned load_count; +} LttvLibraryInfo; + + +typedef enum _LttvModuleError +{ + LTTV_MODULE_NOT_FOUND, + LTTV_MODULE_NO_INIT +} LttvModuleError; + + +/* Insure that a module is loaded and initialized. Require count + (number of times the module was required) and use count + (number of times required or used as prerequisite) serve to + insure that a module is destroyed only after it has been released + as many times as it was required (and prerequired). + + The module is searched among the modules currently loaded, then as a + similarly named library to load which should contain the named module. + If the module cannot be found or loaded, NULL is returned and an + explanation is provided in error. */ + +LttvModule *lttv_module_require(char *name, GError **error); + +void lttv_module_release(LttvModule *m); + + +/* Obtain information about the module, including the containing library */ + +void lttv_module_info(LttvModule *m, LttvModuleInfo *info); + + +/* List the modules on which this module depends */ + +unsigned lttv_module_prerequisite_number(LttvModule *m); + +LttvModule *lttv_module_prerequisite_get(LttvModule *m, unsigned i); + + +/* Insure that a library is loaded. A load count insures that a library + is unloaded only after it has been asked to unload as + many times as it was loaded, and its modules are not in use. The library + is searched along the library path if name is a relative pathname. + If the library cannot be found or loaded, NULL is returned and an + explanation is provided in error. */ + +LttvLibrary *lttv_library_load(char *name, GError **error); + +/* Returns 0 if library is unloaded, > 0 otherwise */ +gint lttv_library_unload(LttvLibrary *l); + + +/* Obtain information about the library */ + +void lttv_library_info(LttvLibrary *l, LttvLibraryInfo *info); + + +/* List the modules contained in a library */ + +unsigned lttv_library_module_number(LttvLibrary *l); + +LttvModule *lttv_library_module_get(LttvLibrary *l, unsigned i); + + +/* List the currently loaded libraries */ + +unsigned lttv_library_number(); + +LttvLibrary *lttv_library_get(unsigned i); + + + +/* Add or remove directory names to the library search path */ + +void lttv_library_path_add(const char *name); + +void lttv_library_path_remove(const char *name); + + +/* List the directory names in the library search path */ + +unsigned lttv_library_path_number(); + +char *lttv_library_path_get(unsigned i); + + +/* To define a module, simply call the LTTV_MODULE macro with the needed + arguments: single word name, one line short description, larger + description, initialization function, destruction function, and + list of names for required modules (e.g., "moduleA", "moduleB"). + This will insure that the module is registered at library load time. + + Example: + + LTTV_MODULE("option", "Command line options processing", "...", \ + init, destroy, "moduleA", "moduleB") +*/ + +#define LTTV_MODULE(name, short_desc, desc, init, destroy, ...) \ + \ + static void _LTTV_MODULE_REGISTER(__LINE__)() \ + __attribute__((constructor)); \ + \ + static void _LTTV_MODULE_REGISTER(__LINE__)() \ + { \ + static char *module_prerequisites[] = { __VA_ARGS__ }; \ + \ + static struct _LttvModuleDescription module = { \ + name, short_desc, desc, init, destroy, \ + sizeof(module_prerequisites) / sizeof(char *), \ + module_prerequisites, NULL}; \ + \ + lttv_module_register(&module); \ + } + + +/* Internal structure and function used to register modules, called by + LTTV_MODULE */ + +#define __LTTV_MODULE_REGISTER(line) _lttv_module_register_ ## line +#define _LTTV_MODULE_REGISTER(line) __LTTV_MODULE_REGISTER(line) + +struct _LttvModuleDescription +{ + char *name; + char *short_description; + char *description; + LttvModuleInit init; + LttvModuleDestroy destroy; + unsigned prerequisites_number; + char **prerequisites; + struct _LttvModuleDescription *next; +}; + +void lttv_module_register(struct _LttvModuleDescription *d); + +#endif // MODULES_H + + + + + + diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/option.c b/tags/lttv-0.11.3-23102008/lttv/lttv/option.c new file mode 100644 index 00000000..f0d44254 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/option.c @@ -0,0 +1,317 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +typedef struct _LttvOption { + char *long_name; + char char_name; + char *description; + char *arg_description; + LttvOptionType t; + gpointer p; + LttvOptionHook hook; + gpointer hook_data; + + /* Keep the order of addition */ + guint val; +} LttvOption; + +GHashTable *options; + + +static void +list_options(gpointer key, gpointer value, gpointer user_data) +{ + GPtrArray *list = (GPtrArray *)user_data; + LttvOption *option = (LttvOption *)value; + + if(list->len < option->val) + g_ptr_array_set_size(list, option->val); + list->pdata[option->val-1] = option; +} + + +static void +free_option(LttvOption *option) +{ + g_free(option->long_name); + g_free(option->description); + g_free(option->arg_description); + g_free(option); +} + + +void lttv_option_add(const char *long_name, const char char_name, + const char *description, const char *arg_description, + const LttvOptionType t, void *p, + const LttvOptionHook h, void *hook_data) +{ + LttvOption *option; + + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Add option %s", long_name); + if(g_hash_table_lookup(options, long_name) != NULL) { + g_warning("duplicate option"); + return; + } + + option = g_new(LttvOption, 1); + option->long_name = g_strdup(long_name); + option->char_name = char_name; + option->description = g_strdup(description); + option->arg_description = g_strdup(arg_description); + option->t = t; + option->p = p; + option->hook = h; + option->hook_data = hook_data; + option->val = g_hash_table_size(options) + 1; + g_hash_table_insert(options, option->long_name, option); +} + + +void +lttv_option_remove(const char *long_name) +{ + LttvOption *option = g_hash_table_lookup(options, long_name); + + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Remove option %s", long_name); + if(option == NULL) { + g_warning("trying to remove unknown option %s", long_name); + return; + } + g_hash_table_remove(options, long_name); + free_option(option); +} + + +static int poptToLTT[] = { + POPT_ARG_NONE, POPT_ARG_STRING, POPT_ARG_INT, POPT_ARG_LONG +}; + +static struct poptOption endOption = { NULL, '\0', 0, NULL, 0, NULL, NULL }; + + +static void +build_popts(GPtrArray **plist, struct poptOption **ppopts, poptContext *pc, + int argc, char **argv) +{ + LttvOption *option; + + GPtrArray *list; + + struct poptOption *popts; + + poptContext c; + + guint i; + + list = g_ptr_array_sized_new(g_hash_table_size(options)); + + g_hash_table_foreach(options, list_options, list); + + /* Build a popt options array from our list */ + + popts = g_new(struct poptOption, list->len + 1); + + /* add the options in the reverse order, so last additions are parsed first */ + for(i = 0 ; i < list->len ; i++) { + guint reverse_i = list->len-1-i; + option = (LttvOption *)list->pdata[i]; + popts[reverse_i].longName = option->long_name; + popts[reverse_i].shortName = option->char_name; + popts[reverse_i].descrip = option->description; + popts[reverse_i].argDescrip = option->arg_description; + popts[reverse_i].argInfo = poptToLTT[option->t]; + popts[reverse_i].arg = option->p; + popts[reverse_i].val = option->val; + } + + /* Terminate the array for popt and create the context */ + + popts[list->len] = endOption; + c = poptGetContext(argv[0], argc, (const char**)argv, popts, 0); + + *plist = list; + *ppopts = popts; + *pc = c; +} + + +static void +destroy_popts(GPtrArray **plist, struct poptOption **ppopts, poptContext *pc) +{ + g_ptr_array_free(*plist, TRUE); *plist = NULL; + g_free(*ppopts); *ppopts = NULL; + poptFreeContext(*pc); +} + + +void lttv_option_parse(int argc, char **argv) +{ + GPtrArray *list; + + LttvOption *option; + + int i, rc, first_arg; + + struct poptOption *popts; + + poptContext c; + + i = 0; + + first_arg = 0; + + guint hash_size = 0; + + build_popts(&list, &popts, &c, argc, argv); + + /* Parse options while not end of options event */ + + while((rc = poptGetNextOpt(c)) != -1) { + + /* The option was recognized and the rc value returned is the argument + position in the array. Call the associated hook if present. */ + + if(rc > 0) { + option = (LttvOption *)(list->pdata[rc - 1]); + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Option %s encountered", + option->long_name); + hash_size = g_hash_table_size(options); + if(option->hook != NULL) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Option %s hook called", + option->long_name); + option->hook(option->hook_data); + } + i++; + + /* If the size of the option hash changed, add new options + * right now. It resolves the conflict of multiple same short + * option use. + */ + if(hash_size != g_hash_table_size(options)) { + destroy_popts(&list, &popts, &c); + build_popts(&list, &popts, &c, argc, argv); + + /* Get back to the same argument */ + + first_arg = i; + for(i = 0; i < first_arg; i++) { + rc = poptGetNextOpt(c); + option = (LttvOption *)(list->pdata[rc - 1]); + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Option %s rescanned, skipped", + option->long_name); + } + } + } + + else if(rc == POPT_ERROR_BADOPT && i != first_arg) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, + "Option %s not recognized, rescan options with new additions", + poptBadOption(c,0)); + + /* Perhaps this option is newly added, restart parsing */ + + destroy_popts(&list, &popts, &c); + build_popts(&list, &popts, &c, argc, argv); + + /* Get back to the same argument */ + + first_arg = i; + for(i = 0; i < first_arg; i++) { + rc = poptGetNextOpt(c); + option = (LttvOption *)(list->pdata[rc - 1]); + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Option %s rescanned, skipped", + option->long_name); + } + } + + else { + + /* The option has some error and it is not because this is a newly + added option not recognized. */ + + g_error("option %s: %s", poptBadOption(c,0), poptStrerror(rc)); + break; + } + + } + + destroy_popts(&list, &popts, &c); +} + +/* CHECK */ +static void show_help(LttvOption *option) +{ + printf("--%s -%c argument: %s\n" , option->long_name, + option->char_name, + option->arg_description); + printf(" %s\n" , option->description); + +} + +void lttv_option_show_help(void) +{ + GPtrArray *list = g_ptr_array_new(); + + guint i; + + g_hash_table_foreach(options, list_options, list); + + printf("Built-in commands available:\n"); + printf("\n"); + + for(i = 0 ; i < list->len ; i++) { + show_help((LttvOption *)list->pdata[i]); + } + g_ptr_array_free(list, TRUE); +} + +static void init() +{ + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Init option.c"); + options = g_hash_table_new(g_str_hash, g_str_equal); +} + + +static void destroy() +{ + GPtrArray *list = g_ptr_array_new(); + + guint i; + + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Destroy option.c"); + g_hash_table_foreach(options, list_options, list); + g_hash_table_destroy(options); + + for(i = 0 ; i < list->len ; i++) { + free_option((LttvOption *)list->pdata[i]); + } + g_ptr_array_free(list, TRUE); +} + +LTTV_MODULE("option", "Command line options processing", \ + "Functions to add, remove and parse command line options", \ + init, destroy) diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/option.h b/tags/lttv-0.11.3-23102008/lttv/lttv/option.h new file mode 100644 index 00000000..fc8f14d5 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/option.h @@ -0,0 +1,53 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 OPTION_H +#define OPTION_H + +/* Define a new option with a long name (--long_name), a short + one character name (-c), a descriptive text, the argument type, and a + pointer to where the argument value will be stored. For an option of + type LTTV_OPT_NONE, the argument is a boolean value set to true when the + option is present. The option hook is called if non NULL. */ + +typedef enum _LttvOptionType +{LTTV_OPT_NONE, LTTV_OPT_STRING, LTTV_OPT_INT, LTTV_OPT_LONG } +LttvOptionType; + +typedef void (*LttvOptionHook)(void *hook_data); + +void lttv_option_add(const char *long_name, const char char_name, + const char *description, const char *arg_description, + const LttvOptionType t, void *p, + const LttvOptionHook h, void *hook_data); + + +/* Remove an option */ + +void lttv_option_remove(const char *long_name); + + +/* Parse command line options. It is possible to add options (through the + hooks being called) while the parsing is done. The new options will be + used for subsequent command line arguments. */ + +void lttv_option_parse(int argc, char **argv); + +void lttv_option_show_help(void); + +#endif // OPTION_H diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/print.c b/tags/lttv-0.11.3-23102008/lttv/lttv/print.c new file mode 100644 index 00000000..4afab112 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/print.c @@ -0,0 +1,307 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * 2005 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +/* print.c + * + * Event printing routines. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static inline void print_enum_events(LttEvent *e, struct marker_field *f, + guint64 value, GString *s, LttvTracefileState *tfs) +{ + LttTrace *trace = ltt_tracefile_get_trace(e->tracefile); + struct marker_info *info = marker_get_info_from_id(trace, e->event_id); + LttvTraceState *ts = (LttvTraceState*)(tfs->parent.t_context); + + //TODO optimize with old quarks. + if (info->name == g_quark_from_static_string("kernel_syscall_entry") && + f->name == LTT_FIELD_SYSCALL_ID) { + g_string_append_printf(s, " [%s]", + g_quark_to_string(ts->syscall_names[value])); + } else if ((info->name == g_quark_from_static_string("kernel_softirq_entry") + || info->name == g_quark_from_static_string("kernel_softirq_exit") + || info->name == g_quark_from_static_string("kernel_softirq_raise")) && + f->name == g_quark_from_static_string("softirq_id")) { + g_string_append_printf(s, " [%s]", + g_quark_to_string(ts->soft_irq_names[value])); + } + +} + +void lttv_print_field(LttEvent *e, struct marker_field *f, GString *s, + gboolean field_names, LttvTracefileState *tfs) +{ + GQuark name; + guint64 value; + + //int nb, i; + + switch(f->type) { + case LTT_TYPE_SIGNED_INT: + if(field_names) { + name = f->name; + if(name) + g_string_append_printf(s, "%s = ", g_quark_to_string(name)); + } + value = ltt_event_get_long_int(e,f); + //g_string_append_printf(s, "%lld", value); + g_string_append_printf(s, f->fmt->str, value); + //g_string_append_printf(s, type->fmt, ltt_event_get_long_int(e,f)); + print_enum_events(e, f, value, s, tfs); + break; + + case LTT_TYPE_UNSIGNED_INT: + if(field_names) { + name = f->name; + if(name) + g_string_append_printf(s, "%s = ", g_quark_to_string(name)); + } + value = ltt_event_get_long_unsigned(e,f); + //g_string_append_printf(s, "%llu", value); + g_string_append_printf(s, f->fmt->str, value); + print_enum_events(e, f, value, s, tfs); + //g_string_append_printf(s, type->fmt, ltt_event_get_long_unsigned(e,f)); + break; + +#if 0 + case LTT_CHAR: + case LTT_UCHAR: + { + unsigned car = ltt_event_get_unsigned(e,f); + if(field_names) { + name = ltt_field_name(f); + if(name) + g_string_append_printf(s, "%s = ", g_quark_to_string(name)); + } + if(isprint(car)) { + if(field_names) { + name = ltt_field_name(f); + if(name) + g_string_append_printf(s, "%s = ", g_quark_to_string(name)); + } + //g_string_append_printf(s, "%c", car); + g_string_append_printf(s, type->fmt, car); + } else { + g_string_append_printf(s, "\\%x", car); + } + } + break; + case LTT_FLOAT: + if(field_names) { + name = ltt_field_name(f); + if(name) + g_string_append_printf(s, "%s = ", g_quark_to_string(name)); + } + //g_string_append_printf(s, "%g", ltt_event_get_double(e,f)); + g_string_append_printf(s, type->fmt, ltt_event_get_double(e,f)); + break; +#endif + + case LTT_TYPE_POINTER: + if(field_names) { + name = f->name; + if(name) + g_string_append_printf(s, "%s = ", g_quark_to_string(name)); + } + g_string_append_printf(s, "0x%llx", ltt_event_get_long_unsigned(e,f)); + //g_string_append_printf(s, type->fmt, ltt_event_get_long_unsigned(e,f)); + break; + + case LTT_TYPE_STRING: + if(field_names) { + name = f->name; + if(name) + g_string_append_printf(s, "%s = ", g_quark_to_string(name)); + } + g_string_append_printf(s, "\"%s\"", ltt_event_get_string(e,f)); + break; + +#if 0 + case LTT_ENUM: + { + GQuark value = ltt_enum_string_get(type, ltt_event_get_unsigned(e,f)); + if(field_names) { + name = ltt_field_name(f); + if(name) + g_string_append_printf(s, "%s = ", g_quark_to_string(name)); + } + if(value) + g_string_append_printf(s, "%s", g_quark_to_string(value)); + else + g_string_append_printf(s, "%lld", ltt_event_get_long_int(e,f)); + } + break; + + case LTT_ARRAY: + case LTT_SEQUENCE: + if(field_names) { + name = ltt_field_name(f); + if(name) + g_string_append_printf(s, "%s = ", g_quark_to_string(name)); + } + // g_string_append_printf(s, "{ "); + //Insert header + g_string_append_printf(s, type->header);//tested, works fine. + + + nb = ltt_event_field_element_number(e,f); + for(i = 0 ; i < nb ; i++) { + LttField *child = ltt_event_field_element_select(e,f,i); + lttv_print_field(e, child, s, field_names, i); + if(iseparator); + } + //g_string_append_printf(s, " }"); + //Insert footer + g_string_append_printf(s, type->footer);//tested, works fine. + break; + + case LTT_STRUCT: + if(field_names) { + name = ltt_field_name(f); + if(name) + g_string_append_printf(s, "%s = ", g_quark_to_string(name)); + } + // g_string_append_printf(s, "{ "); + //Insert header + g_string_append_printf(s, type->header); + + nb = ltt_type_member_number(type); + for(i = 0 ; i < nb ; i++) { + LttField *element; + element = ltt_field_member(f,i); + lttv_print_field(e, element, s, field_names, i); + if(i < nb-1) + g_string_append_printf(s,type->separator); + } + //g_string_append_printf(s, " }"); + //Insert footer + g_string_append_printf(s, type->footer); + break; + + case LTT_UNION: + if(field_names) { + name = ltt_field_name(f); + if(name) + g_string_append_printf(s, "%s = ", g_quark_to_string(name)); + } + // g_string_append_printf(s, "{ "); + g_string_append_printf(s, type->header); + + nb = ltt_type_member_number(type); + for(i = 0 ; i < nb ; i++) { + LttField *element; + element = ltt_field_member(f,i); + lttv_print_field(e, element, s, field_names, i); + if(iseparator); + } + // g_string_append_printf(s, " }"); + g_string_append_printf(s, type->footer); + break; +#endif + case LTT_TYPE_COMPACT: + g_error("compact type printing not implemented"); + break; + case LTT_TYPE_NONE: + break; + } +} + +void lttv_event_to_string(LttEvent *e, GString *s, + gboolean mandatory_fields, gboolean field_names, LttvTracefileState *tfs) +{ + struct marker_field *field; + struct marker_info *info; + + LttTime time; + + guint cpu = tfs->cpu; + LttvTraceState *ts = (LttvTraceState*)tfs->parent.t_context; + LttvProcessState *process = ts->running_process[cpu]; + LttTrace *trace = ts->parent.t; + + s = g_string_set_size(s,0); + + info = marker_get_info_from_id(trace, e->event_id); + + if(mandatory_fields) { + time = ltt_event_time(e); + g_string_append_printf(s,"%s: %ld.%09ld (%s%s_%u)", + g_quark_to_string(info->name), + (long)time.tv_sec, time.tv_nsec, + g_quark_to_string( + ltt_trace_name(ltt_tracefile_get_trace(tfs->parent.tf))), + g_quark_to_string(ltt_tracefile_name(tfs->parent.tf)), + cpu); + /* Print the process id and the state/interrupt type of the process */ + g_string_append_printf(s,", %u, %u, %s, %s, %u, 0x%llX, %s", process->pid, + process->tgid, + g_quark_to_string(process->name), + g_quark_to_string(process->brand), + process->ppid, process->current_function, + g_quark_to_string(process->state->t)); + } + + if(marker_get_num_fields(info) == 0) return; + g_string_append_printf(s, " "); + g_string_append_printf(s, "{ "); + for (field = marker_get_field(info, 0); + field != marker_get_field(info, marker_get_num_fields(info)); + field++) { + if(field != marker_get_field(info, 0)) + g_string_append_printf(s, ", "); + lttv_print_field(e, field, s, field_names, tfs); + } + g_string_append_printf(s, " }"); +} + +static void init() +{ +} + +static void destroy() +{ +} + +LTTV_MODULE("print", "Print events", \ + "Produce a detailed text printout of events", \ + init, destroy) + diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/print.h b/tags/lttv-0.11.3-23102008/lttv/lttv/print.h new file mode 100644 index 00000000..3615abc7 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/print.h @@ -0,0 +1,34 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * 2005 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +/* print.c + * + * Event printing routines header. + * + * Use these functions to print textually event fields. + */ + + + +void lttv_print_field(LttEvent *e, struct marker_field *f, GString *s, + gboolean field_names, LttvTracefileState *tfs); + +void lttv_event_to_string(LttEvent *e, GString *s, + gboolean mandatory_fields, gboolean field_names, LttvTracefileState *tfs); + diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/state.c b/tags/lttv-0.11.3-23102008/lttv/lttv/state.c new file mode 100644 index 00000000..2105cb76 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/state.c @@ -0,0 +1,4282 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#define _GNU_SOURCE +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Comment : + * Mathieu Desnoyers + * usertrace is there only to be able to update the current CPU of the + * usertraces when there is a schedchange. it is a way to link the ProcessState + * to the associated usertrace. Link only created upon thread creation. + * + * The cpu id is necessary : it gives us back the current ProcessState when we + * are considering data from the usertrace. + */ + +#define PREALLOCATED_EXECUTION_STACK 10 + +/* Facilities Quarks */ + +GQuark + LTT_FACILITY_KERNEL, + LTT_FACILITY_KERNEL_ARCH, + LTT_FACILITY_LIST, + LTT_FACILITY_FS, + LTT_FACILITY_USER_GENERIC, + LTT_FACILITY_BLOCK, + LTT_FACILITY_STATEDUMP; + +/* Events Quarks */ + +GQuark + LTT_EVENT_SYSCALL_ENTRY, + LTT_EVENT_SYSCALL_EXIT, + LTT_EVENT_TRAP_ENTRY, + LTT_EVENT_TRAP_EXIT, + LTT_EVENT_IRQ_ENTRY, + LTT_EVENT_IRQ_EXIT, + LTT_EVENT_SOFT_IRQ_RAISE, + LTT_EVENT_SOFT_IRQ_ENTRY, + LTT_EVENT_SOFT_IRQ_EXIT, + LTT_EVENT_SCHED_SCHEDULE, + LTT_EVENT_PROCESS_FORK, + LTT_EVENT_KTHREAD_CREATE, + LTT_EVENT_PROCESS_EXIT, + LTT_EVENT_PROCESS_FREE, + LTT_EVENT_EXEC, + LTT_EVENT_PROCESS_STATE, + LTT_EVENT_STATEDUMP_END, + LTT_EVENT_FUNCTION_ENTRY, + LTT_EVENT_FUNCTION_EXIT, + LTT_EVENT_THREAD_BRAND, + LTT_EVENT_REQUEST_ISSUE, + LTT_EVENT_REQUEST_COMPLETE, + LTT_EVENT_LIST_INTERRUPT, + LTT_EVENT_SYS_CALL_TABLE, + LTT_EVENT_SOFTIRQ_VEC; + +/* Fields Quarks */ + +GQuark + LTT_FIELD_SYSCALL_ID, + LTT_FIELD_TRAP_ID, + LTT_FIELD_IRQ_ID, + LTT_FIELD_SOFT_IRQ_ID, + LTT_FIELD_PREV_PID, + LTT_FIELD_NEXT_PID, + LTT_FIELD_PREV_STATE, + LTT_FIELD_PARENT_PID, + LTT_FIELD_CHILD_PID, + LTT_FIELD_PID, + LTT_FIELD_TGID, + LTT_FIELD_CHILD_TGID, + LTT_FIELD_FILENAME, + LTT_FIELD_NAME, + LTT_FIELD_TYPE, + LTT_FIELD_MODE, + LTT_FIELD_SUBMODE, + LTT_FIELD_STATUS, + LTT_FIELD_THIS_FN, + LTT_FIELD_CALL_SITE, + LTT_FIELD_MINOR, + LTT_FIELD_MAJOR, + LTT_FIELD_OPERATION, + LTT_FIELD_ACTION, + LTT_FIELD_ID, + LTT_FIELD_ADDRESS, + LTT_FIELD_SYMBOL; + +LttvExecutionMode + LTTV_STATE_MODE_UNKNOWN, + LTTV_STATE_USER_MODE, + LTTV_STATE_SYSCALL, + LTTV_STATE_TRAP, + LTTV_STATE_IRQ, + LTTV_STATE_SOFT_IRQ; + +LttvExecutionSubmode + LTTV_STATE_SUBMODE_UNKNOWN, + LTTV_STATE_SUBMODE_NONE; + +LttvProcessStatus + LTTV_STATE_UNNAMED, + LTTV_STATE_WAIT_FORK, + LTTV_STATE_WAIT_CPU, + LTTV_STATE_EXIT, + LTTV_STATE_ZOMBIE, + LTTV_STATE_WAIT, + LTTV_STATE_RUN, + LTTV_STATE_DEAD; + +GQuark + LTTV_STATE_UNBRANDED; + +LttvProcessType + LTTV_STATE_USER_THREAD, + LTTV_STATE_KERNEL_THREAD; + +LttvCPUMode + LTTV_CPU_UNKNOWN, + LTTV_CPU_IDLE, + LTTV_CPU_BUSY, + LTTV_CPU_IRQ, + LTTV_CPU_SOFT_IRQ, + LTTV_CPU_TRAP; + +LttvIRQMode + LTTV_IRQ_UNKNOWN, + LTTV_IRQ_IDLE, + LTTV_IRQ_BUSY; + +LttvBdevMode + LTTV_BDEV_UNKNOWN, + LTTV_BDEV_IDLE, + LTTV_BDEV_BUSY_READING, + LTTV_BDEV_BUSY_WRITING; + +static GQuark + LTTV_STATE_TRACEFILES, + LTTV_STATE_PROCESSES, + LTTV_STATE_PROCESS, + LTTV_STATE_RUNNING_PROCESS, + LTTV_STATE_EVENT, + LTTV_STATE_SAVED_STATES, + LTTV_STATE_SAVED_STATES_TIME, + LTTV_STATE_TIME, + LTTV_STATE_HOOKS, + LTTV_STATE_NAME_TABLES, + LTTV_STATE_TRACE_STATE_USE_COUNT, + LTTV_STATE_RESOURCE_CPUS, + LTTV_STATE_RESOURCE_CPUS_COUNT, + LTTV_STATE_RESOURCE_IRQS, + LTTV_STATE_RESOURCE_SOFT_IRQS, + LTTV_STATE_RESOURCE_TRAPS, + LTTV_STATE_RESOURCE_BLKDEVS; + +static void create_max_time(LttvTraceState *tcs); + +static void get_max_time(LttvTraceState *tcs); + +static void free_max_time(LttvTraceState *tcs); + +static void create_name_tables(LttvTraceState *tcs); + +static void get_name_tables(LttvTraceState *tcs); + +static void free_name_tables(LttvTraceState *tcs); + +static void free_saved_state(LttvTraceState *tcs); + +static void lttv_state_free_process_table(GHashTable *processes); + +static void lttv_trace_states_read_raw(LttvTraceState *tcs, FILE *fp, + GPtrArray *quarktable); + +/* Resource function prototypes */ +static LttvBdevState *get_hashed_bdevstate(LttvTraceState *ts, guint16 devcode); +static LttvBdevState *bdevstate_new(void); +static void bdevstate_free(LttvBdevState *); +static void bdevstate_free_cb(gpointer key, gpointer value, gpointer user_data); +static LttvBdevState *bdevstate_copy(LttvBdevState *bds); + + +void lttv_state_save(LttvTraceState *self, LttvAttribute *container) +{ + LTTV_TRACE_STATE_GET_CLASS(self)->state_save(self, container); +} + + +void lttv_state_restore(LttvTraceState *self, LttvAttribute *container) +{ + LTTV_TRACE_STATE_GET_CLASS(self)->state_restore(self, container); +} + + +void lttv_state_state_saved_free(LttvTraceState *self, + LttvAttribute *container) +{ + LTTV_TRACE_STATE_GET_CLASS(self)->state_saved_free(self, container); +} + + +guint process_hash(gconstpointer key) +{ + guint pid = ((const LttvProcessState *)key)->pid; + return (pid>>8 ^ pid>>4 ^ pid>>2 ^ pid) ; +} + + +/* If the hash table hash function is well distributed, + * the process_equal should compare different pid */ +gboolean process_equal(gconstpointer a, gconstpointer b) +{ + const LttvProcessState *process_a, *process_b; + gboolean ret = TRUE; + + process_a = (const LttvProcessState *)a; + process_b = (const LttvProcessState *)b; + + if(likely(process_a->pid != process_b->pid)) ret = FALSE; + else if(likely(process_a->pid == 0 && + process_a->cpu != process_b->cpu)) ret = FALSE; + + return ret; +} + +static void delete_usertrace(gpointer key, gpointer value, gpointer user_data) +{ + g_tree_destroy((GTree*)value); +} + +static void lttv_state_free_usertraces(GHashTable *usertraces) +{ + g_hash_table_foreach(usertraces, delete_usertrace, NULL); + g_hash_table_destroy(usertraces); +} + +gboolean rettrue(gpointer key, gpointer value, gpointer user_data) +{ + return TRUE; +} + +static guint check_expand(nb, id) +{ + if(likely(nb > id)) + return nb; + else + return max(id + 1, nb * 2); +} + +static void expand_name_table(LttvTraceState *ts, GQuark **table, + guint nb, guint new_nb) +{ + /* Expand an incomplete table */ + GQuark *old_table = *table; + *table = g_new(GQuark, new_nb); + memcpy(*table, old_table, nb * sizeof(GQuark)); +} + +static void fill_name_table(LttvTraceState *ts, GQuark *table, guint nb, + guint new_nb, const char *def_string) +{ + guint i; + GString *fe_name = g_string_new(""); + for(i = nb; i < new_nb; i++) { + g_string_printf(fe_name, "%s %d", def_string, i); + table[i] = g_quark_from_string(fe_name->str); + } + g_string_free(fe_name, TRUE); +} + +static void expand_syscall_table(LttvTraceState *ts, int id) +{ + guint new_nb = check_expand(ts->nb_syscalls, id); + if(likely(new_nb == ts->nb_syscalls)) + return; + expand_name_table(ts, &ts->syscall_names, ts->nb_syscalls, new_nb); + fill_name_table(ts, ts->syscall_names, ts->nb_syscalls, new_nb, "syscall"); + /* Update the table size */ + ts->nb_syscalls = new_nb; +} + +static void expand_trap_table(LttvTraceState *ts, int id) +{ + guint new_nb = check_expand(ts->nb_traps, id); + guint i; + if(likely(new_nb == ts->nb_traps)) + return; + expand_name_table(ts, &ts->trap_names, ts->nb_traps, new_nb); + fill_name_table(ts, ts->trap_names, ts->nb_traps, new_nb, "trap"); + /* Update the table size */ + ts->nb_traps = new_nb; + + LttvTrapState *old_table = ts->trap_states; + ts->trap_states = g_new(LttvTrapState, new_nb); + memcpy(ts->trap_states, old_table, + ts->nb_traps * sizeof(LttvTrapState)); + for(i = ts->nb_traps; i < new_nb; i++) + ts->trap_states[i].running = 0; +} + +static void expand_irq_table(LttvTraceState *ts, int id) +{ + guint new_nb = check_expand(ts->nb_irqs, id); + guint i; + if(likely(new_nb == ts->nb_irqs)) + return; + expand_name_table(ts, &ts->irq_names, ts->nb_irqs, new_nb); + fill_name_table(ts, ts->irq_names, ts->nb_irqs, new_nb, "irq"); + + LttvIRQState *old_table = ts->irq_states; + ts->irq_states = g_new(LttvIRQState, new_nb); + memcpy(ts->irq_states, old_table, ts->nb_irqs * sizeof(LttvIRQState)); + for(i = ts->nb_irqs; i < new_nb; i++) { + ts->irq_states[i].mode_stack = g_array_new(FALSE, FALSE, sizeof(LttvIRQMode)); + } + + /* Update the table size */ + ts->nb_irqs = new_nb; +} + +static void expand_soft_irq_table(LttvTraceState *ts, int id) +{ + guint new_nb = check_expand(ts->nb_soft_irqs, id); + guint i; + if(likely(new_nb == ts->nb_soft_irqs)) + return; + expand_name_table(ts, &ts->soft_irq_names, ts->nb_soft_irqs, new_nb); + fill_name_table(ts, ts->soft_irq_names, ts->nb_soft_irqs, new_nb, "softirq"); + + LttvSoftIRQState *old_table = ts->soft_irq_states; + ts->soft_irq_states = g_new(LttvSoftIRQState, new_nb); + memcpy(ts->soft_irq_states, old_table, + ts->nb_soft_irqs * sizeof(LttvSoftIRQState)); + for(i = ts->nb_soft_irqs; i < new_nb; i++) + ts->soft_irq_states[i].running = 0; + + /* Update the table size */ + ts->nb_soft_irqs = new_nb; +} + +static void +restore_init_state(LttvTraceState *self) +{ + guint i, nb_cpus, nb_irqs, nb_soft_irqs, nb_traps; + + //LttvTracefileState *tfcs; + + LttTime start_time, end_time; + + /* Free the process tables */ + if(self->processes != NULL) lttv_state_free_process_table(self->processes); + if(self->usertraces != NULL) lttv_state_free_usertraces(self->usertraces); + self->processes = g_hash_table_new(process_hash, process_equal); + self->usertraces = g_hash_table_new(g_direct_hash, g_direct_equal); + self->nb_event = 0; + + /* Seek time to beginning */ + // Mathieu : fix : don't seek traceset here : causes inconsistency in seek + // closest. It's the tracecontext job to seek the trace to the beginning + // anyway : the init state might be used at the middle of the trace as well... + //g_tree_destroy(self->parent.ts_context->pqueue); + //self->parent.ts_context->pqueue = g_tree_new(compare_tracefile); + + ltt_trace_time_span_get(self->parent.t, &start_time, &end_time); + + //lttv_process_trace_seek_time(&self->parent, ltt_time_zero); + + nb_cpus = ltt_trace_get_num_cpu(self->parent.t); + nb_irqs = self->nb_irqs; + nb_soft_irqs = self->nb_soft_irqs; + nb_traps = self->nb_traps; + + /* Put the per cpu running_process to beginning state : process 0. */ + for(i=0; i< nb_cpus; i++) { + LttvExecutionState *es; + self->running_process[i] = lttv_state_create_process(self, NULL, i, 0, 0, + LTTV_STATE_UNNAMED, &start_time); + /* We are not sure is it's a kernel thread or normal thread, put the + * bottom stack state to unknown */ + self->running_process[i]->execution_stack = + g_array_set_size(self->running_process[i]->execution_stack, 1); + es = self->running_process[i]->state = + &g_array_index(self->running_process[i]->execution_stack, + LttvExecutionState, 0); + es->t = LTTV_STATE_MODE_UNKNOWN; + es->s = LTTV_STATE_UNNAMED; + + //self->running_process[i]->state->s = LTTV_STATE_RUN; + self->running_process[i]->cpu = i; + + /* reset cpu states */ + if(self->cpu_states[i].mode_stack->len > 0) { + g_array_remove_range(self->cpu_states[i].mode_stack, 0, self->cpu_states[i].mode_stack->len); + self->cpu_states[i].last_irq = -1; + self->cpu_states[i].last_soft_irq = -1; + self->cpu_states[i].last_trap = -1; + } + } + + /* reset irq states */ + for(i=0; iirq_states[i].mode_stack->len > 0) + g_array_remove_range(self->irq_states[i].mode_stack, 0, self->irq_states[i].mode_stack->len); + } + + /* reset softirq states */ + for(i=0; isoft_irq_states[i].pending = 0; + self->soft_irq_states[i].running = 0; + } + + /* reset trap states */ + for(i=0; itrap_states[i].running = 0; + } + + /* reset bdev states */ + g_hash_table_foreach(self->bdev_states, bdevstate_free_cb, NULL); + //g_hash_table_steal_all(self->bdev_states); + g_hash_table_foreach_steal(self->bdev_states, rettrue, NULL); + +#if 0 + nb_tracefile = self->parent.tracefiles->len; + + for(i = 0 ; i < nb_tracefile ; i++) { + tfcs = + LTTV_TRACEFILE_STATE(g_array_index(self->parent.tracefiles, + LttvTracefileContext*, i)); + ltt_trace_time_span_get(self->parent.t, &tfcs->parent.timestamp, NULL); +// tfcs->saved_position = 0; + tfcs->process = lttv_state_create_process(tfcs, NULL,0); + tfcs->process->state->s = LTTV_STATE_RUN; + tfcs->process->last_cpu = tfcs->cpu_name; + tfcs->process->last_cpu_index = ltt_tracefile_num(((LttvTracefileContext*)tfcs)->tf); + } +#endif //0 +} + +//static LttTime time_zero = {0,0}; + +static gint compare_usertraces(gconstpointer a, gconstpointer b, + gpointer user_data) +{ + const LttTime *t1 = (const LttTime *)a; + const LttTime *t2 = (const LttTime *)b; + + return ltt_time_compare(*t1, *t2); +} + +static void free_usertrace_key(gpointer data) +{ + g_free(data); +} + +#define MAX_STRING_LEN 4096 + +static void +state_load_saved_states(LttvTraceState *tcs) +{ + FILE *fp; + GPtrArray *quarktable; + const char *trace_path; + char path[PATH_MAX]; + guint count; + guint i; + tcs->has_precomputed_states = FALSE; + GQuark q; + gchar *string; + gint hdr; + gchar buf[MAX_STRING_LEN]; + guint len; + + trace_path = g_quark_to_string(ltt_trace_name(tcs->parent.t)); + strncpy(path, trace_path, PATH_MAX-1); + count = strnlen(trace_path, PATH_MAX-1); + // quarktable : open, test + strncat(path, "/precomputed/quarktable", PATH_MAX-count-1); + fp = fopen(path, "r"); + if(!fp) return; + quarktable = g_ptr_array_sized_new(4096); + + /* Index 0 is null */ + hdr = fgetc(fp); + if(hdr == EOF) return; + g_assert(hdr == HDR_QUARKS); + q = 1; + do { + hdr = fgetc(fp); + if(hdr == EOF) break; + g_assert(hdr == HDR_QUARK); + g_ptr_array_set_size(quarktable, q+1); + i=0; + while(1) { + fread(&buf[i], sizeof(gchar), 1, fp); + if(buf[i] == '\0' || feof(fp)) break; + i++; + } + len = strnlen(buf, MAX_STRING_LEN-1); + g_ptr_array_index (quarktable, q) = g_new(gchar, len+1); + strncpy(g_ptr_array_index (quarktable, q), buf, len+1); + q++; + } while(1); + + fclose(fp); + + // saved_states : open, test + strncpy(path, trace_path, PATH_MAX-1); + count = strnlen(trace_path, PATH_MAX-1); + strncat(path, "/precomputed/states", PATH_MAX-count-1); + fp = fopen(path, "r"); + if(!fp) return; + + hdr = fgetc(fp); + if(hdr != HDR_TRACE) goto end; + + lttv_trace_states_read_raw(tcs, fp, quarktable); + + tcs->has_precomputed_states = TRUE; + +end: + fclose(fp); + + /* Free the quarktable */ + for(i=0; ilen; i++) { + string = g_ptr_array_index (quarktable, i); + g_free(string); + } + g_ptr_array_free(quarktable, TRUE); + return; +} + +static void +init(LttvTracesetState *self, LttvTraceset *ts) +{ + guint i, j, nb_trace, nb_tracefile, nb_cpu; + guint64 nb_irq; + + LttvTraceContext *tc; + + LttvTraceState *tcs; + + LttvTracefileState *tfcs; + + LttvAttributeValue v; + + LTTV_TRACESET_CONTEXT_CLASS(g_type_class_peek(LTTV_TRACESET_CONTEXT_TYPE))-> + init((LttvTracesetContext *)self, ts); + + nb_trace = lttv_traceset_number(ts); + for(i = 0 ; i < nb_trace ; i++) { + tc = self->parent.traces[i]; + tcs = LTTV_TRACE_STATE(tc); + tcs->save_interval = LTTV_STATE_SAVE_INTERVAL; + lttv_attribute_find(tcs->parent.t_a, LTTV_STATE_TRACE_STATE_USE_COUNT, + LTTV_UINT, &v); + (*v.v_uint)++; + + if(*(v.v_uint) == 1) { + create_name_tables(tcs); + create_max_time(tcs); + } + get_name_tables(tcs); + get_max_time(tcs); + + nb_tracefile = tc->tracefiles->len; + nb_cpu = ltt_trace_get_num_cpu(tc->t); + nb_irq = tcs->nb_irqs; + tcs->processes = NULL; + tcs->usertraces = NULL; + tcs->running_process = g_new(LttvProcessState*, nb_cpu); + + /* init cpu resource stuff */ + tcs->cpu_states = g_new(LttvCPUState, nb_cpu); + for(j = 0; jcpu_states[j].mode_stack = g_array_new(FALSE, FALSE, sizeof(LttvCPUMode)); + tcs->cpu_states[j].last_irq = -1; + tcs->cpu_states[j].last_soft_irq = -1; + tcs->cpu_states[j].last_trap = -1; + g_assert(tcs->cpu_states[j].mode_stack != NULL); + } + + /* init irq resource stuff */ + tcs->irq_states = g_new(LttvIRQState, nb_irq); + for(j = 0; jirq_states[j].mode_stack = g_array_new(FALSE, FALSE, sizeof(LttvIRQMode)); + g_assert(tcs->irq_states[j].mode_stack != NULL); + } + + /* init soft irq stuff */ + /* the kernel has a statically fixed max of 32 softirqs */ + tcs->soft_irq_states = g_new(LttvSoftIRQState, tcs->nb_soft_irqs); + + /* init trap stuff */ + tcs->trap_states = g_new(LttvTrapState, tcs->nb_traps); + + /* init bdev resource stuff */ + tcs->bdev_states = g_hash_table_new(g_int_hash, g_int_equal); + + restore_init_state(tcs); + for(j = 0 ; j < nb_tracefile ; j++) { + tfcs = + LTTV_TRACEFILE_STATE(g_array_index(tc->tracefiles, + LttvTracefileContext*, j)); + tfcs->tracefile_name = ltt_tracefile_name(tfcs->parent.tf); + tfcs->cpu = ltt_tracefile_cpu(tfcs->parent.tf); + tfcs->cpu_state = &(tcs->cpu_states[tfcs->cpu]); + if(ltt_tracefile_tid(tfcs->parent.tf) != 0) { + /* It's a Usertrace */ + guint tid = ltt_tracefile_tid(tfcs->parent.tf); + GTree *usertrace_tree = (GTree*)g_hash_table_lookup(tcs->usertraces, + (gconstpointer)tid); + if(!usertrace_tree) { + usertrace_tree = g_tree_new_full(compare_usertraces, + NULL, free_usertrace_key, NULL); + g_hash_table_insert(tcs->usertraces, + (gpointer)tid, usertrace_tree); + } + LttTime *timestamp = g_new(LttTime, 1); + *timestamp = ltt_interpolate_time_from_tsc(tfcs->parent.tf, + ltt_tracefile_creation(tfcs->parent.tf)); + g_tree_insert(usertrace_tree, timestamp, tfcs); + } + } + + /* See if the trace has saved states */ + state_load_saved_states(tcs); + } +} + +static void +fini(LttvTracesetState *self) +{ + guint i, nb_trace; + + LttvTraceState *tcs; + + //LttvTracefileState *tfcs; + + LttvAttributeValue v; + + nb_trace = lttv_traceset_number(LTTV_TRACESET_CONTEXT(self)->ts); + for(i = 0 ; i < nb_trace ; i++) { + tcs = (LttvTraceState *)(LTTV_TRACESET_CONTEXT(self)->traces[i]); + lttv_attribute_find(tcs->parent.t_a, LTTV_STATE_TRACE_STATE_USE_COUNT, + LTTV_UINT, &v); + + g_assert(*(v.v_uint) != 0); + (*v.v_uint)--; + + if(*(v.v_uint) == 0) { + free_name_tables(tcs); + free_max_time(tcs); + free_saved_state(tcs); + } + g_free(tcs->running_process); + tcs->running_process = NULL; + lttv_state_free_process_table(tcs->processes); + lttv_state_free_usertraces(tcs->usertraces); + tcs->processes = NULL; + tcs->usertraces = NULL; + } + LTTV_TRACESET_CONTEXT_CLASS(g_type_class_peek(LTTV_TRACESET_CONTEXT_TYPE))-> + fini((LttvTracesetContext *)self); +} + + +static LttvTracesetContext * +new_traceset_context(LttvTracesetContext *self) +{ + return LTTV_TRACESET_CONTEXT(g_object_new(LTTV_TRACESET_STATE_TYPE, NULL)); +} + + +static LttvTraceContext * +new_trace_context(LttvTracesetContext *self) +{ + return LTTV_TRACE_CONTEXT(g_object_new(LTTV_TRACE_STATE_TYPE, NULL)); +} + + +static LttvTracefileContext * +new_tracefile_context(LttvTracesetContext *self) +{ + return LTTV_TRACEFILE_CONTEXT(g_object_new(LTTV_TRACEFILE_STATE_TYPE, NULL)); +} + + +/* Write the process state of the trace */ + +static void write_process_state(gpointer key, gpointer value, + gpointer user_data) +{ + LttvProcessState *process; + + LttvExecutionState *es; + + FILE *fp = (FILE *)user_data; + + guint i; + guint64 address; + + process = (LttvProcessState *)value; + fprintf(fp, +" \n", + process, process->pid, process->tgid, process->ppid, + g_quark_to_string(process->type), + process->creation_time.tv_sec, + process->creation_time.tv_nsec, + process->insertion_time.tv_sec, + process->insertion_time.tv_nsec, + g_quark_to_string(process->name), + g_quark_to_string(process->brand), + process->cpu, process->free_events); + + for(i = 0 ; i < process->execution_stack->len; i++) { + es = &g_array_index(process->execution_stack, LttvExecutionState, i); + fprintf(fp, " t), g_quark_to_string(es->n), + es->entry.tv_sec, es->entry.tv_nsec); + fprintf(fp, " CHANGE_S=%lu CHANGE_NS=%lu STATUS=\"%s\"/>\n", + es->change.tv_sec, es->change.tv_nsec, g_quark_to_string(es->s)); + } + + for(i = 0 ; i < process->user_stack->len; i++) { + address = g_array_index(process->user_stack, guint64, i); + fprintf(fp, " \n", + address); + } + + if(process->usertrace) { + fprintf(fp, " ", + g_quark_to_string(process->usertrace->tracefile_name), + process->usertrace->cpu); + } + + + fprintf(fp, " \n"); +} + + +void lttv_state_write(LttvTraceState *self, LttTime t, FILE *fp) +{ + guint i, nb_tracefile, nb_block, offset; + guint64 tsc; + + LttvTracefileState *tfcs; + + LttTracefile *tf; + + LttEventPosition *ep; + + guint nb_cpus; + + ep = ltt_event_position_new(); + + fprintf(fp,"\n", t.tv_sec, t.tv_nsec); + + g_hash_table_foreach(self->processes, write_process_state, fp); + + nb_cpus = ltt_trace_get_num_cpu(self->parent.t); + for(i=0;i\n", + i, self->running_process[i]->pid); + } + + nb_tracefile = self->parent.tracefiles->len; + + for(i = 0 ; i < nb_tracefile ; i++) { + tfcs = + LTTV_TRACEFILE_STATE(g_array_index(self->parent.tracefiles, + LttvTracefileContext*, i)); + fprintf(fp, " parent.timestamp.tv_sec, + tfcs->parent.timestamp.tv_nsec); + LttEvent *e = ltt_tracefile_get_event(tfcs->parent.tf); + if(e == NULL) fprintf(fp,"/>\n"); + else { + ltt_event_position(e, ep); + ltt_event_position_get(ep, &tf, &nb_block, &offset, &tsc); + fprintf(fp, " BLOCK=%u OFFSET=%u TSC=%llu/>\n", nb_block, offset, + tsc); + } + } + g_free(ep); + fprintf(fp,"\n"); +} + + +static void write_process_state_raw(gpointer key, gpointer value, + gpointer user_data) +{ + LttvProcessState *process; + + LttvExecutionState *es; + + FILE *fp = (FILE *)user_data; + + guint i; + guint64 address; + + process = (LttvProcessState *)value; + fputc(HDR_PROCESS, fp); + //fwrite(&header, sizeof(header), 1, fp); + //fprintf(fp, "%s", g_quark_to_string(process->type)); + //fputc('\0', fp); + fwrite(&process->type, sizeof(process->type), 1, fp); + //fprintf(fp, "%s", g_quark_to_string(process->name)); + //fputc('\0', fp); + fwrite(&process->name, sizeof(process->name), 1, fp); + //fprintf(fp, "%s", g_quark_to_string(process->brand)); + //fputc('\0', fp); + fwrite(&process->brand, sizeof(process->brand), 1, fp); + fwrite(&process->pid, sizeof(process->pid), 1, fp); + fwrite(&process->free_events, sizeof(process->free_events), 1, fp); + fwrite(&process->tgid, sizeof(process->tgid), 1, fp); + fwrite(&process->ppid, sizeof(process->ppid), 1, fp); + fwrite(&process->cpu, sizeof(process->cpu), 1, fp); + fwrite(&process->creation_time, sizeof(process->creation_time), 1, fp); + fwrite(&process->insertion_time, sizeof(process->insertion_time), 1, fp); + +#if 0 + fprintf(fp, +" \n", + process, process->pid, process->tgid, process->ppid, + g_quark_to_string(process->type), + process->creation_time.tv_sec, + process->creation_time.tv_nsec, + process->insertion_time.tv_sec, + process->insertion_time.tv_nsec, + g_quark_to_string(process->name), + g_quark_to_string(process->brand), + process->cpu); +#endif //0 + + for(i = 0 ; i < process->execution_stack->len; i++) { + es = &g_array_index(process->execution_stack, LttvExecutionState, i); + + fputc(HDR_ES, fp); + //fprintf(fp, "%s", g_quark_to_string(es->t)); + //fputc('\0', fp); + fwrite(&es->t, sizeof(es->t), 1, fp); + //fprintf(fp, "%s", g_quark_to_string(es->n)); + //fputc('\0', fp); + fwrite(&es->n, sizeof(es->n), 1, fp); + //fprintf(fp, "%s", g_quark_to_string(es->s)); + //fputc('\0', fp); + fwrite(&es->s, sizeof(es->s), 1, fp); + fwrite(&es->entry, sizeof(es->entry), 1, fp); + fwrite(&es->change, sizeof(es->change), 1, fp); + fwrite(&es->cum_cpu_time, sizeof(es->cum_cpu_time), 1, fp); +#if 0 + fprintf(fp, " t), g_quark_to_string(es->n), + es->entry.tv_sec, es->entry.tv_nsec); + fprintf(fp, " CHANGE_S=%lu CHANGE_NS=%lu STATUS=\"%s\"/>\n", + es->change.tv_sec, es->change.tv_nsec, g_quark_to_string(es->s)); +#endif //0 + } + + for(i = 0 ; i < process->user_stack->len; i++) { + address = g_array_index(process->user_stack, guint64, i); + fputc(HDR_USER_STACK, fp); + fwrite(&address, sizeof(address), 1, fp); +#if 0 + fprintf(fp, " \n", + address); +#endif //0 + } + + if(process->usertrace) { + fputc(HDR_USERTRACE, fp); + //fprintf(fp, "%s", g_quark_to_string(process->usertrace->tracefile_name)); + //fputc('\0', fp); + fwrite(&process->usertrace->tracefile_name, + sizeof(process->usertrace->tracefile_name), 1, fp); + fwrite(&process->usertrace->cpu, sizeof(process->usertrace->cpu), 1, fp); +#if 0 + fprintf(fp, " ", + g_quark_to_string(process->usertrace->tracefile_name), + process->usertrace->cpu); +#endif //0 + } + +} + + +void lttv_state_write_raw(LttvTraceState *self, LttTime t, FILE *fp) +{ + guint i, nb_tracefile, nb_block, offset; + guint64 tsc; + + LttvTracefileState *tfcs; + + LttTracefile *tf; + + LttEventPosition *ep; + + guint nb_cpus; + + ep = ltt_event_position_new(); + + //fprintf(fp,"\n", t.tv_sec, t.tv_nsec); + fputc(HDR_PROCESS_STATE, fp); + fwrite(&t, sizeof(t), 1, fp); + + g_hash_table_foreach(self->processes, write_process_state_raw, fp); + + nb_cpus = ltt_trace_get_num_cpu(self->parent.t); + for(i=0;irunning_process[i]->pid, + sizeof(self->running_process[i]->pid), 1, fp); + //fprintf(fp," \n", + // i, self->running_process[i]->pid); + } + + nb_tracefile = self->parent.tracefiles->len; + + for(i = 0 ; i < nb_tracefile ; i++) { + tfcs = + LTTV_TRACEFILE_STATE(g_array_index(self->parent.tracefiles, + LttvTracefileContext*, i)); + // fprintf(fp, " parent.timestamp.tv_sec, + // tfcs->parent.timestamp.tv_nsec); + fputc(HDR_TRACEFILE, fp); + fwrite(&tfcs->parent.timestamp, sizeof(tfcs->parent.timestamp), 1, fp); + /* Note : if timestamp if LTT_TIME_INFINITE, there will be no + * position following : end of trace */ + LttEvent *e = ltt_tracefile_get_event(tfcs->parent.tf); + if(e != NULL) { + ltt_event_position(e, ep); + ltt_event_position_get(ep, &tf, &nb_block, &offset, &tsc); + //fprintf(fp, " BLOCK=%u OFFSET=%u TSC=%llu/>\n", nb_block, offset, + // tsc); + fwrite(&nb_block, sizeof(nb_block), 1, fp); + fwrite(&offset, sizeof(offset), 1, fp); + fwrite(&tsc, sizeof(tsc), 1, fp); + } + } + g_free(ep); +} + + +/* Read process state from a file */ + +/* Called because a HDR_PROCESS was found */ +static void read_process_state_raw(LttvTraceState *self, FILE *fp, + GPtrArray *quarktable) +{ + LttvExecutionState *es; + LttvProcessState *process, *parent_process; + LttvProcessState tmp; + GQuark tmpq; + + guint64 *address; + + /* TODO : check return value */ + fread(&tmp.type, sizeof(tmp.type), 1, fp); + fread(&tmp.name, sizeof(tmp.name), 1, fp); + fread(&tmp.brand, sizeof(tmp.brand), 1, fp); + fread(&tmp.pid, sizeof(tmp.pid), 1, fp); + fread(&tmp.free_events, sizeof(tmp.free_events), 1, fp); + fread(&tmp.tgid, sizeof(tmp.tgid), 1, fp); + fread(&tmp.ppid, sizeof(tmp.ppid), 1, fp); + fread(&tmp.cpu, sizeof(tmp.cpu), 1, fp); + fread(&tmp.creation_time, sizeof(tmp.creation_time), 1, fp); + fread(&tmp.insertion_time, sizeof(tmp.insertion_time), 1, fp); + + if(tmp.pid == 0) { + process = lttv_state_find_process(self, tmp.cpu, tmp.pid); + } else { + /* We must link to the parent */ + parent_process = lttv_state_find_process_or_create(self, ANY_CPU, tmp.ppid, + <t_time_zero); + process = lttv_state_find_process(self, ANY_CPU, tmp.pid); + if(process == NULL) { + process = lttv_state_create_process(self, parent_process, tmp.cpu, + tmp.pid, tmp.tgid, + g_quark_from_string((gchar*)g_ptr_array_index(quarktable, tmp.name)), + &tmp.creation_time); + } + } + process->insertion_time = tmp.insertion_time; + process->creation_time = tmp.creation_time; + process->type = g_quark_from_string( + (gchar*)g_ptr_array_index(quarktable, tmp.type)); + process->tgid = tmp.tgid; + process->ppid = tmp.ppid; + process->brand = g_quark_from_string( + (gchar*)g_ptr_array_index(quarktable, tmp.brand)); + process->name = + g_quark_from_string((gchar*)g_ptr_array_index(quarktable, tmp.name)); + process->free_events = tmp.free_events; + + do { + if(feof(fp) || ferror(fp)) goto end_loop; + + gint hdr = fgetc(fp); + if(hdr == EOF) goto end_loop; + + switch(hdr) { + case HDR_ES: + process->execution_stack = + g_array_set_size(process->execution_stack, + process->execution_stack->len + 1); + es = &g_array_index(process->execution_stack, LttvExecutionState, + process->execution_stack->len-1); + process->state = es; + + fread(&es->t, sizeof(es->t), 1, fp); + es->t = g_quark_from_string( + (gchar*)g_ptr_array_index(quarktable, es->t)); + fread(&es->n, sizeof(es->n), 1, fp); + es->n = g_quark_from_string( + (gchar*)g_ptr_array_index(quarktable, es->n)); + fread(&es->s, sizeof(es->s), 1, fp); + es->s = g_quark_from_string( + (gchar*)g_ptr_array_index(quarktable, es->s)); + fread(&es->entry, sizeof(es->entry), 1, fp); + fread(&es->change, sizeof(es->change), 1, fp); + fread(&es->cum_cpu_time, sizeof(es->cum_cpu_time), 1, fp); + break; + case HDR_USER_STACK: + process->user_stack = g_array_set_size(process->user_stack, + process->user_stack->len + 1); + address = &g_array_index(process->user_stack, guint64, + process->user_stack->len-1); + fread(address, sizeof(address), 1, fp); + process->current_function = *address; + break; + case HDR_USERTRACE: + fread(&tmpq, sizeof(tmpq), 1, fp); + fread(&process->usertrace->cpu, sizeof(process->usertrace->cpu), 1, fp); + break; + default: + ungetc(hdr, fp); + goto end_loop; + }; + } while(1); +end_loop: + return; +} + + +/* Called because a HDR_PROCESS_STATE was found */ +/* Append a saved state to the trace states */ +void lttv_state_read_raw(LttvTraceState *self, FILE *fp, GPtrArray *quarktable) +{ + guint i, nb_tracefile, nb_block, offset; + guint64 tsc; + LttvTracefileState *tfcs; + + LttEventPosition *ep; + + guint nb_cpus; + + int hdr; + + LttTime t; + + LttvAttribute *saved_states_tree, *saved_state_tree; + + LttvAttributeValue value; + GTree *pqueue = self->parent.ts_context->pqueue; + ep = ltt_event_position_new(); + + restore_init_state(self); + + fread(&t, sizeof(t), 1, fp); + + do { + if(feof(fp) || ferror(fp)) goto end_loop; + hdr = fgetc(fp); + if(hdr == EOF) goto end_loop; + + switch(hdr) { + case HDR_PROCESS: + /* Call read_process_state_raw */ + read_process_state_raw(self, fp, quarktable); + break; + case HDR_TRACEFILE: + case HDR_TRACESET: + case HDR_TRACE: + case HDR_QUARKS: + case HDR_QUARK: + case HDR_ES: + case HDR_USER_STACK: + case HDR_USERTRACE: + case HDR_PROCESS_STATE: + case HDR_CPU: + ungetc(hdr, fp); + goto end_loop; + break; + default: + g_error("Error while parsing saved state file : unknown data header %d", + hdr); + }; + } while(1); +end_loop: + + nb_cpus = ltt_trace_get_num_cpu(self->parent.t); + for(i=0;irunning_process[i]->pid, + sizeof(self->running_process[i]->pid), 1, fp); + } + + nb_tracefile = self->parent.tracefiles->len; + + for(i = 0 ; i < nb_tracefile ; i++) { + tfcs = + LTTV_TRACEFILE_STATE(g_array_index(self->parent.tracefiles, + LttvTracefileContext*, i)); + // fprintf(fp, " parent.timestamp.tv_sec, + // tfcs->parent.timestamp.tv_nsec); + g_tree_remove(pqueue, &tfcs->parent); + hdr = fgetc(fp); + g_assert(hdr == HDR_TRACEFILE); + fread(&tfcs->parent.timestamp, sizeof(tfcs->parent.timestamp), 1, fp); + /* Note : if timestamp if LTT_TIME_INFINITE, there will be no + * position following : end of trace */ + if(ltt_time_compare(tfcs->parent.timestamp, ltt_time_infinite) != 0) { + fread(&nb_block, sizeof(nb_block), 1, fp); + fread(&offset, sizeof(offset), 1, fp); + fread(&tsc, sizeof(tsc), 1, fp); + ltt_event_position_set(ep, tfcs->parent.tf, nb_block, offset, tsc); + gint ret = ltt_tracefile_seek_position(tfcs->parent.tf, ep); + g_assert(ret == 0); + g_tree_insert(pqueue, &tfcs->parent, &tfcs->parent); + } + } + g_free(ep); + + saved_states_tree = lttv_attribute_find_subdir(self->parent.t_a, + LTTV_STATE_SAVED_STATES); + saved_state_tree = g_object_new(LTTV_ATTRIBUTE_TYPE, NULL); + value = lttv_attribute_add(saved_states_tree, + lttv_attribute_get_number(saved_states_tree), LTTV_GOBJECT); + *(value.v_gobject) = (GObject *)saved_state_tree; + value = lttv_attribute_add(saved_state_tree, LTTV_STATE_TIME, LTTV_TIME); + *(value.v_time) = t; + lttv_state_save(self, saved_state_tree); + g_debug("Saving state at time %lu.%lu", t.tv_sec, + t.tv_nsec); + + *(self->max_time_state_recomputed_in_seek) = t; + +} + +/* Called when a HDR_TRACE is found */ +void lttv_trace_states_read_raw(LttvTraceState *tcs, FILE *fp, + GPtrArray *quarktable) +{ + int hdr; + + do { + if(feof(fp) || ferror(fp)) goto end_loop; + hdr = fgetc(fp); + if(hdr == EOF) goto end_loop; + + switch(hdr) { + case HDR_PROCESS_STATE: + /* Call read_process_state_raw */ + lttv_state_read_raw(tcs, fp, quarktable); + break; + case HDR_TRACEFILE: + case HDR_TRACESET: + case HDR_TRACE: + case HDR_QUARKS: + case HDR_QUARK: + case HDR_ES: + case HDR_USER_STACK: + case HDR_USERTRACE: + case HDR_PROCESS: + case HDR_CPU: + g_error("Error while parsing saved state file :" + " unexpected data header %d", + hdr); + break; + default: + g_error("Error while parsing saved state file : unknown data header %d", + hdr); + }; + } while(1); +end_loop: + *(tcs->max_time_state_recomputed_in_seek) = tcs->parent.time_span.end_time; + restore_init_state(tcs); + lttv_process_trace_seek_time(&tcs->parent, ltt_time_zero); + return; +} + + + +/* Copy each process from an existing hash table to a new one */ + +static void copy_process_state(gpointer key, gpointer value,gpointer user_data) +{ + LttvProcessState *process, *new_process; + + GHashTable *new_processes = (GHashTable *)user_data; + + guint i; + + process = (LttvProcessState *)value; + new_process = g_new(LttvProcessState, 1); + *new_process = *process; + new_process->execution_stack = g_array_sized_new(FALSE, FALSE, + sizeof(LttvExecutionState), PREALLOCATED_EXECUTION_STACK); + new_process->execution_stack = + g_array_set_size(new_process->execution_stack, + process->execution_stack->len); + for(i = 0 ; i < process->execution_stack->len; i++) { + g_array_index(new_process->execution_stack, LttvExecutionState, i) = + g_array_index(process->execution_stack, LttvExecutionState, i); + } + new_process->state = &g_array_index(new_process->execution_stack, + LttvExecutionState, new_process->execution_stack->len - 1); + new_process->user_stack = g_array_sized_new(FALSE, FALSE, + sizeof(guint64), 0); + new_process->user_stack = + g_array_set_size(new_process->user_stack, + process->user_stack->len); + for(i = 0 ; i < process->user_stack->len; i++) { + g_array_index(new_process->user_stack, guint64, i) = + g_array_index(process->user_stack, guint64, i); + } + new_process->current_function = process->current_function; + g_hash_table_insert(new_processes, new_process, new_process); +} + + +static GHashTable *lttv_state_copy_process_table(GHashTable *processes) +{ + GHashTable *new_processes = g_hash_table_new(process_hash, process_equal); + + g_hash_table_foreach(processes, copy_process_state, new_processes); + return new_processes; +} + +static LttvCPUState *lttv_state_copy_cpu_states(LttvCPUState *states, guint n) +{ + guint i,j; + LttvCPUState *retval; + + retval = g_new(LttvCPUState, n); + + for(i=0; ilen); + for(j=0; jlen; j++) { + g_array_index(retval[i].mode_stack, GQuark, j) = g_array_index(states[i].mode_stack, GQuark, j); + } + } + + return retval; +} + +static void lttv_state_free_cpu_states(LttvCPUState *states, guint n) +{ + guint i; + + for(i=0; ilen); + for(j=0; jlen; j++) { + g_array_index(retval[i].mode_stack, GQuark, j) = g_array_index(states[i].mode_stack, GQuark, j); + } + } + + return retval; +} + +static void lttv_state_free_irq_states(LttvIRQState *states, guint n) +{ + guint i; + + for(i=0; ibdev_states, &devcode_gint); + if(bdev == NULL) { + LttvBdevState *bdevstate = g_new(LttvBdevState, 1); + bdevstate->mode_stack = g_array_new(FALSE, FALSE, sizeof(GQuark)); + + gint * key = g_new(gint, 1); + *key = devcode; + g_hash_table_insert(ts->bdev_states, key, bdevstate); + + bdev = bdevstate; + } + + return bdev; +} + +static LttvBdevState *bdevstate_new(void) +{ + LttvBdevState *retval; + retval = g_new(LttvBdevState, 1); + retval->mode_stack = g_array_new(FALSE, FALSE, sizeof(GQuark)); + + return retval; +} + +static void bdevstate_free(LttvBdevState *bds) +{ + g_array_free(bds->mode_stack, TRUE); + g_free(bds); +} + +static void bdevstate_free_cb(gpointer key, gpointer value, gpointer user_data) +{ + LttvBdevState *bds = (LttvBdevState *) value; + + bdevstate_free(bds); +} + +static LttvBdevState *bdevstate_copy(LttvBdevState *bds) +{ + LttvBdevState *retval; + + retval = bdevstate_new(); + g_array_insert_vals(retval->mode_stack, 0, bds->mode_stack->data, bds->mode_stack->len); + + return retval; +} + +static void insert_and_copy_bdev_state(gpointer k, gpointer v, gpointer u) +{ + //GHashTable *ht = (GHashTable *)u; + LttvBdevState *bds = (LttvBdevState *)v; + LttvBdevState *newbds; + + newbds = bdevstate_copy(bds); + + g_hash_table_insert(u, k, newbds); +} + +static GHashTable *lttv_state_copy_blkdev_hashtable(GHashTable *ht) +{ + GHashTable *retval; + + retval = g_hash_table_new(g_int_hash, g_int_equal); + + g_hash_table_foreach(ht, insert_and_copy_bdev_state, retval); + + return retval; +} + +/* Free a hashtable and the LttvBdevState structures its values + * point to. */ + +static void lttv_state_free_blkdev_hashtable(GHashTable *ht) +{ + g_hash_table_foreach(ht, bdevstate_free_cb, NULL); + g_hash_table_destroy(ht); +} + +/* The saved state for each trace contains a member "processes", which + stores a copy of the process table, and a member "tracefiles" with + one entry per tracefile. Each tracefile has a "process" member pointing + to the current process and a "position" member storing the tracefile + position (needed to seek to the current "next" event. */ + +static void state_save(LttvTraceState *self, LttvAttribute *container) +{ + guint i, nb_tracefile, nb_cpus, nb_irqs, nb_soft_irqs, nb_traps; + + LttvTracefileState *tfcs; + + LttvAttribute *tracefiles_tree, *tracefile_tree; + + guint *running_process; + + LttvAttributeValue value; + + LttEventPosition *ep; + + tracefiles_tree = lttv_attribute_find_subdir(container, + LTTV_STATE_TRACEFILES); + + value = lttv_attribute_add(container, LTTV_STATE_PROCESSES, + LTTV_POINTER); + *(value.v_pointer) = lttv_state_copy_process_table(self->processes); + + /* Add the currently running processes array */ + nb_cpus = ltt_trace_get_num_cpu(self->parent.t); + running_process = g_new(guint, nb_cpus); + for(i=0;irunning_process[i]->pid; + } + value = lttv_attribute_add(container, LTTV_STATE_RUNNING_PROCESS, + LTTV_POINTER); + *(value.v_pointer) = running_process; + + g_info("State save"); + + nb_tracefile = self->parent.tracefiles->len; + + for(i = 0 ; i < nb_tracefile ; i++) { + tfcs = + LTTV_TRACEFILE_STATE(g_array_index(self->parent.tracefiles, + LttvTracefileContext*, i)); + tracefile_tree = g_object_new(LTTV_ATTRIBUTE_TYPE, NULL); + value = lttv_attribute_add(tracefiles_tree, i, + LTTV_GOBJECT); + *(value.v_gobject) = (GObject *)tracefile_tree; +#if 0 + value = lttv_attribute_add(tracefile_tree, LTTV_STATE_PROCESS, + LTTV_UINT); + *(value.v_uint) = tfcs->process->pid; +#endif //0 + value = lttv_attribute_add(tracefile_tree, LTTV_STATE_EVENT, + LTTV_POINTER); + /* Only save the position if the tfs has not infinite time. */ + //if(!g_tree_lookup(self->parent.ts_context->pqueue, &tfcs->parent) + // && current_tfcs != tfcs) { + if(ltt_time_compare(tfcs->parent.timestamp, ltt_time_infinite) == 0) { + *(value.v_pointer) = NULL; + } else { + LttEvent *e = ltt_tracefile_get_event(tfcs->parent.tf); + ep = ltt_event_position_new(); + ltt_event_position(e, ep); + *(value.v_pointer) = ep; + + guint nb_block, offset; + guint64 tsc; + LttTracefile *tf; + ltt_event_position_get(ep, &tf, &nb_block, &offset, &tsc); + g_info("Block %u offset %u tsc %llu time %lu.%lu", nb_block, offset, + tsc, + tfcs->parent.timestamp.tv_sec, tfcs->parent.timestamp.tv_nsec); + } + } + + /* save the cpu state */ + { + value = lttv_attribute_add(container, LTTV_STATE_RESOURCE_CPUS_COUNT, + LTTV_UINT); + *(value.v_uint) = nb_cpus; + + value = lttv_attribute_add(container, LTTV_STATE_RESOURCE_CPUS, + LTTV_POINTER); + *(value.v_pointer) = lttv_state_copy_cpu_states(self->cpu_states, nb_cpus); + } + + /* save the irq state */ + nb_irqs = self->nb_irqs; + { + value = lttv_attribute_add(container, LTTV_STATE_RESOURCE_IRQS, + LTTV_POINTER); + *(value.v_pointer) = lttv_state_copy_irq_states(self->irq_states, nb_irqs); + } + + /* save the soft irq state */ + nb_soft_irqs = self->nb_soft_irqs; + { + value = lttv_attribute_add(container, LTTV_STATE_RESOURCE_SOFT_IRQS, + LTTV_POINTER); + *(value.v_pointer) = lttv_state_copy_soft_irq_states(self->soft_irq_states, nb_soft_irqs); + } + + /* save the trap state */ + nb_traps = self->nb_traps; + { + value = lttv_attribute_add(container, LTTV_STATE_RESOURCE_TRAPS, + LTTV_POINTER); + *(value.v_pointer) = lttv_state_copy_trap_states(self->trap_states, nb_traps); + } + + /* save the blkdev states */ + value = lttv_attribute_add(container, LTTV_STATE_RESOURCE_BLKDEVS, + LTTV_POINTER); + *(value.v_pointer) = lttv_state_copy_blkdev_hashtable(self->bdev_states); +} + + +static void state_restore(LttvTraceState *self, LttvAttribute *container) +{ + guint i, nb_tracefile, pid, nb_cpus, nb_irqs, nb_soft_irqs, nb_traps; + + LttvTracefileState *tfcs; + + LttvAttribute *tracefiles_tree, *tracefile_tree; + + guint *running_process; + + LttvAttributeType type; + + LttvAttributeValue value; + + LttvAttributeName name; + + gboolean is_named; + + LttEventPosition *ep; + + LttvTracesetContext *tsc = self->parent.ts_context; + + tracefiles_tree = lttv_attribute_find_subdir(container, + LTTV_STATE_TRACEFILES); + + type = lttv_attribute_get_by_name(container, LTTV_STATE_PROCESSES, + &value); + g_assert(type == LTTV_POINTER); + lttv_state_free_process_table(self->processes); + self->processes = lttv_state_copy_process_table(*(value.v_pointer)); + + /* Add the currently running processes array */ + nb_cpus = ltt_trace_get_num_cpu(self->parent.t); + type = lttv_attribute_get_by_name(container, LTTV_STATE_RUNNING_PROCESS, + &value); + g_assert(type == LTTV_POINTER); + running_process = *(value.v_pointer); + for(i=0;irunning_process[i] = lttv_state_find_process(self, i, pid); + g_assert(self->running_process[i] != NULL); + } + + nb_tracefile = self->parent.tracefiles->len; + + //g_tree_destroy(tsc->pqueue); + //tsc->pqueue = g_tree_new(compare_tracefile); + + /* restore cpu resource states */ + type = lttv_attribute_get_by_name(container, LTTV_STATE_RESOURCE_CPUS, &value); + g_assert(type == LTTV_POINTER); + lttv_state_free_cpu_states(self->cpu_states, nb_cpus); + self->cpu_states = lttv_state_copy_cpu_states(*(value.v_pointer), nb_cpus); + + /* restore irq resource states */ + nb_irqs = self->nb_irqs; + type = lttv_attribute_get_by_name(container, LTTV_STATE_RESOURCE_IRQS, &value); + g_assert(type == LTTV_POINTER); + lttv_state_free_irq_states(self->irq_states, nb_irqs); + self->irq_states = lttv_state_copy_irq_states(*(value.v_pointer), nb_irqs); + + /* restore soft irq resource states */ + nb_soft_irqs = self->nb_soft_irqs; + type = lttv_attribute_get_by_name(container, LTTV_STATE_RESOURCE_SOFT_IRQS, &value); + g_assert(type == LTTV_POINTER); + lttv_state_free_soft_irq_states(self->soft_irq_states, nb_soft_irqs); + self->soft_irq_states = lttv_state_copy_soft_irq_states(*(value.v_pointer), nb_soft_irqs); + + /* restore trap resource states */ + nb_traps = self->nb_traps; + type = lttv_attribute_get_by_name(container, LTTV_STATE_RESOURCE_TRAPS, &value); + g_assert(type == LTTV_POINTER); + lttv_state_free_trap_states(self->trap_states, nb_traps); + self->trap_states = lttv_state_copy_trap_states(*(value.v_pointer), nb_traps); + + /* restore the blkdev states */ + type = lttv_attribute_get_by_name(container, LTTV_STATE_RESOURCE_BLKDEVS, &value); + g_assert(type == LTTV_POINTER); + lttv_state_free_blkdev_hashtable(self->bdev_states); + self->bdev_states = lttv_state_copy_blkdev_hashtable(*(value.v_pointer)); + + for(i = 0 ; i < nb_tracefile ; i++) { + tfcs = + LTTV_TRACEFILE_STATE(g_array_index(self->parent.tracefiles, + LttvTracefileContext*, i)); + type = lttv_attribute_get(tracefiles_tree, i, &name, &value, &is_named); + g_assert(type == LTTV_GOBJECT); + tracefile_tree = *((LttvAttribute **)(value.v_gobject)); +#if 0 + type = lttv_attribute_get_by_name(tracefile_tree, LTTV_STATE_PROCESS, + &value); + g_assert(type == LTTV_UINT); + pid = *(value.v_uint); + tfcs->process = lttv_state_find_process_or_create(tfcs, pid); +#endif //0 + type = lttv_attribute_get_by_name(tracefile_tree, LTTV_STATE_EVENT, + &value); + g_assert(type == LTTV_POINTER); + //g_assert(*(value.v_pointer) != NULL); + ep = *(value.v_pointer); + g_assert(tfcs->parent.t_context != NULL); + + tfcs->cpu_state = &self->cpu_states[tfcs->cpu]; + + LttvTracefileContext *tfc = LTTV_TRACEFILE_CONTEXT(tfcs); + g_tree_remove(tsc->pqueue, tfc); + + if(ep != NULL) { + g_assert(ltt_tracefile_seek_position(tfc->tf, ep) == 0); + tfc->timestamp = ltt_event_time(ltt_tracefile_get_event(tfc->tf)); + g_assert(ltt_time_compare(tfc->timestamp, ltt_time_infinite) != 0); + g_tree_insert(tsc->pqueue, tfc, tfc); + g_info("Restoring state for a tf at time %lu.%lu", tfc->timestamp.tv_sec, tfc->timestamp.tv_nsec); + } else { + tfc->timestamp = ltt_time_infinite; + } + } +} + + +static void state_saved_free(LttvTraceState *self, LttvAttribute *container) +{ + guint i, nb_tracefile, nb_cpus, nb_irqs, nb_softirqs; + + LttvTracefileState *tfcs; + + LttvAttribute *tracefiles_tree, *tracefile_tree; + + guint *running_process; + + LttvAttributeType type; + + LttvAttributeValue value; + + LttvAttributeName name; + + gboolean is_named; + + tracefiles_tree = lttv_attribute_find_subdir(container, + LTTV_STATE_TRACEFILES); + g_object_ref(G_OBJECT(tracefiles_tree)); + lttv_attribute_remove_by_name(container, LTTV_STATE_TRACEFILES); + + type = lttv_attribute_get_by_name(container, LTTV_STATE_PROCESSES, + &value); + g_assert(type == LTTV_POINTER); + lttv_state_free_process_table(*(value.v_pointer)); + *(value.v_pointer) = NULL; + lttv_attribute_remove_by_name(container, LTTV_STATE_PROCESSES); + + /* Free running processes array */ + type = lttv_attribute_get_by_name(container, LTTV_STATE_RUNNING_PROCESS, + &value); + g_assert(type == LTTV_POINTER); + running_process = *(value.v_pointer); + g_free(running_process); + + /* free cpu resource states */ + type = lttv_attribute_get_by_name(container, LTTV_STATE_RESOURCE_CPUS_COUNT, &value); + g_assert(type == LTTV_UINT); + nb_cpus = *value.v_uint; + type = lttv_attribute_get_by_name(container, LTTV_STATE_RESOURCE_CPUS, &value); + g_assert(type == LTTV_POINTER); + lttv_state_free_cpu_states(*(value.v_pointer), nb_cpus); + + /* free irq resource states */ + nb_irqs = self->nb_irqs; + type = lttv_attribute_get_by_name(container, LTTV_STATE_RESOURCE_IRQS, &value); + g_assert(type == LTTV_POINTER); + lttv_state_free_irq_states(*(value.v_pointer), nb_irqs); + + /* free softirq resource states */ + nb_softirqs = self->nb_irqs; + type = lttv_attribute_get_by_name(container, LTTV_STATE_RESOURCE_SOFT_IRQS, &value); + g_assert(type == LTTV_POINTER); + lttv_state_free_soft_irq_states(*(value.v_pointer), nb_softirqs); + + /* free the blkdev states */ + type = lttv_attribute_get_by_name(container, LTTV_STATE_RESOURCE_BLKDEVS, &value); + g_assert(type == LTTV_POINTER); + lttv_state_free_blkdev_hashtable(*(value.v_pointer)); + + nb_tracefile = self->parent.tracefiles->len; + + for(i = 0 ; i < nb_tracefile ; i++) { + tfcs = + LTTV_TRACEFILE_STATE(g_array_index(self->parent.tracefiles, + LttvTracefileContext*, i)); + type = lttv_attribute_get(tracefiles_tree, i, &name, &value, &is_named); + g_assert(type == LTTV_GOBJECT); + tracefile_tree = *((LttvAttribute **)(value.v_gobject)); + + type = lttv_attribute_get_by_name(tracefile_tree, LTTV_STATE_EVENT, + &value); + g_assert(type == LTTV_POINTER); + if(*(value.v_pointer) != NULL) g_free(*(value.v_pointer)); + } + g_object_unref(G_OBJECT(tracefiles_tree)); +} + + +static void free_saved_state(LttvTraceState *self) +{ + guint i, nb; + + LttvAttributeType type; + + LttvAttributeValue value; + + LttvAttributeName name; + + gboolean is_named; + + LttvAttribute *saved_states; + + saved_states = lttv_attribute_find_subdir(self->parent.t_a, + LTTV_STATE_SAVED_STATES); + + nb = lttv_attribute_get_number(saved_states); + for(i = 0 ; i < nb ; i++) { + type = lttv_attribute_get(saved_states, i, &name, &value, &is_named); + g_assert(type == LTTV_GOBJECT); + state_saved_free(self, *((LttvAttribute **)value.v_gobject)); + } + + lttv_attribute_remove_by_name(self->parent.t_a, LTTV_STATE_SAVED_STATES); +} + + +static void +create_max_time(LttvTraceState *tcs) +{ + LttvAttributeValue v; + + lttv_attribute_find(tcs->parent.t_a, LTTV_STATE_SAVED_STATES_TIME, + LTTV_POINTER, &v); + g_assert(*(v.v_pointer) == NULL); + *(v.v_pointer) = g_new(LttTime,1); + *((LttTime *)*(v.v_pointer)) = ltt_time_zero; +} + + +static void +get_max_time(LttvTraceState *tcs) +{ + LttvAttributeValue v; + + lttv_attribute_find(tcs->parent.t_a, LTTV_STATE_SAVED_STATES_TIME, + LTTV_POINTER, &v); + g_assert(*(v.v_pointer) != NULL); + tcs->max_time_state_recomputed_in_seek = (LttTime *)*(v.v_pointer); +} + + +static void +free_max_time(LttvTraceState *tcs) +{ + LttvAttributeValue v; + + lttv_attribute_find(tcs->parent.t_a, LTTV_STATE_SAVED_STATES_TIME, + LTTV_POINTER, &v); + g_free(*(v.v_pointer)); + *(v.v_pointer) = NULL; +} + + +typedef struct _LttvNameTables { + // FIXME GQuark *eventtype_names; + GQuark *syscall_names; + guint nb_syscalls; + GQuark *trap_names; + guint nb_traps; + GQuark *irq_names; + guint nb_irqs; + GQuark *soft_irq_names; + guint nb_softirqs; +} LttvNameTables; + + +static void +create_name_tables(LttvTraceState *tcs) +{ + int i; + + GString *fe_name = g_string_new(""); + + LttvNameTables *name_tables = g_new(LttvNameTables, 1); + + LttvAttributeValue v; + + GArray *hooks; + + lttv_attribute_find(tcs->parent.t_a, LTTV_STATE_NAME_TABLES, + LTTV_POINTER, &v); + g_assert(*(v.v_pointer) == NULL); + *(v.v_pointer) = name_tables; + + hooks = g_array_sized_new(FALSE, FALSE, sizeof(LttvTraceHook), 1); + + if(!lttv_trace_find_hook(tcs->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SYSCALL_ENTRY, + FIELD_ARRAY(LTT_FIELD_SYSCALL_ID), + NULL, NULL, &hooks)) { + +// th = lttv_trace_hook_get_first(&th); +// +// t = ltt_field_type(lttv_trace_get_hook_field(th, 0)); +// nb = ltt_type_element_number(t); +// +// name_tables->syscall_names = g_new(GQuark, nb); +// name_tables->nb_syscalls = nb; +// +// for(i = 0 ; i < nb ; i++) { +// name_tables->syscall_names[i] = ltt_enum_string_get(t, i); +// if(!name_tables->syscall_names[i]) { +// GString *string = g_string_new(""); +// g_string_printf(string, "syscall %u", i); +// name_tables->syscall_names[i] = g_quark_from_string(string->str); +// g_string_free(string, TRUE); +// } +// } + + name_tables->nb_syscalls = 256; + name_tables->syscall_names = g_new(GQuark, 256); + for(i = 0 ; i < 256 ; i++) { + g_string_printf(fe_name, "syscall %d", i); + name_tables->syscall_names[i] = g_quark_from_string(fe_name->str); + } + } else { + name_tables->syscall_names = NULL; + name_tables->nb_syscalls = 0; + } + lttv_trace_hook_remove_all(&hooks); + + if(!lttv_trace_find_hook(tcs->parent.t, + LTT_FACILITY_KERNEL_ARCH, + LTT_EVENT_TRAP_ENTRY, + FIELD_ARRAY(LTT_FIELD_TRAP_ID), + NULL, NULL, &hooks)) { + +// th = lttv_trace_hook_get_first(&th); +// +// t = ltt_field_type(lttv_trace_get_hook_field(th, 0)); +// //nb = ltt_type_element_number(t); +// +// name_tables->trap_names = g_new(GQuark, nb); +// for(i = 0 ; i < nb ; i++) { +// name_tables->trap_names[i] = g_quark_from_string( +// ltt_enum_string_get(t, i)); +// } + + name_tables->nb_traps = 256; + name_tables->trap_names = g_new(GQuark, 256); + for(i = 0 ; i < 256 ; i++) { + g_string_printf(fe_name, "trap %d", i); + name_tables->trap_names[i] = g_quark_from_string(fe_name->str); + } + } else { + name_tables->trap_names = NULL; + name_tables->nb_traps = 0; + } + lttv_trace_hook_remove_all(&hooks); + + if(!lttv_trace_find_hook(tcs->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_IRQ_ENTRY, + FIELD_ARRAY(LTT_FIELD_IRQ_ID), + NULL, NULL, &hooks)) { + + /* + name_tables->irq_names = g_new(GQuark, nb); + for(i = 0 ; i < nb ; i++) { + name_tables->irq_names[i] = g_quark_from_string(ltt_enum_string_get(t, i)); + } + */ + + name_tables->nb_irqs = 256; + name_tables->irq_names = g_new(GQuark, 256); + for(i = 0 ; i < 256 ; i++) { + g_string_printf(fe_name, "irq %d", i); + name_tables->irq_names[i] = g_quark_from_string(fe_name->str); + } + } else { + name_tables->nb_irqs = 0; + name_tables->irq_names = NULL; + } + lttv_trace_hook_remove_all(&hooks); + /* + name_tables->soft_irq_names = g_new(GQuark, nb); + for(i = 0 ; i < nb ; i++) { + name_tables->soft_irq_names[i] = g_quark_from_string(ltt_enum_string_get(t, i)); + } + */ + + /* the kernel is limited to 32 statically defined softirqs */ + name_tables->nb_softirqs = 32; + name_tables->soft_irq_names = g_new(GQuark, name_tables->nb_softirqs); + for(i = 0 ; i < name_tables->nb_softirqs ; i++) { + g_string_printf(fe_name, "softirq %d", i); + name_tables->soft_irq_names[i] = g_quark_from_string(fe_name->str); + } + g_array_free(hooks, TRUE); + + g_string_free(fe_name, TRUE); +} + + +static void +get_name_tables(LttvTraceState *tcs) +{ + LttvNameTables *name_tables; + + LttvAttributeValue v; + + lttv_attribute_find(tcs->parent.t_a, LTTV_STATE_NAME_TABLES, + LTTV_POINTER, &v); + g_assert(*(v.v_pointer) != NULL); + name_tables = (LttvNameTables *)*(v.v_pointer); + //tcs->eventtype_names = name_tables->eventtype_names; + tcs->syscall_names = name_tables->syscall_names; + tcs->nb_syscalls = name_tables->nb_syscalls; + tcs->trap_names = name_tables->trap_names; + tcs->nb_traps = name_tables->nb_traps; + tcs->irq_names = name_tables->irq_names; + tcs->soft_irq_names = name_tables->soft_irq_names; + tcs->nb_irqs = name_tables->nb_irqs; + tcs->nb_soft_irqs = name_tables->nb_softirqs; +} + + +static void +free_name_tables(LttvTraceState *tcs) +{ + LttvNameTables *name_tables; + + LttvAttributeValue v; + + lttv_attribute_find(tcs->parent.t_a, LTTV_STATE_NAME_TABLES, + LTTV_POINTER, &v); + name_tables = (LttvNameTables *)*(v.v_pointer); + *(v.v_pointer) = NULL; + + // g_free(name_tables->eventtype_names); + if(name_tables->syscall_names) g_free(name_tables->syscall_names); + if(name_tables->trap_names) g_free(name_tables->trap_names); + if(name_tables->irq_names) g_free(name_tables->irq_names); + if(name_tables->soft_irq_names) g_free(name_tables->soft_irq_names); + if(name_tables) g_free(name_tables); +} + +#ifdef HASH_TABLE_DEBUG + +static void test_process(gpointer key, gpointer value, gpointer user_data) +{ + LttvProcessState *process = (LttvProcessState *)value; + + /* Test for process corruption */ + guint stack_len = process->execution_stack->len; +} + +static void hash_table_check(GHashTable *table) +{ + g_hash_table_foreach(table, test_process, NULL); +} + + +#endif + +/* clears the stack and sets the state passed as argument */ +static void cpu_set_base_mode(LttvCPUState *cpust, LttvCPUMode state) +{ + g_array_set_size(cpust->mode_stack, 1); + ((GQuark *)cpust->mode_stack->data)[0] = state; +} + +static void cpu_push_mode(LttvCPUState *cpust, LttvCPUMode state) +{ + g_array_set_size(cpust->mode_stack, cpust->mode_stack->len + 1); + ((GQuark *)cpust->mode_stack->data)[cpust->mode_stack->len - 1] = state; +} + +static void cpu_pop_mode(LttvCPUState *cpust) +{ + if(cpust->mode_stack->len <= 1) + cpu_set_base_mode(cpust, LTTV_CPU_UNKNOWN); + else + g_array_set_size(cpust->mode_stack, cpust->mode_stack->len - 1); +} + +/* clears the stack and sets the state passed as argument */ +static void bdev_set_base_mode(LttvBdevState *bdevst, LttvBdevMode state) +{ + g_array_set_size(bdevst->mode_stack, 1); + ((GQuark *)bdevst->mode_stack->data)[0] = state; +} + +static void bdev_push_mode(LttvBdevState *bdevst, LttvBdevMode state) +{ + g_array_set_size(bdevst->mode_stack, bdevst->mode_stack->len + 1); + ((GQuark *)bdevst->mode_stack->data)[bdevst->mode_stack->len - 1] = state; +} + +static void bdev_pop_mode(LttvBdevState *bdevst) +{ + if(bdevst->mode_stack->len <= 1) + bdev_set_base_mode(bdevst, LTTV_BDEV_UNKNOWN); + else + g_array_set_size(bdevst->mode_stack, bdevst->mode_stack->len - 1); +} + +static void irq_set_base_mode(LttvIRQState *irqst, LttvIRQMode state) +{ + g_array_set_size(irqst->mode_stack, 1); + ((GQuark *)irqst->mode_stack->data)[0] = state; +} + +static void irq_push_mode(LttvIRQState *irqst, LttvIRQMode state) +{ + g_array_set_size(irqst->mode_stack, irqst->mode_stack->len + 1); + ((GQuark *)irqst->mode_stack->data)[irqst->mode_stack->len - 1] = state; +} + +static void irq_pop_mode(LttvIRQState *irqst) +{ + if(irqst->mode_stack->len <= 1) + irq_set_base_mode(irqst, LTTV_IRQ_UNKNOWN); + else + g_array_set_size(irqst->mode_stack, irqst->mode_stack->len - 1); +} + +static void push_state(LttvTracefileState *tfs, LttvExecutionMode t, + guint state_id) +{ + LttvExecutionState *es; + + LttvTraceState *ts = (LttvTraceState*)tfs->parent.t_context; + guint cpu = tfs->cpu; + +#ifdef HASH_TABLE_DEBUG + hash_table_check(ts->processes); +#endif + LttvProcessState *process = ts->running_process[cpu]; + + guint depth = process->execution_stack->len; + + process->execution_stack = + g_array_set_size(process->execution_stack, depth + 1); + /* Keep in sync */ + process->state = + &g_array_index(process->execution_stack, LttvExecutionState, depth - 1); + + es = &g_array_index(process->execution_stack, LttvExecutionState, depth); + es->t = t; + es->n = state_id; + es->entry = es->change = tfs->parent.timestamp; + es->cum_cpu_time = ltt_time_zero; + es->s = process->state->s; + process->state = es; +} + +/* pop state + * return 1 when empty, else 0 */ +int lttv_state_pop_state_cleanup(LttvProcessState *process, + LttvTracefileState *tfs) +{ + guint depth = process->execution_stack->len; + + if(depth == 1){ + return 1; + } + + process->execution_stack = + g_array_set_size(process->execution_stack, depth - 1); + process->state = &g_array_index(process->execution_stack, LttvExecutionState, + depth - 2); + process->state->change = tfs->parent.timestamp; + + return 0; +} + +static void pop_state(LttvTracefileState *tfs, LttvExecutionMode t) +{ + guint cpu = tfs->cpu; + LttvTraceState *ts = (LttvTraceState*)tfs->parent.t_context; + LttvProcessState *process = ts->running_process[cpu]; + + guint depth = process->execution_stack->len; + + if(process->state->t != t){ + g_info("Different execution mode type (%lu.%09lu): ignore it\n", + tfs->parent.timestamp.tv_sec, tfs->parent.timestamp.tv_nsec); + g_info("process state has %s when pop_int is %s\n", + g_quark_to_string(process->state->t), + g_quark_to_string(t)); + g_info("{ %u, %u, %s, %s, %s }\n", + process->pid, + process->ppid, + g_quark_to_string(process->name), + g_quark_to_string(process->brand), + g_quark_to_string(process->state->s)); + return; + } + + if(depth == 1){ + g_info("Trying to pop last state on stack (%lu.%09lu): ignore it\n", + tfs->parent.timestamp.tv_sec, tfs->parent.timestamp.tv_nsec); + return; + } + + process->execution_stack = + g_array_set_size(process->execution_stack, depth - 1); + process->state = &g_array_index(process->execution_stack, LttvExecutionState, + depth - 2); + process->state->change = tfs->parent.timestamp; +} + +struct search_result { + const LttTime *time; /* Requested time */ + LttTime *best; /* Best result */ +}; + +static gint search_usertrace(gconstpointer a, gconstpointer b) +{ + const LttTime *elem_time = (const LttTime*)a; + /* Explicit non const cast */ + struct search_result *res = (struct search_result *)b; + + if(ltt_time_compare(*elem_time, *(res->time)) < 0) { + /* The usertrace was created before the schedchange */ + /* Get larger keys */ + return 1; + } else if(ltt_time_compare(*elem_time, *(res->time)) >= 0) { + /* The usertrace was created after the schedchange time */ + /* Get smaller keys */ + if(res->best) { + if(ltt_time_compare(*elem_time, *res->best) < 0) { + res->best = (LttTime *)elem_time; + } + } else { + res->best = (LttTime *)elem_time; + } + return -1; + } + return 0; +} + +static LttvTracefileState *ltt_state_usertrace_find(LttvTraceState *tcs, + guint pid, const LttTime *timestamp) +{ + LttvTracefileState *tfs = NULL; + struct search_result res; + /* Find the usertrace associated with a pid and time interval. + * Search in the usertraces by PID (within a hash) and then, for each + * corresponding element of the array, find the first one with creation + * timestamp the lowest, but higher or equal to "timestamp". */ + res.time = timestamp; + res.best = NULL; + GTree *usertrace_tree = g_hash_table_lookup(tcs->usertraces, (gpointer)pid); + if(usertrace_tree) { + g_tree_search(usertrace_tree, search_usertrace, &res); + if(res.best) + tfs = g_tree_lookup(usertrace_tree, res.best); + } + + return tfs; +} + + +LttvProcessState * +lttv_state_create_process(LttvTraceState *tcs, LttvProcessState *parent, + guint cpu, guint pid, guint tgid, GQuark name, const LttTime *timestamp) +{ + LttvProcessState *process = g_new(LttvProcessState, 1); + + LttvExecutionState *es; + + char buffer[128]; + + process->pid = pid; + process->tgid = tgid; + process->cpu = cpu; + process->name = name; + process->brand = LTTV_STATE_UNBRANDED; + //process->last_cpu = tfs->cpu_name; + //process->last_cpu_index = ltt_tracefile_num(((LttvTracefileContext*)tfs)->tf); + process->type = LTTV_STATE_USER_THREAD; + process->usertrace = ltt_state_usertrace_find(tcs, pid, timestamp); + process->current_function = 0; //function 0x0 by default. + + g_info("Process %u, core %p", process->pid, process); + g_hash_table_insert(tcs->processes, process, process); + + if(parent) { + process->ppid = parent->pid; + process->creation_time = *timestamp; + } + + /* No parent. This process exists but we are missing all information about + its creation. The birth time is set to zero but we remember the time of + insertion */ + + else { + process->ppid = 0; + process->creation_time = ltt_time_zero; + } + + process->insertion_time = *timestamp; + sprintf(buffer,"%d-%lu.%lu",pid, process->creation_time.tv_sec, + process->creation_time.tv_nsec); + process->pid_time = g_quark_from_string(buffer); + process->cpu = cpu; + process->free_events = 0; + //process->last_cpu = tfs->cpu_name; + //process->last_cpu_index = ltt_tracefile_num(((LttvTracefileContext*)tfs)->tf); + process->execution_stack = g_array_sized_new(FALSE, FALSE, + sizeof(LttvExecutionState), PREALLOCATED_EXECUTION_STACK); + process->execution_stack = g_array_set_size(process->execution_stack, 2); + es = process->state = &g_array_index(process->execution_stack, + LttvExecutionState, 0); + es->t = LTTV_STATE_USER_MODE; + es->n = LTTV_STATE_SUBMODE_NONE; + es->entry = *timestamp; + //g_assert(timestamp->tv_sec != 0); + es->change = *timestamp; + es->cum_cpu_time = ltt_time_zero; + es->s = LTTV_STATE_RUN; + + es = process->state = &g_array_index(process->execution_stack, + LttvExecutionState, 1); + es->t = LTTV_STATE_SYSCALL; + es->n = LTTV_STATE_SUBMODE_NONE; + es->entry = *timestamp; + //g_assert(timestamp->tv_sec != 0); + es->change = *timestamp; + es->cum_cpu_time = ltt_time_zero; + es->s = LTTV_STATE_WAIT_FORK; + + /* Allocate an empty function call stack. If it's empty, use 0x0. */ + process->user_stack = g_array_sized_new(FALSE, FALSE, + sizeof(guint64), 0); + + return process; +} + +LttvProcessState *lttv_state_find_process(LttvTraceState *ts, guint cpu, + guint pid) +{ + LttvProcessState key; + LttvProcessState *process; + + key.pid = pid; + key.cpu = cpu; + process = g_hash_table_lookup(ts->processes, &key); + return process; +} + +LttvProcessState * +lttv_state_find_process_or_create(LttvTraceState *ts, guint cpu, guint pid, + const LttTime *timestamp) +{ + LttvProcessState *process = lttv_state_find_process(ts, cpu, pid); + LttvExecutionState *es; + + /* Put ltt_time_zero creation time for unexisting processes */ + if(unlikely(process == NULL)) { + process = lttv_state_create_process(ts, + NULL, cpu, pid, 0, LTTV_STATE_UNNAMED, timestamp); + /* We are not sure is it's a kernel thread or normal thread, put the + * bottom stack state to unknown */ + process->execution_stack = + g_array_set_size(process->execution_stack, 1); + process->state = es = + &g_array_index(process->execution_stack, LttvExecutionState, 0); + es->t = LTTV_STATE_MODE_UNKNOWN; + es->s = LTTV_STATE_UNNAMED; + } + return process; +} + +/* FIXME : this function should be called when we receive an event telling that + * release_task has been called in the kernel. In happens generally when + * the parent waits for its child terminaison, but may also happen in special + * cases in the child's exit : when the parent ignores its children SIGCCHLD or + * has the flag SA_NOCLDWAIT. It can also happen when the child is part + * of a killed thread group, but isn't the leader. + */ +static int exit_process(LttvTracefileState *tfs, LttvProcessState *process) +{ + LttvTraceState *ts = LTTV_TRACE_STATE(tfs->parent.t_context); + LttvProcessState key; + + /* Wait for both schedule with exit dead and process free to happen. + * They can happen in any order. */ + if (++(process->free_events) < 2) + return 0; + + key.pid = process->pid; + key.cpu = process->cpu; + g_hash_table_remove(ts->processes, &key); + g_array_free(process->execution_stack, TRUE); + g_array_free(process->user_stack, TRUE); + g_free(process); + return 1; +} + + +static void free_process_state(gpointer key, gpointer value,gpointer user_data) +{ + g_array_free(((LttvProcessState *)value)->execution_stack, TRUE); + g_array_free(((LttvProcessState *)value)->user_stack, TRUE); + g_free(value); +} + + +static void lttv_state_free_process_table(GHashTable *processes) +{ + g_hash_table_foreach(processes, free_process_state, NULL); + g_hash_table_destroy(processes); +} + + +static gboolean syscall_entry(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + guint cpu = s->cpu; + LttvTraceState *ts = (LttvTraceState*)s->parent.t_context; + LttvProcessState *process = ts->running_process[cpu]; + LttEvent *e = ltt_tracefile_get_event(s->parent.tf); + LttvTraceHook *th = (LttvTraceHook *)hook_data; + struct marker_field *f = lttv_trace_get_hook_field(th, 0); + LttvExecutionSubmode submode; + + guint syscall = ltt_event_get_unsigned(e, f); + expand_syscall_table(ts, syscall); + submode = ((LttvTraceState *)(s->parent.t_context))->syscall_names[syscall]; + /* There can be no system call from PID 0 : unknown state */ + if(process->pid != 0) + push_state(s, LTTV_STATE_SYSCALL, submode); + return FALSE; +} + + +static gboolean syscall_exit(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + guint cpu = s->cpu; + LttvTraceState *ts = (LttvTraceState*)s->parent.t_context; + LttvProcessState *process = ts->running_process[cpu]; + + /* There can be no system call from PID 0 : unknown state */ + if(process->pid != 0) + pop_state(s, LTTV_STATE_SYSCALL); + return FALSE; +} + + +static gboolean trap_entry(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState *)s->parent.t_context; + LttEvent *e = ltt_tracefile_get_event(s->parent.tf); + LttvTraceHook *th = (LttvTraceHook *)hook_data; + struct marker_field *f = lttv_trace_get_hook_field(th, 0); + + LttvExecutionSubmode submode; + + guint64 trap = ltt_event_get_long_unsigned(e, f); + + expand_trap_table(ts, trap); + + submode = ((LttvTraceState *)(s->parent.t_context))->trap_names[trap]; + + push_state(s, LTTV_STATE_TRAP, submode); + + /* update cpu status */ + cpu_push_mode(s->cpu_state, LTTV_CPU_TRAP); + + /* update trap status */ + s->cpu_state->last_trap = trap; + ts->trap_states[trap].running++; + + return FALSE; +} + +static gboolean trap_exit(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState *)s->parent.t_context; + gint trap = s->cpu_state->last_trap; + + pop_state(s, LTTV_STATE_TRAP); + + /* update cpu status */ + cpu_pop_mode(s->cpu_state); + + /* update trap status */ + if (trap != -1) + if(ts->trap_states[trap].running) + ts->trap_states[trap].running--; + + return FALSE; +} + +static gboolean irq_entry(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState *)s->parent.t_context; + LttEvent *e = ltt_tracefile_get_event(s->parent.tf); + //guint8 ev_id = ltt_event_eventtype_id(e); + LttvTraceHook *th = (LttvTraceHook *)hook_data; + struct marker_field *f = lttv_trace_get_hook_field(th, 0); + + LttvExecutionSubmode submode; + guint64 irq = ltt_event_get_long_unsigned(e, f); + + expand_irq_table(ts, irq); + + submode = ((LttvTraceState *)(s->parent.t_context))->irq_names[irq]; + + /* Do something with the info about being in user or system mode when int? */ + push_state(s, LTTV_STATE_IRQ, submode); + + /* update cpu status */ + cpu_push_mode(s->cpu_state, LTTV_CPU_IRQ); + + /* update irq status */ + s->cpu_state->last_irq = irq; + irq_push_mode(&ts->irq_states[irq], LTTV_IRQ_BUSY); + + return FALSE; +} + +static gboolean soft_irq_exit(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState *)s->parent.t_context; + gint softirq = s->cpu_state->last_soft_irq; + + pop_state(s, LTTV_STATE_SOFT_IRQ); + + /* update softirq status */ + if (softirq != -1) + if(ts->soft_irq_states[softirq].running) + ts->soft_irq_states[softirq].running--; + + /* update cpu status */ + cpu_pop_mode(s->cpu_state); + + return FALSE; +} + +static gboolean irq_exit(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState *)s->parent.t_context; + + pop_state(s, LTTV_STATE_IRQ); + + /* update cpu status */ + cpu_pop_mode(s->cpu_state); + + /* update irq status */ + if (s->cpu_state->last_irq != -1) + irq_pop_mode(&ts->irq_states[s->cpu_state->last_irq]); + + return FALSE; +} + +static gboolean soft_irq_raise(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState *)s->parent.t_context; + LttEvent *e = ltt_tracefile_get_event(s->parent.tf); + //guint8 ev_id = ltt_event_eventtype_id(e); + LttvTraceHook *th = (LttvTraceHook *)hook_data; + struct marker_field *f = lttv_trace_get_hook_field(th, 0); + + LttvExecutionSubmode submode; + guint64 softirq = ltt_event_get_long_unsigned(e, f); + guint64 nb_softirqs = ((LttvTraceState *)(s->parent.t_context))->nb_soft_irqs; + + if(softirq < nb_softirqs) { + submode = ((LttvTraceState *)(s->parent.t_context))->soft_irq_names[softirq]; + } else { + /* Fixup an incomplete irq table */ + GString *string = g_string_new(""); + g_string_printf(string, "softirq %llu", softirq); + submode = g_quark_from_string(string->str); + g_string_free(string, TRUE); + } + + /* update softirq status */ + /* a soft irq raises are not cumulative */ + ts->soft_irq_states[softirq].pending=1; + + return FALSE; +} + +static gboolean soft_irq_entry(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState *)s->parent.t_context; + LttEvent *e = ltt_tracefile_get_event(s->parent.tf); + //guint8 ev_id = ltt_event_eventtype_id(e); + LttvTraceHook *th = (LttvTraceHook *)hook_data; + struct marker_field *f = lttv_trace_get_hook_field(th, 0); + LttvExecutionSubmode submode; + guint64 softirq = ltt_event_get_long_unsigned(e, f); + expand_soft_irq_table(ts, softirq); + submode = ((LttvTraceState *)(s->parent.t_context))->soft_irq_names[softirq]; + + /* Do something with the info about being in user or system mode when int? */ + push_state(s, LTTV_STATE_SOFT_IRQ, submode); + + /* update cpu status */ + cpu_push_mode(s->cpu_state, LTTV_CPU_SOFT_IRQ); + + /* update softirq status */ + s->cpu_state->last_soft_irq = softirq; + if(ts->soft_irq_states[softirq].pending) + ts->soft_irq_states[softirq].pending--; + ts->soft_irq_states[softirq].running++; + + return FALSE; +} + +static gboolean enum_interrupt(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState *)s->parent.t_context; + LttEvent *e = ltt_tracefile_get_event(s->parent.tf); + //guint8 ev_id = ltt_event_eventtype_id(e); + LttvTraceHook *th = (LttvTraceHook *)hook_data; + + GQuark action = g_quark_from_string(ltt_event_get_string(e, + lttv_trace_get_hook_field(th, 0))); + guint irq = ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 1)); + + expand_irq_table(ts, irq); + ts->irq_names[irq] = action; + + return FALSE; +} + + +static gboolean bdev_request_issue(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState *)s->parent.t_context; + LttEvent *e = ltt_tracefile_get_event(s->parent.tf); + //guint8 ev_id = ltt_event_eventtype_id(e); + LttvTraceHook *th = (LttvTraceHook *)hook_data; + + guint major = ltt_event_get_long_unsigned(e, + lttv_trace_get_hook_field(th, 0)); + guint minor = ltt_event_get_long_unsigned(e, + lttv_trace_get_hook_field(th, 1)); + guint oper = ltt_event_get_long_unsigned(e, + lttv_trace_get_hook_field(th, 2)); + guint16 devcode = MKDEV(major,minor); + + /* have we seen this block device before? */ + gpointer bdev = get_hashed_bdevstate(ts, devcode); + + if(oper == 0) + bdev_push_mode(bdev, LTTV_BDEV_BUSY_READING); + else + bdev_push_mode(bdev, LTTV_BDEV_BUSY_WRITING); + + return FALSE; +} + +static gboolean bdev_request_complete(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState *)s->parent.t_context; + LttEvent *e = ltt_tracefile_get_event(s->parent.tf); + LttvTraceHook *th = (LttvTraceHook *)hook_data; + + guint major = ltt_event_get_long_unsigned(e, + lttv_trace_get_hook_field(th, 0)); + guint minor = ltt_event_get_long_unsigned(e, + lttv_trace_get_hook_field(th, 1)); + //guint oper = ltt_event_get_long_unsigned(e, + // lttv_trace_get_hook_field(th, 2)); + guint16 devcode = MKDEV(major,minor); + + /* have we seen this block device before? */ + gpointer bdev = get_hashed_bdevstate(ts, devcode); + + /* update block device */ + bdev_pop_mode(bdev); + + return FALSE; +} + +static void push_function(LttvTracefileState *tfs, guint64 funcptr) +{ + guint64 *new_func; + + LttvTraceState *ts = (LttvTraceState*)tfs->parent.t_context; + guint cpu = tfs->cpu; + LttvProcessState *process = ts->running_process[cpu]; + + guint depth = process->user_stack->len; + + process->user_stack = + g_array_set_size(process->user_stack, depth + 1); + + new_func = &g_array_index(process->user_stack, guint64, depth); + *new_func = funcptr; + process->current_function = funcptr; +} + +static void pop_function(LttvTracefileState *tfs, guint64 funcptr) +{ + guint cpu = tfs->cpu; + LttvTraceState *ts = (LttvTraceState*)tfs->parent.t_context; + LttvProcessState *process = ts->running_process[cpu]; + + if(process->current_function != funcptr){ + g_info("Different functions (%lu.%09lu): ignore it\n", + tfs->parent.timestamp.tv_sec, tfs->parent.timestamp.tv_nsec); + g_info("process state has %llu when pop_function is %llu\n", + process->current_function, funcptr); + g_info("{ %u, %u, %s, %s, %s }\n", + process->pid, + process->ppid, + g_quark_to_string(process->name), + g_quark_to_string(process->brand), + g_quark_to_string(process->state->s)); + return; + } + guint depth = process->user_stack->len; + + if(depth == 0){ + g_info("Trying to pop last function on stack (%lu.%09lu): ignore it\n", + tfs->parent.timestamp.tv_sec, tfs->parent.timestamp.tv_nsec); + return; + } + + process->user_stack = + g_array_set_size(process->user_stack, depth - 1); + process->current_function = + g_array_index(process->user_stack, guint64, depth - 2); +} + + +static gboolean function_entry(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + LttEvent *e = ltt_tracefile_get_event(s->parent.tf); + //guint8 ev_id = ltt_event_eventtype_id(e); + LttvTraceHook *th = (LttvTraceHook *)hook_data; + struct marker_field *f = lttv_trace_get_hook_field(th, 0); + guint64 funcptr = ltt_event_get_long_unsigned(e, f); + + push_function(s, funcptr); + return FALSE; +} + +static gboolean function_exit(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + LttEvent *e = ltt_tracefile_get_event(s->parent.tf); + //guint8 ev_id = ltt_event_eventtype_id(e); + LttvTraceHook *th = (LttvTraceHook *)hook_data; + struct marker_field *f = lttv_trace_get_hook_field(th, 0); + guint64 funcptr = ltt_event_get_long_unsigned(e, f); + + pop_function(s, funcptr); + return FALSE; +} + +static gboolean dump_syscall(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState*)s->parent.t_context; + LttEvent *e = ltt_tracefile_get_event(s->parent.tf); + LttvTraceHook *th = (LttvTraceHook *)hook_data; + guint id; + guint64 address; + char *symbol; + + id = ltt_event_get_unsigned(e, lttv_trace_get_hook_field(th, 0)); + address = ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 1)); + symbol = ltt_event_get_string(e, lttv_trace_get_hook_field(th, 2)); + + expand_syscall_table(ts, id); + ts->syscall_names[id] = g_quark_from_string(symbol); + + return FALSE; +} + +static gboolean dump_softirq(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState*)s->parent.t_context; + LttEvent *e = ltt_tracefile_get_event(s->parent.tf); + LttvTraceHook *th = (LttvTraceHook *)hook_data; + guint id; + guint64 address; + char *symbol; + + id = ltt_event_get_unsigned(e, lttv_trace_get_hook_field(th, 0)); + address = ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 1)); + symbol = ltt_event_get_string(e, lttv_trace_get_hook_field(th, 2)); + + expand_soft_irq_table(ts, id); + ts->soft_irq_names[id] = g_quark_from_string(symbol); + + return FALSE; +} + +static gboolean schedchange(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + guint cpu = s->cpu; + LttvTraceState *ts = (LttvTraceState*)s->parent.t_context; + LttvProcessState *process = ts->running_process[cpu]; + //LttvProcessState *old_process = ts->running_process[cpu]; + + LttEvent *e = ltt_tracefile_get_event(s->parent.tf); + LttvTraceHook *th = (LttvTraceHook *)hook_data; + guint pid_in, pid_out; + gint64 state_out; + + pid_out = ltt_event_get_unsigned(e, lttv_trace_get_hook_field(th, 0)); + pid_in = ltt_event_get_unsigned(e, lttv_trace_get_hook_field(th, 1)); + state_out = ltt_event_get_long_int(e, lttv_trace_get_hook_field(th, 2)); + + if(likely(process != NULL)) { + + /* We could not know but it was not the idle process executing. + This should only happen at the beginning, before the first schedule + event, and when the initial information (current process for each CPU) + is missing. It is not obvious how we could, after the fact, compensate + the wrongly attributed statistics. */ + + //This test only makes sense once the state is known and if there is no + //missing events. We need to silently ignore schedchange coming after a + //process_free, or it causes glitches. (FIXME) + //if(unlikely(process->pid != pid_out)) { + // g_assert(process->pid == 0); + //} + if(process->pid == 0 + && process->state->t == LTTV_STATE_MODE_UNKNOWN) { + if(pid_out == 0) { + /* Scheduling out of pid 0 at beginning of the trace : + * we know for sure it is in syscall mode at this point. */ + g_assert(process->execution_stack->len == 1); + process->state->t = LTTV_STATE_SYSCALL; + process->state->s = LTTV_STATE_WAIT; + process->state->change = s->parent.timestamp; + process->state->entry = s->parent.timestamp; + } + } else { + if(unlikely(process->state->s == LTTV_STATE_EXIT)) { + process->state->s = LTTV_STATE_ZOMBIE; + process->state->change = s->parent.timestamp; + } else { + if(unlikely(state_out == 0)) process->state->s = LTTV_STATE_WAIT_CPU; + else process->state->s = LTTV_STATE_WAIT; + process->state->change = s->parent.timestamp; + } + + if(state_out == 32 || state_out == 64) { /* EXIT_DEAD || TASK_DEAD */ + /* see sched.h for states */ + if (!exit_process(s, process)) { + process->state->s = LTTV_STATE_DEAD; + process->state->change = s->parent.timestamp; + } + } + } + } + process = ts->running_process[cpu] = + lttv_state_find_process_or_create( + (LttvTraceState*)s->parent.t_context, + cpu, pid_in, + &s->parent.timestamp); + process->state->s = LTTV_STATE_RUN; + process->cpu = cpu; + if(process->usertrace) + process->usertrace->cpu = cpu; + // process->last_cpu_index = ltt_tracefile_num(((LttvTracefileContext*)s)->tf); + process->state->change = s->parent.timestamp; + + /* update cpu status */ + if(pid_in == 0) + /* going to idle task */ + cpu_set_base_mode(s->cpu_state, LTTV_CPU_IDLE); + else { + /* scheduling a real task. + * we must be careful here: + * if we just schedule()'ed to a process that is + * in a trap, we must put the cpu in trap mode + */ + cpu_set_base_mode(s->cpu_state, LTTV_CPU_BUSY); + if(process->state->t == LTTV_STATE_TRAP) + cpu_push_mode(s->cpu_state, LTTV_CPU_TRAP); + } + + return FALSE; +} + +static gboolean process_fork(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + LttEvent *e = ltt_tracefile_get_event(s->parent.tf); + LttvTraceHook *th = (LttvTraceHook *)hook_data; + guint parent_pid; + guint child_pid; /* In the Linux Kernel, there is one PID per thread. */ + guint child_tgid; /* tgid in the Linux kernel is the "real" POSIX PID. */ + //LttvProcessState *zombie_process; + guint cpu = s->cpu; + LttvTraceState *ts = (LttvTraceState*)s->parent.t_context; + LttvProcessState *process = ts->running_process[cpu]; + LttvProcessState *child_process; + struct marker_field *f; + + /* Parent PID */ + parent_pid = ltt_event_get_unsigned(e, lttv_trace_get_hook_field(th, 0)); + + /* Child PID */ + child_pid = ltt_event_get_unsigned(e, lttv_trace_get_hook_field(th, 1)); + s->parent.target_pid = child_pid; + + /* Child TGID */ + f = lttv_trace_get_hook_field(th, 2); + if (likely(f)) + child_tgid = ltt_event_get_unsigned(e, f); + else + child_tgid = 0; + + /* Mathieu : it seems like the process might have been scheduled in before the + * fork, and, in a rare case, might be the current process. This might happen + * in a SMP case where we don't have enough precision on the clocks. + * + * Test reenabled after precision fixes on time. (Mathieu) */ +#if 0 + zombie_process = lttv_state_find_process(ts, ANY_CPU, child_pid); + + if(unlikely(zombie_process != NULL)) { + /* Reutilisation of PID. Only now we are sure that the old PID + * has been released. FIXME : should know when release_task happens instead. + */ + guint num_cpus = ltt_trace_get_num_cpu(ts->parent.t); + guint i; + for(i=0; i< num_cpus; i++) { + g_assert(zombie_process != ts->running_process[i]); + } + + exit_process(s, zombie_process); + } +#endif //0 + g_assert(process->pid != child_pid); + // FIXME : Add this test in the "known state" section + // g_assert(process->pid == parent_pid); + child_process = lttv_state_find_process(ts, ANY_CPU, child_pid); + if(child_process == NULL) { + child_process = lttv_state_create_process(ts, process, cpu, + child_pid, child_tgid, + LTTV_STATE_UNNAMED, &s->parent.timestamp); + } else { + /* The process has already been created : due to time imprecision between + * multiple CPUs : it has been scheduled in before creation. Note that we + * shouldn't have this kind of imprecision. + * + * Simply put a correct parent. + */ + g_error("Process %u has been created before fork on cpu %u. Probably an unsynchronized TSC problem on the traced machine.", child_pid, cpu); + //g_assert(0); /* This is a problematic case : the process has been created + // before the fork event */ + child_process->ppid = process->pid; + child_process->tgid = child_tgid; + } + g_assert(child_process->name == LTTV_STATE_UNNAMED); + child_process->name = process->name; + child_process->brand = process->brand; + + return FALSE; +} + +/* We stamp a newly created process as kernel_thread. + * The thread should not be running yet. */ +static gboolean process_kernel_thread(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + LttEvent *e = ltt_tracefile_get_event(s->parent.tf); + LttvTraceHook *th = (LttvTraceHook *)hook_data; + guint pid; + LttvTraceState *ts = (LttvTraceState*)s->parent.t_context; + LttvProcessState *process; + LttvExecutionState *es; + + /* PID */ + pid = (guint)ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 0)); + s->parent.target_pid = pid; + + process = lttv_state_find_process_or_create(ts, ANY_CPU, pid, + <t_time_zero); + if (process->state->s != LTTV_STATE_DEAD) { + process->execution_stack = + g_array_set_size(process->execution_stack, 1); + es = process->state = + &g_array_index(process->execution_stack, LttvExecutionState, 0); + es->t = LTTV_STATE_SYSCALL; + } + process->type = LTTV_STATE_KERNEL_THREAD; + + return FALSE; +} + +static gboolean process_exit(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + LttEvent *e = ltt_tracefile_get_event(s->parent.tf); + LttvTraceHook *th = (LttvTraceHook *)hook_data; + guint pid; + LttvTraceState *ts = (LttvTraceState*)s->parent.t_context; + LttvProcessState *process; // = ts->running_process[cpu]; + + pid = ltt_event_get_unsigned(e, lttv_trace_get_hook_field(th, 0)); + s->parent.target_pid = pid; + + // FIXME : Add this test in the "known state" section + // g_assert(process->pid == pid); + + process = lttv_state_find_process(ts, ANY_CPU, pid); + if(likely(process != NULL)) { + process->state->s = LTTV_STATE_EXIT; + } + return FALSE; +} + +static gboolean process_free(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState*)s->parent.t_context; + LttEvent *e = ltt_tracefile_get_event(s->parent.tf); + LttvTraceHook *th = (LttvTraceHook *)hook_data; + guint release_pid; + LttvProcessState *process; + + /* PID of the process to release */ + release_pid = ltt_event_get_unsigned(e, lttv_trace_get_hook_field(th, 0)); + s->parent.target_pid = release_pid; + + g_assert(release_pid != 0); + + process = lttv_state_find_process(ts, ANY_CPU, release_pid); + if(likely(process != NULL)) + exit_process(s, process); + return FALSE; +//DISABLED + if(likely(process != NULL)) { + /* release_task is happening at kernel level : we can now safely release + * the data structure of the process */ + //This test is fun, though, as it may happen that + //at time t : CPU 0 : process_free + //at time t+150ns : CPU 1 : schedule out + //Clearly due to time imprecision, we disable it. (Mathieu) + //If this weird case happen, we have no choice but to put the + //Currently running process on the cpu to 0. + //I re-enable it following time precision fixes. (Mathieu) + //Well, in the case where an process is freed by a process on another CPU + //and still scheduled, it happens that this is the schedchange that will + //drop the last reference count. Do not free it here! + guint num_cpus = ltt_trace_get_num_cpu(ts->parent.t); + guint i; + for(i=0; i< num_cpus; i++) { + //g_assert(process != ts->running_process[i]); + if(process == ts->running_process[i]) { + //ts->running_process[i] = lttv_state_find_process(ts, i, 0); + break; + } + } + if(i == num_cpus) /* process is not scheduled */ + exit_process(s, process); + } + + return FALSE; +} + + +static gboolean process_exec(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState*)s->parent.t_context; + LttEvent *e = ltt_tracefile_get_event(s->parent.tf); + LttvTraceHook *th = (LttvTraceHook *)hook_data; + //gchar *name; + guint cpu = s->cpu; + LttvProcessState *process = ts->running_process[cpu]; + +#if 0//how to use a sequence that must be transformed in a string + /* PID of the process to release */ + guint64 name_len = ltt_event_field_element_number(e, + lttv_trace_get_hook_field(th, 0)); + //name = ltt_event_get_string(e, lttv_trace_get_hook_field(th, 0)); + LttField *child = ltt_event_field_element_select(e, + lttv_trace_get_hook_field(th, 0), 0); + gchar *name_begin = + (gchar*)(ltt_event_data(e)+ltt_event_field_offset(e, child)); + gchar *null_term_name = g_new(gchar, name_len+1); + memcpy(null_term_name, name_begin, name_len); + null_term_name[name_len] = '\0'; + process->name = g_quark_from_string(null_term_name); +#endif //0 + + process->name = g_quark_from_string(ltt_event_get_string(e, + lttv_trace_get_hook_field(th, 0))); + process->brand = LTTV_STATE_UNBRANDED; + //g_free(null_term_name); + return FALSE; +} + +static gboolean thread_brand(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState*)s->parent.t_context; + LttEvent *e = ltt_tracefile_get_event(s->parent.tf); + LttvTraceHook *th = (LttvTraceHook *)hook_data; + gchar *name; + guint cpu = s->cpu; + LttvProcessState *process = ts->running_process[cpu]; + + name = ltt_event_get_string(e, lttv_trace_get_hook_field(th, 0)); + process->brand = g_quark_from_string(name); + + return FALSE; +} + +static void fix_process(gpointer key, gpointer value, + gpointer user_data) +{ + LttvProcessState *process; + LttvExecutionState *es; + process = (LttvProcessState *)value; + LttTime *timestamp = (LttTime*)user_data; + + if(process->type == LTTV_STATE_KERNEL_THREAD) { + es = &g_array_index(process->execution_stack, LttvExecutionState, 0); + if(es->t == LTTV_STATE_MODE_UNKNOWN) { + es->t = LTTV_STATE_SYSCALL; + es->n = LTTV_STATE_SUBMODE_NONE; + es->entry = *timestamp; + es->change = *timestamp; + es->cum_cpu_time = ltt_time_zero; + if(es->s == LTTV_STATE_UNNAMED) + es->s = LTTV_STATE_WAIT; + } + } else { + es = &g_array_index(process->execution_stack, LttvExecutionState, 0); + if(es->t == LTTV_STATE_MODE_UNKNOWN) { + es->t = LTTV_STATE_USER_MODE; + es->n = LTTV_STATE_SUBMODE_NONE; + es->entry = *timestamp; + //g_assert(timestamp->tv_sec != 0); + es->change = *timestamp; + es->cum_cpu_time = ltt_time_zero; + if(es->s == LTTV_STATE_UNNAMED) + es->s = LTTV_STATE_RUN; + + if(process->execution_stack->len == 1) { + /* Still in bottom unknown mode, means never did a system call + * May be either in user mode, syscall mode, running or waiting.*/ + /* FIXME : we may be tagging syscall mode when being user mode */ + process->execution_stack = + g_array_set_size(process->execution_stack, 2); + es = process->state = &g_array_index(process->execution_stack, + LttvExecutionState, 1); + es->t = LTTV_STATE_SYSCALL; + es->n = LTTV_STATE_SUBMODE_NONE; + es->entry = *timestamp; + //g_assert(timestamp->tv_sec != 0); + es->change = *timestamp; + es->cum_cpu_time = ltt_time_zero; + if(es->s == LTTV_STATE_WAIT_FORK) + es->s = LTTV_STATE_WAIT; + } + } + } +} + +static gboolean statedump_end(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState*)s->parent.t_context; + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + //LttEvent *e = ltt_tracefile_get_event(s->parent.tf); + //LttvTraceHook *th = (LttvTraceHook *)hook_data; + + /* For all processes */ + /* if kernel thread, if stack[0] is unknown, set to syscall mode, wait */ + /* else, if stack[0] is unknown, set to user mode, running */ + + g_hash_table_foreach(ts->processes, fix_process, &tfc->timestamp); + + return FALSE; +} + +static gboolean enum_process_state(void *hook_data, void *call_data) +{ + LttvTracefileState *s = (LttvTracefileState *)call_data; + LttEvent *e = ltt_tracefile_get_event(s->parent.tf); + //It's slow : optimise later by doing this before reading trace. + LttvTraceHook *th = (LttvTraceHook *)hook_data; + guint parent_pid; + guint pid; + guint tgid; + gchar * command; + guint cpu = s->cpu; + LttvTraceState *ts = (LttvTraceState*)s->parent.t_context; + LttvProcessState *process = ts->running_process[cpu]; + LttvProcessState *parent_process; + struct marker_field *f; + GQuark type, mode, submode, status; + LttvExecutionState *es; + guint i, nb_cpus; + + /* PID */ + pid = ltt_event_get_unsigned(e, lttv_trace_get_hook_field(th, 0)); + s->parent.target_pid = pid; + + /* Parent PID */ + parent_pid = ltt_event_get_unsigned(e, lttv_trace_get_hook_field(th, 1)); + + /* Command name */ + command = ltt_event_get_string(e, lttv_trace_get_hook_field(th, 2)); + + /* type */ + f = lttv_trace_get_hook_field(th, 3); + type = ltt_enum_string_get(f, ltt_event_get_unsigned(e, f)); + + //FIXME: type is rarely used, enum must match possible types. + + /* mode */ + f = lttv_trace_get_hook_field(th, 4); + mode = ltt_enum_string_get(f,ltt_event_get_unsigned(e, f)); + + /* submode */ + f = lttv_trace_get_hook_field(th, 5); + submode = ltt_enum_string_get(f, ltt_event_get_unsigned(e, f)); + + /* status */ + f = lttv_trace_get_hook_field(th, 6); + status = ltt_enum_string_get(f, ltt_event_get_unsigned(e, f)); + + /* TGID */ + f = lttv_trace_get_hook_field(th, 7); + if(f) + tgid = ltt_event_get_unsigned(e, f); + else + tgid = 0; + + if(pid == 0) { + nb_cpus = ltt_trace_get_num_cpu(ts->parent.t); + for(i=0; ippid = parent_pid; + process->tgid = tgid; + process->name = g_quark_from_string(command); + es = + &g_array_index(process->execution_stack, LttvExecutionState, 0); + process->type = LTTV_STATE_KERNEL_THREAD; + } + + } else { + /* The process might exist if a process was forked while performing the + * state dump. */ + process = lttv_state_find_process(ts, ANY_CPU, pid); + if(process == NULL) { + parent_process = lttv_state_find_process(ts, ANY_CPU, parent_pid); + process = lttv_state_create_process(ts, parent_process, cpu, + pid, tgid, g_quark_from_string(command), + &s->parent.timestamp); + + /* Keep the stack bottom : a running user mode */ + /* Disabled because of inconsistencies in the current statedump states. */ + if(type == LTTV_STATE_KERNEL_THREAD) { + /* Only keep the bottom + * FIXME Kernel thread : can be in syscall or interrupt or trap. */ + /* Will cause expected trap when in fact being syscall (even after end of + * statedump event) + * Will cause expected interrupt when being syscall. (only before end of + * statedump event) */ + // This will cause a "popping last state on stack, ignoring it." + process->execution_stack = g_array_set_size(process->execution_stack, 1); + es = process->state = &g_array_index(process->execution_stack, + LttvExecutionState, 0); + process->type = LTTV_STATE_KERNEL_THREAD; + es->t = LTTV_STATE_MODE_UNKNOWN; + es->s = LTTV_STATE_UNNAMED; + es->n = LTTV_STATE_SUBMODE_UNKNOWN; + #if 0 + es->t = LTTV_STATE_SYSCALL; + es->s = status; + es->n = submode; + #endif //0 + } else { + /* User space process : + * bottom : user mode + * either currently running or scheduled out. + * can be scheduled out because interrupted in (user mode or in syscall) + * or because of an explicit call to the scheduler in syscall. Note that + * the scheduler call comes after the irq_exit, so never in interrupt + * context. */ + // temp workaround : set size to 1 : only have user mode bottom of stack. + // will cause g_info message of expected syscall mode when in fact being + // in user mode. Can also cause expected trap when in fact being user + // mode in the event of a page fault reenabling interrupts in the handler. + // Expected syscall and trap can also happen after the end of statedump + // This will cause a "popping last state on stack, ignoring it." + process->execution_stack = g_array_set_size(process->execution_stack, 1); + es = process->state = &g_array_index(process->execution_stack, + LttvExecutionState, 0); + es->t = LTTV_STATE_MODE_UNKNOWN; + es->s = LTTV_STATE_UNNAMED; + es->n = LTTV_STATE_SUBMODE_UNKNOWN; + #if 0 + es->t = LTTV_STATE_USER_MODE; + es->s = status; + es->n = submode; + #endif //0 + } + #if 0 + /* UNKNOWN STATE */ + { + es = process->state = &g_array_index(process->execution_stack, + LttvExecutionState, 1); + es->t = LTTV_STATE_MODE_UNKNOWN; + es->s = LTTV_STATE_UNNAMED; + es->n = LTTV_STATE_SUBMODE_UNKNOWN; + } + #endif //0 + } else { + /* The process has already been created : + * Probably was forked while dumping the process state or + * was simply scheduled in prior to get the state dump event. + */ + process->ppid = parent_pid; + process->tgid = tgid; + process->name = g_quark_from_string(command); + process->type = type; + es = + &g_array_index(process->execution_stack, LttvExecutionState, 0); +#if 0 + if(es->t == LTTV_STATE_MODE_UNKNOWN) { + if(type == LTTV_STATE_KERNEL_THREAD) + es->t = LTTV_STATE_SYSCALL; + else + es->t = LTTV_STATE_USER_MODE; + } +#endif //0 + /* Don't mess around with the stack, it will eventually become + * ok after the end of state dump. */ + } + } + + return FALSE; +} + +gint lttv_state_hook_add_event_hooks(void *hook_data, void *call_data) +{ + LttvTracesetState *tss = (LttvTracesetState*)(call_data); + + lttv_state_add_event_hooks(tss); + + return 0; +} + +void lttv_state_add_event_hooks(LttvTracesetState *self) +{ + LttvTraceset *traceset = self->parent.ts; + + guint i, j, k, nb_trace, nb_tracefile; + + LttvTraceState *ts; + + LttvTracefileState *tfs; + + GArray *hooks; + + LttvTraceHook *th; + + LttvAttributeValue val; + + nb_trace = lttv_traceset_number(traceset); + for(i = 0 ; i < nb_trace ; i++) { + ts = (LttvTraceState *)self->parent.traces[i]; + + /* Find the eventtype id for the following events and register the + associated by id hooks. */ + + hooks = g_array_sized_new(FALSE, FALSE, sizeof(LttvTraceHook), 19); + //hooks = g_array_set_size(hooks, 19); // Max possible number of hooks. + //hn = 0; + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SYSCALL_ENTRY, + FIELD_ARRAY(LTT_FIELD_SYSCALL_ID), + syscall_entry, NULL, &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SYSCALL_EXIT, + NULL, + syscall_exit, NULL, &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL_ARCH, + LTT_EVENT_TRAP_ENTRY, + FIELD_ARRAY(LTT_FIELD_TRAP_ID), + trap_entry, NULL, &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL_ARCH, + LTT_EVENT_TRAP_EXIT, + NULL, + trap_exit, NULL, &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_IRQ_ENTRY, + FIELD_ARRAY(LTT_FIELD_IRQ_ID), + irq_entry, NULL, &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_IRQ_EXIT, + NULL, + irq_exit, NULL, &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SOFT_IRQ_RAISE, + FIELD_ARRAY(LTT_FIELD_SOFT_IRQ_ID), + soft_irq_raise, NULL, &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SOFT_IRQ_ENTRY, + FIELD_ARRAY(LTT_FIELD_SOFT_IRQ_ID), + soft_irq_entry, NULL, &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SOFT_IRQ_EXIT, + NULL, + soft_irq_exit, NULL, &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SCHED_SCHEDULE, + FIELD_ARRAY(LTT_FIELD_PREV_PID, LTT_FIELD_NEXT_PID, + LTT_FIELD_PREV_STATE), + schedchange, NULL, &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_PROCESS_FORK, + FIELD_ARRAY(LTT_FIELD_PARENT_PID, LTT_FIELD_CHILD_PID, + LTT_FIELD_CHILD_TGID), + process_fork, NULL, &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL_ARCH, + LTT_EVENT_KTHREAD_CREATE, + FIELD_ARRAY(LTT_FIELD_PID), + process_kernel_thread, NULL, &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_PROCESS_EXIT, + FIELD_ARRAY(LTT_FIELD_PID), + process_exit, NULL, &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_PROCESS_FREE, + FIELD_ARRAY(LTT_FIELD_PID), + process_free, NULL, &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_FS, + LTT_EVENT_EXEC, + FIELD_ARRAY(LTT_FIELD_FILENAME), + process_exec, NULL, &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_USER_GENERIC, + LTT_EVENT_THREAD_BRAND, + FIELD_ARRAY(LTT_FIELD_NAME), + thread_brand, NULL, &hooks); + + /* statedump-related hooks */ + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_LIST, + LTT_EVENT_PROCESS_STATE, + FIELD_ARRAY(LTT_FIELD_PID, LTT_FIELD_PARENT_PID, LTT_FIELD_NAME, + LTT_FIELD_TYPE, LTT_FIELD_MODE, LTT_FIELD_SUBMODE, + LTT_FIELD_STATUS, LTT_FIELD_TGID), + enum_process_state, NULL, &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_LIST, + LTT_EVENT_STATEDUMP_END, + NULL, + statedump_end, NULL, &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_LIST, + LTT_EVENT_LIST_INTERRUPT, + FIELD_ARRAY(LTT_FIELD_ACTION, LTT_FIELD_IRQ_ID), + enum_interrupt, NULL, &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_BLOCK, + LTT_EVENT_REQUEST_ISSUE, + FIELD_ARRAY(LTT_FIELD_MAJOR, LTT_FIELD_MINOR, LTT_FIELD_OPERATION), + bdev_request_issue, NULL, &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_BLOCK, + LTT_EVENT_REQUEST_COMPLETE, + FIELD_ARRAY(LTT_FIELD_MAJOR, LTT_FIELD_MINOR, LTT_FIELD_OPERATION), + bdev_request_complete, NULL, &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_USER_GENERIC, + LTT_EVENT_FUNCTION_ENTRY, + FIELD_ARRAY(LTT_FIELD_THIS_FN, LTT_FIELD_CALL_SITE), + function_entry, NULL, &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_USER_GENERIC, + LTT_EVENT_FUNCTION_EXIT, + FIELD_ARRAY(LTT_FIELD_THIS_FN, LTT_FIELD_CALL_SITE), + function_exit, NULL, &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_STATEDUMP, + LTT_EVENT_SYS_CALL_TABLE, + FIELD_ARRAY(LTT_FIELD_ID, LTT_FIELD_ADDRESS, LTT_FIELD_SYMBOL), + dump_syscall, NULL, &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_STATEDUMP, + LTT_EVENT_SOFTIRQ_VEC, + FIELD_ARRAY(LTT_FIELD_ID, LTT_FIELD_ADDRESS, LTT_FIELD_SYMBOL), + dump_softirq, NULL, &hooks); + + /* Add these hooks to each event_by_id hooks list */ + + nb_tracefile = ts->parent.tracefiles->len; + + for(j = 0 ; j < nb_tracefile ; j++) { + tfs = + LTTV_TRACEFILE_STATE(g_array_index(ts->parent.tracefiles, + LttvTracefileContext*, j)); + + for(k = 0 ; k < hooks->len ; k++) { + th = &g_array_index(hooks, LttvTraceHook, k); + lttv_hooks_add( + lttv_hooks_by_id_find(tfs->parent.event_by_id, th->id), + th->h, + th, + LTTV_PRIO_STATE); + } + } + lttv_attribute_find(ts->parent.a, LTTV_STATE_HOOKS, LTTV_POINTER, &val); + *(val.v_pointer) = hooks; + } +} + +gint lttv_state_hook_remove_event_hooks(void *hook_data, void *call_data) +{ + LttvTracesetState *tss = (LttvTracesetState*)(call_data); + + lttv_state_remove_event_hooks(tss); + + return 0; +} + +void lttv_state_remove_event_hooks(LttvTracesetState *self) +{ + LttvTraceset *traceset = self->parent.ts; + + guint i, j, k, nb_trace, nb_tracefile; + + LttvTraceState *ts; + + LttvTracefileState *tfs; + + GArray *hooks; + + LttvTraceHook *th; + + LttvAttributeValue val; + + nb_trace = lttv_traceset_number(traceset); + for(i = 0 ; i < nb_trace ; i++) { + ts = LTTV_TRACE_STATE(self->parent.traces[i]); + + lttv_attribute_find(ts->parent.a, LTTV_STATE_HOOKS, LTTV_POINTER, &val); + hooks = *(val.v_pointer); + + /* Remove these hooks from each event_by_id hooks list */ + + nb_tracefile = ts->parent.tracefiles->len; + + for(j = 0 ; j < nb_tracefile ; j++) { + tfs = + LTTV_TRACEFILE_STATE(g_array_index(ts->parent.tracefiles, + LttvTracefileContext*, j)); + + for(k = 0 ; k < hooks->len ; k++) { + th = &g_array_index(hooks, LttvTraceHook, k); + lttv_hooks_remove_data( + lttv_hooks_by_id_find(tfs->parent.event_by_id, th->id), + th->h, + th); + } + } + lttv_trace_hook_remove_all(&hooks); + g_array_free(hooks, TRUE); + } +} + +static gboolean state_save_event_hook(void *hook_data, void *call_data) +{ + guint *event_count = (guint*)hook_data; + + /* Only save at LTTV_STATE_SAVE_INTERVAL */ + if(likely((*event_count)++ < LTTV_STATE_SAVE_INTERVAL)) + return FALSE; + else + *event_count = 0; + + LttvTracefileState *self = (LttvTracefileState *)call_data; + + LttvTraceState *tcs = (LttvTraceState *)(self->parent.t_context); + + LttvAttribute *saved_states_tree, *saved_state_tree; + + LttvAttributeValue value; + + saved_states_tree = lttv_attribute_find_subdir(tcs->parent.t_a, + LTTV_STATE_SAVED_STATES); + saved_state_tree = g_object_new(LTTV_ATTRIBUTE_TYPE, NULL); + value = lttv_attribute_add(saved_states_tree, + lttv_attribute_get_number(saved_states_tree), LTTV_GOBJECT); + *(value.v_gobject) = (GObject *)saved_state_tree; + value = lttv_attribute_add(saved_state_tree, LTTV_STATE_TIME, LTTV_TIME); + *(value.v_time) = self->parent.timestamp; + lttv_state_save(tcs, saved_state_tree); + g_debug("Saving state at time %lu.%lu", self->parent.timestamp.tv_sec, + self->parent.timestamp.tv_nsec); + + *(tcs->max_time_state_recomputed_in_seek) = self->parent.timestamp; + + return FALSE; +} + +static gboolean state_save_after_trace_hook(void *hook_data, void *call_data) +{ + LttvTraceState *tcs = (LttvTraceState *)(call_data); + + *(tcs->max_time_state_recomputed_in_seek) = tcs->parent.time_span.end_time; + + return FALSE; +} + +guint lttv_state_current_cpu(LttvTracefileState *tfs) +{ + return tfs->cpu; +} + + + +#if 0 +static gboolean block_start(void *hook_data, void *call_data) +{ + LttvTracefileState *self = (LttvTracefileState *)call_data; + + LttvTracefileState *tfcs; + + LttvTraceState *tcs = (LttvTraceState *)(self->parent.t_context); + + LttEventPosition *ep; + + guint i, nb_block, nb_event, nb_tracefile; + + LttTracefile *tf; + + LttvAttribute *saved_states_tree, *saved_state_tree; + + LttvAttributeValue value; + + ep = ltt_event_position_new(); + + nb_tracefile = tcs->parent.tracefiles->len; + + /* Count the number of events added since the last block end in any + tracefile. */ + + for(i = 0 ; i < nb_tracefile ; i++) { + tfcs = + LTTV_TRACEFILE_STATE(&g_array_index(tcs->parent.tracefiles, + LttvTracefileContext, i)); + ltt_event_position(tfcs->parent.e, ep); + ltt_event_position_get(ep, &nb_block, &nb_event, &tf); + tcs->nb_event += nb_event - tfcs->saved_position; + tfcs->saved_position = nb_event; + } + g_free(ep); + + if(tcs->nb_event >= tcs->save_interval) { + saved_states_tree = lttv_attribute_find_subdir(tcs->parent.t_a, + LTTV_STATE_SAVED_STATES); + saved_state_tree = g_object_new(LTTV_ATTRIBUTE_TYPE, NULL); + value = lttv_attribute_add(saved_states_tree, + lttv_attribute_get_number(saved_states_tree), LTTV_GOBJECT); + *(value.v_gobject) = (GObject *)saved_state_tree; + value = lttv_attribute_add(saved_state_tree, LTTV_STATE_TIME, LTTV_TIME); + *(value.v_time) = self->parent.timestamp; + lttv_state_save(tcs, saved_state_tree); + tcs->nb_event = 0; + g_debug("Saving state at time %lu.%lu", self->parent.timestamp.tv_sec, + self->parent.timestamp.tv_nsec); + } + *(tcs->max_time_state_recomputed_in_seek) = self->parent.timestamp; + return FALSE; +} +#endif //0 + +#if 0 +static gboolean block_end(void *hook_data, void *call_data) +{ + LttvTracefileState *self = (LttvTracefileState *)call_data; + + LttvTraceState *tcs = (LttvTraceState *)(self->parent.t_context); + + LttTracefile *tf; + + LttEventPosition *ep; + + guint nb_block, nb_event; + + ep = ltt_event_position_new(); + ltt_event_position(self->parent.e, ep); + ltt_event_position_get(ep, &nb_block, &nb_event, &tf); + tcs->nb_event += nb_event - self->saved_position + 1; + self->saved_position = 0; + *(tcs->max_time_state_recomputed_in_seek) = self->parent.timestamp; + g_free(ep); + + return FALSE; +} +#endif //0 +#if 0 +void lttv_state_save_add_event_hooks(LttvTracesetState *self) +{ + LttvTraceset *traceset = self->parent.ts; + + guint i, j, nb_trace, nb_tracefile; + + LttvTraceState *ts; + + LttvTracefileState *tfs; + + LttvTraceHook hook_start, hook_end; + + nb_trace = lttv_traceset_number(traceset); + for(i = 0 ; i < nb_trace ; i++) { + ts = (LttvTraceState *)self->parent.traces[i]; + + lttv_trace_find_hook(ts->parent.t, "core","block_start",NULL, + NULL, NULL, block_start, &hook_start); + lttv_trace_find_hook(ts->parent.t, "core","block_end",NULL, + NULL, NULL, block_end, &hook_end); + + nb_tracefile = ts->parent.tracefiles->len; + + for(j = 0 ; j < nb_tracefile ; j++) { + tfs = + LTTV_TRACEFILE_STATE(&g_array_index(ts->parent.tracefiles, + LttvTracefileContext, j)); + lttv_hooks_add(lttv_hooks_by_id_find(tfs->parent.event_by_id, + hook_start.id), hook_start.h, NULL, LTTV_PRIO_STATE); + lttv_hooks_add(lttv_hooks_by_id_find(tfs->parent.event_by_id, + hook_end.id), hook_end.h, NULL, LTTV_PRIO_STATE); + } + } +} +#endif //0 + +void lttv_state_save_add_event_hooks(LttvTracesetState *self) +{ + LttvTraceset *traceset = self->parent.ts; + + guint i, j, nb_trace, nb_tracefile; + + LttvTraceState *ts; + + LttvTracefileState *tfs; + + + nb_trace = lttv_traceset_number(traceset); + for(i = 0 ; i < nb_trace ; i++) { + + ts = (LttvTraceState *)self->parent.traces[i]; + nb_tracefile = ts->parent.tracefiles->len; + + if(ts->has_precomputed_states) continue; + + guint *event_count = g_new(guint, 1); + *event_count = 0; + + for(j = 0 ; j < nb_tracefile ; j++) { + tfs = + LTTV_TRACEFILE_STATE(g_array_index(ts->parent.tracefiles, + LttvTracefileContext*, j)); + lttv_hooks_add(tfs->parent.event, + state_save_event_hook, + event_count, + LTTV_PRIO_STATE); + + } + } + + lttv_process_traceset_begin(&self->parent, + NULL, NULL, NULL, NULL, NULL); + +} + +gint lttv_state_save_hook_add_event_hooks(void *hook_data, void *call_data) +{ + LttvTracesetState *tss = (LttvTracesetState*)(call_data); + + lttv_state_save_add_event_hooks(tss); + + return 0; +} + + +#if 0 +void lttv_state_save_remove_event_hooks(LttvTracesetState *self) +{ + LttvTraceset *traceset = self->parent.ts; + + guint i, j, nb_trace, nb_tracefile; + + LttvTraceState *ts; + + LttvTracefileState *tfs; + + LttvTraceHook hook_start, hook_end; + + nb_trace = lttv_traceset_number(traceset); + for(i = 0 ; i < nb_trace ; i++) { + ts = LTTV_TRACE_STATE(self->parent.traces[i]); + + lttv_trace_find_hook(ts->parent.t, "core","block_start",NULL, + NULL, NULL, block_start, &hook_start); + + lttv_trace_find_hook(ts->parent.t, "core","block_end",NULL, + NULL, NULL, block_end, &hook_end); + + nb_tracefile = ts->parent.tracefiles->len; + + for(j = 0 ; j < nb_tracefile ; j++) { + tfs = + LTTV_TRACEFILE_STATE(&g_array_index(ts->parent.tracefiles, + LttvTracefileContext, j)); + lttv_hooks_remove_data(lttv_hooks_by_id_find( + tfs->parent.event_by_id, hook_start.id), hook_start.h, NULL); + lttv_hooks_remove_data(lttv_hooks_by_id_find( + tfs->parent.event_by_id, hook_end.id), hook_end.h, NULL); + } + } +} +#endif //0 + +void lttv_state_save_remove_event_hooks(LttvTracesetState *self) +{ + LttvTraceset *traceset = self->parent.ts; + + guint i, j, nb_trace, nb_tracefile; + + LttvTraceState *ts; + + LttvTracefileState *tfs; + + LttvHooks *after_trace = lttv_hooks_new(); + + lttv_hooks_add(after_trace, + state_save_after_trace_hook, + NULL, + LTTV_PRIO_STATE); + + + lttv_process_traceset_end(&self->parent, + NULL, after_trace, NULL, NULL, NULL); + + lttv_hooks_destroy(after_trace); + + nb_trace = lttv_traceset_number(traceset); + for(i = 0 ; i < nb_trace ; i++) { + + ts = (LttvTraceState *)self->parent.traces[i]; + nb_tracefile = ts->parent.tracefiles->len; + + if(ts->has_precomputed_states) continue; + + guint *event_count = NULL; + + for(j = 0 ; j < nb_tracefile ; j++) { + tfs = + LTTV_TRACEFILE_STATE(g_array_index(ts->parent.tracefiles, + LttvTracefileContext*, j)); + event_count = lttv_hooks_remove(tfs->parent.event, + state_save_event_hook); + } + if(event_count) g_free(event_count); + } +} + +gint lttv_state_save_hook_remove_event_hooks(void *hook_data, void *call_data) +{ + LttvTracesetState *tss = (LttvTracesetState*)(call_data); + + lttv_state_save_remove_event_hooks(tss); + + return 0; +} + +void lttv_state_traceset_seek_time_closest(LttvTracesetState *self, LttTime t) +{ + LttvTraceset *traceset = self->parent.ts; + + guint i, nb_trace; + + int min_pos, mid_pos, max_pos; + + guint call_rest = 0; + + LttvTraceState *tcs; + + LttvAttributeValue value; + + LttvAttributeType type; + + LttvAttributeName name; + + gboolean is_named; + + LttvAttribute *saved_states_tree, *saved_state_tree, *closest_tree; + + //g_tree_destroy(self->parent.pqueue); + //self->parent.pqueue = g_tree_new(compare_tracefile); + + g_info("Entering seek_time_closest for time %lu.%lu", t.tv_sec, t.tv_nsec); + + nb_trace = lttv_traceset_number(traceset); + for(i = 0 ; i < nb_trace ; i++) { + tcs = (LttvTraceState *)self->parent.traces[i]; + + if(ltt_time_compare(t, *(tcs->max_time_state_recomputed_in_seek)) < 0) { + saved_states_tree = lttv_attribute_find_subdir(tcs->parent.t_a, + LTTV_STATE_SAVED_STATES); + min_pos = -1; + + if(saved_states_tree) { + max_pos = lttv_attribute_get_number(saved_states_tree) - 1; + mid_pos = max_pos / 2; + while(min_pos < max_pos) { + type = lttv_attribute_get(saved_states_tree, mid_pos, &name, &value, + &is_named); + g_assert(type == LTTV_GOBJECT); + saved_state_tree = *((LttvAttribute **)(value.v_gobject)); + type = lttv_attribute_get_by_name(saved_state_tree, LTTV_STATE_TIME, + &value); + g_assert(type == LTTV_TIME); + if(ltt_time_compare(*(value.v_time), t) < 0) { + min_pos = mid_pos; + closest_tree = saved_state_tree; + } + else max_pos = mid_pos - 1; + + mid_pos = (min_pos + max_pos + 1) / 2; + } + } + + /* restore the closest earlier saved state */ + if(min_pos != -1) { + lttv_state_restore(tcs, closest_tree); + call_rest = 1; + } + + /* There is no saved state, yet we want to have it. Restart at T0 */ + else { + restore_init_state(tcs); + lttv_process_trace_seek_time(&(tcs->parent), ltt_time_zero); + } + } + /* We want to seek quickly without restoring/updating the state */ + else { + restore_init_state(tcs); + lttv_process_trace_seek_time(&(tcs->parent), t); + } + } + if(!call_rest) g_info("NOT Calling restore"); +} + + +static void +traceset_state_instance_init (GTypeInstance *instance, gpointer g_class) +{ +} + + +static void +traceset_state_finalize (LttvTracesetState *self) +{ + G_OBJECT_CLASS(g_type_class_peek(LTTV_TRACESET_CONTEXT_TYPE))-> + finalize(G_OBJECT(self)); +} + + +static void +traceset_state_class_init (LttvTracesetContextClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + + gobject_class->finalize = (void (*)(GObject *self)) traceset_state_finalize; + klass->init = (void (*)(LttvTracesetContext *self, LttvTraceset *ts))init; + klass->fini = (void (*)(LttvTracesetContext *self))fini; + klass->new_traceset_context = new_traceset_context; + klass->new_trace_context = new_trace_context; + klass->new_tracefile_context = new_tracefile_context; +} + + +GType +lttv_traceset_state_get_type(void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (LttvTracesetStateClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) traceset_state_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (LttvTracesetState), + 0, /* n_preallocs */ + (GInstanceInitFunc) traceset_state_instance_init, /* instance_init */ + NULL /* value handling */ + }; + + type = g_type_register_static (LTTV_TRACESET_CONTEXT_TYPE, "LttvTracesetStateType", + &info, 0); + } + return type; +} + + +static void +trace_state_instance_init (GTypeInstance *instance, gpointer g_class) +{ +} + + +static void +trace_state_finalize (LttvTraceState *self) +{ + G_OBJECT_CLASS(g_type_class_peek(LTTV_TRACE_CONTEXT_TYPE))-> + finalize(G_OBJECT(self)); +} + + +static void +trace_state_class_init (LttvTraceStateClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + + gobject_class->finalize = (void (*)(GObject *self)) trace_state_finalize; + klass->state_save = state_save; + klass->state_restore = state_restore; + klass->state_saved_free = state_saved_free; +} + + +GType +lttv_trace_state_get_type(void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (LttvTraceStateClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) trace_state_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (LttvTraceState), + 0, /* n_preallocs */ + (GInstanceInitFunc) trace_state_instance_init, /* instance_init */ + NULL /* value handling */ + }; + + type = g_type_register_static (LTTV_TRACE_CONTEXT_TYPE, + "LttvTraceStateType", &info, 0); + } + return type; +} + + +static void +tracefile_state_instance_init (GTypeInstance *instance, gpointer g_class) +{ +} + + +static void +tracefile_state_finalize (LttvTracefileState *self) +{ + G_OBJECT_CLASS(g_type_class_peek(LTTV_TRACEFILE_CONTEXT_TYPE))-> + finalize(G_OBJECT(self)); +} + + +static void +tracefile_state_class_init (LttvTracefileStateClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + + gobject_class->finalize = (void (*)(GObject *self)) tracefile_state_finalize; +} + + +GType +lttv_tracefile_state_get_type(void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (LttvTracefileStateClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) tracefile_state_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (LttvTracefileState), + 0, /* n_preallocs */ + (GInstanceInitFunc) tracefile_state_instance_init, /* instance_init */ + NULL /* value handling */ + }; + + type = g_type_register_static (LTTV_TRACEFILE_CONTEXT_TYPE, + "LttvTracefileStateType", &info, 0); + } + return type; +} + + +static void module_init() +{ + LTTV_STATE_UNNAMED = g_quark_from_string(""); + LTTV_STATE_UNBRANDED = g_quark_from_string(""); + LTTV_STATE_MODE_UNKNOWN = g_quark_from_string("MODE_UNKNOWN"); + LTTV_STATE_USER_MODE = g_quark_from_string("USER_MODE"); + LTTV_STATE_SYSCALL = g_quark_from_string("SYSCALL"); + LTTV_STATE_TRAP = g_quark_from_string("TRAP"); + LTTV_STATE_IRQ = g_quark_from_string("IRQ"); + LTTV_STATE_SOFT_IRQ = g_quark_from_string("SOFTIRQ"); + LTTV_STATE_SUBMODE_UNKNOWN = g_quark_from_string("UNKNOWN"); + LTTV_STATE_SUBMODE_NONE = g_quark_from_string("NONE"); + LTTV_STATE_WAIT_FORK = g_quark_from_string("WAIT_FORK"); + LTTV_STATE_WAIT_CPU = g_quark_from_string("WAIT_CPU"); + LTTV_STATE_EXIT = g_quark_from_string("EXIT"); + LTTV_STATE_ZOMBIE = g_quark_from_string("ZOMBIE"); + LTTV_STATE_WAIT = g_quark_from_string("WAIT"); + LTTV_STATE_RUN = g_quark_from_string("RUN"); + LTTV_STATE_DEAD = g_quark_from_string("DEAD"); + LTTV_STATE_USER_THREAD = g_quark_from_string("USER_THREAD"); + LTTV_STATE_KERNEL_THREAD = g_quark_from_string("KERNEL_THREAD"); + LTTV_STATE_TRACEFILES = g_quark_from_string("tracefiles"); + LTTV_STATE_PROCESSES = g_quark_from_string("processes"); + LTTV_STATE_PROCESS = g_quark_from_string("process"); + LTTV_STATE_RUNNING_PROCESS = g_quark_from_string("running_process"); + LTTV_STATE_EVENT = g_quark_from_string("event"); + LTTV_STATE_SAVED_STATES = g_quark_from_string("saved states"); + LTTV_STATE_SAVED_STATES_TIME = g_quark_from_string("saved states time"); + LTTV_STATE_TIME = g_quark_from_string("time"); + LTTV_STATE_HOOKS = g_quark_from_string("saved state hooks"); + LTTV_STATE_NAME_TABLES = g_quark_from_string("name tables"); + LTTV_STATE_TRACE_STATE_USE_COUNT = + g_quark_from_string("trace_state_use_count"); + LTTV_STATE_RESOURCE_CPUS = g_quark_from_string("cpu resource states"); + LTTV_STATE_RESOURCE_CPUS = g_quark_from_string("cpu count"); + LTTV_STATE_RESOURCE_IRQS = g_quark_from_string("irq resource states"); + LTTV_STATE_RESOURCE_SOFT_IRQS = g_quark_from_string("soft irq resource states"); + LTTV_STATE_RESOURCE_TRAPS = g_quark_from_string("trap resource states"); + LTTV_STATE_RESOURCE_BLKDEVS = g_quark_from_string("blkdevs resource states"); + + + LTT_FACILITY_KERNEL = g_quark_from_string("kernel"); + LTT_FACILITY_KERNEL_ARCH = g_quark_from_string("kernel_arch"); + LTT_FACILITY_FS = g_quark_from_string("fs"); + LTT_FACILITY_LIST = g_quark_from_string("list"); + LTT_FACILITY_USER_GENERIC = g_quark_from_string("user_generic"); + LTT_FACILITY_BLOCK = g_quark_from_string("block"); + LTT_FACILITY_STATEDUMP = g_quark_from_string("statedump"); + + LTT_EVENT_SYSCALL_ENTRY = g_quark_from_string("syscall_entry"); + LTT_EVENT_SYSCALL_EXIT = g_quark_from_string("syscall_exit"); + LTT_EVENT_TRAP_ENTRY = g_quark_from_string("trap_entry"); + LTT_EVENT_TRAP_EXIT = g_quark_from_string("trap_exit"); + LTT_EVENT_IRQ_ENTRY = g_quark_from_string("irq_entry"); + LTT_EVENT_IRQ_EXIT = g_quark_from_string("irq_exit"); + LTT_EVENT_SOFT_IRQ_RAISE = g_quark_from_string("softirq_raise"); + LTT_EVENT_SOFT_IRQ_ENTRY = g_quark_from_string("softirq_entry"); + LTT_EVENT_SOFT_IRQ_EXIT = g_quark_from_string("softirq_exit"); + LTT_EVENT_SCHED_SCHEDULE = g_quark_from_string("sched_schedule"); + LTT_EVENT_PROCESS_FORK = g_quark_from_string("process_fork"); + LTT_EVENT_KTHREAD_CREATE = g_quark_from_string("kthread_create"); + LTT_EVENT_PROCESS_EXIT = g_quark_from_string("process_exit"); + LTT_EVENT_PROCESS_FREE = g_quark_from_string("process_free"); + LTT_EVENT_EXEC = g_quark_from_string("exec"); + LTT_EVENT_PROCESS_STATE = g_quark_from_string("process_state"); + LTT_EVENT_STATEDUMP_END = g_quark_from_string("statedump_end"); + LTT_EVENT_FUNCTION_ENTRY = g_quark_from_string("function_entry"); + LTT_EVENT_FUNCTION_EXIT = g_quark_from_string("function_exit"); + LTT_EVENT_THREAD_BRAND = g_quark_from_string("thread_brand"); + LTT_EVENT_REQUEST_ISSUE = g_quark_from_string("_blk_request_issue"); + LTT_EVENT_REQUEST_COMPLETE = g_quark_from_string("_blk_request_complete"); + LTT_EVENT_LIST_INTERRUPT = g_quark_from_string("interrupt"); + LTT_EVENT_SYS_CALL_TABLE = g_quark_from_string("sys_call_table"); + LTT_EVENT_SOFTIRQ_VEC = g_quark_from_string("softirq_vec"); + + LTT_FIELD_SYSCALL_ID = g_quark_from_string("syscall_id"); + LTT_FIELD_TRAP_ID = g_quark_from_string("trap_id"); + LTT_FIELD_IRQ_ID = g_quark_from_string("irq_id"); + LTT_FIELD_SOFT_IRQ_ID = g_quark_from_string("softirq_id"); + LTT_FIELD_PREV_PID = g_quark_from_string("prev_pid"); + LTT_FIELD_NEXT_PID = g_quark_from_string("next_pid"); + LTT_FIELD_PREV_STATE = g_quark_from_string("prev_state"); + LTT_FIELD_PARENT_PID = g_quark_from_string("parent_pid"); + LTT_FIELD_CHILD_PID = g_quark_from_string("child_pid"); + LTT_FIELD_PID = g_quark_from_string("pid"); + LTT_FIELD_TGID = g_quark_from_string("tgid"); + LTT_FIELD_CHILD_TGID = g_quark_from_string("child_tgid"); + LTT_FIELD_FILENAME = g_quark_from_string("filename"); + LTT_FIELD_NAME = g_quark_from_string("name"); + LTT_FIELD_TYPE = g_quark_from_string("type"); + LTT_FIELD_MODE = g_quark_from_string("mode"); + LTT_FIELD_SUBMODE = g_quark_from_string("submode"); + LTT_FIELD_STATUS = g_quark_from_string("status"); + LTT_FIELD_THIS_FN = g_quark_from_string("this_fn"); + LTT_FIELD_CALL_SITE = g_quark_from_string("call_site"); + LTT_FIELD_MAJOR = g_quark_from_string("major"); + LTT_FIELD_MINOR = g_quark_from_string("minor"); + LTT_FIELD_OPERATION = g_quark_from_string("direction"); + LTT_FIELD_ACTION = g_quark_from_string("action"); + LTT_FIELD_ID = g_quark_from_string("id"); + LTT_FIELD_ADDRESS = g_quark_from_string("address"); + LTT_FIELD_SYMBOL = g_quark_from_string("symbol"); + + LTTV_CPU_UNKNOWN = g_quark_from_string("unknown"); + LTTV_CPU_IDLE = g_quark_from_string("idle"); + LTTV_CPU_BUSY = g_quark_from_string("busy"); + LTTV_CPU_IRQ = g_quark_from_string("irq"); + LTTV_CPU_SOFT_IRQ = g_quark_from_string("softirq"); + LTTV_CPU_TRAP = g_quark_from_string("trap"); + + LTTV_IRQ_UNKNOWN = g_quark_from_string("unknown"); + LTTV_IRQ_IDLE = g_quark_from_string("idle"); + LTTV_IRQ_BUSY = g_quark_from_string("busy"); + + LTTV_BDEV_UNKNOWN = g_quark_from_string("unknown"); + LTTV_BDEV_IDLE = g_quark_from_string("idle"); + LTTV_BDEV_BUSY_READING = g_quark_from_string("busy_reading"); + LTTV_BDEV_BUSY_WRITING = g_quark_from_string("busy_writing"); +} + +static void module_destroy() +{ +} + + +LTTV_MODULE("state", "State computation", \ + "Update the system state, possibly saving it at intervals", \ + module_init, module_destroy) + + + diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/state.h b/tags/lttv-0.11.3-23102008/lttv/lttv/state.h new file mode 100644 index 00000000..306bef5c --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/state.h @@ -0,0 +1,443 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 STATE_H +#define STATE_H + +#include +#include +#include + +/* The operating system state, kept during the trace analysis, + contains a subset of the real operating system state, + sufficient for the analysis, and possibly organized quite differently. + + The state information is added to LttvTracesetContext, LttvTraceContext + and LttvTracefileContext objects, used by process_traceset, through + subtyping. The context objects already reflect the multiple tracefiles + (one per cpu) per trace and multiple traces per trace set. The state + objects defined here simply add fields to the relevant context objects. + + There is no traceset specific state yet. It may eventually contains such + things as clock differences over time. + + The trace state currently consists in a process table. + + The tracefile level state relates to the associated cpu. It contains the + position of the current event in the tracefile (since the state depends on + which events have been processed) and a pointer to the current process, + in the process table, being run on that cpu. + + For each process in the process table, various informations such as exec + file name, pid, ppid and creation time are stored. Each process state also + contains an execution mode stack (e.g. irq within system call, called + from user mode). */ + +/* Priority of state hooks */ +#define LTTV_PRIO_STATE 25 + +#define LTTV_STATE_SAVE_INTERVAL 50000 + +/* Facilities Quarks */ + +extern GQuark + LTT_FACILITY_KERNEL, + LTT_FACILITY_KERNEL_ARCH, + LTT_FACILITY_FS, + LTT_FACILITY_LIST, + LTT_FACILITY_USER_GENERIC, + LTT_FACILITY_BLOCK, + LTT_FACILITY_STATEDUMP; + +/* Events Quarks */ + +extern GQuark + LTT_EVENT_SYSCALL_ENTRY, + LTT_EVENT_SYSCALL_EXIT, + LTT_EVENT_TRAP_ENTRY, + LTT_EVENT_TRAP_EXIT, + LTT_EVENT_IRQ_ENTRY, + LTT_EVENT_IRQ_EXIT, + LTT_EVENT_SOFT_IRQ_RAISE, + LTT_EVENT_SOFT_IRQ_ENTRY, + LTT_EVENT_SOFT_IRQ_EXIT, + LTT_EVENT_SCHED_SCHEDULE, + LTT_EVENT_PROCESS_FORK, + LTT_EVENT_KTHREAD_CREATE, + LTT_EVENT_PROCESS_EXIT, + LTT_EVENT_PROCESS_FREE, + LTT_EVENT_EXEC, + LTT_EVENT_PROCESS_STATE, + LTT_EVENT_STATEDUMP_END, + LTT_EVENT_FUNCTION_ENTRY, + LTT_EVENT_FUNCTION_EXIT, + LTT_EVENT_THREAD_BRAND, + LTT_EVENT_REQUEST_ISSUE, + LTT_EVENT_REQUEST_COMPLETE, + LTT_EVENT_LIST_INTERRUPT, + LTT_EVENT_SYS_CALL_TABLE, + LTT_EVENT_SOFTIRQ_VEC; + +/* Fields Quarks */ + +extern GQuark + LTT_FIELD_SYSCALL_ID, + LTT_FIELD_TRAP_ID, + LTT_FIELD_IRQ_ID, + LTT_FIELD_SOFT_IRQ_ID, + LTT_FIELD_PREV_PID, + LTT_FIELD_NEXT_PID, + LTT_FIELD_PREV_STATE, + LTT_FIELD_PARENT_PID, + LTT_FIELD_CHILD_PID, + LTT_FIELD_PID, + LTT_FIELD_TGID, + LTT_FIELD_FILENAME, + LTT_FIELD_NAME, + LTT_FIELD_TYPE, + LTT_FIELD_MODE, + LTT_FIELD_SUBMODE, + LTT_FIELD_STATUS, + LTT_FIELD_THIS_FN, + LTT_FIELD_CALL_SITE, + LTT_FIELD_MINOR, + LTT_FIELD_MAJOR, + LTT_FIELD_OPERATION, + LTT_FIELD_ACTION, + LTT_FIELD_ID, + LTT_FIELD_ADDRESS, + LTT_FIELD_SYMBOL; + +typedef struct _LttvTracesetState LttvTracesetState; +typedef struct _LttvTracesetStateClass LttvTracesetStateClass; + +typedef struct _LttvTraceState LttvTraceState; +typedef struct _LttvTraceStateClass LttvTraceStateClass; + +typedef struct _LttvTracefileState LttvTracefileState; +typedef struct _LttvTracefileStateClass LttvTracefileStateClass; + +gint lttv_state_hook_add_event_hooks(void *hook_data, void *call_data); +void lttv_state_add_event_hooks(LttvTracesetState *self); + +gint lttv_state_hook_remove_event_hooks(void *hook_data, void *call_data); +void lttv_state_remove_event_hooks(LttvTracesetState *self); + +void lttv_state_save_add_event_hooks(LttvTracesetState *self); +// Hook wrapper. call_data is a trace context. +gint lttv_state_save_hook_add_event_hooks(void *hook_data, void *call_data); + +void lttv_state_save_remove_event_hooks(LttvTracesetState *self); +// Hook wrapper. call_data is a trace context. +gint lttv_state_save_hook_remove_event_hooks(void *hook_data, void *call_data); + +void lttv_state_traceset_seek_time_closest(LttvTracesetState *self, LttTime t); + +/* The LttvProcessState structure defines the current state for each process. + A process can make system calls (in some rare cases nested) and receive + interrupts/faults. For instance, a process may issue a system call, + generate a page fault while reading an argument from user space, and + get caught by an interrupt. To represent these nested states, an + execution mode stack is maintained. The stack bottom is normal user mode + and the top of stack is the current execution mode. + + The execution mode stack tells about the process status, execution mode and + submode (interrupt, system call or IRQ number). All these could be + defined as enumerations but may need extensions (e.g. new process state). + GQuark are thus used. They are as easy to manipulate as integers but have + a string associated, just like enumerations. + + The execution mode is one of "user mode", "kernel thread", "system call", + "interrupt request", "fault". */ + +typedef GQuark LttvExecutionMode; + +extern LttvExecutionMode + LTTV_STATE_USER_MODE, + LTTV_STATE_SYSCALL, + LTTV_STATE_TRAP, + LTTV_STATE_IRQ, + LTTV_STATE_SOFT_IRQ, + LTTV_STATE_MODE_UNKNOWN; + + +/* The submode number depends on the execution mode. For user mode or kernel + thread, which are the normal mode (execution mode stack bottom), + it is set to "none". For interrupt requests, faults and system calls, + it is set respectively to the interrupt name (e.g. "timer"), fault name + (e.g. "page fault"), and system call name (e.g. "select"). */ + +typedef GQuark LttvExecutionSubmode; + +extern LttvExecutionSubmode + LTTV_STATE_SUBMODE_NONE, + LTTV_STATE_SUBMODE_UNKNOWN; + +/* The process status is one of "running", "wait-cpu" (runnable), or "wait-*" + where "*" describes the resource waited for (e.g. timer, process, + disk...). */ + +typedef GQuark LttvProcessStatus; + +extern LttvProcessStatus + LTTV_STATE_UNNAMED, + LTTV_STATE_WAIT_FORK, + LTTV_STATE_WAIT_CPU, + LTTV_STATE_EXIT, + LTTV_STATE_ZOMBIE, + LTTV_STATE_WAIT, + LTTV_STATE_RUN, + LTTV_STATE_DEAD; + +extern GQuark + LTTV_STATE_UNBRANDED; + +typedef GQuark LttvProcessType; + +extern LttvProcessType + LTTV_STATE_USER_THREAD, + LTTV_STATE_KERNEL_THREAD; + +typedef GQuark LttvCPUMode; +extern LttvCPUMode + LTTV_CPU_UNKNOWN, + LTTV_CPU_IDLE, + LTTV_CPU_BUSY, + LTTV_CPU_IRQ, + LTTV_CPU_SOFT_IRQ, + LTTV_CPU_TRAP; + +typedef GQuark LttvIRQMode; +extern LttvIRQMode + LTTV_IRQ_UNKNOWN, + LTTV_IRQ_IDLE, + LTTV_IRQ_BUSY; + +typedef GQuark LttvBdevMode; +extern LttvBdevMode + LTTV_BDEV_UNKNOWN, + LTTV_BDEV_IDLE, + LTTV_BDEV_BUSY_READING, + LTTV_BDEV_BUSY_WRITING; + +typedef struct _LttvExecutionState { + LttvExecutionMode t; + LttvExecutionSubmode n; + LttTime entry; + LttTime change; + LttTime cum_cpu_time; + LttvProcessStatus s; +} LttvExecutionState; + +typedef struct _LttvProcessState { + guint pid; + guint tgid; + guint ppid; + LttTime creation_time; + LttTime insertion_time; + GQuark name; + GQuark brand; + GQuark pid_time; + GArray *execution_stack; /* Array of LttvExecutionState */ + LttvExecutionState *state; /* Top of interrupt stack */ + /* WARNING : each time the execution_stack size is modified, the state + * must be reget : g_array_set_size can have to move the array. + * (Mathieu) */ + guint cpu; /* CPU where process is scheduled (being either in + the active or inactive runqueue)*/ +// guint last_tracefile_index; /* index in the trace for cpu tracefile */ + LttvTracefileState *usertrace; /* Associated usertrace */ + /* opened file descriptors, address map?... */ + GArray *user_stack; /* User space function call stack */ + guint64 current_function; + LttvProcessType type; /* kernel thread or user space ? */ + guint target_pid; /* target PID of the current event. */ + guint free_events; /* 0 : none, 1 : free or exit dead, 2 : should delete */ +} LttvProcessState; + +#define ANY_CPU 0 /* For clarity sake : a call to lttv_state_find_process for + a PID != 0 will search on any cpu automatically. */ + +LttvProcessState * +lttv_state_find_process(LttvTraceState *ts, guint cpu, guint pid); + +LttvProcessState * +lttv_state_find_process_or_create(LttvTraceState *ts, guint cpu, guint pid, + const LttTime *timestamp); + +LttvProcessState * +lttv_state_create_process(LttvTraceState *tcs, LttvProcessState *parent, + guint cpu, guint pid, guint tgid, GQuark name, const LttTime *timestamp); + +void lttv_state_write(LttvTraceState *self, LttTime t, FILE *fp); +void lttv_state_write_raw(LttvTraceState *self, LttTime t, FILE *fp); + +/* The LttvTracesetState, LttvTraceState and LttvTracefileState types + inherit from the corresponding Context objects defined in processTrace. */ + +#define LTTV_TRACESET_STATE_TYPE (lttv_traceset_state_get_type ()) +#define LTTV_TRACESET_STATE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LTTV_TRACESET_STATE_TYPE, LttvTracesetState)) +#define LTTV_TRACESET_STATE_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), LTTV_TRACESET_STATE_TYPE, LttvTracesetStateClass)) +#define LTTV_IS_TRACESET_STATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LTTV_TRACESET_STATE_TYPE)) +#define LTTV_IS_TRACESET_STATE_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), LTTV_TRACESET_STATE_TYPE)) +#define LTTV_TRACESET_STATE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), LTTV_TRACESET_STATE_TYPE, LttvTracesetStateClass)) + +struct _LttvTracesetState { + LttvTracesetContext parent; +}; + +struct _LttvTracesetStateClass { + LttvTracesetContextClass parent; +}; + +GType lttv_traceset_state_get_type (void); + + +#define LTTV_TRACE_STATE_TYPE (lttv_trace_state_get_type ()) +#define LTTV_TRACE_STATE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LTTV_TRACE_STATE_TYPE, LttvTraceState)) +#define LTTV_TRACE_STATE_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), LTTV_TRACE_STATE_TYPE, LttvTraceStateClass)) +#define LTTV_IS_TRACE_STATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LTTV_TRACE_STATE_TYPE)) +#define LTTV_IS_TRACE_STATE_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), LTTV_TRACE_STATE_TYPE)) +#define LTTV_TRACE_STATE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), LTTV_TRACE_STATE_TYPE, LttvTraceStateClass)) + +typedef struct _LttvCPUState { + GArray *mode_stack; + gint last_irq; + gint last_soft_irq; + gint last_trap; +} LttvCPUState; + +typedef struct _LttvIRQState { + GArray *mode_stack; +} LttvIRQState; + +typedef struct _LttvSoftIRQState { + guint pending; /* number of times it is pending */ + guint running; /* number of times it is currently running (on different processors) */ +} LttvSoftIRQState; + +typedef struct _LttvTrapState { + guint running; /* number of times it is currently running (on different processors) */ +} LttvTrapState; + +typedef struct _LttvBdevState { + GArray *mode_stack; +} LttvBdevState; + +struct _LttvTraceState { + LttvTraceContext parent; + + GHashTable *processes; /* LttvProcessState objects indexed by pid and + last_cpu */ + GHashTable *usertraces; /* GPtrArray objects indexed by pid, containing + pointers to LttvTracefileState objects. */ + guint nb_event, save_interval; + /* Block/char devices, locks, memory pages... */ + GQuark *eventtype_names; + GQuark *syscall_names; + guint nb_syscalls; + GQuark *trap_names; + guint nb_traps; + guint nb_irqs; + guint nb_soft_irqs; + GQuark *irq_names; + GQuark *soft_irq_names; + LttTime *max_time_state_recomputed_in_seek; + + /* Array of per cpu running process */ + LttvProcessState **running_process; + gboolean has_precomputed_states; + LttvCPUState *cpu_states; /* state of each cpu */ + LttvIRQState *irq_states; /* state of each irq handler */ + LttvSoftIRQState *soft_irq_states; /* state of each softirq */ + LttvTrapState *trap_states; /* state of each trap */ + GHashTable *bdev_states; /* state of the block devices */ +}; + +struct _LttvTraceStateClass { + LttvTraceContextClass parent; + + void (*state_save) (LttvTraceState *self, LttvAttribute *container); + void (*state_restore) (LttvTraceState *self, LttvAttribute *container); + void (*state_saved_free) (LttvTraceState *self, LttvAttribute *container); +}; + +GType lttv_trace_state_get_type (void); + +void lttv_state_save(LttvTraceState *self, LttvAttribute *container); + +void lttv_state_restore(LttvTraceState *self, LttvAttribute *container); + +void lttv_state_state_saved_free(LttvTraceState *self, + LttvAttribute *container); + +int lttv_state_pop_state_cleanup(LttvProcessState *process, + LttvTracefileState *tfs); + +#define LTTV_TRACEFILE_STATE_TYPE (lttv_tracefile_state_get_type ()) +#define LTTV_TRACEFILE_STATE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LTTV_TRACEFILE_STATE_TYPE, LttvTracefileState)) +#define LTTV_TRACEFILE_STATE_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), LTTV_TRACEFILE_STATE_TYPE, LttvTracefileStateClass)) +#define LTTV_IS_TRACEFILE_STATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LTTV_TRACEFILE_STATE_TYPE)) +#define LTTV_IS_TRACEFILE_STATE_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), LTTV_TRACEFILE_STATE_TYPE)) +#define LTTV_TRACEFILE_STATE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), LTTV_TRACEFILE_STATE_TYPE, LttvTracefileStateClass)) + +struct _LttvTracefileState { + LttvTracefileContext parent; + + GQuark tracefile_name; + guint cpu; /* Current cpu of the tracefile */ /* perhaps merge in cpu_state */ + LttvCPUState *cpu_state; /* cpu resource state */ +}; + +struct _LttvTracefileStateClass { + LttvTracefileContextClass parent; +}; + +GType lttv_tracefile_state_get_type (void); + +static inline guint lttv_state_get_target_pid(LttvTracefileState *tfs) +{ + LttvTraceState *ts = (LttvTraceState*)tfs->parent.t_context; + guint cpu = tfs->cpu; + LttvProcessState *process = ts->running_process[cpu]; + + if(tfs->parent.target_pid >= 0) return tfs->parent.target_pid; + else return process->pid; +} + + +#define HDR_PROCESS 0 +#define HDR_ES 1 +#define HDR_USER_STACK 2 +#define HDR_USERTRACE 3 +#define HDR_PROCESS_STATE 4 +#define HDR_CPU 5 +#define HDR_TRACEFILE 6 +#define HDR_TRACESET 7 +#define HDR_TRACE 8 +#define HDR_QUARKS 9 +#define HDR_QUARK 10 + +/* Device number manipulation macros from kernel source */ +#define MINORBITS 20 +#define MINORMASK ((1U << MINORBITS) - 1) +#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) +#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) +#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi)) + +#endif // STATE_H diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/stats.c b/tags/lttv-0.11.3-23102008/lttv/lttv/stats.c new file mode 100644 index 00000000..0bfb4546 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/stats.c @@ -0,0 +1,1417 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#define BUF_SIZE 256 +#define MAX_64_HEX_STRING_LEN 19 + +GQuark + LTTV_STATS_PROCESS_UNKNOWN, + LTTV_STATS_PROCESSES, + LTTV_STATS_CPU, + LTTV_STATS_MODE_TYPES, + LTTV_STATS_MODES, + LTTV_STATS_SUBMODES, + LTTV_STATS_FUNCTIONS, + LTTV_STATS_EVENT_TYPES, + LTTV_STATS_CPU_TIME, + LTTV_STATS_CUMULATIVE_CPU_TIME, + LTTV_STATS_ELAPSED_TIME, + LTTV_STATS_EVENTS, + LTTV_STATS_EVENTS_COUNT, + LTTV_STATS_USE_COUNT, + LTTV_STATS, + LTTV_STATS_TRACEFILES, + LTTV_STATS_SUMMED, + LTTV_STATS_BEFORE_HOOKS, + LTTV_STATS_AFTER_HOOKS; + +static void +find_event_tree(LttvTracefileStats *tfcs, GQuark pid_time, guint cpu, + guint64 function, + GQuark mode, GQuark sub_mode, LttvAttribute **events_tree, + LttvAttribute **event_types_tree); + + +static void lttv_stats_init(LttvTracesetStats *self) +{ + guint i, j, nb_trace, nb_tracefile; + + LttvTraceContext *tc; + + LttvTraceStats *tcs; + + LttvTracefileContext **tfs; + LttvTracefileStats *tfcs; + + LttvAttributeValue v; + + LttvAttribute *tracefiles_stats; + + LttvTraceset *ts = self->parent.parent.ts; + + self->stats = lttv_attribute_find_subdir( + lttv_traceset_attribute(self->parent.parent.ts), + LTTV_STATS); + lttv_attribute_find(lttv_traceset_attribute(self->parent.parent.ts), + LTTV_STATS_USE_COUNT, + LTTV_UINT, &v); + + (*(v.v_uint))++; + if(*(v.v_uint) == 1) { + g_assert(lttv_attribute_get_number(self->stats) == 0); + } + + nb_trace = lttv_traceset_number(ts); + + for(i = 0 ; i < nb_trace ; i++) { + tc = self->parent.parent.traces[i]; + tcs = LTTV_TRACE_STATS(tc); + + tcs->stats = lttv_attribute_find_subdir(tcs->parent.parent.t_a,LTTV_STATS); + tracefiles_stats = lttv_attribute_find_subdir(tcs->parent.parent.t_a, + LTTV_STATS_TRACEFILES); + lttv_attribute_find(tcs->parent.parent.t_a, LTTV_STATS_USE_COUNT, + LTTV_UINT, &v); + + (*(v.v_uint))++; + if(*(v.v_uint) == 1) { + g_assert(lttv_attribute_get_number(tcs->stats) == 0); + } + + nb_tracefile = tc->tracefiles->len; + + for(j = 0 ; j < nb_tracefile ; j++) { + tfs = &g_array_index(tc->tracefiles, + LttvTracefileContext*, j); + tfcs = LTTV_TRACEFILE_STATS(*tfs); + tfcs->stats = lttv_attribute_find_subdir(tracefiles_stats, + ltt_tracefile_long_name(tfcs->parent.parent.tf)); + guint cpu = tfcs->parent.cpu; + find_event_tree(tfcs, LTTV_STATS_PROCESS_UNKNOWN, + cpu, + 0x0ULL, + LTTV_STATE_MODE_UNKNOWN, + LTTV_STATE_SUBMODE_UNKNOWN, &tfcs->current_events_tree, + &tfcs->current_event_types_tree); + } + } + +} + +static void lttv_stats_fini(LttvTracesetStats *self) +{ + guint i, j, nb_trace, nb_tracefile; + + LttvTraceset *ts; + + LttvTraceContext *tc; + + LttvTraceStats *tcs; + + LttvTracefileContext *tfc; + + LttvTracefileStats *tfcs; + + LttvAttributeValue v; + + LttvAttribute *tracefiles_stats; + + lttv_attribute_find(self->parent.parent.ts_a, LTTV_STATS_USE_COUNT, + LTTV_UINT, &v); + (*(v.v_uint))--; + + if(*(v.v_uint) == 0) { + lttv_attribute_remove_by_name(self->parent.parent.ts_a, LTTV_STATS); + } + self->stats = NULL; + + ts = self->parent.parent.ts; + nb_trace = lttv_traceset_number(ts); + + for(i = 0 ; i < nb_trace ; i++) { + tcs = (LttvTraceStats *)(tc = (LTTV_TRACESET_CONTEXT(self)->traces[i])); + + lttv_attribute_find(tcs->parent.parent.t_a, LTTV_STATS_USE_COUNT, + LTTV_UINT, &v); + (*(v.v_uint))--; + + if(*(v.v_uint) == 0) { + lttv_attribute_remove_by_name(tcs->parent.parent.t_a,LTTV_STATS); + tracefiles_stats = lttv_attribute_find_subdir(tcs->parent.parent.t_a, + LTTV_STATS_TRACEFILES); + lttv_attribute_remove_by_name(tcs->parent.parent.t_a, + LTTV_STATS_TRACEFILES); + } + tcs->stats = NULL; + + nb_tracefile = tc->tracefiles->len; + + for(j = 0 ; j < nb_tracefile ; j++) { + tfc = g_array_index(tc->tracefiles, + LttvTracefileContext*, j); + tfcs = (LttvTracefileStats *)tfc; + tfcs->stats = NULL; + tfcs->current_events_tree = NULL; + tfcs->current_event_types_tree = NULL; + } + } +} + + +void lttv_stats_reset(LttvTracesetStats *self) +{ + lttv_stats_fini(self); + lttv_stats_init(self); +} + + + +static void +init(LttvTracesetStats *self, LttvTraceset *ts) +{ + LTTV_TRACESET_CONTEXT_CLASS(g_type_class_peek(LTTV_TRACESET_STATE_TYPE))-> + init((LttvTracesetContext *)self, ts); + + lttv_stats_init(self); +} + + +static void +fini(LttvTracesetStats *self) +{ + lttv_stats_fini(self); + + LTTV_TRACESET_CONTEXT_CLASS(g_type_class_peek(LTTV_TRACESET_STATE_TYPE))-> + fini((LttvTracesetContext *)self); +} + + +static LttvTracesetContext * +new_traceset_context(LttvTracesetContext *self) +{ + return LTTV_TRACESET_CONTEXT(g_object_new(LTTV_TRACESET_STATS_TYPE, NULL)); +} + + +static LttvTraceContext * +new_trace_context(LttvTracesetContext *self) +{ + return LTTV_TRACE_CONTEXT(g_object_new(LTTV_TRACE_STATS_TYPE, NULL)); +} + + +static LttvTracefileContext * +new_tracefile_context(LttvTracesetContext *self) +{ + return LTTV_TRACEFILE_CONTEXT(g_object_new(LTTV_TRACEFILE_STATS_TYPE, NULL)); +} + + +static void +traceset_stats_instance_init (GTypeInstance *instance, gpointer g_class) +{ +} + + +static void +traceset_stats_finalize (LttvTracesetStats *self) +{ + G_OBJECT_CLASS(g_type_class_peek(LTTV_TRACESET_STATE_TYPE))-> + finalize(G_OBJECT(self)); +} + + +static void +traceset_stats_class_init (LttvTracesetContextClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + + gobject_class->finalize = (void (*)(GObject *self)) traceset_stats_finalize; + klass->init = (void (*)(LttvTracesetContext *self, LttvTraceset *ts))init; + klass->fini = (void (*)(LttvTracesetContext *self))fini; + klass->new_traceset_context = new_traceset_context; + klass->new_trace_context = new_trace_context; + klass->new_tracefile_context = new_tracefile_context; +} + + +GType +lttv_traceset_stats_get_type(void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (LttvTracesetStatsClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) traceset_stats_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (LttvTracesetStats), + 0, /* n_preallocs */ + (GInstanceInitFunc) traceset_stats_instance_init, /* instance_init */ + NULL /* Value handling */ + }; + + type = g_type_register_static (LTTV_TRACESET_STATE_TYPE, + "LttvTracesetStatsType", + &info, 0); + } + return type; +} + + +static void +trace_stats_instance_init (GTypeInstance *instance, gpointer g_class) +{ +} + + +static void +trace_stats_finalize (LttvTraceStats *self) +{ + G_OBJECT_CLASS(g_type_class_peek(LTTV_TRACE_STATE_TYPE))-> + finalize(G_OBJECT(self)); +} + + +static void +trace_stats_class_init (LttvTraceContextClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + + gobject_class->finalize = (void (*)(GObject *self)) trace_stats_finalize; +} + + +GType +lttv_trace_stats_get_type(void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (LttvTraceStatsClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) trace_stats_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (LttvTraceStats), + 0, /* n_preallocs */ + (GInstanceInitFunc) trace_stats_instance_init, /* instance_init */ + NULL /* Value handling */ + }; + + type = g_type_register_static (LTTV_TRACE_STATE_TYPE, + "LttvTraceStatsType", &info, 0); + } + return type; +} + + +static void +tracefile_stats_instance_init (GTypeInstance *instance, gpointer g_class) +{ +} + + +static void +tracefile_stats_finalize (LttvTracefileStats *self) +{ + G_OBJECT_CLASS(g_type_class_peek(LTTV_TRACEFILE_STATE_TYPE))-> + finalize(G_OBJECT(self)); +} + + +static void +tracefile_stats_class_init (LttvTracefileStatsClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + + gobject_class->finalize = (void (*)(GObject *self)) tracefile_stats_finalize; +} + + +GType +lttv_tracefile_stats_get_type(void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (LttvTracefileStatsClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) tracefile_stats_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (LttvTracefileStats), + 0, /* n_preallocs */ + (GInstanceInitFunc) tracefile_stats_instance_init, /* instance_init */ + NULL /* Value handling */ + }; + + type = g_type_register_static (LTTV_TRACEFILE_STATE_TYPE, + "LttvTracefileStatsType", &info, 0); + } + return type; +} + +static void +find_event_tree(LttvTracefileStats *tfcs, + GQuark pid_time, + guint cpu, + guint64 function, + GQuark mode, + GQuark sub_mode, + LttvAttribute **events_tree, + LttvAttribute **event_types_tree) +{ + LttvAttribute *a; + gchar fstring[MAX_64_HEX_STRING_LEN]; + gint ret; + + ret = snprintf(fstring, MAX_64_HEX_STRING_LEN-1, + "0x%llX", function) > 0; + g_assert(ret > 0); + fstring[MAX_64_HEX_STRING_LEN-1] = '\0'; + + LttvTraceStats *tcs = (LttvTraceStats*)tfcs->parent.parent.t_context; + a = lttv_attribute_find_subdir(tcs->stats, LTTV_STATS_PROCESSES); + a = lttv_attribute_find_subdir(a, pid_time); + a = lttv_attribute_find_subdir(a, LTTV_STATS_CPU); + a = lttv_attribute_find_subdir_unnamed(a, cpu); + a = lttv_attribute_find_subdir(a, LTTV_STATS_FUNCTIONS); + a = lttv_attribute_find_subdir(a, g_quark_from_string(fstring)); + a = lttv_attribute_find_subdir(a, LTTV_STATS_MODE_TYPES); + a = lttv_attribute_find_subdir(a, mode); + a = lttv_attribute_find_subdir(a, LTTV_STATS_SUBMODES); + a = lttv_attribute_find_subdir(a, sub_mode); + *events_tree = a; + a = lttv_attribute_find_subdir(a, LTTV_STATS_EVENT_TYPES); + *event_types_tree = a; +} + +static void update_event_tree(LttvTracefileStats *tfcs) +{ + guint cpu = tfcs->parent.cpu; + LttvTraceState *ts = (LttvTraceState *)tfcs->parent.parent.t_context; + LttvProcessState *process = ts->running_process[cpu]; + LttvExecutionState *es = process->state; + + find_event_tree(tfcs, process->pid_time, + cpu, + process->current_function, + es->t, es->n, &(tfcs->current_events_tree), + &(tfcs->current_event_types_tree)); +} + + +/* Update the trace event tree for the specified cpu */ +static void update_trace_event_tree(LttvTraceStats *tcs) +{ + LttvTracefileStats *tfcs; + LttvTraceContext *tc = (LttvTraceContext*)tcs; + guint j, nb_tracefile; + + /* For each tracefile, update the event tree */ + nb_tracefile = tc->tracefiles->len; + for(j = 0; j < nb_tracefile; j++) { + tfcs = LTTV_TRACEFILE_STATS(g_array_index(tc->tracefiles, + LttvTracefileContext*, j)); + update_event_tree(tfcs); + } +} + +static void mode_change(LttvTracefileStats *tfcs) +{ + LttvTraceState *ts = (LttvTraceState *)tfcs->parent.parent.t_context; + guint cpu = tfcs->parent.cpu; + LttvProcessState *process = ts->running_process[cpu]; + LttvAttributeValue cpu_time; + + LttTime delta; + + if(process->state->s == LTTV_STATE_RUN && + process->state->t != LTTV_STATE_MODE_UNKNOWN) + delta = ltt_time_sub(tfcs->parent.parent.timestamp, + process->state->change); + else + delta = ltt_time_zero; + + lttv_attribute_find(tfcs->current_events_tree, LTTV_STATS_CPU_TIME, + LTTV_TIME, &cpu_time); + *(cpu_time.v_time) = ltt_time_add(*(cpu_time.v_time), delta); + + process->state->cum_cpu_time = ltt_time_add(process->state->cum_cpu_time, + delta); +} + +/* Note : every mode_end must come with a cumulative cpu time update in the + * after hook. */ +static void mode_end(LttvTracefileStats *tfcs) +{ + LttvTraceState *ts = (LttvTraceState *)tfcs->parent.parent.t_context; + guint cpu = tfcs->parent.cpu; + LttvProcessState *process = ts->running_process[cpu]; + LttvAttributeValue elapsed_time, cpu_time, cum_cpu_time; + + LttTime delta; + + /* FIXME put there in case of a missing update after a state modification */ + //void *lasttree = tfcs->current_events_tree; + //update_event_tree(tfcs); + //g_assert (lasttree == tfcs->current_events_tree); + lttv_attribute_find(tfcs->current_events_tree, LTTV_STATS_ELAPSED_TIME, + LTTV_TIME, &elapsed_time); + + if(process->state->t != LTTV_STATE_MODE_UNKNOWN) { + delta = ltt_time_sub(tfcs->parent.parent.timestamp, + process->state->entry); + } else + delta = ltt_time_zero; + + *(elapsed_time.v_time) = ltt_time_add(*(elapsed_time.v_time), delta); + + lttv_attribute_find(tfcs->current_events_tree, LTTV_STATS_CPU_TIME, + LTTV_TIME, &cpu_time); + + /* if it is a running mode, we must count its cpu time */ + if(process->state->s == LTTV_STATE_RUN && + process->state->t != LTTV_STATE_MODE_UNKNOWN) + delta = ltt_time_sub(tfcs->parent.parent.timestamp, + process->state->change); + else + delta = ltt_time_zero; + + *(cpu_time.v_time) = ltt_time_add(*(cpu_time.v_time), delta); + process->state->cum_cpu_time = ltt_time_add(process->state->cum_cpu_time, + delta); + + lttv_attribute_find(tfcs->current_events_tree, LTTV_STATS_CUMULATIVE_CPU_TIME, + LTTV_TIME, &cum_cpu_time); + *(cum_cpu_time.v_time) = ltt_time_add(*(cum_cpu_time.v_time), + process->state->cum_cpu_time); +} + + +static void after_mode_end(LttvTracefileStats *tfcs) +{ + LttvTraceState *ts = (LttvTraceState *)tfcs->parent.parent.t_context; + guint cpu = tfcs->parent.cpu; + LttvProcessState *process = ts->running_process[cpu]; + + LttTime nested_delta; + + nested_delta = process->state->cum_cpu_time; + process->state->cum_cpu_time = ltt_time_zero; /* For after traceset hook */ + + update_event_tree(tfcs); + + process->state->cum_cpu_time = ltt_time_add(process->state->cum_cpu_time, + nested_delta); +} + +static gboolean before_syscall_entry(void *hook_data, void *call_data) +{ + mode_change((LttvTracefileStats *)call_data); + return FALSE; +} + + +static gboolean after_syscall_entry(void *hook_data, void *call_data) +{ + update_event_tree((LttvTracefileStats *)call_data); + return FALSE; +} + + +static gboolean before_syscall_exit(void *hook_data, void *call_data) +{ + mode_end((LttvTracefileStats *)call_data); + return FALSE; +} + + +static gboolean after_syscall_exit(void *hook_data, void *call_data) +{ + after_mode_end((LttvTracefileStats *)call_data); + return FALSE; +} + + +static gboolean before_trap_entry(void *hook_data, void *call_data) +{ + mode_change((LttvTracefileStats *)call_data); + return FALSE; +} + + +static gboolean after_trap_entry(void *hook_data, void *call_data) +{ + update_event_tree((LttvTracefileStats *)call_data); + return FALSE; +} + + +static gboolean before_trap_exit(void *hook_data, void *call_data) +{ + mode_end((LttvTracefileStats *)call_data); + return FALSE; +} + + +static gboolean after_trap_exit(void *hook_data, void *call_data) +{ + after_mode_end((LttvTracefileStats *)call_data); + return FALSE; +} + + +static gboolean before_irq_entry(void *hook_data, void *call_data) +{ + mode_change((LttvTracefileStats *)call_data); + return FALSE; +} + +static gboolean after_irq_entry(void *hook_data, void *call_data) +{ + update_event_tree((LttvTracefileStats *)call_data); + return FALSE; +} + + +static gboolean before_irq_exit(void *hook_data, void *call_data) +{ + mode_end((LttvTracefileStats *)call_data); + return FALSE; +} + + +static gboolean after_irq_exit(void *hook_data, void *call_data) +{ + after_mode_end((LttvTracefileStats *)call_data); + return FALSE; +} + + +static gboolean before_soft_irq_entry(void *hook_data, void *call_data) +{ + mode_change((LttvTracefileStats *)call_data); + return FALSE; +} + +static gboolean after_soft_irq_entry(void *hook_data, void *call_data) +{ + update_event_tree((LttvTracefileStats *)call_data); + return FALSE; +} + +static gboolean before_soft_irq_exit(void *hook_data, void *call_data) +{ + mode_end((LttvTracefileStats *)call_data); + return FALSE; +} + + +static gboolean after_soft_irq_exit(void *hook_data, void *call_data) +{ + after_mode_end((LttvTracefileStats *)call_data); + return FALSE; +} + +static gboolean before_function_entry(void *hook_data, void *call_data) +{ + mode_change((LttvTracefileStats *)call_data); + return FALSE; +} + +static gboolean after_function_entry(void *hook_data, void *call_data) +{ + update_event_tree((LttvTracefileStats *)call_data); + return FALSE; +} + +static gboolean before_function_exit(void *hook_data, void *call_data) +{ + mode_end((LttvTracefileStats *)call_data); + return FALSE; +} + +static gboolean after_function_exit(void *hook_data, void *call_data) +{ + after_mode_end((LttvTracefileStats *)call_data); + return FALSE; +} + + +static gboolean before_schedchange(void *hook_data, void *call_data) +{ + LttvTracefileStats *tfcs = (LttvTracefileStats *)call_data; + + LttEvent *e = ltt_tracefile_get_event(tfcs->parent.parent.tf); + + LttvTraceHook *th = (LttvTraceHook *)hook_data; + + guint pid_in, pid_out; + + gint64 state_out; + + pid_out = ltt_event_get_unsigned(e, lttv_trace_get_hook_field(th, 0)); + pid_in = ltt_event_get_unsigned(e, lttv_trace_get_hook_field(th, 1)); + state_out = ltt_event_get_long_int(e, lttv_trace_get_hook_field(th, 2)); + + /* compute the time for the process to schedule out */ + mode_change(tfcs); + + return FALSE; +} + +static gboolean after_schedchange(void *hook_data, void *call_data) +{ + LttvTracefileStats *tfcs = (LttvTracefileStats *)call_data; + + LttvTraceState *ts = (LttvTraceState*)tfcs->parent.parent.t_context; + + LttEvent *e = ltt_tracefile_get_event(tfcs->parent.parent.tf); + + LttvTraceHook *th = (LttvTraceHook *)hook_data; + + guint pid_in, pid_out; + + gint64 state_out; + + LttvProcessState *process; + + pid_out = ltt_event_get_unsigned(e, lttv_trace_get_hook_field(th, 0)); + pid_in = ltt_event_get_unsigned(e, lttv_trace_get_hook_field(th, 1)); + state_out = ltt_event_get_long_int(e, lttv_trace_get_hook_field(th, 2)); + + /* get the information for the process scheduled in */ + guint cpu = tfcs->parent.cpu; + process = ts->running_process[cpu]; + + find_event_tree(tfcs, process->pid_time, + cpu, + process->current_function, + process->state->t, process->state->n, &(tfcs->current_events_tree), + &(tfcs->current_event_types_tree)); + + /* compute the time waiting for the process to schedule in */ + mode_change(tfcs); + + return FALSE; +} + +static gboolean process_fork(void *hook_data, void *call_data) +{ + return FALSE; +} + +static gboolean process_exit(void *hook_data, void *call_data) +{ + update_event_tree((LttvTracefileStats *)call_data); + return FALSE; +} + +static gboolean before_enum_process_state(void *hook_data, void *call_data) +{ +#if 0 + /* Broken : adds up time in the current process doing the dump */ + LttvTracefileStats *tfcs = (LttvTracefileStats *)call_data; + mode_end(tfcs); + after_mode_end(tfcs); + mode_change(tfcs); +#endif //0 + return FALSE; +} + +static gboolean after_enum_process_state(void *hook_data, void *call_data) +{ + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + LttvTraceStats *tcs = (LttvTraceStats*)tfc->t_context; + update_trace_event_tree(tcs); + return FALSE; +} + +static gboolean after_statedump_end(void *hook_data, void *call_data) +{ + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + LttvTraceStats *tcs = (LttvTraceStats*)tfc->t_context; + update_trace_event_tree(tcs); + return FALSE; +} + +static gboolean process_free(void *hook_data, void *call_data) +{ + return FALSE; +} + +static gboolean every_event(void *hook_data, void *call_data) +{ + LttvTracefileStats *tfcs = (LttvTracefileStats *)call_data; + + LttEvent *e = ltt_tracefile_get_event(tfcs->parent.parent.tf); + + LttvAttributeValue v; + + LttTrace *trace = ((LttvTracefileContext *)tfcs)->t_context->t; + + struct marker_info *info; + + /* The current branch corresponds to the tracefile/process/interrupt state. + Statistics are added within it, to count the number of events of this + type occuring in this context. A quark has been pre-allocated for each + event type and is used as name. */ + + info = marker_get_info_from_id(trace, e->event_id); + + lttv_attribute_find(tfcs->current_event_types_tree, + info->name, LTTV_UINT, &v); + (*(v.v_uint))++; + return FALSE; +} + +struct cleanup_state_struct { + LttvTraceState *ts; + LttTime current_time; +}; + +//static void lttv_stats_cleanup_process_state(LttvTraceState *ts, +// LttvProcessState *process, LttTime current_time) +static void lttv_stats_cleanup_process_state(gpointer key, gpointer value, + gpointer user_data) +{ + struct cleanup_state_struct *cleanup_closure = + (struct cleanup_state_struct *)user_data; + LttvTraceState *ts = cleanup_closure->ts; + LttvProcessState *process = (LttvProcessState *)value; + LttTime current_time = cleanup_closure->current_time; + LttvTracefileStats **tfs = (LttvTracefileStats **) + &g_array_index(ts->parent.tracefiles, LttvTracefileContext*, + process->cpu); + int cleanup_empty = 0; + LttTime nested_delta = ltt_time_zero; + + /* FIXME : ok, this is a hack. The time is infinite here :( */ + //LttTime save_time = (*tfs)->parent.parent.timestamp; + //LttTime start, end; + //ltt_trace_time_span_get(ts->parent.t, &start, &end); + //(*tfs)->parent.parent.timestamp = end; + + do { + if(ltt_time_compare(process->state->cum_cpu_time, ltt_time_zero) != 0) { + find_event_tree(*tfs, process->pid_time, + process->cpu, + process->current_function, + process->state->t, process->state->n, &((*tfs)->current_events_tree), + &((*tfs)->current_event_types_tree)); + /* Call mode_end only if not at end of trace */ + if(ltt_time_compare(current_time, ltt_time_infinite) != 0) + mode_end(*tfs); + nested_delta = process->state->cum_cpu_time; + } + cleanup_empty = lttv_state_pop_state_cleanup(process, + (LttvTracefileState *)*tfs); + process->state->cum_cpu_time = ltt_time_add(process->state->cum_cpu_time, + nested_delta); + + } while(cleanup_empty != 1); + + //(*tfs)->parent.parent.timestamp = save_time; +} + +/* For each cpu, for each of their stacked states, + * perform sum of needed values. */ +static void lttv_stats_cleanup_state(LttvTraceStats *tcs, LttTime current_time) +{ + LttvTraceState *ts = (LttvTraceState *)tcs; + struct cleanup_state_struct cleanup_closure; +#if 0 + guint nb_cpus, i; + + nb_cpus = ltt_trace_get_num_cpu(ts->parent.t); + + for(i=0; irunning_process[i], current_time); + } +#endif //0 + cleanup_closure.ts = ts; + cleanup_closure.current_time = current_time; + g_hash_table_foreach(ts->processes, lttv_stats_cleanup_process_state, + &cleanup_closure); +} + +void +lttv_stats_sum_trace(LttvTraceStats *self, LttvAttribute *ts_stats, + LttTime current_time) +{ + LttvAttribute *sum_container = self->stats; + + LttvAttributeType type; + + LttvAttributeValue value; + + LttvAttributeName name; + + gboolean is_named; + + unsigned sum; + + int trace_is_summed; + + int i, j, k, l, m, nb_process, nb_cpu, nb_mode_type, nb_submode, + nb_event_type, nf, nb_functions; + + LttvAttribute *main_tree, *processes_tree, *process_tree, *cpus_tree, + *cpu_tree, *mode_tree, *mode_types_tree, *submodes_tree, + *submode_tree, *event_types_tree, *mode_events_tree, + *cpu_functions_tree, + *function_tree, + *function_mode_types_tree, + *trace_cpu_tree; + + + main_tree = sum_container; + + lttv_attribute_find(sum_container, + LTTV_STATS_SUMMED, + LTTV_UINT, &value); + trace_is_summed = *(value.v_uint); + *(value.v_uint) = 1; + + /* First cleanup the state : sum all stalled information (never ending + * states). */ + if(!trace_is_summed) + lttv_stats_cleanup_state(self, current_time); + + processes_tree = lttv_attribute_find_subdir(main_tree, + LTTV_STATS_PROCESSES); + nb_process = lttv_attribute_get_number(processes_tree); + + for(i = 0 ; i < nb_process ; i++) { + type = lttv_attribute_get(processes_tree, i, &name, &value, &is_named); + process_tree = LTTV_ATTRIBUTE(*(value.v_gobject)); + + cpus_tree = lttv_attribute_find_subdir(process_tree, LTTV_STATS_CPU); + nb_cpu = lttv_attribute_get_number(cpus_tree); + + for(j = 0 ; j < nb_cpu ; j++) { + type = lttv_attribute_get(cpus_tree, j, &name, &value, &is_named); + cpu_tree = LTTV_ATTRIBUTE(*(value.v_gobject)); + + trace_cpu_tree = lttv_attribute_find_subdir(main_tree, LTTV_STATS_CPU); + trace_cpu_tree = lttv_attribute_find_subdir_unnamed(trace_cpu_tree, name); + cpu_functions_tree = lttv_attribute_find_subdir(cpu_tree, + LTTV_STATS_FUNCTIONS); + nb_functions = lttv_attribute_get_number(cpu_functions_tree); + + for(nf=0; nf < nb_functions; nf++) { + type = lttv_attribute_get(cpu_functions_tree, nf, &name, &value, + &is_named); + function_tree = LTTV_ATTRIBUTE(*(value.v_gobject)); + function_mode_types_tree = lttv_attribute_find_subdir(function_tree, + LTTV_STATS_MODE_TYPES); + nb_mode_type = lttv_attribute_get_number(function_mode_types_tree); + for(k = 0 ; k < nb_mode_type ; k++) { + type = lttv_attribute_get(function_mode_types_tree, k, &name, &value, + &is_named); + mode_tree = LTTV_ATTRIBUTE(*(value.v_gobject)); + + submodes_tree = lttv_attribute_find_subdir(mode_tree, + LTTV_STATS_SUBMODES); + mode_events_tree = lttv_attribute_find_subdir(mode_tree, + LTTV_STATS_EVENTS); + mode_types_tree = lttv_attribute_find_subdir(mode_tree, + LTTV_STATS_MODE_TYPES); + + nb_submode = lttv_attribute_get_number(submodes_tree); + + for(l = 0 ; l < nb_submode ; l++) { + type = lttv_attribute_get(submodes_tree, l, &name, &value, + &is_named); + submode_tree = LTTV_ATTRIBUTE(*(value.v_gobject)); + + event_types_tree = lttv_attribute_find_subdir(submode_tree, + LTTV_STATS_EVENT_TYPES); + nb_event_type = lttv_attribute_get_number(event_types_tree); + + sum = 0; + for(m = 0 ; m < nb_event_type ; m++) { + type = lttv_attribute_get(event_types_tree, m, &name, &value, + &is_named); + sum += *(value.v_uint); + } + lttv_attribute_find(submode_tree, LTTV_STATS_EVENTS_COUNT, + LTTV_UINT, &value); + *(value.v_uint) = sum; + + type = lttv_attribute_get(submodes_tree, l, &name, &value, + &is_named); + submode_tree = LTTV_ATTRIBUTE(*(value.v_gobject)); + if(!trace_is_summed) { + lttv_attribute_recursive_add(mode_events_tree, event_types_tree); + lttv_attribute_recursive_add(mode_types_tree, submode_tree); + } + } + if(!trace_is_summed) { + lttv_attribute_recursive_add(function_tree, mode_types_tree); + } + } + if(!trace_is_summed) { + lttv_attribute_recursive_add(cpu_tree, function_tree); + lttv_attribute_recursive_add(process_tree, function_tree); + lttv_attribute_recursive_add(trace_cpu_tree, function_tree); + lttv_attribute_recursive_add(main_tree, function_tree); + } + lttv_attribute_recursive_add(ts_stats, function_tree); + } + } + } +} + + +gboolean lttv_stats_sum_traceset_hook(void *hook_data, void *call_data) +{ + struct sum_traceset_closure *closure = + (struct sum_traceset_closure *)call_data; + lttv_stats_sum_traceset(closure->tss, closure->current_time); + return 0; +} + +void +lttv_stats_sum_traceset(LttvTracesetStats *self, LttTime current_time) +{ + LttvTraceset *traceset = self->parent.parent.ts; + LttvAttribute *sum_container = self->stats; + + LttvTraceStats *tcs; + + int i, nb_trace; + + LttvAttributeValue value; + + lttv_attribute_find(sum_container, LTTV_STATS_SUMMED, + LTTV_UINT, &value); + if(*(value.v_uint) != 0) return; + *(value.v_uint) = 1; + + nb_trace = lttv_traceset_number(traceset); + + for(i = 0 ; i < nb_trace ; i++) { + tcs = (LttvTraceStats *)(self->parent.parent.traces[i]); + lttv_stats_sum_trace(tcs, self->stats, current_time); + // lttv_attribute_recursive_add(sum_container, tcs->stats); + } +} + + +// Hook wrapper. call_data is a traceset context. +gboolean lttv_stats_hook_add_event_hooks(void *hook_data, void *call_data) +{ + LttvTracesetStats *tss = (LttvTracesetStats*)call_data; + + lttv_stats_add_event_hooks(tss); + + return 0; +} + +void lttv_stats_add_event_hooks(LttvTracesetStats *self) +{ + LttvTraceset *traceset = self->parent.parent.ts; + + guint i, j, k, nb_trace, nb_tracefile; + + LttvTraceStats *ts; + + LttvTracefileStats *tfs; + + GArray *hooks, *before_hooks, *after_hooks; + + LttvTraceHook *th; + + LttvAttributeValue val; + + nb_trace = lttv_traceset_number(traceset); + for(i = 0 ; i < nb_trace ; i++) { + ts = (LttvTraceStats *)self->parent.parent.traces[i]; + + /* Find the eventtype id for the following events and register the + associated by id hooks. */ + + hooks = g_array_sized_new(FALSE, FALSE, sizeof(LttvTraceHook), 12); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_KERNEL_ARCH, + LTT_EVENT_SYSCALL_ENTRY, + FIELD_ARRAY(LTT_FIELD_SYSCALL_ID), + before_syscall_entry, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_KERNEL_ARCH, + LTT_EVENT_SYSCALL_EXIT, + NULL, + before_syscall_exit, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_KERNEL_ARCH, + LTT_EVENT_TRAP_ENTRY, + FIELD_ARRAY(LTT_FIELD_TRAP_ID), + before_trap_entry, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_KERNEL_ARCH, + LTT_EVENT_TRAP_EXIT, + NULL, + before_trap_exit, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_IRQ_ENTRY, + FIELD_ARRAY(LTT_FIELD_IRQ_ID), + before_irq_entry, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_IRQ_EXIT, + NULL, + before_irq_exit, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SOFT_IRQ_ENTRY, + FIELD_ARRAY(LTT_FIELD_SOFT_IRQ_ID), + before_soft_irq_entry, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SOFT_IRQ_EXIT, + NULL, + before_soft_irq_exit, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SCHED_SCHEDULE, + FIELD_ARRAY(LTT_FIELD_PREV_PID, LTT_FIELD_NEXT_PID, LTT_FIELD_PREV_STATE), + before_schedchange, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_USER_GENERIC, + LTT_EVENT_FUNCTION_ENTRY, + FIELD_ARRAY(LTT_FIELD_THIS_FN, LTT_FIELD_CALL_SITE), + before_function_entry, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_USER_GENERIC, + LTT_EVENT_FUNCTION_EXIT, + FIELD_ARRAY(LTT_FIELD_THIS_FN, LTT_FIELD_CALL_SITE), + before_function_exit, NULL, + &hooks); + + /* statedump-related hooks */ + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_LIST, + LTT_EVENT_PROCESS_STATE, + FIELD_ARRAY(LTT_FIELD_PID, LTT_FIELD_PARENT_PID, LTT_FIELD_NAME), + before_enum_process_state, NULL, + &hooks); + + before_hooks = hooks; + + hooks = g_array_sized_new(FALSE, FALSE, sizeof(LttvTraceHook), 16); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_KERNEL_ARCH, + LTT_EVENT_SYSCALL_ENTRY, + FIELD_ARRAY(LTT_FIELD_SYSCALL_ID), + after_syscall_entry, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_KERNEL_ARCH, + LTT_EVENT_SYSCALL_EXIT, + NULL, + after_syscall_exit, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_KERNEL_ARCH, + LTT_EVENT_TRAP_ENTRY, + FIELD_ARRAY(LTT_FIELD_TRAP_ID), + after_trap_entry, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_KERNEL_ARCH, + LTT_EVENT_TRAP_EXIT, + NULL, + after_trap_exit, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_IRQ_ENTRY, + FIELD_ARRAY(LTT_FIELD_IRQ_ID), + after_irq_entry, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_IRQ_EXIT, + NULL, + after_irq_exit, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SOFT_IRQ_ENTRY, + FIELD_ARRAY(LTT_FIELD_SOFT_IRQ_ID), + after_soft_irq_entry, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SOFT_IRQ_EXIT, + NULL, + after_soft_irq_exit, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SCHED_SCHEDULE, + FIELD_ARRAY(LTT_FIELD_PREV_PID, LTT_FIELD_NEXT_PID, LTT_FIELD_PREV_STATE), + after_schedchange, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_PROCESS_FORK, + FIELD_ARRAY(LTT_FIELD_PARENT_PID, LTT_FIELD_CHILD_PID), + process_fork, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_PROCESS_EXIT, + FIELD_ARRAY(LTT_FIELD_PID), + process_exit, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_PROCESS_FREE, + FIELD_ARRAY(LTT_FIELD_PID), + process_free, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_USER_GENERIC, + LTT_EVENT_FUNCTION_ENTRY, + FIELD_ARRAY(LTT_FIELD_THIS_FN, LTT_FIELD_CALL_SITE), + after_function_entry, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_USER_GENERIC, + LTT_EVENT_FUNCTION_EXIT, + FIELD_ARRAY(LTT_FIELD_THIS_FN, LTT_FIELD_CALL_SITE), + after_function_exit, NULL, + &hooks); + + /* statedump-related hooks */ + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_LIST, + LTT_EVENT_PROCESS_STATE, + FIELD_ARRAY(LTT_FIELD_PID, LTT_FIELD_PARENT_PID, LTT_FIELD_NAME), + after_enum_process_state, NULL, + &hooks); + + lttv_trace_find_hook(ts->parent.parent.t, + LTT_FACILITY_LIST, + LTT_EVENT_STATEDUMP_END, + NULL, + after_statedump_end, NULL, + &hooks); + + after_hooks = hooks; + + /* Add these hooks to each event_by_id hooks list */ + + nb_tracefile = ts->parent.parent.tracefiles->len; + + for(j = 0 ; j < nb_tracefile ; j++) { + tfs = LTTV_TRACEFILE_STATS(g_array_index(ts->parent.parent.tracefiles, + LttvTracefileContext*, j)); + lttv_hooks_add(tfs->parent.parent.event, every_event, NULL, + LTTV_PRIO_DEFAULT); + + for(k = 0 ; k < before_hooks->len ; k++) { + th = &g_array_index(before_hooks, LttvTraceHook, k); + lttv_hooks_add( + lttv_hooks_by_id_find(tfs->parent.parent.event_by_id, th->id), + th->h, + th, + LTTV_PRIO_STATS_BEFORE_STATE); + } + for(k = 0 ; k < after_hooks->len ; k++) { + th = &g_array_index(after_hooks, LttvTraceHook, k); + lttv_hooks_add( + lttv_hooks_by_id_find(tfs->parent.parent.event_by_id, th->id), + th->h, + th, + LTTV_PRIO_STATS_AFTER_STATE); + } + } + lttv_attribute_find(self->parent.parent.a, LTTV_STATS_BEFORE_HOOKS, + LTTV_POINTER, &val); + *(val.v_pointer) = before_hooks; + lttv_attribute_find(self->parent.parent.a, LTTV_STATS_AFTER_HOOKS, + LTTV_POINTER, &val); + *(val.v_pointer) = after_hooks; + } +} + +// Hook wrapper. call_data is a traceset context. +gboolean lttv_stats_hook_remove_event_hooks(void *hook_data, void *call_data) +{ + LttvTracesetStats *tss = (LttvTracesetStats*)call_data; + + lttv_stats_remove_event_hooks(tss); + + return 0; +} + +void lttv_stats_remove_event_hooks(LttvTracesetStats *self) +{ + LttvTraceset *traceset = self->parent.parent.ts; + + guint i, j, k, nb_trace, nb_tracefile; + + LttvTraceStats *ts; + + LttvTracefileStats *tfs; + + GArray *before_hooks, *after_hooks; + + LttvTraceHook *th; + + LttvAttributeValue val; + + nb_trace = lttv_traceset_number(traceset); + for(i = 0 ; i < nb_trace ; i++) { + ts = (LttvTraceStats*)self->parent.parent.traces[i]; + lttv_attribute_find(self->parent.parent.a, LTTV_STATS_BEFORE_HOOKS, + LTTV_POINTER, &val); + before_hooks = *(val.v_pointer); + lttv_attribute_find(self->parent.parent.a, LTTV_STATS_AFTER_HOOKS, + LTTV_POINTER, &val); + after_hooks = *(val.v_pointer); + + /* Remove these hooks from each event_by_id hooks list */ + + nb_tracefile = ts->parent.parent.tracefiles->len; + + for(j = 0 ; j < nb_tracefile ; j++) { + tfs = LTTV_TRACEFILE_STATS(g_array_index(ts->parent.parent.tracefiles, + LttvTracefileContext*, j)); + lttv_hooks_remove_data(tfs->parent.parent.event, every_event, + NULL); + + for(k = 0 ; k < before_hooks->len ; k++) { + th = &g_array_index(before_hooks, LttvTraceHook, k); + lttv_hooks_remove_data( + lttv_hooks_by_id_find(tfs->parent.parent.event_by_id, th->id), + th->h, + th); + } + for(k = 0 ; k < after_hooks->len ; k++) { + th = &g_array_index(after_hooks, LttvTraceHook, k); + lttv_hooks_remove_data( + lttv_hooks_by_id_find(tfs->parent.parent.event_by_id, th->id), + th->h, + th); + + } + } + g_debug("lttv_stats_remove_event_hooks()"); + lttv_trace_hook_remove_all(&before_hooks); + lttv_trace_hook_remove_all(&after_hooks); + g_array_free(before_hooks, TRUE); + g_array_free(after_hooks, TRUE); + } +} + + +static void module_init() +{ + LTTV_STATS_PROCESS_UNKNOWN = g_quark_from_string("unknown process"); + LTTV_STATS_PROCESSES = g_quark_from_string("processes"); + LTTV_STATS_CPU = g_quark_from_string("cpu"); + LTTV_STATS_MODE_TYPES = g_quark_from_string("mode_types"); + LTTV_STATS_MODES = g_quark_from_string("modes"); + LTTV_STATS_SUBMODES = g_quark_from_string("submodes"); + LTTV_STATS_FUNCTIONS = g_quark_from_string("functions"); + LTTV_STATS_EVENT_TYPES = g_quark_from_string("event_types"); + LTTV_STATS_CPU_TIME = g_quark_from_string("cpu time"); + LTTV_STATS_CUMULATIVE_CPU_TIME = g_quark_from_string("cumulative cpu time (includes nested routines and modes)"); + LTTV_STATS_ELAPSED_TIME = g_quark_from_string("elapsed time (includes per process waiting time)"); + LTTV_STATS_EVENTS = g_quark_from_string("events"); + LTTV_STATS_EVENTS_COUNT = g_quark_from_string("events count"); + LTTV_STATS_BEFORE_HOOKS = g_quark_from_string("saved stats before hooks"); + LTTV_STATS_AFTER_HOOKS = g_quark_from_string("saved stats after hooks"); + LTTV_STATS_USE_COUNT = g_quark_from_string("stats_use_count"); + LTTV_STATS = g_quark_from_string("statistics"); + LTTV_STATS_TRACEFILES = g_quark_from_string("tracefiles statistics"); + LTTV_STATS_SUMMED = g_quark_from_string("statistics summed"); +} + +static void module_destroy() +{ +} + + +LTTV_MODULE("stats", "Compute processes statistics", \ + "Accumulate statistics for event types, processes and CPUs", \ + module_init, module_destroy, "state"); + +/* Change the places where stats are called (create/read/write stats) + + Check for options in batchtest.c to reduce writing and see what tests are + best candidates for performance analysis. Once OK, commit, move to main + and run tests. Update the gui for statistics. */ diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/stats.h b/tags/lttv-0.11.3-23102008/lttv/lttv/stats.h new file mode 100644 index 00000000..f2eed92d --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/stats.h @@ -0,0 +1,261 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 STATS_H +#define STATS_H + +#include +#include + +/* The statistics are for a complete time interval. These structures differ + from the system state since they relate to static components of the + system (all processes which existed instead of just the currently + existing processes). + + The basic attributes tree to gather for several different execution modes + (e.g., user mode, syscall, irq), thereafter called the "events tree", + contains the following attributes: the number of events of each type, + the total number of events, the number of bytes written, the time spent + executing, waiting for a resource, waiting for a cpu, and possibly many + others. The name "facility-event_type" below is to be replaced + by specific event types (e.g., core-schedchange, code-syscall_entry...). + + event_types/ + "facility-event_type" + events_count + cpu_time + cumulative_cpu_time + elapsed_time + wait_time + bytes_written + packets_sent + ... + + The events for several different execution modes are joined together to + form the "execution modes tree". The name "execution mode" is to be replaced + by "system call", "trap", "irq", "user mode" or "kernel thread". + The name "submode" is to be replaced by the specific system call, trap or + irq name. The "submode" is an empty string if none is applicable, which is + the case for "user mode" and "kernel thread". + + An "events tree" for each "execution mode" contains the sum for all its + different submodes. An "events tree" in the "execution modes tree" contains + the sum for all its different execution modes. + + mode_types/ + "execution mode"/ + submodes/ + "submode"/ + Events Tree + events/ + Event Tree + events/ + Events Tree + + Each trace set contains an "execution modes tree". While the traces + come from possibly different systems, which may differ in their system + calls..., most of the system calls will have the same name, even if their + actual internal numeric id differs. Categories such as cpu id and process + id are not kept since these are specific to each system. When several + traces are taken from the same system, these categories may make sense and + could eventually be considered. + + Each trace contains a global "execution modes tree", one for each + cpu and process, and one for each process/cpu combination. The name + "cpu number" stands for the cpu identifier, and "process_id-start_time" + is a unique process identifier composed of the process id + (unique at any given time but which may be reused over time) concatenated + with the process start time. Each process has a "functions" tree which + contains each process'function address (when the information is available). + If not, only the 0x0 function will appear. + + modes/ + Execution Modes Tree + cpu/ + "cpu number"/ + Execution Modes Tree + processes/ + "process_id-start_time"/ + exec_file_name + parent + start_time + end_time + modes/ + Execution Modes Tree + cpu/ + "cpu number"/ + Execution Modes Tree + functions/ + "function address"/ + Execution Modes Tree + functions/ + "function address"/ + Execution Modes Tree + + All the events and derived values (cpu, elapsed and wait time) are + added during the trace analysis in the relevant + trace/processes/ * /cpu/ * /functions/ * /mode_types/ * /submodes/ * + "events tree". To achieve this efficiently, each tracefile context + contains a pointer to the current relevant "events tree" and "event_types" + tree within it. + + Once all the events are processed, the total number of events is computed + within each + trace/processes/ * /cpu/ * /functions/ * /mode_types/ * /submodes/ *. + Then, the "events tree" are summed for all submodes within each mode type + and for all mode types within a processes/ * /cpu/ * /functions/ * + "execution modes tree". + + Then, the "execution modes trees" for all functions within a + trace/processes/ * /cpu for all cpu within a process, for all processes, + and for all traces are computed. Separately, the "execution modes tree" for + each function (over all cpus) for all processes, and for all traces are + summed in the trace/processes/ * /functions/ * subtree. + + Finally, the "execution modes trees" for all cpu within a process, + for all processes, and for all traces are computed. Separately, + the "execution modes tree" for each cpu but for all processes within a + trace are summed in the trace / cpu / * subtrees. + + */ + + +/* The various statistics branch names are GQuarks. They are pre-computed for + easy and efficient access */ + +#define LTTV_PRIO_STATS_BEFORE_STATE LTTV_PRIO_STATE-5 +#define LTTV_PRIO_STATS_AFTER_STATE LTTV_PRIO_STATE+5 + + +extern GQuark + LTTV_STATS_PROCESS_UNKNOWN, + LTTV_STATS_PROCESSES, + LTTV_STATS_CPU, + LTTV_STATS_MODE_TYPES, + LTTV_STATS_SUBMODES, + LTTV_STATS_FUNCTIONS, + LTTV_STATS_EVENT_TYPES, + LTTV_STATS_CPU_TIME, + LTTV_STATS_CUMULATIVE_CPU_TIME, + LTTV_STATS_ELAPSED_TIME, + LTTV_STATS_EVENTS, + LTTV_STATS_EVENTS_COUNT, + LTTV_STATS_BEFORE_HOOKS, + LTTV_STATS_AFTER_HOOKS; + + +typedef struct _LttvTracesetStats LttvTracesetStats; +typedef struct _LttvTracesetStatsClass LttvTracesetStatsClass; + +typedef struct _LttvTraceStats LttvTraceStats; +typedef struct _LttvTraceStatsClass LttvTraceStatsClass; + +typedef struct _LttvTracefileStats LttvTracefileStats; +typedef struct _LttvTracefileStatsClass LttvTracefileStatsClass; + + + +// Hook wrapper. call_data is a trace context. +gboolean lttv_stats_hook_add_event_hooks(void *hook_data, void *call_data); +void lttv_stats_add_event_hooks(LttvTracesetStats *self); + +// Hook wrapper. call_data is a trace context. +gboolean lttv_stats_hook_remove_event_hooks(void *hook_data, void *call_data); +void lttv_stats_remove_event_hooks(LttvTracesetStats *self); + +gboolean lttv_stats_sum_traceset_hook(void *hook_data, void *call_data); +void lttv_stats_sum_traceset(LttvTracesetStats *self, LttTime current_time); + +void lttv_stats_sum_trace(LttvTraceStats *self, LttvAttribute *ts_stats, + LttTime current_time); + +/* Reset all statistics containers */ +void lttv_stats_reset(LttvTracesetStats *self); + + +/* The LttvTracesetStats, LttvTraceStats and LttvTracefileStats types + inherit from the corresponding State objects defined in state.h.. */ + +#define LTTV_TRACESET_STATS_TYPE (lttv_traceset_stats_get_type ()) +#define LTTV_TRACESET_STATS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LTTV_TRACESET_STATS_TYPE, LttvTracesetStats)) +#define LTTV_TRACESET_STATS_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), LTTV_TRACESET_STATS_TYPE, LttvTracesetStatsClass)) +#define LTTV_IS_TRACESET_STATS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LTTV_TRACESET_STATS_TYPE)) +#define LTTV_IS_TRACESET_STATS_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), LTTV_TRACESET_STATS_TYPE)) +#define LTTV_TRACESET_STATS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), LTTV_TRACESET_STATS_TYPE, LttvTracesetStatsClass)) + +struct _LttvTracesetStats { + LttvTracesetState parent; + + LttvAttribute *stats; +}; + +struct _LttvTracesetStatsClass { + LttvTracesetStateClass parent; +}; + +GType lttv_traceset_stats_get_type (void); + + +#define LTTV_TRACE_STATS_TYPE (lttv_trace_stats_get_type ()) +#define LTTV_TRACE_STATS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LTTV_TRACE_STATS_TYPE, LttvTraceStats)) +#define LTTV_TRACE_STATS_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), LTTV_TRACE_STATS_TYPE, LttvTraceStatsClass)) +#define LTTV_IS_TRACE_STATS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LTTV_TRACE_STATS_TYPE)) +#define LTTV_IS_TRACE_STATS_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), LTTV_TRACE_STATS_TYPE)) +#define LTTV_TRACE_STATS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), LTTV_TRACE_STATS_TYPE, LttvTraceStatsClass)) + +struct _LttvTraceStats { + LttvTraceState parent; + + LttvAttribute *stats; +}; + +struct _LttvTraceStatsClass { + LttvTraceStateClass parent; +}; + +GType lttv_trace_stats_get_type (void); + + +#define LTTV_TRACEFILE_STATS_TYPE (lttv_tracefile_stats_get_type ()) +#define LTTV_TRACEFILE_STATS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LTTV_TRACEFILE_STATS_TYPE, LttvTracefileStats)) +#define LTTV_TRACEFILE_STATS_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), LTTV_TRACEFILE_STATS_TYPE, LttvTracefileStatsClass)) +#define LTTV_IS_TRACEFILE_STATS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LTTV_TRACEFILE_STATS_TYPE)) +#define LTTV_IS_TRACEFILE_STATS_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), LTTV_TRACEFILE_STATS_TYPE)) +#define LTTV_TRACEFILE_STATS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), LTTV_TRACEFILE_STATS_TYPE, LttvTracefileStatsClass)) + +struct _LttvTracefileStats { + LttvTracefileState parent; + + LttvAttribute *stats; + LttvAttribute *current_events_tree; + LttvAttribute *current_event_types_tree; +}; + +struct _LttvTracefileStatsClass { + LttvTracefileStateClass parent; +}; + +GType lttv_tracefile_stats_get_type (void); + +struct sum_traceset_closure { + LttvTracesetStats *tss; + LttTime current_time; +}; + + +#endif // STATS_H diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/tracecontext.c b/tags/lttv-0.11.3-23102008/lttv/lttv/tracecontext.c new file mode 100644 index 00000000..f5b78b5c --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/tracecontext.c @@ -0,0 +1,1624 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#define min(a,b) (((a)<(b))?(a):(b)) + + +gint compare_tracefile(gconstpointer a, gconstpointer b) +{ + gint comparison = 0; + + const LttvTracefileContext *trace_a = (const LttvTracefileContext *)a; + const LttvTracefileContext *trace_b = (const LttvTracefileContext *)b; + + if(likely(trace_a != trace_b)) { + comparison = ltt_time_compare(trace_a->timestamp, trace_b->timestamp); + if(unlikely(comparison == 0)) { + if(trace_a->index < trace_b->index) comparison = -1; + else if(trace_a->index > trace_b->index) comparison = 1; + else if(trace_a->t_context->index < trace_b->t_context->index) + comparison = -1; + else if(trace_a->t_context->index > trace_b->t_context->index) + comparison = 1; + } + } + return comparison; +} + +typedef struct _LttvTracefileContextPosition { + LttEventPosition *event; + LttvTracefileContext *tfc; + gboolean used; /* Tells if the tfc is at end of traceset position */ +} LttvTracefileContextPosition; + + +struct _LttvTracesetContextPosition { + GArray *tfcp; /* Array of LttvTracefileContextPosition */ + LttTime timestamp; /* Current time at the saved position */ + /* If ltt_time_infinite : no position is + * set, else, a position is set (may be end + * of trace, with ep->len == 0) */ +}; + +void lttv_context_init(LttvTracesetContext *self, LttvTraceset *ts) +{ + LTTV_TRACESET_CONTEXT_GET_CLASS(self)->init(self, ts); +} + + +void lttv_context_fini(LttvTracesetContext *self) +{ + LTTV_TRACESET_CONTEXT_GET_CLASS(self)->fini(self); +} + + +LttvTracesetContext * +lttv_context_new_traceset_context(LttvTracesetContext *self) +{ + return LTTV_TRACESET_CONTEXT_GET_CLASS(self)->new_traceset_context(self); +} + + + + +LttvTraceContext * +lttv_context_new_trace_context(LttvTracesetContext *self) +{ + return LTTV_TRACESET_CONTEXT_GET_CLASS(self)->new_trace_context(self); +} + + +LttvTracefileContext * +lttv_context_new_tracefile_context(LttvTracesetContext *self) +{ + return LTTV_TRACESET_CONTEXT_GET_CLASS(self)->new_tracefile_context(self); +} + +/**************************************************************************** + * lttv_traceset_context_compute_time_span + * + * Keep the time span is sync with on the fly addition and removal of traces + * in a trace set. It must be called each time a trace is added/removed from + * the traceset. It could be more efficient to call it only once a bunch + * of traces are loaded, but the calculation is not long, so it's not + * critical. + * + * Author : Xang Xiu Yang + ***************************************************************************/ +static void lttv_traceset_context_compute_time_span( + LttvTracesetContext *self, + TimeInterval *time_span) +{ + LttvTraceset * traceset = self->ts; + int numTraces = lttv_traceset_number(traceset); + int i; + LttTime s, e; + LttvTraceContext *tc; + LttTrace * trace; + + time_span->start_time.tv_sec = 0; + time_span->start_time.tv_nsec = 0; + time_span->end_time.tv_sec = 0; + time_span->end_time.tv_nsec = 0; + + for(i=0; itraces[i]; + trace = tc->t; + + ltt_trace_time_span_get(trace, &s, &e); + tc->time_span.start_time = s; + tc->time_span.end_time = e; + + if(i==0){ + time_span->start_time = s; + time_span->end_time = e; + }else{ + if(s.tv_sec < time_span->start_time.tv_sec + || (s.tv_sec == time_span->start_time.tv_sec + && s.tv_nsec < time_span->start_time.tv_nsec)) + time_span->start_time = s; + if(e.tv_sec > time_span->end_time.tv_sec + || (e.tv_sec == time_span->end_time.tv_sec + && e.tv_nsec > time_span->end_time.tv_nsec)) + time_span->end_time = e; + } + } +} + +static void init_tracefile_context(LttTracefile *tracefile, + LttvTraceContext *tc) +{ + LttvTracefileContext *tfc; + LttvTracesetContext *tsc = tc->ts_context; + + tfc = LTTV_TRACESET_CONTEXT_GET_CLASS(tsc)->new_tracefile_context(tsc); + + tfc->index = tc->tracefiles->len; + tc->tracefiles = g_array_append_val(tc->tracefiles, tfc); + + tfc->tf = tracefile; + + tfc->t_context = tc; + tfc->event = lttv_hooks_new(); + tfc->event_by_id = lttv_hooks_by_id_new(); + tfc->a = g_object_new(LTTV_ATTRIBUTE_TYPE, NULL); + tfc->target_pid = -1; +} + + +static void +init(LttvTracesetContext *self, LttvTraceset *ts) +{ + guint i, nb_trace; + + LttvTraceContext *tc; + + GData **tracefiles_groups; + + struct compute_tracefile_group_args args; + + nb_trace = lttv_traceset_number(ts); + self->ts = ts; + self->traces = g_new(LttvTraceContext *, nb_trace); + self->a = g_object_new(LTTV_ATTRIBUTE_TYPE, NULL); + self->ts_a = lttv_traceset_attribute(ts); + for(i = 0 ; i < nb_trace ; i++) { + tc = LTTV_TRACESET_CONTEXT_GET_CLASS(self)->new_trace_context(self); + self->traces[i] = tc; + + tc->ts_context = self; + tc->index = i; + tc->vt = lttv_traceset_get(ts, i); + tc->t = lttv_trace(tc->vt); + tc->a = g_object_new(LTTV_ATTRIBUTE_TYPE, NULL); + tc->t_a = lttv_trace_attribute(tc->vt); + tc->tracefiles = g_array_sized_new(FALSE, TRUE, + sizeof(LttvTracefileContext*), 10); + + tracefiles_groups = ltt_trace_get_tracefiles_groups(tc->t); + if(tracefiles_groups != NULL) { + args.func = (ForEachTraceFileFunc)init_tracefile_context; + args.func_args = tc; + + g_datalist_foreach(tracefiles_groups, + (GDataForeachFunc)compute_tracefile_group, + &args); + } + +#if 0 + nb_control = ltt_trace_control_tracefile_number(tc->t); + nb_per_cpu = ltt_trace_per_cpu_tracefile_number(tc->t); + nb_tracefile = nb_control + nb_per_cpu; + tc->tracefiles = g_new(LttvTracefileContext *, nb_tracefile); + + for(j = 0 ; j < nb_tracefile ; j++) { + tfc = LTTV_TRACESET_CONTEXT_GET_CLASS(self)->new_tracefile_context(self); + tc->tracefiles[j] = tfc; + tfc->index = j; + + if(j < nb_control) { + tfc->control = TRUE; + tfc->tf = ltt_trace_control_tracefile_get(tc->t, j); + } + else { + tfc->control = FALSE; + tfc->tf = ltt_trace_per_cpu_tracefile_get(tc->t, j - nb_control); + } + + tfc->t_context = tc; + tfc->e = ltt_event_new(); + tfc->event = lttv_hooks_new(); + tfc->event_by_id = lttv_hooks_by_id_new(); + tfc->a = g_object_new(LTTV_ATTRIBUTE_TYPE, NULL); + } +#endif //0 + + } + self->sync_position = lttv_traceset_context_position_new(self); + self->pqueue = g_tree_new(compare_tracefile); + lttv_process_traceset_seek_time(self, ltt_time_zero); + lttv_traceset_context_compute_time_span(self, &self->time_span); + +} + + +void fini(LttvTracesetContext *self) +{ + guint i, j, nb_trace, nb_tracefile; + + LttvTraceContext *tc; + + LttvTracefileContext **tfc; + + LttvTraceset *ts = self->ts; + + g_tree_destroy(self->pqueue); + g_object_unref(self->a); + lttv_traceset_context_position_destroy(self->sync_position); + + nb_trace = lttv_traceset_number(ts); + + for(i = 0 ; i < nb_trace ; i++) { + tc = self->traces[i]; + + g_object_unref(tc->a); + + nb_tracefile = tc->tracefiles->len; + + for(j = 0 ; j < nb_tracefile ; j++) { + tfc = &g_array_index(tc->tracefiles, LttvTracefileContext*, j); + lttv_hooks_destroy((*tfc)->event); + lttv_hooks_by_id_destroy((*tfc)->event_by_id); + g_object_unref((*tfc)->a); + g_object_unref(*tfc); + } + g_array_free(tc->tracefiles, TRUE); + g_object_unref(tc); + } + g_free(self->traces); +} + + +void lttv_traceset_context_add_hooks(LttvTracesetContext *self, + LttvHooks *before_traceset, + LttvHooks *before_trace, + LttvHooks *before_tracefile, + LttvHooks *event, + LttvHooksById *event_by_id) +{ + LttvTraceset *ts = self->ts; + + guint i, nb_trace; + + LttvTraceContext *tc; + + lttv_hooks_call(before_traceset, self); + + nb_trace = lttv_traceset_number(ts); + + for(i = 0 ; i < nb_trace ; i++) { + tc = self->traces[i]; + lttv_trace_context_add_hooks(tc, + before_trace, + before_tracefile, + event, + event_by_id); + } +} + + +void lttv_traceset_context_remove_hooks(LttvTracesetContext *self, + LttvHooks *after_traceset, + LttvHooks *after_trace, + LttvHooks *after_tracefile, + LttvHooks *event, + LttvHooksById *event_by_id) +{ + + LttvTraceset *ts = self->ts; + + guint i, nb_trace; + + LttvTraceContext *tc; + + nb_trace = lttv_traceset_number(ts); + + for(i = 0 ; i < nb_trace ; i++) { + tc = self->traces[i]; + lttv_trace_context_remove_hooks(tc, + after_trace, + after_tracefile, + event, + event_by_id); + } + + lttv_hooks_call(after_traceset, self); + + +} + +void lttv_trace_context_add_hooks(LttvTraceContext *self, + LttvHooks *before_trace, + LttvHooks *before_tracefile, + LttvHooks *event, + LttvHooksById *event_by_id) +{ + guint i, nb_tracefile; + + LttvTracefileContext **tfc; + + lttv_hooks_call(before_trace, self); + + nb_tracefile = self->tracefiles->len; + + for(i = 0 ; i < nb_tracefile ; i++) { + tfc = &g_array_index(self->tracefiles, LttvTracefileContext*, i); + lttv_tracefile_context_add_hooks(*tfc, + before_tracefile, + event, + event_by_id); + } +} + + + +void lttv_trace_context_remove_hooks(LttvTraceContext *self, + LttvHooks *after_trace, + LttvHooks *after_tracefile, + LttvHooks *event, + LttvHooksById *event_by_id) +{ + guint i, nb_tracefile; + + LttvTracefileContext **tfc; + + nb_tracefile = self->tracefiles->len; + + for(i = 0 ; i < nb_tracefile ; i++) { + tfc = &g_array_index(self->tracefiles, LttvTracefileContext*, i); + lttv_tracefile_context_remove_hooks(*tfc, + after_tracefile, + event, + event_by_id); + } + + lttv_hooks_call(after_trace, self); +} + +void lttv_tracefile_context_add_hooks(LttvTracefileContext *self, + LttvHooks *before_tracefile, + LttvHooks *event, + LttvHooksById *event_by_id) +{ + guint i, index; + + LttvHooks *hook; + + lttv_hooks_call(before_tracefile, self); + lttv_hooks_add_list(self->event, event); + if(event_by_id != NULL) { + for(i = 0; i < event_by_id->array->len; i++) { + index = g_array_index(event_by_id->array, guint, i); + hook = lttv_hooks_by_id_find(self->event_by_id, index); + lttv_hooks_add_list(hook, lttv_hooks_by_id_get(event_by_id, index)); + } + } +} + +void lttv_tracefile_context_remove_hooks(LttvTracefileContext *self, + LttvHooks *after_tracefile, + LttvHooks *event, + LttvHooksById *event_by_id) +{ + guint i, index; + + LttvHooks *hook; + + lttv_hooks_remove_list(self->event, event); + if(event_by_id != NULL) { + for(i = 0; i < event_by_id->array->len; i++) { + index = g_array_index(event_by_id->array, guint, i); + hook = lttv_hooks_by_id_get(self->event_by_id, index); + if(hook != NULL) + lttv_hooks_remove_list(hook, lttv_hooks_by_id_get(event_by_id, index)); + } + } + + lttv_hooks_call(after_tracefile, self); +} + + + +void lttv_tracefile_context_add_hooks_by_id(LttvTracefileContext *tfc, + unsigned i, + LttvHooks *event_by_id) +{ + LttvHooks * h; + h = lttv_hooks_by_id_find(tfc->event_by_id, i); + lttv_hooks_add_list(h, event_by_id); +} + +void lttv_tracefile_context_remove_hooks_by_id(LttvTracefileContext *tfc, + unsigned i) +{ + lttv_hooks_by_id_remove(tfc->event_by_id, i); +} + +static LttvTracesetContext * +new_traceset_context(LttvTracesetContext *self) +{ + return g_object_new(LTTV_TRACESET_CONTEXT_TYPE, NULL); +} + + +static LttvTraceContext * +new_trace_context(LttvTracesetContext *self) +{ + return g_object_new(LTTV_TRACE_CONTEXT_TYPE, NULL); +} + + +static LttvTracefileContext * +new_tracefile_context(LttvTracesetContext *self) +{ + return g_object_new(LTTV_TRACEFILE_CONTEXT_TYPE, NULL); +} + + +static void +traceset_context_instance_init (GTypeInstance *instance, gpointer g_class) +{ + /* Be careful of anything which would not work well with shallow copies */ +} + + +static void +traceset_context_finalize (LttvTracesetContext *self) +{ + G_OBJECT_CLASS(g_type_class_peek(g_type_parent(LTTV_TRACESET_CONTEXT_TYPE))) + ->finalize(G_OBJECT(self)); +} + + +static void +traceset_context_class_init (LttvTracesetContextClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + + gobject_class->finalize = (void (*)(GObject *self))traceset_context_finalize; + klass->init = init; + klass->fini = fini; + klass->new_traceset_context = new_traceset_context; + klass->new_trace_context = new_trace_context; + klass->new_tracefile_context = new_tracefile_context; +} + + +GType +lttv_traceset_context_get_type(void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (LttvTracesetContextClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) traceset_context_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (LttvTracesetContext), + 0, /* n_preallocs */ + (GInstanceInitFunc) traceset_context_instance_init, /* instance_init */ + NULL /* Value handling */ + }; + + type = g_type_register_static (G_TYPE_OBJECT, "LttvTracesetContextType", + &info, 0); + } + return type; +} + + +static void +trace_context_instance_init (GTypeInstance *instance, gpointer g_class) +{ + /* Be careful of anything which would not work well with shallow copies */ +} + + +static void +trace_context_finalize (LttvTraceContext *self) +{ + G_OBJECT_CLASS(g_type_class_peek(g_type_parent(LTTV_TRACE_CONTEXT_TYPE)))-> + finalize(G_OBJECT(self)); +} + + +static void +trace_context_class_init (LttvTraceContextClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + + gobject_class->finalize = (void (*)(GObject *self)) trace_context_finalize; +} + + +GType +lttv_trace_context_get_type(void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (LttvTraceContextClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) trace_context_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (LttvTraceContext), + 0, /* n_preallocs */ + (GInstanceInitFunc) trace_context_instance_init, /* instance_init */ + NULL /* Value handling */ + }; + + type = g_type_register_static (G_TYPE_OBJECT, "LttvTraceContextType", + &info, 0); + } + return type; +} + + +static void +tracefile_context_instance_init (GTypeInstance *instance, gpointer g_class) +{ + /* Be careful of anything which would not work well with shallow copies */ +} + + +static void +tracefile_context_finalize (LttvTracefileContext *self) +{ + G_OBJECT_CLASS(g_type_class_peek(g_type_parent(LTTV_TRACEFILE_CONTEXT_TYPE))) + ->finalize(G_OBJECT(self)); +} + + +static void +tracefile_context_class_init (LttvTracefileContextClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + + gobject_class->finalize = (void (*)(GObject *self))tracefile_context_finalize; +} + + +GType +lttv_tracefile_context_get_type(void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (LttvTracefileContextClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) tracefile_context_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (LttvTracefileContext), + 0, /* n_preallocs */ + (GInstanceInitFunc) tracefile_context_instance_init, /* instance_init */ + NULL /* Value handling */ + }; + + type = g_type_register_static (G_TYPE_OBJECT, "LttvTracefileContextType", + &info, 0); + } + return type; +} + + + +static gboolean get_first(gpointer key, gpointer value, gpointer user_data) { + g_assert(key == value); + *((LttvTracefileContext **)user_data) = (LttvTracefileContext *)value; + return TRUE; +} + +#ifdef DEBUG +// Test to see if pqueue is traversed in the right order. +static LttTime test_time; + +static gboolean test_tree(gpointer key, gpointer value, gpointer user_data) { + + LttvTracefileContext *tfc = (LttvTracefileContext *)key; + + g_debug("Tracefile name %s, time %lu.%lu, tfi %u, ti %u", + g_quark_to_string(ltt_tracefile_name(tfc->tf)), + tfc->timestamp.tv_sec, tfc->timestamp.tv_nsec, + tfc->index, tfc->t_context->index); + + if(user_data != NULL) { + if(((LttvTracefileContext *)user_data) == (LttvTracefileContext *)value) { + g_assert(compare_tracefile(user_data, value) == 0); + } else + g_assert(compare_tracefile(user_data, value) != 0); + } + g_assert(ltt_time_compare(test_time, tfc->timestamp) <= 0); + test_time.tv_sec = tfc->timestamp.tv_sec; + test_time.tv_nsec = tfc->timestamp.tv_nsec; + + + //g_assert(((LttvTracefileContext *)user_data) != (LttvTracefileContext *)value); + return FALSE; +} +#endif //DEBUG + + + +void lttv_process_traceset_begin(LttvTracesetContext *self, + LttvHooks *before_traceset, + LttvHooks *before_trace, + LttvHooks *before_tracefile, + LttvHooks *event, + LttvHooksById *event_by_id) +{ + + /* simply add hooks in context. _before hooks are called by add_hooks. */ + /* It calls all before_traceset, before_trace, and before_tracefile hooks. */ + lttv_traceset_context_add_hooks(self, + before_traceset, + before_trace, + before_tracefile, + event, + event_by_id); + +} + +//enum read_state { LAST_NONE, LAST_OK, LAST_EMPTY }; + +/* Note : a _middle must be preceded from a _seek or another middle */ +guint lttv_process_traceset_middle(LttvTracesetContext *self, + LttTime end, + gulong nb_events, + const LttvTracesetContextPosition *end_position) +{ + GTree *pqueue = self->pqueue; + + LttvTracefileContext *tfc; + + LttEvent *e; + + unsigned count = 0; + + guint read_ret; + + //enum read_state last_read_state = LAST_NONE; + + gint last_ret = 0; /* return value of the last hook list called */ + + /* Get the next event from the pqueue, call its hooks, + reinsert in the pqueue the following event from the same tracefile + unless the tracefile is finished or the event is later than the + end time. */ + + while(TRUE) { + tfc = NULL; + g_tree_foreach(pqueue, get_first, &tfc); + /* End of traceset : tfc is NULL */ + if(unlikely(tfc == NULL)) + { + return count; + } + + /* Have we reached : + * - the maximum number of events specified? + * - the end position ? + * - the end time ? + * then the read is finished. We leave the queue in the same state and + * break the loop. + */ + + if(unlikely(last_ret == TRUE || + ((count >= nb_events) && (nb_events != G_MAXULONG)) || + (end_position!=NULL&<tv_traceset_context_ctx_pos_compare(self, + end_position) == 0)|| + ltt_time_compare(end, tfc->timestamp) <= 0)) + { + return count; + } + + /* Get the tracefile with an event for the smallest time found. If two + or more tracefiles have events for the same time, hope that lookup + and remove are consistent. */ + +#ifdef DEBUG + test_time.tv_sec = 0; + test_time.tv_nsec = 0; + g_debug("test tree before remove"); + g_tree_foreach(pqueue, test_tree, tfc); +#endif //DEBUG + g_tree_remove(pqueue, tfc); + +#ifdef DEBUG + test_time.tv_sec = 0; + test_time.tv_nsec = 0; + g_debug("test tree after remove"); + g_tree_foreach(pqueue, test_tree, tfc); +#endif //DEBUG + + + e = ltt_tracefile_get_event(tfc->tf); + + //if(last_read_state != LAST_EMPTY) { + /* Only call hooks if the last read has given an event or if we are at the + * first pass (not if last read returned end of tracefile) */ + count++; + + tfc->target_pid = -1; /* unset target PID */ + /* Hooks : + * return values : 0 : continue read, 1 : go to next position and stop read, + * 2 : stay at the current position and stop read */ + last_ret = lttv_hooks_call_merge(tfc->event, tfc, + lttv_hooks_by_id_get(tfc->event_by_id, e->event_id), tfc); + +#if 0 + /* This is buggy : it won't work well with state computation */ + if(unlikely(last_ret == 2)) { + /* This is a case where we want to stay at this position and stop read. */ + g_tree_insert(pqueue, tfc, tfc); + return count - 1; + } +#endif //0 + read_ret = ltt_tracefile_read(tfc->tf); + + + if(likely(!read_ret)) { + //g_debug("An event is ready"); + tfc->timestamp = ltt_event_time(e); + g_assert(ltt_time_compare(tfc->timestamp, ltt_time_infinite) != 0); + g_tree_insert(pqueue, tfc, tfc); +#ifdef DEBUG + test_time.tv_sec = 0; + test_time.tv_nsec = 0; + g_debug("test tree after event ready"); + g_tree_foreach(pqueue, test_tree, NULL); +#endif //DEBUG + + //last_read_state = LAST_OK; + } else { + tfc->timestamp = ltt_time_infinite; + + if(read_ret == ERANGE) { + // last_read_state = LAST_EMPTY; + g_debug("End of trace"); + } else + g_error("Error happened in lttv_process_traceset_middle"); + } + } +} + + +void lttv_process_traceset_end(LttvTracesetContext *self, + LttvHooks *after_traceset, + LttvHooks *after_trace, + LttvHooks *after_tracefile, + LttvHooks *event, + LttvHooksById *event_by_id) +{ + /* Remove hooks from context. _after hooks are called by remove_hooks. */ + /* It calls all after_traceset, after_trace, and after_tracefile hooks. */ + lttv_traceset_context_remove_hooks(self, + after_traceset, + after_trace, + after_tracefile, + event, + event_by_id); +} + +/* Subtile modification : + * if tracefile has no event at or after the time requested, it is not put in + * the queue, as the next read would fail. + * + * Don't forget to empty the traceset pqueue before calling this. + */ +void lttv_process_trace_seek_time(LttvTraceContext *self, LttTime start) +{ + guint i, nb_tracefile; + + gint ret; + + LttvTracefileContext **tfc; + + nb_tracefile = self->tracefiles->len; + + GTree *pqueue = self->ts_context->pqueue; + + for(i = 0 ; i < nb_tracefile ; i++) { + tfc = &g_array_index(self->tracefiles, LttvTracefileContext*, i); + + g_tree_remove(pqueue, *tfc); + + ret = ltt_tracefile_seek_time((*tfc)->tf, start); + if(ret == EPERM) g_error("error in lttv_process_trace_seek_time seek"); + + if(ret == 0) { /* not ERANGE especially */ + (*tfc)->timestamp = ltt_event_time(ltt_tracefile_get_event((*tfc)->tf)); + g_assert(ltt_time_compare((*tfc)->timestamp, ltt_time_infinite) != 0); + g_tree_insert(pqueue, (*tfc), (*tfc)); + } else { + (*tfc)->timestamp = ltt_time_infinite; + } + } +#ifdef DEBUG + test_time.tv_sec = 0; + test_time.tv_nsec = 0; + g_debug("test tree after seek_time"); + g_tree_foreach(pqueue, test_tree, NULL); +#endif //DEBUG +} + + +void lttv_process_traceset_seek_time(LttvTracesetContext *self, LttTime start) +{ + guint i, nb_trace; + + LttvTraceContext *tc; + + //g_tree_destroy(self->pqueue); + //self->pqueue = g_tree_new(compare_tracefile); + + nb_trace = lttv_traceset_number(self->ts); + for(i = 0 ; i < nb_trace ; i++) { + tc = self->traces[i]; + lttv_process_trace_seek_time(tc, start); + } +} + + +gboolean lttv_process_traceset_seek_position(LttvTracesetContext *self, + const LttvTracesetContextPosition *pos) +{ + guint i; + /* If a position is set, seek the traceset to this position */ + if(ltt_time_compare(pos->timestamp, ltt_time_infinite) != 0) { + + /* Test to see if the traces has been added to the trace set : + * It should NEVER happen. Clear all positions if a new trace comes in. */ + /* FIXME I know this test is not optimal : should keep a number of + * tracefiles variable in the traceset.. eventually */ + guint num_traces = lttv_traceset_number(self->ts); + guint tf_count = 0; + for(i=0; itraces[i]->tracefiles; + guint j; + guint num_tracefiles = tracefiles->len; + for(j=0;jtfcp->len); + + + //g_tree_destroy(self->pqueue); + //self->pqueue = g_tree_new(compare_tracefile); + + for(i=0;itfcp->len; i++) { + LttvTracefileContextPosition *tfcp = + &g_array_index(pos->tfcp, LttvTracefileContextPosition, i); + + g_tree_remove(self->pqueue, tfcp->tfc); + + if(tfcp->used == TRUE) { + if(ltt_tracefile_seek_position(tfcp->tfc->tf, tfcp->event) != 0) + return 1; + tfcp->tfc->timestamp = + ltt_event_time(ltt_tracefile_get_event(tfcp->tfc->tf)); + g_assert(ltt_time_compare(tfcp->tfc->timestamp, + ltt_time_infinite) != 0); + g_tree_insert(self->pqueue, tfcp->tfc, tfcp->tfc); + + } else { + tfcp->tfc->timestamp = ltt_time_infinite; + } + } + } +#ifdef DEBUG + test_time.tv_sec = 0; + test_time.tv_nsec = 0; + g_debug("test tree after seek_position"); + g_tree_foreach(self->pqueue, test_tree, NULL); +#endif //DEBUG + + + + return 0; +} + + +#if 0 // pmf: temporary disable +static LttField * +find_field(LttEventType *et, const GQuark field) +{ + LttField *f; + + if(field == 0) return NULL; + + f = ltt_eventtype_field_by_name(et, field); + if (!f) { + g_warning("Cannot find field %s in event %s.%s", g_quark_to_string(field), + g_quark_to_string(ltt_facility_name(ltt_eventtype_facility(et))), + g_quark_to_string(ltt_eventtype_name(et))); + } + + return f; +} +#endif + +struct marker_info *lttv_trace_hook_get_marker(LttTrace *t, LttvTraceHook *th) +{ + return marker_get_info_from_id(t, th->id); +} + +int lttv_trace_find_hook(LttTrace *t, GQuark facility_name, GQuark event_name, + GQuark fields[], LttvHook h, gpointer hook_data, GArray **trace_hooks) +{ + struct marker_info *info; + guint16 marker_id; + int init_array_size; + GQuark marker_name; + + marker_name = lttv_merge_facility_event_name(facility_name, event_name); + + info = marker_get_info_from_name(t, marker_name); + if(unlikely(info == NULL)) { + g_warning("No marker of name %s found", g_quark_to_string(marker_name)); + return 1; + } + + init_array_size = (*trace_hooks)->len; + + /* for each marker with the requested name */ + do { + LttvTraceHook tmpth; + int found; + GQuark *f; + struct marker_field *marker_field; + + marker_id = marker_get_id_from_info(t, info); + + tmpth.h = h; + tmpth.id = marker_id; + tmpth.hook_data = hook_data; + tmpth.fields = g_ptr_array_new(); + + /* for each field requested */ + for(f = fields; f && *f != 0; f++) { + found = 0; + for_each_marker_field(marker_field, info) { + if(marker_field->name == *f) { + found = 1; + g_ptr_array_add(tmpth.fields, marker_field); + break; + } + } + if(!found) { + /* Did not find the one of the fields in this instance of the + marker. Print a warning and skip this marker completely. + Still iterate on other markers with same name. */ + g_ptr_array_free(tmpth.fields, TRUE); + g_warning("Field %s cannot be found in marker %s", + g_quark_to_string(*f), g_quark_to_string(marker_name)); + goto skip_marker; + } + } + /* all fields were found: add the tracehook to the array */ + *trace_hooks = g_array_append_val(*trace_hooks, tmpth); +skip_marker: + info = info->next; + } while(info != NULL); + + /* Error if no new trace hook has been added */ + if (init_array_size == (*trace_hooks)->len) { + g_warning("No marker of name %s has all requested fields", + g_quark_to_string(marker_name)); + return 1; + } + return 0; +} + +void lttv_trace_hook_remove_all(GArray **th) +{ + int i; + for(i=0; i<(*th)->len; i++) { + g_ptr_array_free(g_array_index(*th, LttvTraceHook, i).fields, TRUE); + } + if((*th)->len) + *th = g_array_remove_range(*th, 0, (*th)->len); +} + +LttvTracesetContextPosition *lttv_traceset_context_position_new( + const LttvTracesetContext *self) +{ + guint num_traces = lttv_traceset_number(self->ts); + guint tf_count = 0; + guint i; + + for(i=0; itraces[i]->tracefiles; + guint j; + guint num_tracefiles = tracefiles->len; + for(j=0;jtfcp = g_array_sized_new(FALSE, TRUE, + sizeof(LttvTracefileContextPosition), + tf_count); + g_array_set_size(pos->tfcp, tf_count); + for(i=0;itfcp->len;i++) { + LttvTracefileContextPosition *tfcp = + &g_array_index(pos->tfcp, LttvTracefileContextPosition, i); + tfcp->event = ltt_event_position_new(); + } + + pos->timestamp = ltt_time_infinite; + return pos; +} + +/* Save all positions, the ones with infinite time will have NULL + * ep. */ +/* note : a position must be destroyed when a trace is added/removed from a + * traceset */ +void lttv_traceset_context_position_save(const LttvTracesetContext *self, + LttvTracesetContextPosition *pos) +{ + guint i; + guint num_traces = lttv_traceset_number(self->ts); + guint tf_count = 0; + + pos->timestamp = ltt_time_infinite; + + for(i=0; itraces[i]->tracefiles; + guint j; + guint num_tracefiles = tracefiles->len; + + for(j=0;jtfcp->len); + LttvTracefileContext **tfc = &g_array_index(tracefiles, + LttvTracefileContext*, j); + LttvTracefileContextPosition *tfcp = + &g_array_index(pos->tfcp, LttvTracefileContextPosition, tf_count); + + tfcp->tfc = *tfc; + + if(ltt_time_compare((*tfc)->timestamp, ltt_time_infinite) != 0) { + LttEvent *event = ltt_tracefile_get_event((*tfc)->tf); + ltt_event_position(event, tfcp->event); + if(ltt_time_compare((*tfc)->timestamp, pos->timestamp) < 0) + pos->timestamp = (*tfc)->timestamp; + tfcp->used = TRUE; + } else { + tfcp->used = FALSE; + } + + //g_array_append_val(pos->tfc, *tfc); + //g_array_append_val(pos->ep, ep); + tf_count++; + } + + } +} + +void lttv_traceset_context_position_destroy(LttvTracesetContextPosition *pos) +{ + int i; + + for(i=0;itfcp->len;i++) { + LttvTracefileContextPosition *tfcp = + &g_array_index(pos->tfcp, LttvTracefileContextPosition, i); + g_free(tfcp->event); + tfcp->event = NULL; + tfcp->used = FALSE; + } + g_array_free(pos->tfcp, TRUE); + g_free(pos); +} + +void lttv_traceset_context_position_copy(LttvTracesetContextPosition *dest, + const LttvTracesetContextPosition *src) +{ + int i; + LttvTracefileContextPosition *src_tfcp, *dest_tfcp; + + g_assert(src->tfcp->len == src->tfcp->len); + + for(i=0;itfcp->len;i++) { + src_tfcp = + &g_array_index(src->tfcp, LttvTracefileContextPosition, i); + dest_tfcp = + &g_array_index(dest->tfcp, LttvTracefileContextPosition, i); + + dest_tfcp->used = src_tfcp->used; + dest_tfcp->tfc = src_tfcp->tfc; + + if(src_tfcp->used) { + ltt_event_position_copy( + dest_tfcp->event, + src_tfcp->event); + } + } + dest->timestamp = src->timestamp; +} + +gint lttv_traceset_context_ctx_pos_compare(const LttvTracesetContext *self, + const LttvTracesetContextPosition *pos) +{ + int i; + int ret = 0; + + if(pos->tfcp->len == 0) { + if(lttv_traceset_number(self->ts) == 0) return 0; + else return 1; + } + if(lttv_traceset_number(self->ts) == 0) + return -1; + + for(i=0;itfcp->len;i++) { + LttvTracefileContextPosition *tfcp = + &g_array_index(pos->tfcp, LttvTracefileContextPosition, i); + + if(tfcp->used == FALSE) { + if(ltt_time_compare(tfcp->tfc->timestamp, ltt_time_infinite) < 0) { + ret = -1; + } + } else { + if(ltt_time_compare(tfcp->tfc->timestamp, ltt_time_infinite) == 0) { + ret = 1; + } else { + LttEvent *event = ltt_tracefile_get_event(tfcp->tfc->tf); + + ret = ltt_event_position_compare((LttEventPosition*)event, + tfcp->event); + } + } + if(ret != 0) return ret; + + } + return 0; +} + + +gint lttv_traceset_context_pos_pos_compare( + const LttvTracesetContextPosition *pos1, + const LttvTracesetContextPosition *pos2) +{ + int i, j; + int ret = 0; + + if(ltt_time_compare(pos1->timestamp, ltt_time_infinite) == 0) { + if(ltt_time_compare(pos2->timestamp, ltt_time_infinite) == 0) + return 0; + else + return 1; + } + if(ltt_time_compare(pos2->timestamp, ltt_time_infinite) == 0) + return -1; + + for(i=0;itfcp->len;i++) { + LttvTracefileContextPosition *tfcp1 = + &g_array_index(pos1->tfcp, LttvTracefileContextPosition, i); + + if(tfcp1->used == TRUE) { + for(j=0;jtfcp->len;j++) { + LttvTracefileContextPosition *tfcp2 = + &g_array_index(pos2->tfcp, LttvTracefileContextPosition, j); + + if(tfcp1->tfc == tfcp2->tfc) { + if(tfcp2->used == TRUE) + ret = ltt_event_position_compare(tfcp1->event, tfcp2->event); + else + ret = -1; + + if(ret != 0) return ret; + } + } + + } else { + for(j=0;jtfcp->len;j++) { + LttvTracefileContextPosition *tfcp2 = + &g_array_index(pos2->tfcp, LttvTracefileContextPosition, j); + + if(tfcp1->tfc == tfcp2->tfc) + if(tfcp2->used == TRUE) ret = 1; + if(ret != 0) return ret; + } + } + } + return 0; +} + + +LttTime lttv_traceset_context_position_get_time( + const LttvTracesetContextPosition *pos) +{ + return pos->timestamp; +} + + +LttvTracefileContext *lttv_traceset_context_get_current_tfc(LttvTracesetContext *self) +{ + GTree *pqueue = self->pqueue; + LttvTracefileContext *tfc = NULL; + + g_tree_foreach(pqueue, get_first, &tfc); + + return tfc; +} + +/* lttv_process_traceset_synchronize_tracefiles + * + * Use the sync_position field of the trace set context to synchronize each + * tracefile with the previously saved position. + * + * If no previous position has been saved, it simply does nothing. + */ +void lttv_process_traceset_synchronize_tracefiles(LttvTracesetContext *tsc) +{ + g_assert(lttv_process_traceset_seek_position(tsc, tsc->sync_position) == 0); +} + + + + +void lttv_process_traceset_get_sync_data(LttvTracesetContext *tsc) +{ + lttv_traceset_context_position_save(tsc, tsc->sync_position); +} + +struct seek_back_data { + guint first_event; /* Index of the first event in the array : we will always + overwrite at this position : this is a circular array. + */ + guint events_found; + guint n; /* number of events requested */ + GPtrArray *array; /* array of LttvTracesetContextPositions pointers */ + LttvFilter *filter1; + LttvFilter *filter2; + LttvFilter *filter3; + gpointer data; + check_handler *check; + gboolean *stop_flag; + guint raw_event_count; +}; + +static gint seek_back_event_hook(void *hook_data, void* call_data) +{ + struct seek_back_data *sd = (struct seek_back_data*)hook_data; + LttvTracefileContext *tfc = (LttvTracefileContext*)call_data; + LttvTracesetContext *tsc = tfc->t_context->ts_context; + LttvTracesetContextPosition *pos; + + if(sd->check && sd->check(sd->raw_event_count, sd->stop_flag, sd->data)) + return TRUE; + sd->raw_event_count++; + + if(sd->filter1 != NULL && sd->filter1->head != NULL && + !lttv_filter_tree_parse(sd->filter1->head, + ltt_tracefile_get_event(tfc->tf), + tfc->tf, + tfc->t_context->t, + tfc,NULL,NULL)) { + return FALSE; + } + if(sd->filter2 != NULL && sd->filter2->head != NULL && + !lttv_filter_tree_parse(sd->filter2->head, + ltt_tracefile_get_event(tfc->tf), + tfc->tf, + tfc->t_context->t, + tfc,NULL,NULL)) { + return FALSE; + } + if(sd->filter3 != NULL && sd->filter3->head != NULL && + !lttv_filter_tree_parse(sd->filter3->head, + ltt_tracefile_get_event(tfc->tf), + tfc->tf, + tfc->t_context->t, + tfc,NULL,NULL)) { + return FALSE; + } + + pos = (LttvTracesetContextPosition*)g_ptr_array_index (sd->array, + sd->first_event); + + lttv_traceset_context_position_save(tsc, pos); + + if(sd->first_event >= sd->array->len - 1) sd->first_event = 0; + else sd->first_event++; + + sd->events_found = min(sd->n, sd->events_found + 1); + + return FALSE; +} + +/* Seek back n events back from the current position. + * + * Parameters : + * @self The trace set context + * @n number of events to jump over + * @first_offset The initial offset value used. + * never put first_offset at ltt_time_zero. + * @time_seeker Function pointer of the function to use to seek time : + * either lttv_process_traceset_seek_time + * or lttv_state_traceset_seek_time_closest + * @filter The filter to call. + * + * Return value : the number of events found (might be lower than the number + * requested if beginning of traceset is reached). + * + * The first search will go back first_offset and try to find the last n events + * matching the filter. If there are not enough, it will try to go back from the + * new trace point from first_offset*2, and so on, until beginning of trace or n + * events are found. + * + * Note : this function does not take in account the LttvFilter : use the + * similar function found in state.c instead. + * + * Note2 : the caller must make sure that the LttvTracesetContext does not + * contain any hook, as process_traceset_middle is used in this routine. + */ +guint lttv_process_traceset_seek_n_backward(LttvTracesetContext *self, + guint n, LttTime first_offset, + seek_time_fct time_seeker, + check_handler *check, + gboolean *stop_flag, + LttvFilter *filter1, + LttvFilter *filter2, + LttvFilter *filter3, + gpointer data) +{ + if(lttv_traceset_number(self->ts) == 0) return 0; + g_assert(ltt_time_compare(first_offset, ltt_time_zero) != 0); + + guint i; + LttvTracesetContextPosition *next_iter_end_pos = + lttv_traceset_context_position_new(self); + LttvTracesetContextPosition *end_pos = + lttv_traceset_context_position_new(self); + LttvTracesetContextPosition *saved_pos = + lttv_traceset_context_position_new(self); + LttTime time; + LttTime asked_time; + LttTime time_offset; + struct seek_back_data sd; + LttvHooks *hooks = lttv_hooks_new(); + + sd.first_event = 0; + sd.events_found = 0; + sd.array = g_ptr_array_sized_new(n); + sd.filter1 = filter1; + sd.filter2 = filter2; + sd.filter3 = filter3; + sd.data = data; + sd.n = n; + sd.check = check; + sd.stop_flag = stop_flag; + sd.raw_event_count = 0; + g_ptr_array_set_size(sd.array, n); + for(i=0;itime_span.end_time) > 0) { + time = self->time_span.end_time; + } + asked_time = time; + time_offset = first_offset; + + lttv_hooks_add(hooks, seek_back_event_hook, &sd, LTTV_PRIO_DEFAULT); + + lttv_process_traceset_begin(self, NULL, NULL, NULL, hooks, NULL); + + while(1) { + /* stop criteria : - n events found + * - asked_time < beginning of trace */ + if(ltt_time_compare(asked_time, self->time_span.start_time) < 0) break; + + lttv_traceset_context_position_copy(end_pos, next_iter_end_pos); + + /* We must seek the traceset back to time - time_offset */ + /* this time becomes the new reference time */ + time = ltt_time_sub(time, time_offset); + asked_time = time; + + time_seeker(self, time); + lttv_traceset_context_position_save(self, next_iter_end_pos); + /* Resync the time in case of a seek_closest */ + time = lttv_traceset_context_position_get_time(next_iter_end_pos); + if(ltt_time_compare(time, self->time_span.end_time) > 0) { + time = self->time_span.end_time; + } + + /* Process the traceset, calling a hook which adds events + * to the array, overwriting the tail. It changes first_event and + * events_found too. */ + /* We would like to have a clean context here : no other hook than our's */ + + lttv_process_traceset_middle(self, ltt_time_infinite, + G_MAXUINT, end_pos); + + if(sd.events_found < n) { + if(sd.first_event > 0) { + /* Save the first position */ + LttvTracesetContextPosition *pos = + (LttvTracesetContextPosition*)g_ptr_array_index (sd.array, 0); + lttv_traceset_context_position_copy(saved_pos, pos); + } + g_assert(n-sd.events_found <= sd.array->len); + /* Change array size to n - events_found */ + for(i=n-sd.events_found;ilen;i++) { + LttvTracesetContextPosition *pos = + (LttvTracesetContextPosition*)g_ptr_array_index (sd.array, i); + lttv_traceset_context_position_destroy(pos); + } + g_ptr_array_set_size(sd.array, n-sd.events_found); + sd.first_event = 0; + + } else break; /* Second end criterion : n events found */ + + time_offset = ltt_time_mul(time_offset, BACKWARD_SEEK_MUL); + } + + lttv_traceset_context_position_destroy(end_pos); + lttv_traceset_context_position_destroy(next_iter_end_pos); + + lttv_process_traceset_end(self, NULL, NULL, NULL, hooks, NULL); + + if(sd.events_found >= n) { + /* Seek the traceset to the first event in the circular array */ + LttvTracesetContextPosition *pos = + (LttvTracesetContextPosition*)g_ptr_array_index (sd.array, + sd.first_event); + g_assert(lttv_process_traceset_seek_position(self, pos) == 0); + } else { + /* Will seek to the last saved position : in the worst case, it will be the + * original position (if events_found is 0) */ + g_assert(lttv_process_traceset_seek_position(self, saved_pos) == 0); + } + + for(i=0;ilen;i++) { + LttvTracesetContextPosition *pos = + (LttvTracesetContextPosition*)g_ptr_array_index (sd.array, i); + lttv_traceset_context_position_destroy(pos); + } + g_ptr_array_free(sd.array, TRUE); + + lttv_hooks_destroy(hooks); + + lttv_traceset_context_position_destroy(saved_pos); + + return sd.events_found; +} + + +struct seek_forward_data { + guint event_count; /* event counter */ + guint n; /* requested number of events to jump over */ + LttvFilter *filter1; + LttvFilter *filter2; + LttvFilter *filter3; + gpointer data; + check_handler *check; + gboolean *stop_flag; + guint raw_event_count; /* event counter */ +}; + +static gint seek_forward_event_hook(void *hook_data, void* call_data) +{ + struct seek_forward_data *sd = (struct seek_forward_data*)hook_data; + LttvTracefileContext *tfc = (LttvTracefileContext*)call_data; + + if(sd->check && sd->check(sd->raw_event_count, sd->stop_flag, sd->data)) + return TRUE; + sd->raw_event_count++; + + if(sd->filter1 != NULL && sd->filter1->head != NULL && + !lttv_filter_tree_parse(sd->filter1->head, + ltt_tracefile_get_event(tfc->tf), + tfc->tf, + tfc->t_context->t, + tfc,NULL,NULL)) { + return FALSE; + } + if(sd->filter2 != NULL && sd->filter2->head != NULL && + !lttv_filter_tree_parse(sd->filter2->head, + ltt_tracefile_get_event(tfc->tf), + tfc->tf, + tfc->t_context->t, + tfc,NULL,NULL)) { + return FALSE; + } + if(sd->filter3 != NULL && sd->filter3->head != NULL && + !lttv_filter_tree_parse(sd->filter3->head, + ltt_tracefile_get_event(tfc->tf), + tfc->tf, + tfc->t_context->t, + tfc,NULL,NULL)) { + return FALSE; + } + + sd->event_count++; + if(sd->event_count >= sd->n) + return TRUE; + return FALSE; +} + +/* Seek back n events forward from the current position (1 to n) + * 0 is ok too, but it will actually do nothing. + * + * Parameters : + * @self the trace set context + * @n number of events to jump over + * @filter filter to call. + * + * returns : the number of events jumped over (may be less than requested if end + * of traceset reached) */ +guint lttv_process_traceset_seek_n_forward(LttvTracesetContext *self, + guint n, + check_handler *check, + gboolean *stop_flag, + LttvFilter *filter1, + LttvFilter *filter2, + LttvFilter *filter3, + gpointer data) +{ + struct seek_forward_data sd; + sd.event_count = 0; + sd.n = n; + sd.filter1 = filter1; + sd.filter2 = filter2; + sd.filter3 = filter3; + sd.data = data; + sd.check = check; + sd.stop_flag = stop_flag; + sd.raw_event_count = 0; + + if(sd.event_count >= sd.n) return sd.event_count; + + LttvHooks *hooks = lttv_hooks_new(); + + lttv_hooks_add(hooks, seek_forward_event_hook, &sd, LTTV_PRIO_DEFAULT); + + lttv_process_traceset_begin(self, NULL, NULL, NULL, hooks, NULL); + + /* it will end on the end of traceset, or the fact that the + * hook returns TRUE. + */ + lttv_process_traceset_middle(self, ltt_time_infinite, + G_MAXUINT, NULL); + + /* Here, our position is either the end of traceset, or the exact position + * after n events : leave it like this. This might be placed on an event that + * will be filtered out, we don't care : all we know is that the following + * event filtered in will be the right one. */ + + lttv_process_traceset_end(self, NULL, NULL, NULL, hooks, NULL); + + lttv_hooks_destroy(hooks); + + return sd.event_count; +} + + diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/tracecontext.h b/tags/lttv-0.11.3-23102008/lttv/lttv/tracecontext.h new file mode 100644 index 00000000..8bee8890 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/tracecontext.h @@ -0,0 +1,396 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 PROCESSTRACE_H +#define PROCESSTRACE_H + +#include +#include +#include +#include +#include +#include + +/* This is the generic part of trace processing. All events within a + certain time interval are accessed and processing hooks are called for + each. The events are examined in monotonically increasing time to more + closely follow the traced system behavior. + + Hooks are called at several different places during the processing: + before traceset, after traceset, check trace, before trace, after trace, + check tracefile, before tracefile, after tracefile, + check_event, before_event, before_event_by_id, + after_event, after_event_by_id. + + In each case the "check" hooks are called first to determine if further + processing of the trace, tracefile or event is wanted. Then, the before + hooks and the after hooks are called. The before hooks for a traceset + are called before those for the contained traces, which are called before + those for the contained tracefiles. The after hooks are called in reverse + order. The event hooks are called after all the before_tracefile hooks + and before all the after_tracefile hooks. + + The hooks receive two arguments, the hook_data and call_data. The hook_data + is specified when the hook is registered and typically links to the + object registering the hook (e.g. a graphical events viewer). The call_data + must contain all the context related to the call. The traceset hooks receive + the LttvTracesetContext provided by the caller. The trace hooks receive + the LttvTraceContext from the traces array in the LttvTracesetContext. + The tracefile and event hooks receive the LttvTracefileContext from + the tracefiles array in the LttvTraceContext. The LttEvent and LttTime + fields in the tracefile context are set to the current event and current + event time before calling the event hooks. No other context field is + modified. + + The contexts in the traces and tracefiles arrays must be allocated by + the caller, either before the call or during the before hooks of the + enclosing traceset or trace. The order in the traces array must + correspond to the lttv_traceset_get function. The order in the tracefiles + arrays must correspond to the ltt_trace_control_tracefile_get and + ltt_trace_per_cpu_tracefile_get functions. The traceset, trace and + tracefile contexts may be subtyped as needed. Indeed, both the contexts + and the hooks are defined by the caller. */ + + +typedef struct _LttvTracesetContext LttvTracesetContext; +typedef struct _LttvTracesetContextClass LttvTracesetContextClass; + +typedef struct _LttvTraceContext LttvTraceContext; +typedef struct _LttvTraceContextClass LttvTraceContextClass; + +typedef struct _LttvTracefileContext LttvTracefileContext; +typedef struct _LttvTracefileContextClass LttvTracefileContextClass; + +typedef struct _LttvTracesetContextPosition LttvTracesetContextPosition; +typedef struct _LttvTraceContextPosition LttvTraceContextPosition; + +#ifndef LTTVFILTER_TYPE_DEFINED +typedef struct _LttvFilter LttvFilter; +#define LTTVFILTER_TYPE_DEFINED +#endif + +#define LTTV_TRACESET_CONTEXT_TYPE (lttv_traceset_context_get_type ()) +#define LTTV_TRACESET_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LTTV_TRACESET_CONTEXT_TYPE, LttvTracesetContext)) +#define LTTV_TRACESET_CONTEXT_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), LTTV_TRACESET_CONTEXT_TYPE, LttvTracesetContextClass)) +#define LTTV_IS_TRACESET_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LTTV_TRACESET_CONTEXT_TYPE)) +#define LTTV_IS_TRACESET_CONTEXT_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), LTTV_TRACESET_CONTEXT_TYPE)) +#define LTTV_TRACESET_CONTEXT_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), LTTV_TRACESET_CONTEXT_TYPE, LttvTracesetContextClass)) + +struct _LttvTracesetContext { + GObject parent; + + LttvTraceset *ts; + LttvTraceContext **traces; + LttvAttribute *a; + LttvAttribute *ts_a; + TimeInterval time_span; + GTree *pqueue; + + LttvTracesetContextPosition *sync_position; /* position at which to sync the + trace context */ +}; + +struct _LttvTracesetContextClass { + GObjectClass parent; + + void (*init) (LttvTracesetContext *self, LttvTraceset *ts); + void (*fini) (LttvTracesetContext *self); + LttvTracesetContext* (*new_traceset_context) (LttvTracesetContext *self); + LttvTraceContext* (*new_trace_context) (LttvTracesetContext *self); + LttvTracefileContext* (*new_tracefile_context) (LttvTracesetContext *self); +}; + +GType lttv_traceset_context_get_type (void); + +void lttv_context_init(LttvTracesetContext *self, LttvTraceset *ts); + +void lttv_context_fini(LttvTracesetContext *self); + +LttvTracesetContext * +lttv_context_new_traceset_context(LttvTracesetContext *self); + +LttvTraceContext * +lttv_context_new_trace_context(LttvTracesetContext *self); + +LttvTracefileContext * +lttv_context_new_tracefile_context(LttvTracesetContext *self); + + +#define LTTV_TRACE_CONTEXT_TYPE (lttv_trace_context_get_type ()) +#define LTTV_TRACE_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LTTV_TRACE_CONTEXT_TYPE, LttvTraceContext)) +#define LTTV_TRACE_CONTEXT_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), LTTV_TRACE_CONTEXT_TYPE, LttvTraceContextClass)) +#define LTTV_IS_TRACE_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LTTV_TRACE_CONTEXT_TYPE)) +#define LTTV_IS_TRACE_CONTEXT_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), LTTV_TRACE_CONTEXT_TYPE)) +#define LTTV_TRACE_CONTEXT_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), LTTV_TRACE_CONTEXT_TYPE, LttvTraceContextClass)) + +struct _LttvTraceContext { + GObject parent; + + LttvTracesetContext *ts_context; + guint index; /* in ts_context->traces */ + LttTrace *t; + LttvTrace *vt; + //LttvTracefileContext **tracefiles; + GArray *tracefiles; + LttvAttribute *a; + LttvAttribute *t_a; + TimeInterval time_span; +}; + +struct _LttvTraceContextClass { + GObjectClass parent; +}; + +GType lttv_trace_context_get_type (void); + +#define LTTV_TRACEFILE_CONTEXT_TYPE (lttv_tracefile_context_get_type ()) +#define LTTV_TRACEFILE_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LTTV_TRACEFILE_CONTEXT_TYPE, LttvTracefileContext)) +#define LTTV_TRACEFILE_CONTEXT_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), LTTV_TRACEFILE_CONTEXT_TYPE, LttvTracefileContextClass)) +#define LTTV_IS_TRACEFILE_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LTTV_TRACEFILE_CONTEXT_TYPE)) +#define LTTV_IS_TRACEFILE_CONTEXT_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), LTTV_TRACEFILE_CONTEXT_TYPE)) +#define LTTV_TRACEFILE_CONTEXT_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), LTTV_TRACEFILE_CONTEXT_TYPE, LttvTracefileContextClass)) + +struct _LttvTracefileContext { + GObject parent; + + LttvTraceContext *t_context; + // gboolean control; + guint index; /* in ts_context->tracefiles */ + LttTracefile *tf; + // LttEvent *e; + LttvHooks *event; + LttvHooksById *event_by_id; + LttTime timestamp; + LttvAttribute *a; + gint target_pid; /* Target PID of the event. + Updated by state.c. -1 means unset. */ +}; + +struct _LttvTracefileContextClass { + GObjectClass parent; +}; + +GType lttv_tracefile_context_get_type (void); + +/* Run through the events in a traceset in sorted order calling all the + hooks appropriately. It starts at the current time and runs until end or + nb_events are processed. */ + +void lttv_process_traceset(LttvTracesetContext *self, LttTime end, + unsigned nb_events); + +/* Process traceset can also be done in smaller pieces calling begin, + * then seek and middle repeatedly, and end. The middle function return the + * number of events processed. It will be smaller than nb_events if the end time + * or end position is reached. */ + + +void lttv_process_traceset_begin(LttvTracesetContext *self, + LttvHooks *before_traceset, + LttvHooks *before_trace, + LttvHooks *before_tracefile, + LttvHooks *event, + LttvHooksById *event_by_id); + + +guint lttv_process_traceset_middle(LttvTracesetContext *self, + LttTime end, + gulong nb_events, + const LttvTracesetContextPosition *end_position); + +void lttv_process_traceset_end(LttvTracesetContext *self, + LttvHooks *after_traceset, + LttvHooks *after_trace, + LttvHooks *after_tracefile, + LttvHooks *event, + LttvHooksById *event_by_id); + + +void lttv_process_traceset_seek_time(LttvTracesetContext *self, LttTime start); + +gboolean lttv_process_traceset_seek_position(LttvTracesetContext *self, + const LttvTracesetContextPosition *pos); + +void lttv_process_trace_seek_time(LttvTraceContext *self, LttTime start); + +void lttv_traceset_context_add_hooks(LttvTracesetContext *self, + LttvHooks *before_traceset, + LttvHooks *before_trace, + LttvHooks *before_tracefile, + LttvHooks *event, + LttvHooksById *event_by_id); + +void lttv_traceset_context_remove_hooks(LttvTracesetContext *self, + LttvHooks *after_traceset, + LttvHooks *after_trace, + LttvHooks *after_tracefile, + LttvHooks *event, + LttvHooksById *event_by_id); + +void lttv_trace_context_add_hooks(LttvTraceContext *self, + LttvHooks *before_trace, + LttvHooks *before_tracefile, + LttvHooks *event, + LttvHooksById *event_by_id); + +void lttv_trace_context_remove_hooks(LttvTraceContext *self, + LttvHooks *after_trace, + LttvHooks *after_tracefile, + LttvHooks *event, + LttvHooksById *event_by_id); + +void lttv_tracefile_context_add_hooks(LttvTracefileContext *self, + LttvHooks *before_tracefile, + LttvHooks *event, + LttvHooksById *event_by_id); + + +void lttv_tracefile_context_remove_hooks(LttvTracefileContext *self, + LttvHooks *after_tracefile, + LttvHooks *event, + LttvHooksById *event_by_id); + + +void lttv_tracefile_context_add_hooks_by_id(LttvTracefileContext *self, + unsigned i, + LttvHooks *event_by_id); + +void lttv_tracefile_context_remove_hooks_by_id(LttvTracefileContext *self, + unsigned i); + +typedef struct _LttvTraceHook { + LttvHook h; + guint16 id; /* id of the marker associated with this hook */ + GPtrArray *fields; /* struct marker_fields pointers */ + gpointer hook_data; +} LttvTraceHook; + +/* Get the head of marker list corresponding to the given trace hook. + */ +struct marker_info *lttv_trace_hook_get_marker(LttTrace *t, LttvTraceHook *th); + +/* Remove the hooks from the array. Does not free the array itself. */ +void lttv_trace_hook_remove_all(GArray **th); + +/* Search in the trace for the id of the named event type within the named + facility. Then, find the three (if non null) named fields. All that + information is then used to fill the LttvTraceHook structure. This + is useful to find the specific id for an event within a trace, for + registering a hook using this structure as event data; + it already contains the (up to three) needed fields handles. + Returns the modified LttvTraceHook array. + Prints warnings if events or markers are not found. returns 1 on error, + 0 on success. + Adds the hooks to the trace_hooks array. + */ + +int lttv_trace_find_hook(LttTrace *t, GQuark facility_name, GQuark event_name, + GQuark fields[], LttvHook h, gpointer hook_data, GArray **trace_hooks); + +static inline struct marker_field * +lttv_trace_get_hook_field(LttvTraceHook *hook, unsigned int index) +{ + return g_ptr_array_index(hook->fields, index); +} + +static inline GQuark lttv_merge_facility_event_name(GQuark fac, GQuark ev) +{ + char *tmp; + const char *sfac, *sev; + GQuark ret; + + sfac = g_quark_to_string(fac); + sev = g_quark_to_string(ev); + tmp = g_new(char, strlen(sfac) + strlen(sev) + 3); /* 3: _ \0 \0 */ + strcpy(tmp, sfac); + strcat(tmp, "_"); + strcat(tmp, sev); + ret = g_quark_from_string(tmp); + g_free(tmp); + return ret; +} + +LttvTracefileContext *lttv_traceset_context_get_current_tfc( + LttvTracesetContext *self); + + +LttvTracesetContextPosition *lttv_traceset_context_position_new( + const LttvTracesetContext *self); + +void lttv_traceset_context_position_save(const LttvTracesetContext *self, + LttvTracesetContextPosition *pos); + +void lttv_traceset_context_position_destroy(LttvTracesetContextPosition *pos); + +void lttv_traceset_context_position_copy(LttvTracesetContextPosition *dest, + const LttvTracesetContextPosition *src); + +gint lttv_traceset_context_pos_pos_compare( + const LttvTracesetContextPosition *pos1, + const LttvTracesetContextPosition *pos2); + +gint lttv_traceset_context_ctx_pos_compare(const LttvTracesetContext *self, + const LttvTracesetContextPosition *pos2); + +LttTime lttv_traceset_context_position_get_time( + const LttvTracesetContextPosition *pos); + +gint compare_tracefile(gconstpointer a, gconstpointer b); + + +/* Synchronisation helpers : save/restore synchronization between ltt traces and + * a traceset context. */ +void lttv_process_traceset_synchronize_tracefiles(LttvTracesetContext *tsc); + +void lttv_process_traceset_get_sync_data(LttvTracesetContext *tsc); + +/* Seek n events forward and backward (without filtering) : only use these where + * necessary : the seek backward is costy. */ + +#define BACKWARD_SEEK_MUL 2 /* Multiplication factor of time_offset between + backward seek iterations */ + +static const LttTime seek_back_default_offset = { 1, 0 }; + +typedef gboolean check_handler(guint count, gboolean *stop_flag, gpointer data); + +guint lttv_process_traceset_seek_n_forward(LttvTracesetContext *self, + guint n, + check_handler *check, + gboolean *stop_flag, + LttvFilter *filter1, + LttvFilter *filter2, + LttvFilter *filter3, + gpointer data); +typedef void (*seek_time_fct)(LttvTracesetContext *self, LttTime start); + +/* If first_offset is ltt_time_zero, it will choose a default value */ +guint lttv_process_traceset_seek_n_backward(LttvTracesetContext *self, + guint n, + LttTime first_offset, + seek_time_fct, + check_handler *check, + gboolean *stop_flag, + LttvFilter *filter1, + LttvFilter *filter2, + LttvFilter *filter3, + gpointer data); + +#define FIELD_ARRAY(val...) ((GQuark[]){ val, 0 }) + +#endif // PROCESSTRACE_H diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/traceset.c b/tags/lttv-0.11.3-23102008/lttv/lttv/traceset.c new file mode 100644 index 00000000..181bdd53 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/traceset.c @@ -0,0 +1,214 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +/* A trace is a sequence of events gathered in the same tracing session. The + events may be stored in several tracefiles in the same directory. + A trace set is defined when several traces are to be analyzed together, + possibly to study the interactions between events in the different traces. +*/ + +struct _LttvTraceset { + char * filename; + GPtrArray *traces; + LttvAttribute *a; +}; + + +struct _LttvTrace { + LttTrace *t; + LttvAttribute *a; + guint ref_count; +}; + + +LttvTraceset *lttv_traceset_new() +{ + LttvTraceset *s; + + s = g_new(LttvTraceset, 1); + s->filename = NULL; + s->traces = g_ptr_array_new(); + s->a = g_object_new(LTTV_ATTRIBUTE_TYPE, NULL); + return s; +} + +char * lttv_traceset_name(LttvTraceset * s) +{ + return s->filename; +} + +LttvTrace *lttv_trace_new(LttTrace *t) +{ + LttvTrace *new_trace; + + new_trace = g_new(LttvTrace, 1); + new_trace->a = g_object_new(LTTV_ATTRIBUTE_TYPE, NULL); + new_trace->t = t; + new_trace->ref_count = 0; + return new_trace; +} + + +LttvTraceset *lttv_traceset_copy(LttvTraceset *s_orig) +{ + guint i; + LttvTraceset *s; + LttvTrace * trace; + + s = g_new(LttvTraceset, 1); + s->filename = NULL; + s->traces = g_ptr_array_new(); + for(i=0;itraces->len;i++) + { + trace = g_ptr_array_index(s_orig->traces, i); + trace->ref_count++; + + g_ptr_array_add(s->traces, + trace); + } + s->a = LTTV_ATTRIBUTE(lttv_iattribute_deep_copy(LTTV_IATTRIBUTE(s_orig->a))); + return s; +} + + +LttvTraceset *lttv_traceset_load(const gchar *filename) +{ + LttvTraceset *s = g_new(LttvTraceset,1); + FILE *tf; + + s->filename = g_strdup(filename); + tf = fopen(filename,"r"); + + g_critical("NOT IMPLEMENTED : load traceset data from a XML file"); + + fclose(tf); + return s; +} + +gint lttv_traceset_save(LttvTraceset *s) +{ + FILE *tf; + + tf = fopen(s->filename, "w"); + + g_critical("NOT IMPLEMENTED : save traceset data in a XML file"); + + fclose(tf); + return 0; +} + +void lttv_traceset_destroy(LttvTraceset *s) +{ + guint i; + + for(i=0;itraces->len;i++) { + LttvTrace *trace = g_ptr_array_index(s->traces, i); + lttv_trace_unref(trace); + if(lttv_trace_get_ref_number(trace) == 0) + lttv_trace_destroy(trace); + } + g_ptr_array_free(s->traces, TRUE); + g_object_unref(s->a); + g_free(s); +} + +void lttv_trace_destroy(LttvTrace *t) +{ + g_object_unref(t->a); + g_free(t); +} + + +void lttv_traceset_add(LttvTraceset *s, LttvTrace *t) +{ + t->ref_count++; + g_ptr_array_add(s->traces, t); +} + + +unsigned lttv_traceset_number(LttvTraceset *s) +{ + return s->traces->len; +} + + +LttvTrace *lttv_traceset_get(LttvTraceset *s, unsigned i) +{ + g_assert(s->traces->len > i); + return ((LttvTrace *)s->traces->pdata[i]); +} + + +void lttv_traceset_remove(LttvTraceset *s, unsigned i) +{ + LttvTrace * t; + g_assert(s->traces->len > i); + t = (LttvTrace *)s->traces->pdata[i]; + t->ref_count--; + g_ptr_array_remove_index(s->traces, i); +} + + +/* A set of attributes is attached to each trace set, trace and tracefile + to store user defined data as needed. */ + +LttvAttribute *lttv_traceset_attribute(LttvTraceset *s) +{ + return s->a; +} + + +LttvAttribute *lttv_trace_attribute(LttvTrace *t) +{ + return t->a; +} + + +LttTrace *lttv_trace(LttvTrace *t) +{ + return t->t; +} + +guint lttv_trace_get_ref_number(LttvTrace * t) +{ + return t->ref_count; +} + +guint lttv_trace_ref(LttvTrace * t) +{ + t->ref_count++; + + return t->ref_count; +} + +guint lttv_trace_unref(LttvTrace * t) +{ + if(likely(t->ref_count > 0)) + t->ref_count--; + + return t->ref_count; +} + diff --git a/tags/lttv-0.11.3-23102008/lttv/lttv/traceset.h b/tags/lttv-0.11.3-23102008/lttv/lttv/traceset.h new file mode 100644 index 00000000..7ef85be5 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/lttv/traceset.h @@ -0,0 +1,73 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 TRACESET_H +#define TRACESET_H + +#include +#include +#include + +/* A traceset is a set of traces to be analyzed together. */ + +typedef struct _LttvTraceset LttvTraceset; + +typedef struct _LttvTrace LttvTrace; + +/* Tracesets may be added to, removed from and their content listed. */ + +LttvTraceset *lttv_traceset_new(); + +char * lttv_traceset_name(LttvTraceset * s); + +LttvTrace *lttv_trace_new(LttTrace *t); + +LttvTraceset *lttv_traceset_copy(LttvTraceset *s_orig); + +LttvTraceset *lttv_traceset_load(const gchar *filename); + +gint lttv_traceset_save(LttvTraceset *s); + +void lttv_traceset_destroy(LttvTraceset *s); + +void lttv_trace_destroy(LttvTrace *t); + +void lttv_traceset_add(LttvTraceset *s, LttvTrace *t); + +unsigned lttv_traceset_number(LttvTraceset *s); + +LttvTrace *lttv_traceset_get(LttvTraceset *s, unsigned i); + +void lttv_traceset_remove(LttvTraceset *s, unsigned i); + +/* An attributes table is attached to the set and to each trace in the set. */ + +LttvAttribute *lttv_traceset_attribute(LttvTraceset *s); + +LttvAttribute *lttv_trace_attribute(LttvTrace *t); + +LttTrace *lttv_trace(LttvTrace *t); + +guint lttv_trace_get_ref_number(LttvTrace * t); + +guint lttv_trace_ref(LttvTrace * t); + +guint lttv_trace_unref(LttvTrace * t); + +#endif // TRACESET_H + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/Makefile.am b/tags/lttv-0.11.3-23102008/lttv/modules/Makefile.am new file mode 100644 index 00000000..14a47143 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/Makefile.am @@ -0,0 +1,28 @@ +# This file is part of the Linux Trace Toolkit viewer +# Copyright (C) 2003-2004 Mathieu Desnoyers +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License Version 2 as +# published by the Free Software Foundation; +# +# 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. + + + +# +# Makefile for LTT New generation user interface : plugins. +# +# Created by Mathieu Desnoyers on May 6, 2003 +# + +# the gui subdir comes first : dependency +SUBDIRS = text gui + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/README b/tags/lttv-0.11.3-23102008/lttv/modules/README new file mode 100644 index 00000000..8dce1b7f --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/README @@ -0,0 +1,21 @@ + +|-----------------| +|Graphical Plugins| +|-----------------| +gui/ + +detailedevents : Display events for time interval into a list. +controlflow : Display the control flow of the trace for time interval + using gdk and a GtkDrawingArea. Pixmaps for icons are + defined with each event inside the event related + information. +statistics : Display statistics computed from the trace. + +|---------------------| +|Text Oriented Plugins| +|---------------------| +text/ + +batchAnalysis : Call processing hooks for every events of the trace. +textDump : Dump in clear text all data related to events on standard output. + It depends on batchAnalysis. diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/examples/Makefile.am b/tags/lttv-0.11.3-23102008/lttv/modules/examples/Makefile.am new file mode 100644 index 00000000..82f7ec9d --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/examples/Makefile.am @@ -0,0 +1,17 @@ +# +# Makefile for LTT New generation user interface : test plugins. +# +# Created by Mathieu Desnoyers on May 6, 2003 +# + +libdir = ${lttvplugindir} +AM_CFLAGS = $(GLIB_CFLAGS) +LIBS += $(GLIB_LIBS) + +lib_LTLIBRARIES = libsampledep.la libsamplemodule.la libsamplemodule2.la +libsampledep_la_LDFLAGS = -module -avoid-version +libsampledep_la_SOURCES = sampledep.c +libsamplemodule_la_LDFLAGS = -module -avoid-version +libsamplemodule_la_SOURCES = samplemodule.c +libsamplemodule2_la_LDFLAGS = -module -avoid-version +libsamplemodule2_la_SOURCES = samplemodule2.c diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/examples/sampledep.c b/tags/lttv-0.11.3-23102008/lttv/modules/examples/sampledep.c new file mode 100644 index 00000000..9f0ca614 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/examples/sampledep.c @@ -0,0 +1,25 @@ +/* Sample module for Linux Trace Toolkit new generation User Interface */ + +/* Created by Mathieu Desnoyers, may 2003 */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +/* Include module.h from lttv headers for module loading */ +#include + +static void init() { + g_critical("Sample module dependant init()"); +} + +static void destroy() { + g_critical("Sample module dependant destroy()"); +} + + +LTTV_MODULE("sampledep", "Medium desc...", "Long desc...", init, destroy, \ + { "samplemodule" }) + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/examples/samplemodule.c b/tags/lttv-0.11.3-23102008/lttv/modules/examples/samplemodule.c new file mode 100644 index 00000000..6e676d5c --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/examples/samplemodule.c @@ -0,0 +1,22 @@ +/* Sample module for Linux Trace Toolkit new generation User Interface */ + +/* Created by Mathieu Desnoyers, may 2003 */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +static void init() { + g_critical("Sample module init()"); +} + +static void destroy() { + g_critical("Sample module destroy()"); +} + + +LTTV_MODULE("sampledep", "Medium...", "Long...", init, destroy, {}) + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/examples/samplemodule2.c b/tags/lttv-0.11.3-23102008/lttv/modules/examples/samplemodule2.c new file mode 100644 index 00000000..19a094d1 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/examples/samplemodule2.c @@ -0,0 +1,22 @@ +/* Sample module for Linux Trace Toolkit new generation User Interface */ + +/* Created by Mathieu Desnoyers, may 2003 */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +static void init() { + g_critical("Sample module 2 init()"); +} + +static void destroy() { + g_critical("Sample module 2 destroy()"); +} + + +LTTV_MODULE("samplemodule2", "Medium...", "Long...", init, destroy, {}) + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/Makefile.am b/tags/lttv-0.11.3-23102008/lttv/modules/gui/Makefile.am new file mode 100644 index 00000000..8ef0fcdb --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/Makefile.am @@ -0,0 +1,15 @@ +# +# Makefile for LTT New generation user interface : plugins. +# +# Created by Mathieu Desnoyers on May 6, 2003 +# + +# WARNING : subdirs order is important : mainWin depends on API + +SUBDIRS = lttvwindow controlflow detailedevents statistics filter tracecontrol resourceview histogram interrupts + +# TODO: PORT for 0.10 +#diskperformance tutorial + + + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/Makefile.am b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/Makefile.am new file mode 100644 index 00000000..0d977d41 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/Makefile.am @@ -0,0 +1,41 @@ +# This file is part of the Linux Trace Toolkit viewer +# Copyright (C) 2003-2004 Mathieu Desnoyers +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License Version 2 as +# published by the Free Software Foundation; +# +# 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. + + +# +# Makefile for LTT New generation user interface : plugins. +# +# Created by Mathieu Desnoyers on September 27, 2003 +# + +AM_CFLAGS = $(GLIB_CFLAGS) +AM_CFLAGS += $(GTK_CFLAGS) +AM_CFLAGS += -fvisibility=hidden +LIBS += $(GLIB_LIBS) +LIBS += $(GTK_LIBS) -L${top_builddir}/lttv/modules/gui/lttvwindow/lttvwindow -llttvwindow + +libdir = ${lttvplugindir} + +lib_LTLIBRARIES = libguicontrolflow.la +libguicontrolflow_la_LDFLAGS = -module -avoid-version +libguicontrolflow_la_SOURCES = module.c eventhooks.c cfv.c processlist.c\ + drawing.c drawitem.c lttv_plugin_cfv.c + +noinst_HEADERS = eventhooks.h cfv.h processlist.h\ + drawing.h drawitem.h lttv_plugin_cfv.h + +EXTRA_DIST = hGuiControlFlowInsert.xpm diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/TODO b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/TODO new file mode 100644 index 00000000..ade18326 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/TODO @@ -0,0 +1,64 @@ +TODO list on control flow view by Mathieu Desnoyers + +- Make "before hook" update process's info : it creates an inconsistency + between state.c process table and process list. Waiting to see if + state.c will take first event time as creation time or first schedule. + + +- Split in process view and CPU ciew : + - remove process 0 from the process list + - add a new viewer to the module : + - cfv.c -> new cpuctl.c : core of the view + - eventhooks.c -> cpuhooks.c : draw cpu states + - processlist.c -> cpulist.c + +- Check cfv for stability, modules load/unload, viewer start (many:3+), stop, + memory leaks; standardize module unloading : in all cases they should call + main window's API for viewer widget removal, and only then the memory should + be released (from the call of the main window). + +- Add viewer selected signal (call main window's API function) +- When viewing a big trace, use the fact that the drawing cannot be more + precise than the amount of horizontal pixels it has : "jump" from one event + to another, converting pixels to time in the trace. It will use efficiently + the "saved state" functionnality of underlying state.c. + +- Modify widgets'organization so the time bar is not affected by scrolling : + it may need to separate process list from the drawing area (not in a + scrolled window anymore). The idea would be to use a scrolled window for + the drawing area, put it in the right side of a hbox, put the tree view + in the left side and then connect the tree view's adjustment to the + scrolled window's scroll bar. + +- Modify icon, text and arc drawing so they are drawn only on top of the + background. They will have to be drawn from the following event or from + closure function in order to respect the priorities. + +Wish list + +- Tool bar for the viewer so many actions can be done on the screen. + +- Ability to select events with the mouse : in a "event selection mode", the + mouse click would "redraw" the image virtually and select an event. + (just like selectionGL in OpenGL). + +- Shown more events if wanted. + +- Use filter to show additionnal events. (do not filter schedule events! + The filtering should be applied to the additionnal events only). Note that it + require no change to filter : just do not make the filter apply on every + incoming events in the viewer's hooks. + +- Add vertical lines showing control flow between processes : it may become + confusing when working on multiple cpu. + +- Add a horizontal line showing the currently selected process. May be a + light dotted line, similar to the current time line, but with a different + color/pattern. + +- Add events customization window : make the user able to select the + text/icon/line color/background color/dots linked with any kind of + event. The framework is there, it must be completed. (the use of + Attributes to call hooks for every event type comes from there) + + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/cfv.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/cfv.c new file mode 100644 index 00000000..603ac630 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/cfv.c @@ -0,0 +1,389 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "cfv.h" +#include "drawing.h" +#include "processlist.h" +#include "eventhooks.h" +#include "lttv_plugin_cfv.h" + +extern GSList *g_control_flow_data_list; + +static gboolean +header_size_allocate(GtkWidget *widget, + GtkAllocation *allocation, + gpointer user_data) +{ + Drawing_t *drawing = (Drawing_t*)user_data; + + gtk_widget_set_size_request(drawing->ruler, -1, allocation->height); + //gtk_widget_queue_resize(drawing->padding); + //gtk_widget_queue_resize(drawing->ruler); + gtk_container_check_resize(GTK_CONTAINER(drawing->ruler_hbox)); + return 0; +} + +gboolean cfv_scroll_event(GtkWidget *widget, GdkEventScroll *event, + gpointer data) +{ + ControlFlowData *control_flow_data = (ControlFlowData*)data; + unsigned int cell_height = + get_cell_height( + GTK_TREE_VIEW(control_flow_data->process_list->process_list_widget)); + gdouble new; + + switch(event->direction) { + case GDK_SCROLL_UP: + { + new = gtk_adjustment_get_value(control_flow_data->v_adjust) + - cell_height; + } + break; + case GDK_SCROLL_DOWN: + { + new = gtk_adjustment_get_value(control_flow_data->v_adjust) + + cell_height; + } + break; + default: + return FALSE; + } + if(new >= control_flow_data->v_adjust->lower && + new <= control_flow_data->v_adjust->upper + - control_flow_data->v_adjust->page_size) + gtk_adjustment_set_value(control_flow_data->v_adjust, new); + return TRUE; +} + + +/* Toolbar callbacks */ +static void property_button (GtkToolButton *toolbutton, + gpointer user_data) +{ + ControlFlowData *control_flow_data = (ControlFlowData*)user_data; + + g_printf("CFV Property button clicked\n"); + +} + +/* Toolbar callbacks */ +static void filter_button (GtkToolButton *toolbutton, + gpointer user_data) +{ + LttvPluginCFV *plugin_cfv = (LttvPluginCFV*)user_data; + LttvAttribute *attribute; + LttvAttributeValue value; + gboolean ret; + g_printf("Filter button clicked\n"); + + attribute = LTTV_ATTRIBUTE(lttv_iattribute_find_subdir( + LTTV_IATTRIBUTE(lttv_global_attributes()), + LTTV_VIEWER_CONSTRUCTORS)); + g_assert(attribute); + + ret = lttv_iattribute_find_by_path(LTTV_IATTRIBUTE(attribute), + "guifilter", LTTV_POINTER, &value); + g_assert(ret); + lttvwindow_viewer_constructor constructor = + (lttvwindow_viewer_constructor)*(value.v_pointer); + if(constructor) constructor(&plugin_cfv->parent); + else g_warning("Filter module not loaded."); + + //FIXME : viewer returned. +} + +static void legend_button(GtkToolButton *toolbutton, gpointer user_data) +{ + GtkWindow *legend = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); + + gtk_window_set_title(legend, "Control Flow View Legend"); + + GtkWidget *pixmap = create_pixmap(GTK_WIDGET(legend), "controlflow-legend.png"); + + gtk_container_add(GTK_CONTAINER(legend), GTK_WIDGET(pixmap)); + + gtk_widget_show(GTK_WIDGET(pixmap)); + gtk_widget_show(GTK_WIDGET(legend)); +} + + + +/***************************************************************************** + * Control Flow Viewer class implementation * + *****************************************************************************/ +/** + * Control Flow Viewer's constructor + * + * This constructor is given as a parameter to the menuitem and toolbar button + * registration. It creates the drawing widget. + * @param ParentWindow A pointer to the parent window. + * @return The widget created. + */ +ControlFlowData * +guicontrolflow(LttvPluginTab *ptab) +{ + Tab *tab = ptab->tab; + GtkWidget *tmp_toolbar_icon; + GtkWidget *process_list_widget, *drawing_widget, *drawing_area; + //ControlFlowData* control_flow_data = g_new(ControlFlowData,1) ; + LttvPluginCFV *plugin_cfv = g_object_new(LTTV_TYPE_PLUGIN_CFV, NULL); + GtkTooltips *tooltips = gtk_tooltips_new(); + ControlFlowData* control_flow_data = plugin_cfv->cfd; + control_flow_data->ptab = ptab; + control_flow_data->tab = ptab->tab; + + control_flow_data->v_adjust = + GTK_ADJUSTMENT(gtk_adjustment_new( 0.0, /* Value */ + 0.0, /* Lower */ + 0.0, /* Upper */ + 0.0, /* Step inc. */ + 0.0, /* Page inc. */ + 0.0)); /* page size */ + + /* Create the drawing */ + control_flow_data->drawing = drawing_construct(control_flow_data); + + drawing_widget = + drawing_get_widget(control_flow_data->drawing); + + drawing_area = + drawing_get_drawing_area(control_flow_data->drawing); + + control_flow_data->number_of_process = 0; + control_flow_data->background_info_waiting = 0; + + /* Create the Process list */ + control_flow_data->process_list = processlist_construct(); + + process_list_widget = + processlist_get_widget(control_flow_data->process_list); + + gtk_tree_view_set_vadjustment(GTK_TREE_VIEW(process_list_widget), + GTK_ADJUSTMENT( + control_flow_data->v_adjust)); + + g_signal_connect (G_OBJECT(process_list_widget), + "scroll-event", + G_CALLBACK (cfv_scroll_event), + (gpointer)control_flow_data); + g_signal_connect (G_OBJECT(drawing_area), + "scroll-event", + G_CALLBACK (cfv_scroll_event), + (gpointer)control_flow_data); + + g_signal_connect (G_OBJECT(control_flow_data->process_list->button), + "size-allocate", + G_CALLBACK(header_size_allocate), + (gpointer)control_flow_data->drawing); +#if 0 /* not ready */ + g_signal_connect ( + // G_OBJECT(control_flow_data->process_list->process_list_widget), + G_OBJECT(control_flow_data->process_list->list_store), + "row-changed", + G_CALLBACK (tree_row_activated), + (gpointer)control_flow_data); +#endif //0 + + control_flow_data->hbox = gtk_hbox_new(FALSE, 1); + control_flow_data->toolbar = gtk_toolbar_new(); + gtk_toolbar_set_orientation(GTK_TOOLBAR(control_flow_data->toolbar), + GTK_ORIENTATION_VERTICAL); + + tmp_toolbar_icon = create_pixmap (main_window_get_widget(tab), + "guifilter16x16.png"); + gtk_widget_show(tmp_toolbar_icon); + control_flow_data->button_filter = gtk_tool_button_new(tmp_toolbar_icon, + "Filter"); + g_signal_connect (G_OBJECT(control_flow_data->button_filter), + "clicked", + G_CALLBACK (filter_button), + (gpointer)plugin_cfv); + gtk_toolbar_insert(GTK_TOOLBAR(control_flow_data->toolbar), + control_flow_data->button_filter, + 0); + gtk_tool_item_set_tooltip(GTK_TOOL_ITEM(control_flow_data->button_filter), + tooltips, "Open the filter window", NULL); + + tmp_toolbar_icon = create_pixmap (main_window_get_widget(tab), + "properties.png"); + gtk_widget_show(tmp_toolbar_icon); + control_flow_data->button_prop = gtk_tool_button_new(tmp_toolbar_icon, + "Properties"); + g_signal_connect (G_OBJECT(control_flow_data->button_prop), + "clicked", + G_CALLBACK (property_button), + (gpointer)control_flow_data); + gtk_toolbar_insert(GTK_TOOLBAR(control_flow_data->toolbar), + control_flow_data->button_prop, + 1); + + tmp_toolbar_icon = create_pixmap (main_window_get_widget(tab), + "qmark.png"); + gtk_widget_show(tmp_toolbar_icon); + control_flow_data->button_legend = gtk_tool_button_new(tmp_toolbar_icon, + "Legend"); + g_signal_connect (G_OBJECT(control_flow_data->button_legend), + "clicked", + G_CALLBACK (legend_button), + (gpointer)plugin_cfv); + gtk_toolbar_insert(GTK_TOOLBAR(control_flow_data->toolbar), + control_flow_data->button_legend, + 0); + gtk_tool_item_set_tooltip(GTK_TOOL_ITEM(control_flow_data->button_legend), + tooltips, "Show the legend", NULL); + + gtk_toolbar_set_style(GTK_TOOLBAR(control_flow_data->toolbar), + GTK_TOOLBAR_ICONS); + + gtk_box_pack_start(GTK_BOX(control_flow_data->hbox), + control_flow_data->toolbar, + FALSE, FALSE, 0); + control_flow_data->h_paned = gtk_hpaned_new(); + control_flow_data->box = gtk_event_box_new(); + gtk_box_pack_end(GTK_BOX(control_flow_data->hbox), + control_flow_data->box, + TRUE, TRUE, 0); + control_flow_data->top_widget = control_flow_data->hbox; + plugin_cfv->parent.top_widget = control_flow_data->top_widget; + gtk_container_add(GTK_CONTAINER(control_flow_data->box), + control_flow_data->h_paned); + + gtk_paned_pack1(GTK_PANED(control_flow_data->h_paned), + process_list_widget, FALSE, TRUE); + gtk_paned_pack2(GTK_PANED(control_flow_data->h_paned), + drawing_widget, TRUE, TRUE); + + gtk_container_set_border_width(GTK_CONTAINER(control_flow_data->box), 1); + + /* Set the size of the drawing area */ + //drawing_Resize(drawing, h, w); + + /* Get trace statistics */ + //control_flow_data->Trace_Statistics = get_trace_statistics(Trace); + + gtk_widget_show(drawing_widget); + gtk_widget_show(process_list_widget); + gtk_widget_show(control_flow_data->h_paned); + gtk_widget_show(control_flow_data->box); + gtk_widget_show(control_flow_data->toolbar); + //gtk_widget_show(GTK_WIDGET(control_flow_data->button_prop)); + gtk_widget_show(GTK_WIDGET(control_flow_data->button_filter)); + gtk_widget_show(GTK_WIDGET(control_flow_data->button_legend)); + gtk_widget_show(control_flow_data->hbox); + + g_object_set_data_full( + G_OBJECT(control_flow_data->top_widget), + "plugin_data", + plugin_cfv, + (GDestroyNotify)guicontrolflow_destructor); + + g_object_set_data( + G_OBJECT(drawing_area), + "control_flow_data", + control_flow_data); + + g_control_flow_data_list = g_slist_append( + g_control_flow_data_list, + plugin_cfv); + + control_flow_data->filter = NULL; + + //WARNING : The widget must be + //inserted in the main window before the drawing area + //can be configured (and this must happend bedore sending + //data) + + return control_flow_data; + +} + +/* Destroys widget also */ +void +guicontrolflow_destructor_full(gpointer data) +{ + LttvPluginCFV *plugin_cfv = (LttvPluginCFV*)data; + g_info("CFV.c : guicontrolflow_destructor_full, %p", plugin_cfv); + /* May already have been done by GTK window closing */ + if(GTK_IS_WIDGET(guicontrolflow_get_widget(plugin_cfv->cfd))) + gtk_widget_destroy(guicontrolflow_get_widget(plugin_cfv->cfd)); + //control_flow_data->mw = NULL; + //FIXME guicontrolflow_destructor(control_flow_data); +} + +/* When this destructor is called, the widgets are already disconnected */ +void +guicontrolflow_destructor(gpointer data) +{ + LttvPluginCFV *plugin_cfv = (LttvPluginCFV*)data; + Tab *tab = plugin_cfv->cfd->tab; + ControlFlowData *control_flow_data = plugin_cfv->cfd; + + g_info("CFV.c : guicontrolflow_destructor, %p", plugin_cfv); + g_info("%p, %p, %p", update_time_window_hook, plugin_cfv, tab); + if(GTK_IS_WIDGET(guicontrolflow_get_widget(plugin_cfv->cfd))) + g_info("widget still exists"); + + lttv_filter_destroy(plugin_cfv->cfd->filter); + /* Process List is removed with it's widget */ + //ProcessList_destroy(control_flow_data->process_list); + if(tab != NULL) + { + /* Delete reading hooks */ + lttvwindow_unregister_traceset_notify(tab, + traceset_notify, + control_flow_data); + + lttvwindow_unregister_time_window_notify(tab, + update_time_window_hook, + control_flow_data); + + lttvwindow_unregister_current_time_notify(tab, + update_current_time_hook, + control_flow_data); + + lttvwindow_unregister_redraw_notify(tab, redraw_notify, control_flow_data); + lttvwindow_unregister_continue_notify(tab, + continue_notify, + control_flow_data); + + lttvwindow_events_request_remove_all(control_flow_data->tab, + control_flow_data); + + } + lttvwindowtraces_background_notify_remove(control_flow_data); + g_control_flow_data_list = + g_slist_remove(g_control_flow_data_list, control_flow_data); + + g_info("CFV.c : guicontrolflow_destructor end, %p", control_flow_data); + //g_free(control_flow_data); + g_object_unref(plugin_cfv); +} + + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/cfv.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/cfv.h new file mode 100644 index 00000000..c8516e18 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/cfv.h @@ -0,0 +1,95 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 _CFV_H +#define _CFV_H + +#include +#include +#include +#include "processlist.h" +#include + +extern GQuark LTT_NAME_CPU; + +#ifndef TYPE_DRAWING_T_DEFINED +#define TYPE_DRAWING_T_DEFINED +typedef struct _Drawing_t Drawing_t; +#endif //TYPE_DRAWING_T_DEFINED + +#ifndef TYPE_CONTROLFLOWDATA_DEFINED +#define TYPE_CONTROLFLOWDATA_DEFINED +typedef struct _ControlFlowData ControlFlowData; +#endif //TYPE_CONTROLFLOWDATA_DEFINED + +struct _ControlFlowData { + + GtkWidget *top_widget; + Tab *tab; + LttvPluginTab *ptab; + + GtkWidget *hbox; + GtkWidget *toolbar; /* Vbox that contains the viewer's toolbar */ + GtkToolItem *button_prop; /* Properties button. */ + GtkToolItem *button_filter; /* Properties button. */ + GtkToolItem *button_legend; /* Properties button. */ + GtkWidget *box; /* box that contains the hpaned. necessary for it to work */ + GtkWidget *h_paned; + + ProcessList *process_list; + + Drawing_t *drawing; + GtkAdjustment *v_adjust ; + + /* Shown events information */ +// TimeWindow time_window; +// LttTime current_time; + + //guint currently_Selected_Event ; + guint number_of_process; + guint background_info_waiting; /* Number of background requests waited for + in order to have all the info ready. */ + + LttvFilter *filter; + +} ; + +/* Control Flow Data constructor */ +ControlFlowData *guicontrolflow(LttvPluginTab *ptab); +void +guicontrolflow_destructor_full(gpointer data); +void +guicontrolflow_destructor(gpointer data); + +static inline GtkWidget *guicontrolflow_get_widget( + ControlFlowData *control_flow_data) +{ + return control_flow_data->top_widget ; +} + +static inline ProcessList *guicontrolflow_get_process_list + (ControlFlowData *control_flow_data) +{ + return control_flow_data->process_list ; +} + + + +#endif // _CFV_H diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/drawing.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/drawing.c new file mode 100644 index 00000000..9acbb545 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/drawing.c @@ -0,0 +1,1382 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "drawing.h" +#include "eventhooks.h" +#include "cfv.h" + +#define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format) + +//FIXME +// fixed #define TRACE_NUMBER 0 +#define EXTRA_ALLOC 1024 // pixels + + +#if 0 /* colors for two lines representation */ +GdkColor drawing_colors[NUM_COLORS] = +{ /* Pixel, R, G, B */ + { 0, 0, 0, 0 }, /* COL_BLACK */ + { 0, 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_WHITE */ + { 0, 0x0FFF, 0xFFFF, 0xFFFF }, /* COL_WAIT_FORK : pale blue */ + { 0, 0xFFFF, 0xFFFF, 0x0000 }, /* COL_WAIT_CPU : yellow */ + { 0, 0xFFFF, 0xA000, 0xFCFF }, /* COL_EXIT : pale magenta */ + { 0, 0xFFFF, 0x0000, 0xFFFF }, /* COL_ZOMBIE : purple */ + { 0, 0xFFFF, 0x0000, 0x0000 }, /* COL_WAIT : red */ + { 0, 0x0000, 0xFFFF, 0x0000 }, /* COL_RUN : green */ + { 0, 0x8800, 0xFFFF, 0x8A00 }, /* COL_USER_MODE : pale green */ + { 0, 0x09FF, 0x01FF, 0xFFFF }, /* COL_SYSCALL : blue */ + { 0, 0xF900, 0x4200, 0xFF00 }, /* COL_TRAP : pale purple */ + { 0, 0xFFFF, 0x5AFF, 0x01FF }, /* COL_IRQ : orange */ + { 0, 0xFFFF, 0xFFFF, 0xFFFF } /* COL_MODE_UNKNOWN : white */ + +}; +#endif //0 + + +GdkColor drawing_colors[NUM_COLORS] = +{ /* Pixel, R, G, B */ + { 0, 0, 0, 0 }, /* COL_BLACK */ + { 0, 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_WHITE */ + { 0, 0x0000, 0xFF00, 0x0000 }, /* COL_RUN_USER_MODE : green */ + { 0, 0x0100, 0x9E00, 0xFFFF }, /* COL_RUN_SYSCALL : pale blue */ + { 0, 0xFF00, 0xFF00, 0x0100 }, /* COL_RUN_TRAP : yellow */ + { 0, 0xFFFF, 0x5E00, 0x0000 }, /* COL_RUN_IRQ : orange */ + { 0, 0xFFFF, 0x9400, 0x9600 }, /* COL_RUN_SOFT_IRQ : pink */ + { 0, 0x6600, 0x0000, 0x0000 }, /* COL_WAIT : dark red */ + { 0, 0x7700, 0x7700, 0x0000 }, /* COL_WAIT_CPU : dark yellow */ + { 0, 0x6400, 0x0000, 0x5D00 }, /* COL_ZOMBIE : dark purple */ + { 0, 0x0700, 0x6400, 0x0000 }, /* COL_WAIT_FORK : dark green */ + { 0, 0x8900, 0x0000, 0x8400 }, /* COL_EXIT : "less dark" magenta */ + { 0, 0x0000, 0x0000, 0x0000 }, /* COL_DEAD : black */ + { 0, 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_MODE_UNKNOWN : white */ + { 0, 0xFFFF, 0xFFFF, 0xFFFF } /* COL_UNNAMED : white */ + +}; + +/* +RUN+USER MODE green +RUN+SYSCALL +RUN+TRAP +RUN+IRQ +WAIT+foncé +WAIT CPU + WAIT FORK vert foncé ou jaune +IRQ rouge +TRAP: orange +SYSCALL: bleu pâle + +ZOMBIE + WAIT EXIT +*/ + + +/***************************************************************************** + * drawing functions * + *****************************************************************************/ + +static gboolean +expose_ruler( GtkWidget *widget, GdkEventExpose *event, gpointer user_data ); + +static gboolean +motion_notify_ruler(GtkWidget *widget, GdkEventMotion *event, gpointer user_data); + + +/* Function responsible for updating the exposed area. + * It must do an events request to the lttvwindow API to ask for this update. + * Note : this function cannot clear the background, because it may + * erase drawing already present (SAFETY). + */ +void drawing_data_request(Drawing_t *drawing, + gint x, gint y, + gint width, + gint height) +{ + if(width < 0) return ; + if(height < 0) return ; + + + Tab *tab = drawing->control_flow_data->tab; + TimeWindow time_window = + lttvwindow_get_time_window(tab); + + ControlFlowData *control_flow_data = drawing->control_flow_data; + // (ControlFlowData*)g_object_get_data( + // G_OBJECT(drawing->drawing_area), "control_flow_data"); + + LttTime start, time_end; + LttTime window_end = time_window.end_time; + + g_debug("req : window start_time : %lu, %lu", time_window.start_time.tv_sec, + time_window.start_time.tv_nsec); + + g_debug("req : window time width : %lu, %lu", time_window.time_width.tv_sec, + time_window.time_width.tv_nsec); + + g_debug("req : window_end : %lu, %lu", window_end.tv_sec, + window_end.tv_nsec); + + g_debug("x is : %i, x+width is : %i", x, x+width); + + convert_pixels_to_time(drawing->width, x, + time_window, + &start); + + convert_pixels_to_time(drawing->width, x+width, + time_window, + &time_end); + time_end = ltt_time_add(time_end, ltt_time_one); // because main window + // doesn't deliver end time. + + lttvwindow_events_request_remove_all(tab, + control_flow_data); + + { + /* find the tracehooks */ + LttvTracesetContext *tsc = lttvwindow_get_traceset_context(tab); + + LttvTraceset *traceset = tsc->ts; + + guint i, k, l, nb_trace; + + LttvTraceState *ts; + + LttvTracefileState *tfs; + + GArray *hooks; + + LttvTraceHook *hook; + + LttvTraceHook *th; + + guint ret; + gint first_after; + + nb_trace = lttv_traceset_number(traceset); + // FIXME (fixed) : eventually request for more traces + for(i = 0 ; i < nb_trace ; i++) { + EventsRequest *events_request = g_new(EventsRequest, 1); + // Create the hooks + //LttvHooks *event = lttv_hooks_new(); + LttvHooksById *event_by_id = lttv_hooks_by_id_new(); + LttvHooks *before_chunk_traceset = lttv_hooks_new(); + LttvHooks *after_chunk_traceset = lttv_hooks_new(); + LttvHooks *before_request_hook = lttv_hooks_new(); + LttvHooks *after_request_hook = lttv_hooks_new(); + + lttv_hooks_add(before_chunk_traceset, + before_chunk, + events_request, + LTTV_PRIO_DEFAULT); + + lttv_hooks_add(after_chunk_traceset, + after_chunk, + events_request, + LTTV_PRIO_DEFAULT); + + lttv_hooks_add(before_request_hook, + before_request, + events_request, + LTTV_PRIO_DEFAULT); + + lttv_hooks_add(after_request_hook, + after_request, + events_request, + LTTV_PRIO_DEFAULT); + + + ts = (LttvTraceState *)tsc->traces[i]; + + /* Find the eventtype id for the following events and register the + associated by id hooks. */ + + hooks = g_array_sized_new(FALSE, FALSE, sizeof(LttvTraceHook), 18); + + /* before hooks */ + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SYSCALL_ENTRY, + FIELD_ARRAY(LTT_FIELD_SYSCALL_ID), + before_execmode_hook, + events_request, + &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SYSCALL_EXIT, + NULL, + before_execmode_hook, + events_request, + &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL_ARCH, + LTT_EVENT_TRAP_ENTRY, + FIELD_ARRAY(LTT_FIELD_TRAP_ID), + before_execmode_hook, + events_request, + &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL_ARCH, + LTT_EVENT_TRAP_EXIT, + NULL, + before_execmode_hook, + events_request, + &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_IRQ_ENTRY, + FIELD_ARRAY(LTT_FIELD_IRQ_ID), + before_execmode_hook, + events_request, + &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_IRQ_EXIT, + NULL, + before_execmode_hook, + events_request, + &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SOFT_IRQ_ENTRY, + FIELD_ARRAY(LTT_FIELD_SOFT_IRQ_ID), + before_execmode_hook, + events_request, + &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SOFT_IRQ_EXIT, + NULL, + before_execmode_hook, + events_request, + &hooks); + + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SCHED_SCHEDULE, + FIELD_ARRAY(LTT_FIELD_PREV_PID, LTT_FIELD_NEXT_PID, LTT_FIELD_PREV_STATE), + before_schedchange_hook, + events_request, + &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_PROCESS_EXIT, + FIELD_ARRAY(LTT_FIELD_PID), + before_process_exit_hook, + events_request, + &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_PROCESS_FREE, + FIELD_ARRAY(LTT_FIELD_PID), + before_process_release_hook, + events_request, + &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_LIST, + LTT_EVENT_STATEDUMP_END, + NULL, + before_statedump_end, + events_request, + &hooks); + + /* After hooks */ + first_after = hooks->len; + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SCHED_SCHEDULE, + FIELD_ARRAY(LTT_FIELD_PREV_PID, LTT_FIELD_NEXT_PID, LTT_FIELD_PREV_STATE), + after_schedchange_hook, + events_request, + &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_PROCESS_FORK, + FIELD_ARRAY(LTT_FIELD_PARENT_PID, LTT_FIELD_CHILD_PID), + after_process_fork_hook, + events_request, + &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_PROCESS_EXIT, + FIELD_ARRAY(LTT_FIELD_PID), + after_process_exit_hook, + events_request, + &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_FS, + LTT_EVENT_EXEC, + NULL, + after_fs_exec_hook, + events_request, + &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_USER_GENERIC, + LTT_EVENT_THREAD_BRAND, + FIELD_ARRAY(LTT_FIELD_NAME), + after_user_generic_thread_brand_hook, + events_request, + &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_LIST, + LTT_EVENT_PROCESS_STATE, + FIELD_ARRAY(LTT_FIELD_PID, LTT_FIELD_PARENT_PID, LTT_FIELD_NAME), + after_event_enum_process_hook, + events_request, + &hooks); + + + /* Add these hooks to each event_by_id hooks list */ + /* add before */ + for(k = 0 ; k < first_after ; k++) { + th = &g_array_index(hooks, LttvTraceHook, k); + lttv_hooks_add(lttv_hooks_by_id_find(event_by_id, th->id), + th->h, + th, + LTTV_PRIO_STATE-5); + } + + /* add after */ + for(k = first_after ; k < hooks->len ; k++) { + th = &g_array_index(hooks, LttvTraceHook, k); + lttv_hooks_add(lttv_hooks_by_id_find(event_by_id, th->id), + th->h, + th, + LTTV_PRIO_STATE+5); + } + + events_request->hooks = hooks; + + // Fill the events request + events_request->owner = control_flow_data; + events_request->viewer_data = control_flow_data; + events_request->servicing = FALSE; + events_request->start_time = start; + events_request->start_position = NULL; + events_request->stop_flag = FALSE; + events_request->end_time = time_end; + events_request->num_events = G_MAXUINT; + events_request->end_position = NULL; + events_request->trace = i; //fixed /* FIXME */ + events_request->before_chunk_traceset = before_chunk_traceset; + events_request->before_chunk_trace = NULL; + events_request->before_chunk_tracefile = NULL; + events_request->event = NULL; + events_request->event_by_id = event_by_id; + events_request->after_chunk_tracefile = NULL; + events_request->after_chunk_trace = NULL; + events_request->after_chunk_traceset = after_chunk_traceset; + events_request->before_request = before_request_hook; + events_request->after_request = after_request_hook; + + g_debug("req : start : %lu, %lu", start.tv_sec, + start.tv_nsec); + + g_debug("req : end : %lu, %lu", time_end.tv_sec, + time_end.tv_nsec); + + lttvwindow_events_request(tab, events_request); + } + } +} + + +static void set_last_start(gpointer key, gpointer value, gpointer user_data) +{ + ProcessInfo *process_info = (ProcessInfo*)key; + HashedProcessData *hashed_process_data = (HashedProcessData*)value; + guint x = (guint)user_data; + + hashed_process_data->x.over = x; + hashed_process_data->x.over_used = FALSE; + hashed_process_data->x.over_marked = FALSE; + hashed_process_data->x.middle = x; + hashed_process_data->x.middle_used = FALSE; + hashed_process_data->x.middle_marked = FALSE; + hashed_process_data->x.under = x; + hashed_process_data->x.under_used = FALSE; + hashed_process_data->x.under_marked = FALSE; + hashed_process_data->next_good_time = ltt_time_zero; + + return; +} + +void drawing_data_request_begin(EventsRequest *events_request, LttvTracesetState *tss) +{ + g_debug("Begin of data request"); + ControlFlowData *cfd = events_request->viewer_data; + LttvTracesetContext *tsc = LTTV_TRACESET_CONTEXT(tss); + TimeWindow time_window = + lttvwindow_get_time_window(cfd->tab); + + guint width = cfd->drawing->width; + guint x=0; + + cfd->drawing->last_start = events_request->start_time; + + convert_time_to_pixels( + time_window, + events_request->start_time, + width, + &x); + + g_hash_table_foreach(cfd->process_list->process_hash, set_last_start, + (gpointer)x); + +} + +void drawing_chunk_begin(EventsRequest *events_request, LttvTracesetState *tss) +{ + g_debug("Begin of chunk"); + ControlFlowData *cfd = events_request->viewer_data; + LttvTracesetContext *tsc = &tss->parent.parent; + //LttTime current_time = lttv_traceset_context_get_current_tfc(tsc)->timestamp; + guint i; + LttvTraceset *traceset = tsc->ts; + guint nb_trace = lttv_traceset_number(traceset); + + if(!cfd->process_list->current_hash_data) { + cfd->process_list->current_hash_data = g_new(HashedProcessData**,nb_trace); + for(i = 0 ; i < nb_trace ; i++) { + guint num_cpu = ltt_trace_get_num_cpu(tss->parent.traces[i]->t); + cfd->process_list->current_hash_data[i] = g_new(HashedProcessData*,num_cpu); + memset(cfd->process_list->current_hash_data[i], 0, + sizeof(HashedProcessData*)*num_cpu); + } + } + //cfd->drawing->last_start = LTT_TIME_MIN(current_time, + // events_request->end_time); +} + + +void drawing_request_expose(EventsRequest *events_request, + LttvTracesetState *tss, + LttTime end_time) +{ + gint x, width; + guint x_end; + + ControlFlowData *cfd = events_request->viewer_data; + LttvTracesetContext *tsc = (LttvTracesetContext*)tss; + Drawing_t *drawing = cfd->drawing; + + TimeWindow time_window = + lttvwindow_get_time_window(cfd->tab); + + g_debug("request expose"); + + convert_time_to_pixels( + time_window, + end_time, + drawing->width, + &x_end); + x = drawing->damage_begin; + + width = x_end - x; + + drawing->damage_begin = x+width; + + // FIXME ? + gtk_widget_queue_draw_area ( drawing->drawing_area, + x, 0, + width, drawing->drawing_area->allocation.height); + + /* Update directly when scrolling */ + gdk_window_process_updates(drawing->drawing_area->window, + TRUE); +} + + +/* Callbacks */ + + +/* Create a new backing pixmap of the appropriate size */ +/* As the scaling will always change, it's of no use to copy old + * pixmap. + * + * Only change the size if width changes. The height is specified and changed + * when process ID are added or removed from the process list. + */ +static gboolean +configure_event( GtkWidget *widget, GdkEventConfigure *event, + gpointer user_data) +{ + Drawing_t *drawing = (Drawing_t*)user_data; + + + /* First, get the new time interval of the main window */ + /* we assume (see documentation) that the main window + * has updated the time interval before this configure gets + * executed. + */ + //lttvwindow_get_time_window(drawing->control_flow_data->mw, + // &drawing->control_flow_data->time_window); + + /* New pixmap, size of the configure event */ + //GdkPixmap *pixmap = gdk_pixmap_new(widget->window, + // widget->allocation.width + SAFETY, + // widget->allocation.height + SAFETY, + // -1); + + if(widget->allocation.width != drawing->width) { + g_debug("drawing configure event"); + g_debug("New alloc draw size : %i by %i",widget->allocation.width, + widget->allocation.height); + + drawing->width = widget->allocation.width; + + if(drawing->alloc_width < widget->allocation.width) { + //if(drawing->pixmap) + // gdk_pixmap_unref(drawing->pixmap); + + //drawing->pixmap = gdk_pixmap_new(widget->window, + // drawing->width + SAFETY + EXTRA_ALLOC, + // drawing->height + EXTRA_ALLOC, + // -1); + drawing->alloc_width = drawing->width + SAFETY + EXTRA_ALLOC; + drawing->alloc_height = drawing->height + EXTRA_ALLOC; + update_pixmap_size(drawing->control_flow_data->process_list, + drawing->alloc_width); + update_index_to_pixmap(drawing->control_flow_data->process_list); + } + //drawing->height = widget->allocation.height; + + //ProcessList_get_height + // (GuiControlFlow_get_process_list(drawing->control_flow_data)), + + + // Clear the image + //gdk_draw_rectangle (drawing->pixmap, + // widget->style->black_gc, + // TRUE, + // 0, 0, + // drawing->width+SAFETY, + // drawing->height); + + //g_info("init data request"); + + + /* Initial data request */ + /* no, do initial data request in the expose event */ + // Do not need to ask for data of 1 pixel : not synchronized with + // main window time at this moment. + //drawing_data_request(drawing, &drawing->pixmap, 0, 0, + // widget->allocation.width, + // widget->allocation.height); + + //drawing->width = widget->allocation.width; + //drawing->height = widget->allocation.height; + + drawing->damage_begin = 0; + drawing->damage_end = widget->allocation.width; + + if((widget->allocation.width != 1 && + widget->allocation.height != 1) + && drawing->damage_begin < drawing->damage_end) + { + + rectangle_pixmap (drawing->control_flow_data->process_list, + drawing->drawing_area->style->black_gc, + TRUE, + 0, 0, + drawing->alloc_width, // do not overlap + -1); + + + drawing_data_request(drawing, + drawing->damage_begin, + 0, + drawing->damage_end - drawing->damage_begin, + drawing->height); + } + } + return TRUE; +} + + +/* Redraw the screen from the backing pixmap */ +static gboolean +expose_event( GtkWidget *widget, GdkEventExpose *event, gpointer user_data ) +{ + Drawing_t *drawing = (Drawing_t*)user_data; + + ControlFlowData *control_flow_data = + (ControlFlowData*)g_object_get_data( + G_OBJECT(widget), + "control_flow_data"); +#if 0 + if(unlikely(drawing->gc == NULL)) { + drawing->gc = gdk_gc_new(drawing->drawing_area->window); + gdk_gc_copy(drawing->gc, drawing->drawing_area->style->black_gc); + } +#endif //0 + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + LttTime current_time = + lttvwindow_get_current_time(control_flow_data->tab); + + guint cursor_x=0; + + LttTime window_end = time_window.end_time; + + /* update the screen from the pixmap buffer */ +#if 0 + gdk_draw_pixmap(widget->window, + widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + drawing->pixmap, + event->area.x, event->area.y, + event->area.x, event->area.y, + event->area.width, event->area.height); +#endif //0 + drawing->height = processlist_get_height(control_flow_data->process_list); +#if 0 + copy_pixmap_to_screen(control_flow_data->process_list, + widget->window, + widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + event->area.x, event->area.y, + event->area.width, event->area.height); +#endif //0 + copy_pixmap_to_screen(control_flow_data->process_list, + widget->window, + drawing->gc, + event->area.x, event->area.y, + event->area.width, event->area.height); + + + /* Erase the dotted lines left.. */ + if(widget->allocation.height > drawing->height) + { + gdk_draw_rectangle (widget->window, + drawing->drawing_area->style->black_gc, + TRUE, + event->area.x, drawing->height, + event->area.width, // do not overlap + widget->allocation.height - drawing->height); + } + if(ltt_time_compare(time_window.start_time, current_time) <= 0 && + ltt_time_compare(window_end, current_time) >= 0) + { + /* Draw the dotted lines */ + convert_time_to_pixels( + time_window, + current_time, + drawing->width, + &cursor_x); + +#if 0 + if(drawing->dotted_gc == NULL) { + + drawing->dotted_gc = gdk_gc_new(drawing->drawing_area->window); + gdk_gc_copy(drawing->dotted_gc, widget->style->white_gc); + + gint8 dash_list[] = { 1, 2 }; + gdk_gc_set_line_attributes(drawing->dotted_gc, + 1, + GDK_LINE_ON_OFF_DASH, + GDK_CAP_BUTT, + GDK_JOIN_MITER); + gdk_gc_set_dashes(drawing->dotted_gc, + 0, + dash_list, + 2); + } +#endif //0 + gint height_tot = MAX(widget->allocation.height, drawing->height); + gdk_draw_line(widget->window, + drawing->dotted_gc, + cursor_x, 0, + cursor_x, height_tot); + } + return FALSE; +} + +static gboolean +after_expose_event( GtkWidget *widget, GdkEventExpose *event, gpointer user_data ) +{ + //g_assert(0); + g_debug("AFTER EXPOSE"); + + return FALSE; + + +} + +#if 0 +void +tree_row_activated(GtkTreeModel *treemodel, + GtkTreePath *arg1, + GtkTreeViewColumn *arg2, + gpointer user_data) +{ + ControlFlowData *cfd = (ControlFlowData*)user_data; + Drawing_t *drawing = cfd->drawing; + GtkTreeView *treeview = cfd->process_list->process_list_widget; + gint *path_indices; + gint height; + + path_indices = gtk_tree_path_get_indices (arg1); + + height = get_cell_height(cfd->process_list, + GTK_TREE_VIEW(treeview)); + drawing->horizontal_sel = height * path_indices[0]; + g_critical("new hor sel : %i", drawing->horizontal_sel); +} +#endif //0 + +/* mouse click */ +static gboolean +button_press_event( GtkWidget *widget, GdkEventButton *event, gpointer user_data ) +{ + ControlFlowData *control_flow_data = + (ControlFlowData*)g_object_get_data( + G_OBJECT(widget), + "control_flow_data"); + Drawing_t *drawing = control_flow_data->drawing; + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + + g_debug("click"); + if(event->button == 1) + { + LttTime time; + + /* left mouse button click */ + g_debug("x click is : %f", event->x); + + convert_pixels_to_time(drawing->width, (guint)event->x, + time_window, + &time); + + lttvwindow_report_current_time(control_flow_data->tab, time); + + } + + return FALSE; +} + +static gboolean +scrollbar_size_allocate(GtkWidget *widget, + GtkAllocation *allocation, + gpointer user_data) +{ + Drawing_t *drawing = (Drawing_t*)user_data; + + gtk_widget_set_size_request(drawing->padding, allocation->width, -1); + //gtk_widget_queue_resize(drawing->padding); + //gtk_widget_queue_resize(drawing->ruler); + gtk_container_check_resize(GTK_CONTAINER(drawing->ruler_hbox)); + return 0; +} + + + +Drawing_t *drawing_construct(ControlFlowData *control_flow_data) +{ + Drawing_t *drawing = g_new(Drawing_t, 1); + + drawing->control_flow_data = control_flow_data; + + drawing->vbox = gtk_vbox_new(FALSE, 1); + + + drawing->ruler_hbox = gtk_hbox_new(FALSE, 1); + drawing->ruler = gtk_drawing_area_new (); + //gtk_widget_set_size_request(drawing->ruler, -1, 27); + + drawing->padding = gtk_drawing_area_new (); + //gtk_widget_set_size_request(drawing->padding, -1, 27); + gtk_box_pack_start(GTK_BOX(drawing->ruler_hbox), drawing->ruler, + TRUE, TRUE, 0); + gtk_box_pack_end(GTK_BOX(drawing->ruler_hbox), drawing->padding, + FALSE, FALSE, 0); + + + + drawing->drawing_area = gtk_drawing_area_new (); + + drawing->gc = NULL; + + drawing->hbox = gtk_hbox_new(FALSE, 1); + drawing->viewport = gtk_viewport_new(NULL, control_flow_data->v_adjust); + drawing->scrollbar = gtk_vscrollbar_new(control_flow_data->v_adjust); + gtk_box_pack_start(GTK_BOX(drawing->hbox), drawing->viewport, + TRUE, TRUE, 0); + gtk_box_pack_end(GTK_BOX(drawing->hbox), drawing->scrollbar, + FALSE, FALSE, 0); + + //drawing->scrolled_window = + // gtk_scrolled_window_new (NULL, + // control_flow_data->v_adjust); + + //gtk_scrolled_window_set_policy( + // GTK_SCROLLED_WINDOW(drawing->scrolled_window), + // GTK_POLICY_NEVER, + // GTK_POLICY_AUTOMATIC); + + gtk_container_add(GTK_CONTAINER(drawing->viewport), + drawing->drawing_area); + //gtk_scrolled_window_add_with_viewport( + // GTK_SCROLLED_WINDOW(drawing->scrolled_window), + // drawing->drawing_area); + + gtk_box_pack_start(GTK_BOX(drawing->vbox), drawing->ruler_hbox, + FALSE, FALSE, 0); + gtk_box_pack_end(GTK_BOX(drawing->vbox), drawing->hbox, + TRUE, TRUE, 0); + + drawing->pango_layout = + gtk_widget_create_pango_layout(drawing->drawing_area, NULL); + + drawing->height = 1; + drawing->width = 1; + drawing->depth = 0; + drawing->alloc_height = 1; + drawing->alloc_width = 1; + + drawing->damage_begin = 0; + drawing->damage_end = 0; + drawing->horizontal_sel = -1; + + //gtk_widget_set_size_request(drawing->drawing_area->window, 50, 50); + g_object_set_data_full( + G_OBJECT(drawing->drawing_area), + "Link_drawing_Data", + drawing, + (GDestroyNotify)drawing_destroy); + + g_object_set_data( + G_OBJECT(drawing->ruler), + "drawing", + drawing); + + + //gtk_widget_modify_bg( drawing->drawing_area, + // GTK_STATE_NORMAL, + // &CF_Colors[BLACK]); + + //gdk_window_get_geometry(drawing->drawing_area->window, + // NULL, NULL, + // &(drawing->width), + // &(drawing->height), + // -1); + + //drawing->pixmap = gdk_pixmap_new( + // drawing->drawing_area->window, + // drawing->width, + // drawing->height, + // drawing->depth); + + //drawing->pixmap = NULL; + +// drawing->pixmap = gdk_pixmap_new(drawing->drawing_area->window, +// drawing->drawing_area->allocation.width, +// drawing->drawing_area->allocation.height, +// -1); + + g_signal_connect (G_OBJECT(drawing->drawing_area), + "configure_event", + G_CALLBACK (configure_event), + (gpointer)drawing); + + g_signal_connect (G_OBJECT(drawing->ruler), + "expose_event", + G_CALLBACK(expose_ruler), + (gpointer)drawing); + + gtk_widget_add_events(drawing->ruler, GDK_POINTER_MOTION_MASK); + + g_signal_connect (G_OBJECT(drawing->ruler), + "motion-notify-event", + G_CALLBACK(motion_notify_ruler), + (gpointer)drawing); + + + g_signal_connect (G_OBJECT(drawing->scrollbar), + "size-allocate", + G_CALLBACK(scrollbar_size_allocate), + (gpointer)drawing); + + + + g_signal_connect (G_OBJECT(drawing->drawing_area), + "expose_event", + G_CALLBACK (expose_event), + (gpointer)drawing); + + g_signal_connect_after (G_OBJECT(drawing->drawing_area), + "expose_event", + G_CALLBACK (after_expose_event), + (gpointer)drawing); + + g_signal_connect (G_OBJECT(drawing->drawing_area), + "button-press-event", + G_CALLBACK (button_press_event), + (gpointer)drawing); + + + gtk_widget_show(drawing->ruler); + gtk_widget_show(drawing->padding); + gtk_widget_show(drawing->ruler_hbox); + + gtk_widget_show(drawing->drawing_area); + //gtk_widget_show(drawing->scrolled_window); + gtk_widget_show(drawing->viewport); + gtk_widget_show(drawing->scrollbar); + gtk_widget_show(drawing->hbox); + + /* Allocate the colors */ + GdkColormap* colormap = gdk_colormap_get_system(); + gboolean success[NUM_COLORS]; + gdk_colormap_alloc_colors(colormap, drawing_colors, NUM_COLORS, FALSE, + TRUE, success); + + drawing->gc = + gdk_gc_new(GDK_DRAWABLE(main_window_get_widget(control_flow_data->tab)->window)); + drawing->dotted_gc = + gdk_gc_new(GDK_DRAWABLE(main_window_get_widget(control_flow_data->tab)->window)); + + gdk_gc_copy(drawing->gc, + main_window_get_widget(control_flow_data->tab)->style->black_gc); + gdk_gc_copy(drawing->dotted_gc, + main_window_get_widget(control_flow_data->tab)->style->white_gc); + + gint8 dash_list[] = { 1, 2 }; + gdk_gc_set_line_attributes(drawing->dotted_gc, + 1, + GDK_LINE_ON_OFF_DASH, + GDK_CAP_BUTT, + GDK_JOIN_MITER); + gdk_gc_set_dashes(drawing->dotted_gc, + 0, + dash_list, + 2); + + drawing->ruler_gc_butt = + gdk_gc_new(GDK_DRAWABLE(main_window_get_widget(control_flow_data->tab)->window)); + gdk_gc_copy(drawing->ruler_gc_butt, + main_window_get_widget(control_flow_data->tab)->style->black_gc); + drawing->ruler_gc_round = + gdk_gc_new(GDK_DRAWABLE(main_window_get_widget(control_flow_data->tab)->window)); + gdk_gc_copy(drawing->ruler_gc_round, + main_window_get_widget(control_flow_data->tab)->style->black_gc); + + + gdk_gc_set_line_attributes(drawing->ruler_gc_butt, + 2, + GDK_LINE_SOLID, + GDK_CAP_BUTT, + GDK_JOIN_MITER); + + gdk_gc_set_line_attributes(drawing->ruler_gc_round, + 2, + GDK_LINE_SOLID, + GDK_CAP_ROUND, + GDK_JOIN_ROUND); + + + return drawing; +} + +void drawing_destroy(Drawing_t *drawing) +{ + g_info("drawing_destroy %p", drawing); + + /* Free the colors */ + GdkColormap* colormap = gdk_colormap_get_system(); + + gdk_colormap_free_colors(colormap, drawing_colors, NUM_COLORS); + + + + // Do not unref here, Drawing_t destroyed by it's widget. + //g_object_unref( G_OBJECT(drawing->drawing_area)); + if(drawing->gc != NULL) + gdk_gc_unref(drawing->gc); + + g_object_unref(drawing->pango_layout); + if(drawing->dotted_gc != NULL) gdk_gc_unref(drawing->dotted_gc); + if(drawing->ruler_gc_butt != NULL) gdk_gc_unref(drawing->ruler_gc_butt); + if(drawing->ruler_gc_round != NULL) gdk_gc_unref(drawing->ruler_gc_round); + + g_free(drawing); + g_info("drawing_destroy end"); +} + +GtkWidget *drawing_get_drawing_area(Drawing_t *drawing) +{ + return drawing->drawing_area; +} + +GtkWidget *drawing_get_widget(Drawing_t *drawing) +{ + return drawing->vbox; +} + +void drawing_draw_line( Drawing_t *drawing, + GdkPixmap *pixmap, + guint x1, guint y1, + guint x2, guint y2, + GdkGC *GC) +{ + gdk_draw_line (pixmap, + GC, + x1, y1, x2, y2); +} + +void drawing_clear(Drawing_t *drawing) +{ + //if (drawing->pixmap) + // gdk_pixmap_unref(drawing->pixmap); + ControlFlowData *cfd = drawing->control_flow_data; + + + rectangle_pixmap(cfd->process_list, + drawing->drawing_area->style->black_gc, + TRUE, + 0, 0, + drawing->alloc_width, // do not overlap + -1); + + //drawing->height = 1; + /* Allocate a new pixmap with new height */ + //drawing->pixmap = gdk_pixmap_new(drawing->drawing_area->window, + // drawing->width + SAFETY + EXTRA_ALLOC, + // drawing->height + EXTRA_ALLOC, + // -1); + //drawing->alloc_width = drawing->width + SAFETY + EXTRA_ALLOC; + //drawing->alloc_height = drawing->height + EXTRA_ALLOC; + + //gtk_widget_set_size_request(drawing->drawing_area, + // -1, + // drawing->height); + //gtk_widget_queue_resize_no_redraw(drawing->drawing_area); + + /* ask for the buffer to be redrawn */ + gtk_widget_queue_draw ( drawing->drawing_area); +} + +#if 0 +/* Insert a square corresponding to a new process in the list */ +/* Applies to whole drawing->width */ +void drawing_insert_square(Drawing_t *drawing, + guint y, + guint height) +{ + //GdkRectangle update_rect; + gboolean reallocate = FALSE; + GdkPixmap *new_pixmap; + + /* Allocate a new pixmap with new height */ + if(drawing->alloc_height < drawing->height + height) { + + new_pixmap = gdk_pixmap_new(drawing->drawing_area->window, + drawing->width + SAFETY + EXTRA_ALLOC, + drawing->height + height + EXTRA_ALLOC, + -1); + drawing->alloc_width = drawing->width + SAFETY + EXTRA_ALLOC; + drawing->alloc_height = drawing->height + height + EXTRA_ALLOC; + reallocate = TRUE; + + /* Copy the high region */ + gdk_draw_pixmap (new_pixmap, + drawing->drawing_area->style->black_gc, + drawing->pixmap, + 0, 0, + 0, 0, + drawing->width + SAFETY, y); + + } else { + new_pixmap = drawing->pixmap; + } + + //GdkPixmap *pixmap = gdk_pixmap_new(drawing->drawing_area->window, + // drawing->width + SAFETY, + // drawing->height + height, + // -1); + + /* add an empty square */ + gdk_draw_rectangle (new_pixmap, + drawing->drawing_area->style->black_gc, + TRUE, + 0, y, + drawing->width + SAFETY, // do not overlap + height); + + /* copy the bottom of the region */ + gdk_draw_pixmap (new_pixmap, + drawing->drawing_area->style->black_gc, + drawing->pixmap, + 0, y, + 0, y + height, + drawing->width+SAFETY, drawing->height - y); + + + if(reallocate && likely(drawing->pixmap)) { + gdk_pixmap_unref(drawing->pixmap); + drawing->pixmap = new_pixmap; + } + + if(unlikely(drawing->height==1)) drawing->height = height; + else drawing->height += height; + + gtk_widget_set_size_request(drawing->drawing_area, + -1, + drawing->height); + gtk_widget_queue_resize_no_redraw(drawing->drawing_area); + + /* ask for the buffer to be redrawn */ + gtk_widget_queue_draw_area ( drawing->drawing_area, + 0, y, + drawing->width, drawing->height-y); +} + + +/* Remove a square corresponding to a removed process in the list */ +void drawing_remove_square(Drawing_t *drawing, + guint y, + guint height) +{ + GdkPixmap *pixmap; + + if(unlikely((guint)drawing->height == height)) { + //pixmap = gdk_pixmap_new( + // drawing->drawing_area->window, + // drawing->width + SAFETY, + // 1, + // -1); + pixmap = drawing->pixmap; + drawing->height=1; + } else { + /* Allocate a new pixmap with new height */ + //pixmap = gdk_pixmap_new( + // drawing->drawing_area->window, + // drawing->width + SAFETY, + // drawing->height - height, + // -1); + /* Keep the same preallocated pixmap */ + pixmap = drawing->pixmap; + + /* Copy the high region */ + gdk_draw_pixmap (pixmap, + drawing->drawing_area->style->black_gc, + drawing->pixmap, + 0, 0, + 0, 0, + drawing->width + SAFETY, y); + + /* Copy up the bottom of the region */ + gdk_draw_pixmap (pixmap, + drawing->drawing_area->style->black_gc, + drawing->pixmap, + 0, y + height, + 0, y, + drawing->width, drawing->height - y - height); + + drawing->height-=height; + } + + //if(likely(drawing->pixmap)) + // gdk_pixmap_unref(drawing->pixmap); + + //drawing->pixmap = pixmap; + + gtk_widget_set_size_request(drawing->drawing_area, + -1, + drawing->height); + gtk_widget_queue_resize_no_redraw(drawing->drawing_area); + /* ask for the buffer to be redrawn */ + gtk_widget_queue_draw_area ( drawing->drawing_area, + 0, y, + drawing->width, MAX(drawing->height-y, 1)); +} +#endif //0 + +void drawing_update_ruler(Drawing_t *drawing, TimeWindow *time_window) +{ + GtkRequisition req; + GdkRectangle rect; + + req.width = drawing->ruler->allocation.width; + req.height = drawing->ruler->allocation.height; + + + rect.x = 0; + rect.y = 0; + rect.width = req.width; + rect.height = req.height; + + gtk_widget_queue_draw(drawing->ruler); + //gtk_widget_draw( drawing->ruler, &rect); +} + +/* Redraw the ruler */ +static gboolean +expose_ruler( GtkWidget *widget, GdkEventExpose *event, gpointer user_data ) +{ + Drawing_t *drawing = (Drawing_t*)user_data; + TimeWindow time_window = lttvwindow_get_time_window(drawing->control_flow_data->tab); + gchar text[255]; + + PangoContext *context; + PangoLayout *layout; + PangoFontDescription *FontDesc; + PangoRectangle ink_rect; + gint global_width=0; + GdkColor foreground = { 0, 0, 0, 0 }; + GdkColor background = { 0, 0xffff, 0xffff, 0xffff }; + + LttTime window_end = time_window.end_time; + LttTime half_width = + ltt_time_div(time_window.time_width,2.0); + LttTime window_middle = + ltt_time_add(half_width, + time_window.start_time); + g_debug("ruler expose event"); + + gdk_draw_rectangle (drawing->ruler->window, + drawing->ruler->style->white_gc, + TRUE, + event->area.x, event->area.y, + event->area.width, + event->area.height); + + gdk_draw_line (drawing->ruler->window, + drawing->ruler_gc_butt, + event->area.x, 1, + event->area.x + event->area.width, 1); + + + snprintf(text, 255, "%lus\n%luns", + time_window.start_time.tv_sec, + time_window.start_time.tv_nsec); + + layout = gtk_widget_create_pango_layout(drawing->drawing_area, NULL); + + context = pango_layout_get_context(layout); + FontDesc = pango_context_get_font_description(context); + + pango_font_description_set_size(FontDesc, 6*PANGO_SCALE); + pango_layout_context_changed(layout); + + pango_layout_set_text(layout, text, -1); + pango_layout_get_pixel_extents(layout, &ink_rect, NULL); + global_width += ink_rect.width; + + gdk_draw_layout_with_colors(drawing->ruler->window, + drawing->ruler_gc_butt, + 0, + 6, + layout, &foreground, &background); + + gdk_draw_line (drawing->ruler->window, + drawing->ruler_gc_round, + 1, 1, + 1, 7); + + + snprintf(text, 255, "%lus\n%luns", window_end.tv_sec, + window_end.tv_nsec); + + pango_layout_set_text(layout, text, -1); + pango_layout_get_pixel_extents(layout, &ink_rect, NULL); + global_width += ink_rect.width; + + if(global_width <= drawing->ruler->allocation.width) + { + gdk_draw_layout_with_colors(drawing->ruler->window, + drawing->ruler_gc_butt, + drawing->ruler->allocation.width - ink_rect.width, + 6, + layout, &foreground, &background); + + gdk_draw_line (drawing->ruler->window, + drawing->ruler_gc_butt, + drawing->ruler->allocation.width-1, 1, + drawing->ruler->allocation.width-1, 7); + } + + + snprintf(text, 255, "%lus\n%luns", window_middle.tv_sec, + window_middle.tv_nsec); + + pango_layout_set_text(layout, text, -1); + pango_layout_get_pixel_extents(layout, &ink_rect, NULL); + global_width += ink_rect.width; + + if(global_width <= drawing->ruler->allocation.width) + { + gdk_draw_layout_with_colors(drawing->ruler->window, + drawing->ruler_gc_butt, + (drawing->ruler->allocation.width - ink_rect.width)/2, + 6, + layout, &foreground, &background); + + gdk_draw_line (drawing->ruler->window, + drawing->ruler_gc_butt, + drawing->ruler->allocation.width/2, 1, + drawing->ruler->allocation.width/2, 7); + + + + + } + + g_object_unref(layout); + + return FALSE; +} + + +/* notify mouse on ruler */ +static gboolean +motion_notify_ruler(GtkWidget *widget, GdkEventMotion *event, gpointer user_data) +{ + //g_debug("motion"); + //eventually follow mouse and show time here + return 0; +} diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/drawing.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/drawing.h new file mode 100644 index 00000000..3e4b3dc8 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/drawing.h @@ -0,0 +1,222 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 _DRAWING_H +#define _DRAWING_H + +#include +#include +#include +#include +#include +#include +#include +#include "cfv.h" +#include "drawitem.h" + + +#define SAFETY 50 // safety pixels at right and bottom of pixmap buffer + +typedef enum _draw_color { + COL_BLACK, + COL_WHITE, + COL_RUN_USER_MODE,/* green */ + COL_RUN_SYSCALL, /* pale blue */ + COL_RUN_TRAP, /* yellow */ + COL_RUN_IRQ, /* orange */ + COL_RUN_SOFT_IRQ, /* red */ + COL_WAIT, /* dark red */ + COL_WAIT_CPU, /* dark yellow */ + COL_ZOMBIE, /* dark purple */ + COL_WAIT_FORK, /* dark green */ + COL_EXIT, /* "less dark" magenta */ + COL_DEAD, /* black */ + COL_MODE_UNKNOWN, /* white */ + COL_UNNAMED, /* white */ + NUM_COLORS } draw_color; + +extern GdkColor drawing_colors[NUM_COLORS]; + +/* This part of the viewer does : + * Draw horizontal lines, getting graphic context as arg. + * Copy region of the screen into another. + * Modify the boundaries to reflect a scale change. (resize) + * Refresh the physical screen with the pixmap + * A helper function is provided here to convert from time to process + * identifier to pixels and the contrary (will be useful for mouse selection). + * Insert an empty square in the drawing, moving the bottom part. + * + * Note: The last point is exactly why it would not be so easy to add the + * vertical line functionnality as in the original version of LTT. In order + * to do so, we should keep all processes in the list for the duration of + * all the trace instead of dynamically adding and removing them when we + * scroll. Another possibility is to redraw all the visible area when a new + * process is added to the list. The second solution seems more appropriate + * to me. + * + * + * The pixmap used has the width of the physical window, but the height + * of the shown processes. + */ + +#ifndef TYPE_DRAWING_T_DEFINED +#define TYPE_DRAWING_T_DEFINED +typedef struct _Drawing_t Drawing_t; +#endif //TYPE_DRAWING_T_DEFINED + +#ifndef TYPE_CONTROLFLOWDATA_DEFINED +#define TYPE_CONTROLFLOWDATA_DEFINED +typedef struct _ControlFlowData ControlFlowData; +#endif //TYPE_CONTROLFLOWDATA_DEFINED + +struct _Drawing_t { + GtkWidget *vbox; + GtkWidget *drawing_area; + //GtkWidget *scrolled_window; + GtkWidget *hbox; + GtkWidget *viewport; + GtkWidget *scrollbar; + + GtkWidget *ruler_hbox; + GtkWidget *ruler; + GtkWidget *padding; + //GdkPixmap *pixmap; + ControlFlowData *control_flow_data; + + PangoLayout *pango_layout; + + gint height, width, depth; + /* height and width of allocated buffer pixmap */ + gint alloc_height, alloc_width; + + /* X coordinate of damaged region */ + gint damage_begin, damage_end; /* damaged region to be exposed, + updated per chunk */ + LttTime last_start; + GdkGC *dotted_gc; + GdkGC *gc; + GdkGC *ruler_gc_butt; + GdkGC *ruler_gc_round; + + /* Position of the horizontal selector, -1 for none */ + gint horizontal_sel; +}; + +Drawing_t *drawing_construct(ControlFlowData *control_flow_data); +void drawing_destroy(Drawing_t *drawing); + +GtkWidget *drawing_get_widget(Drawing_t *drawing); +GtkWidget *drawing_get_drawing_area(Drawing_t *drawing); + + +void drawing_data_request(Drawing_t *drawing, + gint x, gint y, + gint width, + gint height); + +void drawing_draw_line( Drawing_t *drawing, + GdkPixmap *pixmap, + guint x1, guint y1, + guint x2, guint y2, + GdkGC *GC); + +//void drawing_copy( Drawing_t *drawing, +// guint xsrc, guint ysrc, +// guint xdest, guint ydest, +// guint width, guint height); + +/* Clear the drawing : make it 1xwidth. */ +void drawing_clear(Drawing_t *drawing); + +/* Insert a square corresponding to a new process in the list */ +void drawing_insert_square(Drawing_t *drawing, + guint y, + guint height); + +/* Remove a square corresponding to a removed process in the list */ +void drawing_remove_square(Drawing_t *drawing, + guint y, + guint height); + +void drawing_update_ruler(Drawing_t *drawing, TimeWindow *time_window); + +void drawing_request_expose(EventsRequest *events_request, + LttvTracesetState *tss, + LttTime end_time); + +void drawing_data_request_begin(EventsRequest *events_request, + LttvTracesetState *tss); +void drawing_chunk_begin(EventsRequest *events_request, LttvTracesetState *tss); + + + +void +tree_row_activated(GtkTreeModel *treemodel, + GtkTreePath *arg1, + GtkTreeViewColumn *arg2, + gpointer user_data); + + +/* convert_pixels_to_time + * + * Convert from window pixel and time interval to an absolute time. + */ +static inline void convert_pixels_to_time( + gint width, + guint x, + TimeWindow time_window, + LttTime *time) +{ + double time_d; + + time_d = time_window.time_width_double; + time_d = time_d / (double)width * (double)x; + *time = ltt_time_from_double(time_d); + *time = ltt_time_add(time_window.start_time, *time); +} + + +static inline void convert_time_to_pixels( + TimeWindow time_window, + LttTime time, + int width, + guint *x) +{ + double time_d; +#ifdef EXTRA_CHECK + g_assert(ltt_time_compare(window_time_begin, time) <= 0 && + ltt_time_compare(window_time_end, time) >= 0); +#endif //EXTRA_CHECK + + time = ltt_time_sub(time, time_window.start_time); + + time_d = ltt_time_to_double(time); + + if(time_window.time_width_double == 0.0) { + g_assert(time_d == 0.0); + *x = 0; + } else { + *x = (guint)(time_d / time_window.time_width_double * (double)width); + } + +} + + + +#endif // _DRAWING_H diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/drawitem.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/drawitem.c new file mode 100644 index 00000000..d97628b0 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/drawitem.c @@ -0,0 +1,465 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + + + +/****************************************************************************** + * drawitem.c + * + * This file contains methods responsible for drawing a generic type of data + * in a drawable. Doing this generically will permit user defined drawing + * behavior in a later time. + * + * This file provides an API which is meant to be reusable for all viewers that + * need to show information in line, icon, text, background or point form in + * a drawable area having time for x axis. The y axis, in the control flow + * viewer case, is corresponding to the different processes, but it can be + * reused integrally for cpu, and eventually locks, buffers, network + * interfaces... What will differ between the viewers is the precise + * information which interests us. We may think that the most useful + * information for control flow are some specific events, like schedule + * change, and processes'states. It may differ for a cpu viewer : the + * interesting information could be more the execution mode of each cpu. + * This API in meant to make viewer's writers life easier : it will become + * a simple choice of icons and line types for the precise information + * the viewer has to provide (agremented with keeping supplementary records + * and modifying slightly the DrawContext to suit the needs.) + * + * We keep each data type in attributes, keys to specific information + * being formed from the GQuark corresponding to the information received. + * (facilities / facility_name / events / eventname.) + * (cpus/cpu_name, process_states/ps_name, + * execution_modes/em_name, execution_submodes/es_name). + * The goal is then to provide a generic way to print information on the + * screen for all this different information. + * + * Information can be printed as + * + * - text (text + color + size + position (over or under line) + * - icon (icon filename, corresponding to a loaded icon, accessible through + * a GQuark. Icons are loaded statically at the guiControlFlow level during + * module initialization and can be added on the fly if not present in the + * GQuark.) The habitual place for xpm icons is in + * ${prefix}/share/LinuxTraceToolkit.) + position (over or under line) + * - line (color, width, style) + * - Arc (big points) (color, size) + * - background color (color) + * + * An item is a leaf of the attributes tree. It is, in that case, including + * all kind of events categories we can have. It then associates each category + * with one or more actions (drawing something) or nothing. + * + * Each item has an array of hooks (hook list). Each hook represents an + * operation to perform. We seek the array each time we want to + * draw an item. We execute each operation in order. An operation type + * is associated with each hook to permit user listing and modification + * of these operations. The operation type is also used to find the + * corresponding priority for the sorting. Operation type and priorities + * are enum and a static int table. + * + * The array has to be sorted by priority each time we add a task in it. + * A priority is associated with each operation type. It permits + * to perform background color selection before line or text drawing. We also + * draw lines before text, so the text appears over the lines. + * + * Executing all the arrays of operations for a specific event (which + * implies information for state, event, cpu, execution mode and submode) + * has to be done in a same DrawContext. The goal there is to keep the offset + * of the text and icons over and under the middle line, so a specific + * event could be printed as ( R Si 0 for running, scheduled in, cpu 0 ), + * text being easy to replace with icons. The DrawContext is passed as + * call_data for the operation hooks. + * + * We use the lttv global attributes to keep track of the loaded icons. + * If we need an icon, we look for it in the icons / icon name pathname. + * If found, we use the pointer to it. If not, we load the pixmap in + * memory and set the pointer to the GdkPixmap in the attributes. The + * structure pointed to contains the pixmap and the mask bitmap. + * + * Author : Mathieu Desnoyers, October 2003 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "drawing.h" +#include "drawitem.h" + + +#define MAX_PATH_LEN 256 + +/* drawing hook functions */ +gboolean draw_text( void *hook_data, void *call_data) +{ + PropertiesText *properties = (PropertiesText*)hook_data; + DrawContext *draw_context = (DrawContext*)call_data; + + PangoContext *context; + PangoLayout *layout; + PangoFontDescription *font_desc;// = pango_font_description_new(); + PangoRectangle ink_rect; + + layout = draw_context->pango_layout; + + context = pango_layout_get_context(layout); + font_desc = pango_context_get_font_description(context); + + pango_font_description_set_size(font_desc, properties->size*PANGO_SCALE); + pango_layout_context_changed(layout); + + pango_layout_set_text(layout, properties->text, -1); + pango_layout_get_pixel_extents(layout, &ink_rect, NULL); + + gint x=0, y=0; + gint *offset=NULL; + gboolean enough_space = FALSE; + gint width = ink_rect.width; + + switch(properties->position.x) { + case POS_START: + x = draw_context->drawinfo.start.x; + switch(properties->position.y) { + case OVER: + offset = &draw_context->drawinfo.start.offset.over; + x += draw_context->drawinfo.start.offset.over; + y = draw_context->drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->drawinfo.start.offset.middle; + x += draw_context->drawinfo.start.offset.middle; + y = draw_context->drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->drawinfo.start.offset.under; + x += draw_context->drawinfo.start.offset.under; + y = draw_context->drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x + width <= draw_context->drawinfo.end.x)) { + enough_space = TRUE; + *offset += width; + } + break; + case POS_END: + x = draw_context->drawinfo.end.x; + switch(properties->position.y) { + case OVER: + offset = &draw_context->drawinfo.end.offset.over; + x += draw_context->drawinfo.end.offset.over; + y = draw_context->drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->drawinfo.end.offset.middle; + x += draw_context->drawinfo.end.offset.middle; + y = draw_context->drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->drawinfo.end.offset.under; + x += draw_context->drawinfo.end.offset.under; + y = draw_context->drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x - width >= draw_context->drawinfo.start.x)) { + enough_space = TRUE; + *offset -= width; + } + break; + } + + if(unlikely(enough_space)) + gdk_draw_layout_with_colors(draw_context->drawable, + draw_context->gc, + x, + y, + layout, properties->foreground, properties->background); + + return 0; +} + + +/* To speed up the process, search in already loaded icons list first. Only + * load it if not present. + */ +gboolean draw_icon( void *hook_data, void *call_data) +{ + PropertiesIcon *properties = (PropertiesIcon*)hook_data; + DrawContext *draw_context = (DrawContext*)call_data; + + LttvIAttribute *attributes = LTTV_IATTRIBUTE(lttv_global_attributes()); + LttvAttributeValue value; + gchar icon_name[MAX_PATH_LEN] = "icons/"; + IconStruct *icon_info; + + strcat(icon_name, properties->icon_name); + + g_assert(lttv_iattribute_find_by_path(attributes, icon_name, + LTTV_POINTER, &value)); + if(unlikely(*(value.v_pointer) == NULL)) + { + *(value.v_pointer) = icon_info = g_new(IconStruct,1); + + icon_info->pixmap = gdk_pixmap_create_from_xpm(draw_context->drawable, + &icon_info->mask, NULL, properties->icon_name); + } + else + { + icon_info = *(value.v_pointer); + } + + gint x=0, y=0; + gint *offset=NULL; + gboolean enough_space = FALSE; + gint width = properties->width; + + switch(properties->position.x) { + case POS_START: + x = draw_context->drawinfo.start.x; + switch(properties->position.y) { + case OVER: + offset = &draw_context->drawinfo.start.offset.over; + x += draw_context->drawinfo.start.offset.over; + y = draw_context->drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->drawinfo.start.offset.middle; + x += draw_context->drawinfo.start.offset.middle; + y = draw_context->drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->drawinfo.start.offset.under; + x += draw_context->drawinfo.start.offset.under; + y = draw_context->drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x + width <= draw_context->drawinfo.end.x)) { + enough_space = TRUE; + *offset += width; + } + break; + case POS_END: + x = draw_context->drawinfo.end.x; + switch(properties->position.y) { + case OVER: + offset = &draw_context->drawinfo.end.offset.over; + x += draw_context->drawinfo.end.offset.over; + y = draw_context->drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->drawinfo.end.offset.middle; + x += draw_context->drawinfo.end.offset.middle; + y = draw_context->drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->drawinfo.end.offset.under; + x += draw_context->drawinfo.end.offset.under; + y = draw_context->drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x - width >= draw_context->drawinfo.start.x)) { + enough_space = TRUE; + *offset -= width; + } + break; + } + + if(unlikely(enough_space)) { + gdk_gc_set_clip_mask(draw_context->gc, icon_info->mask); + + gdk_gc_set_clip_origin( + draw_context->gc, + x, + y); + gdk_draw_drawable(draw_context->drawable, + draw_context->gc, + icon_info->pixmap, + 0, 0, + x, + y, + properties->width, properties->height); + + gdk_gc_set_clip_origin(draw_context->gc, 0, 0); + gdk_gc_set_clip_mask(draw_context->gc, NULL); + } + return 0; +} + +gboolean draw_line( void *hook_data, void *call_data) +{ + PropertiesLine *properties = (PropertiesLine*)hook_data; + DrawContext *draw_context = (DrawContext*)call_data; + + gdk_gc_set_foreground(draw_context->gc, &properties->color); + //gdk_gc_set_rgb_fg_color(draw_context->gc, &properties->color); + gdk_gc_set_line_attributes( draw_context->gc, + properties->line_width, + properties->style, + GDK_CAP_BUTT, + GDK_JOIN_MITER); + //g_critical("DRAWING LINE : x1: %i, y1: %i, x2:%i, y2:%i", + // draw_context->previous->middle->x, + // draw_context->previous->middle->y, + // draw_context->drawinfo.middle.x, + // draw_context->drawinfo.middle.y); + + gint x_begin=0, x_end=0, y=0; + + x_begin = draw_context->drawinfo.start.x; + x_end = draw_context->drawinfo.end.x; + + switch(properties->y) { + case OVER: + y = draw_context->drawinfo.y.over; + break; + case MIDDLE: + y = draw_context->drawinfo.y.middle; + break; + case UNDER: + y = draw_context->drawinfo.y.under; + break; + } + + drawing_draw_line( + NULL, draw_context->drawable, + x_begin, + y, + x_end, + y, + draw_context->gc); + + return 0; +} + +gboolean draw_arc( void *hook_data, void *call_data) +{ + PropertiesArc *properties = (PropertiesArc*)hook_data; + DrawContext *draw_context = (DrawContext*)call_data; + + gdk_gc_set_foreground(draw_context->gc, properties->color); + //gdk_gc_set_rgb_fg_color(draw_context->gc, properties->color); + + gint x=0, y=0; + gint *offset=NULL; + gboolean enough_space = FALSE; + gint width = properties->size; + + switch(properties->position.x) { + case POS_START: + x = draw_context->drawinfo.start.x; + switch(properties->position.y) { + case OVER: + offset = &draw_context->drawinfo.start.offset.over; + x += draw_context->drawinfo.start.offset.over; + y = draw_context->drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->drawinfo.start.offset.middle; + x += draw_context->drawinfo.start.offset.middle; + y = draw_context->drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->drawinfo.start.offset.under; + x += draw_context->drawinfo.start.offset.under; + y = draw_context->drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x + width <= draw_context->drawinfo.end.x)) { + enough_space = TRUE; + *offset += width; + } + break; + case POS_END: + x = draw_context->drawinfo.end.x; + switch(properties->position.y) { + case OVER: + offset = &draw_context->drawinfo.end.offset.over; + x += draw_context->drawinfo.end.offset.over; + y = draw_context->drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->drawinfo.end.offset.middle; + x += draw_context->drawinfo.end.offset.middle; + y = draw_context->drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->drawinfo.end.offset.under; + x += draw_context->drawinfo.end.offset.under; + y = draw_context->drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x - width >= draw_context->drawinfo.start.x)) { + enough_space = TRUE; + *offset -= width; + } + break; + } + + if(unlikely(enough_space)) + gdk_draw_arc(draw_context->drawable, draw_context->gc, + properties->filled, + x, + y, + properties->size, properties->size, 0, 360*64); + + return 0; +} + +gboolean draw_bg( void *hook_data, void *call_data) +{ + PropertiesBG *properties = (PropertiesBG*)hook_data; + DrawContext *draw_context = (DrawContext*)call_data; + + gdk_gc_set_foreground(draw_context->gc, properties->color); + //gdk_gc_set_rgb_fg_color(draw_context->gc, properties->color); + + //g_critical("DRAWING RECT : x: %i, y: %i, w:%i, h:%i, val1 :%i, val2:%i ", + // draw_context->previous->over->x, + // draw_context->previous->over->y, + // draw_context->drawinfo.over.x - draw_context->previous->over->x, + // draw_context->previous->under->y-draw_context->previous->over->y, + // draw_context->drawinfo.over.x, + // draw_context->previous->over->x); + gdk_draw_rectangle(draw_context->drawable, draw_context->gc, + TRUE, + draw_context->drawinfo.start.x, + draw_context->drawinfo.y.over, + draw_context->drawinfo.end.x - draw_context->drawinfo.start.x, + draw_context->drawinfo.y.under - draw_context->drawinfo.y.over); + + return 0; +} + + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/drawitem.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/drawitem.h new file mode 100644 index 00000000..28fdc183 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/drawitem.h @@ -0,0 +1,279 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 _DRAW_ITEM_H +#define _DRAW_ITEM_H + +#include + +typedef struct _DrawContext DrawContext; +typedef struct _DrawInfo DrawInfo; +typedef struct _ItemInfo ItemInfo; + +typedef struct _IconStruct IconStruct; + +typedef struct _DrawOperation DrawOperation; + + +typedef struct _PropertiesText PropertiesText; +typedef struct _PropertiesIcon PropertiesIcon; +typedef struct _PropertiesLine PropertiesLine; +typedef struct _PropertiesArc PropertiesArc; +typedef struct _PropertiesBG PropertiesBG; + +typedef enum _DrawableItems DrawableItems; +enum _DrawableItems { + ITEM_TEXT, ITEM_ICON, ITEM_LINE, ITEM_POINT, ITEM_BACKGROUND +}; + +typedef enum _RelPosX { + POS_START, POS_END +} RelPosX; + +typedef enum _RelPosY { + OVER, MIDDLE, UNDER +} RelPosY; + + +/* The DrawContext keeps information about the current drawing position and + * the previous one, so we can use both to draw lines. + * + * over : position for drawing over the middle line. + * middle : middle line position. + * under : position for drawing under the middle line. + * + * the modify_* are used to take into account that we should go forward + * when we draw a text, an arc or an icon, while it's unneeded when we + * draw a line or background. + * + * The modify_* positions are altered by the draw item functions. + * + */ + + +struct _DrawContext { + GdkDrawable *drawable; + GdkGC *gc; + PangoLayout *pango_layout; + + struct { + struct { + gint x; + struct { + gint over; + gint middle; + gint under; + } offset; + } start; + + struct { + gint x; + struct { + gint over; + gint middle; + gint under; + } offset; + } end; + + struct { + gint over; + gint middle; + gint under; + } y; + + } drawinfo; +}; + + + + +/* + * Structure used to keep information about icons. + */ +struct _IconStruct { + GdkPixmap *pixmap; + GdkBitmap *mask; +}; + + +/* + * The Item element is only used so the DrawOperation is modifiable by users. + * During drawing, only the Hook is needed. + */ +struct _DrawOperation { + DrawableItems item; + LttvHooks *hook; +}; +#if 0 +/* + * We define here each items that can be drawn, together with their + * associated priority. Many item types can have the same priority, + * it's only used for quicksorting the operations when we add a new one + * to the array of operations to perform. Lower priorities are executed + * first. So, for example, we may want to give background color a value + * of 10 while a line would have 20, so the background color, which + * is in fact a rectangle, does not hide the line. + */ + +static int Items_Priorities[] = { + 50, /* ITEM_TEXT */ + 40, /* ITEM_ICON */ + 20, /* ITEM_LINE */ + 30, /* ITEM_POINT */ + 10 /* ITEM_BACKGROUND */ +}; +#endif //0 + +/* + * Here are the different structures describing each item type that can be + * drawn. They contain the information necessary to draw the item : not the + * position (this is provided by the DrawContext), but the text, icon name, + * line width, color; all the properties of the specific items. + */ + +struct _PropertiesText { + GdkColor *foreground; + GdkColor *background; + gint size; + gchar *text; + struct { + RelPosX x; + RelPosY y; + } position; +}; + + +struct _PropertiesIcon { + gchar *icon_name; + gint width; + gint height; + struct { + RelPosX x; + RelPosY y; + } position; +}; + +struct _PropertiesLine { + GdkColor color; + gint line_width; + GdkLineStyle style; + RelPosY y; +}; + +struct _PropertiesArc { + GdkColor *color; + gint size; /* We force circle by width = height */ + gboolean filled; + struct { + RelPosX x; + RelPosY y; + } position; +}; + +struct _PropertiesBG { + GdkColor *color; +}; + + + +void draw_item( GdkDrawable *drawable, + gint x, + gint y, + LttvTraceState *ts, + LttvTracefileState *tfs, + LttvIAttribute *attributes); + +/* + * The tree of attributes used to store drawing operations goes like this : + * + * event_types/ + * "facility-event_type" + * cpus/ + * "cpu name" + * mode_types/ + * "execution mode"/ + * submodes/ + * "submode" + * process_states/ + * "state name" + * + * So if, for example, we want to add a hook to get called each time we + * receive an event that is in state LTTV_STATE_SYSCALL, we put the + * pointer to the GArray of DrawOperation in + * process_states/ "name associated with LTTV_STATE_SYSCALL" + */ + + +#if 0 +/* + * The add_operation has to do a quick sort by priority to keep the operations + * in the right order. + */ +void add_operation( LttvIAttribute *attributes, + gchar *pathname, + DrawOperation *operation); + +/* + * The del_operation seeks the array present at pathname (if any) and + * removes the DrawOperation if present. It returns 0 on success, -1 + * if it fails. + */ +gint del_operation( LttvIAttribute *attributes, + gchar *pathname, + DrawOperation *operation); + +/* + * The clean_operations removes all operations present at a pathname. + * returns 0 on success, -1 if it fails. + */ +gint clean_operations( LttvIAttribute *attributes, + gchar *pathname ); + + +/* + * The list_operations gives a pointer to the operation array associated + * with the pathname. It will be NULL if no operation is present. + */ +void list_operations( LttvIAttribute *attributes, + gchar *pathname, + GArray **operation); + + + +/* + * exec_operation executes the operations if present in the attributes, or + * do nothing if not present. + */ +void exec_operations( LttvIAttribute *attributes, + gchar *pathname); +#endif //0 + +/* + * Here follow the prototypes of the hook functions used to draw the + * different items. + */ + +gboolean draw_text( void *hook_data, void *call_data); +gboolean draw_icon( void *hook_data, void *call_data); +gboolean draw_line( void *hook_data, void *call_data); +gboolean draw_arc( void *hook_data, void *call_data); +gboolean draw_bg( void *hook_data, void *call_data); + + +#endif // _DRAW_ITEM_H diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/eventhooks.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/eventhooks.c new file mode 100644 index 00000000..db1604c4 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/eventhooks.c @@ -0,0 +1,2706 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + + +/***************************************************************************** + * Hooks to be called by the main window * + *****************************************************************************/ + + +/* Event hooks are the drawing hooks called during traceset read. They draw the + * icons, text, lines and background color corresponding to the events read. + * + * Two hooks are used for drawing : before_schedchange and after_schedchange hooks. The + * before_schedchange is called before the state update that occurs with an event and + * the after_schedchange hook is called after this state update. + * + * The before_schedchange hooks fulfill the task of drawing the visible objects that + * corresponds to the data accumulated by the after_schedchange hook. + * + * The after_schedchange hook accumulates the data that need to be shown on the screen + * (items) into a queue. Then, the next before_schedchange hook will draw what that + * queue contains. That's the Right Way (TM) of drawing items on the screen, + * because we need to draw the background first (and then add icons, text, ... + * over it), but we only know the length of a background region once the state + * corresponding to it is over, which happens to be at the next before_schedchange + * hook. + * + * We also have a hook called at the end of a chunk to draw the information left + * undrawn in each process queue. We use the current time as end of + * line/background. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +//#define PANGO_ENABLE_BACKEND +#include +#include +#include +#include +#include +#include + +//#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +#include "eventhooks.h" +#include "cfv.h" +#include "processlist.h" +#include "drawing.h" + + +#define MAX_PATH_LEN 256 +#define STATE_LINE_WIDTH 6 +#define COLLISION_POSITION(height) (((height - STATE_LINE_WIDTH)/2) -3) + +extern GSList *g_legend_list; + + +/* Action to do when background computation completed. + * + * Wait for all the awaited computations to be over. + */ + +static gint background_ready(void *hook_data, void *call_data) +{ + ControlFlowData *control_flow_data = (ControlFlowData *)hook_data; + LttvTrace *trace = (LttvTrace*)call_data; + + control_flow_data->background_info_waiting--; + + if(control_flow_data->background_info_waiting == 0) { + g_message("control flow viewer : background computation data ready."); + + drawing_clear(control_flow_data->drawing); + processlist_clear(control_flow_data->process_list); + gtk_widget_set_size_request( + control_flow_data->drawing->drawing_area, + -1, processlist_get_height(control_flow_data->process_list)); + redraw_notify(control_flow_data, NULL); + } + + return 0; +} + + +/* Request background computation. Verify if it is in progress or ready first. + * Only for each trace in the tab's traceset. + */ +static void request_background_data(ControlFlowData *control_flow_data) +{ + LttvTracesetContext * tsc = + lttvwindow_get_traceset_context(control_flow_data->tab); + gint num_traces = lttv_traceset_number(tsc->ts); + gint i; + LttvTrace *trace; + LttvTraceState *tstate; + + LttvHooks *background_ready_hook = + lttv_hooks_new(); + lttv_hooks_add(background_ready_hook, background_ready, control_flow_data, + LTTV_PRIO_DEFAULT); + control_flow_data->background_info_waiting = 0; + + for(i=0;its, i); + tstate = LTTV_TRACE_STATE(tsc->traces[i]); + + if(lttvwindowtraces_get_ready(g_quark_from_string("state"),trace)==FALSE + && !tstate->has_precomputed_states) { + + if(lttvwindowtraces_get_in_progress(g_quark_from_string("state"), + trace) == FALSE) { + /* We first remove requests that could have been done for the same + * information. Happens when two viewers ask for it before servicing + * starts. + */ + if(!lttvwindowtraces_background_request_find(trace, "state")) + lttvwindowtraces_background_request_queue( + main_window_get_widget(control_flow_data->tab), trace, "state"); + lttvwindowtraces_background_notify_queue(control_flow_data, + trace, + ltt_time_infinite, + NULL, + background_ready_hook); + control_flow_data->background_info_waiting++; + } else { /* in progress */ + + lttvwindowtraces_background_notify_current(control_flow_data, + trace, + ltt_time_infinite, + NULL, + background_ready_hook); + control_flow_data->background_info_waiting++; + } + } else { + /* Data ready. By its nature, this viewer doesn't need to have + * its data ready hook called there, because a background + * request is always linked with a redraw. + */ + } + + } + + lttv_hooks_destroy(background_ready_hook); +} + + + + +/** + * Event Viewer's constructor hook + * + * This constructor is given as a parameter to the menuitem and toolbar button + * registration. It creates the list. + * @param tab A pointer to the parent tab. + * @return The widget created. + */ +GtkWidget * +h_guicontrolflow(LttvPlugin *plugin) +{ + LttvPluginTab *ptab = LTTV_PLUGIN_TAB(plugin); + Tab *tab = ptab->tab; + g_info("h_guicontrolflow, %p", tab); + ControlFlowData *control_flow_data = guicontrolflow(ptab); + + control_flow_data->tab = tab; + + // Unreg done in the GuiControlFlow_Destructor + lttvwindow_register_traceset_notify(tab, + traceset_notify, + control_flow_data); + + lttvwindow_register_time_window_notify(tab, + update_time_window_hook, + control_flow_data); + lttvwindow_register_current_time_notify(tab, + update_current_time_hook, + control_flow_data); + lttvwindow_register_redraw_notify(tab, + redraw_notify, + control_flow_data); + lttvwindow_register_continue_notify(tab, + continue_notify, + control_flow_data); + request_background_data(control_flow_data); + + + return guicontrolflow_get_widget(control_flow_data) ; + +} + +int event_selected_hook(void *hook_data, void *call_data) +{ + ControlFlowData *control_flow_data = (ControlFlowData*) hook_data; + guint *event_number = (guint*) call_data; + + g_debug("DEBUG : event selected by main window : %u", *event_number); + + return 0; +} + +/* Function that selects the color of status&exemode line */ +static inline PropertiesLine prepare_s_e_line(LttvProcessState *process) +{ + PropertiesLine prop_line; + prop_line.line_width = STATE_LINE_WIDTH; + prop_line.style = GDK_LINE_SOLID; + prop_line.y = MIDDLE; + //GdkColormap *colormap = gdk_colormap_get_system(); + + if(process->state->s == LTTV_STATE_RUN) { + if(process->state->t == LTTV_STATE_USER_MODE) + prop_line.color = drawing_colors[COL_RUN_USER_MODE]; + else if(process->state->t == LTTV_STATE_SYSCALL) + prop_line.color = drawing_colors[COL_RUN_SYSCALL]; + else if(process->state->t == LTTV_STATE_TRAP) + prop_line.color = drawing_colors[COL_RUN_TRAP]; + else if(process->state->t == LTTV_STATE_IRQ) + prop_line.color = drawing_colors[COL_RUN_IRQ]; + else if(process->state->t == LTTV_STATE_SOFT_IRQ) + prop_line.color = drawing_colors[COL_RUN_SOFT_IRQ]; + else if(process->state->t == LTTV_STATE_MODE_UNKNOWN) + prop_line.color = drawing_colors[COL_MODE_UNKNOWN]; + else + g_assert(FALSE); /* RUNNING MODE UNKNOWN */ + } else if(process->state->s == LTTV_STATE_WAIT) { + /* We don't show if we wait while in user mode, trap, irq or syscall */ + prop_line.color = drawing_colors[COL_WAIT]; + } else if(process->state->s == LTTV_STATE_WAIT_CPU) { + /* We don't show if we wait for CPU while in user mode, trap, irq + * or syscall */ + prop_line.color = drawing_colors[COL_WAIT_CPU]; + } else if(process->state->s == LTTV_STATE_ZOMBIE) { + prop_line.color = drawing_colors[COL_ZOMBIE]; + } else if(process->state->s == LTTV_STATE_WAIT_FORK) { + prop_line.color = drawing_colors[COL_WAIT_FORK]; + } else if(process->state->s == LTTV_STATE_EXIT) { + prop_line.color = drawing_colors[COL_EXIT]; + } else if(process->state->s == LTTV_STATE_UNNAMED) { + prop_line.color = drawing_colors[COL_UNNAMED]; + } else if(process->state->s == LTTV_STATE_DEAD) { + prop_line.color = drawing_colors[COL_DEAD]; + } else { + g_critical("unknown state : %s", g_quark_to_string(process->state->s)); + g_assert(FALSE); /* UNKNOWN STATE */ + } + + return prop_line; + +} + + +/* before_schedchange_hook + * + * This function basically draw lines and icons. Two types of lines are drawn : + * one small (3 pixels?) representing the state of the process and the second + * type is thicker (10 pixels?) representing on which CPU a process is running + * (and this only in running state). + * + * Extremums of the lines : + * x_min : time of the last event context for this process kept in memory. + * x_max : time of the current event. + * y : middle of the process in the process list. The process is found in the + * list, therefore is it's position in pixels. + * + * The choice of lines'color is defined by the context of the last event for this + * process. + */ + + +int before_schedchange_hook(void *hook_data, void *call_data) +{ + LttvTraceHook *th = (LttvTraceHook*)hook_data; + EventsRequest *events_request = (EventsRequest*)th->hook_data; + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + gint target_pid_saved = tfc->target_pid; + + LttTime evtime = ltt_event_time(e); + LttvFilter *filter = control_flow_data->filter; + + /* we are in a schedchange, before the state update. We must draw the + * items corresponding to the state before it changes : now is the right + * time to do it. + */ + + guint pid_out; + guint pid_in; + guint state_out; + { + pid_out = ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 0)); + pid_in = ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 1)); + state_out = ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 2)); + } + + tfc->target_pid = pid_out; + if(!filter || !filter->head || + lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) { + /* For the pid_out */ + /* First, check if the current process is in the state computation + * process list. If it is there, that means we must add it right now and + * draw items from the beginning of the read for it. If it is not + * present, it's a new process and it was not present : it will + * be added after the state update. */ + guint cpu = tfs->cpu; + guint trace_num = ts->parent.index; + LttvProcessState *process = ts->running_process[cpu]; + /* unknown state, bad current pid */ + if(process->pid != pid_out) + process = lttv_state_find_process(ts, + tfs->cpu, pid_out); + + if(process != NULL) { + /* Well, the process_out existed : we must get it in the process hash + * or add it, and draw its items. + */ + /* Add process to process list (if not present) */ + guint pl_height = 0; + HashedProcessData *hashed_process_data = NULL; + ProcessList *process_list = control_flow_data->process_list; + LttTime birth = process->creation_time; + + hashed_process_data = processlist_get_process_data(process_list, + pid_out, + process->cpu, + &birth, + trace_num); + if(hashed_process_data == NULL) + { + g_assert(pid_out == 0 || pid_out != process->ppid); + /* Process not present */ + ProcessInfo *process_info; + Drawing_t *drawing = control_flow_data->drawing; + processlist_add(process_list, + drawing, + pid_out, + process->tgid, + process->cpu, + process->ppid, + &birth, + trace_num, + process->name, + process->brand, + &pl_height, + &process_info, + &hashed_process_data); + gtk_widget_set_size_request(drawing->drawing_area, + -1, + pl_height); + gtk_widget_queue_draw(drawing->drawing_area); + + } + + /* Now, the process is in the state hash and our own process hash. + * We definitely can draw the items related to the ending state. + */ + + if(ltt_time_compare(hashed_process_data->next_good_time, + evtime) > 0) + { + if(hashed_process_data->x.middle_marked == FALSE) { + + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return FALSE; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint x; + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + } else { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return FALSE; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint x; + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + + /* Jump over draw if we are at the same x position */ + if(x == hashed_process_data->x.middle && + hashed_process_data->x.middle_used) + { + if(hashed_process_data->x.middle_marked == FALSE) { + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + /* jump */ + } else { + DrawContext draw_context; + + /* Now create the drawing context that will be used to draw + * items related to the last state. */ + draw_context.drawable = hashed_process_data->pixmap; + draw_context.gc = drawing->gc; + draw_context.pango_layout = drawing->pango_layout; + draw_context.drawinfo.start.x = hashed_process_data->x.middle; + draw_context.drawinfo.end.x = x; + + draw_context.drawinfo.y.over = 1; + draw_context.drawinfo.y.middle = (hashed_process_data->height/2); + draw_context.drawinfo.y.under = hashed_process_data->height; + + draw_context.drawinfo.start.offset.over = 0; + draw_context.drawinfo.start.offset.middle = 0; + draw_context.drawinfo.start.offset.under = 0; + draw_context.drawinfo.end.offset.over = 0; + draw_context.drawinfo.end.offset.middle = 0; + draw_context.drawinfo.end.offset.under = 0; + + { + /* Draw the line */ + PropertiesLine prop_line = prepare_s_e_line(process); + draw_line((void*)&prop_line, (void*)&draw_context); + + } + /* become the last x position */ + hashed_process_data->x.middle = x; + hashed_process_data->x.middle_used = TRUE; + hashed_process_data->x.middle_marked = FALSE; + + /* Calculate the next good time */ + convert_pixels_to_time(width, x+1, time_window, + &hashed_process_data->next_good_time); + } + } + } + } + + tfc->target_pid = pid_in; + if(!filter || !filter->head || + lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) { + /* For the pid_in */ + /* First, check if the current process is in the state computation + * process list. If it is there, that means we must add it right now and + * draw items from the beginning of the read for it. If it is not + * present, it's a new process and it was not present : it will + * be added after the state update. */ + LttvProcessState *process; + process = lttv_state_find_process(ts, + tfs->cpu, pid_in); + guint trace_num = ts->parent.index; + + if(process != NULL) { + /* Well, the process existed : we must get it in the process hash + * or add it, and draw its items. + */ + /* Add process to process list (if not present) */ + guint pl_height = 0; + HashedProcessData *hashed_process_data = NULL; + ProcessList *process_list = control_flow_data->process_list; + LttTime birth = process->creation_time; + + hashed_process_data = processlist_get_process_data(process_list, + pid_in, + tfs->cpu, + &birth, + trace_num); + if(hashed_process_data == NULL) + { + g_assert(pid_in == 0 || pid_in != process->ppid); + /* Process not present */ + ProcessInfo *process_info; + Drawing_t *drawing = control_flow_data->drawing; + processlist_add(process_list, + drawing, + pid_in, + process->tgid, + tfs->cpu, + process->ppid, + &birth, + trace_num, + process->name, + process->brand, + &pl_height, + &process_info, + &hashed_process_data); + gtk_widget_set_size_request(drawing->drawing_area, + -1, + pl_height); + gtk_widget_queue_draw(drawing->drawing_area); + + } + //We could set the current process and hash here, but will be done + //by after schedchange hook + + /* Now, the process is in the state hash and our own process hash. + * We definitely can draw the items related to the ending state. + */ + + if(ltt_time_compare(hashed_process_data->next_good_time, + evtime) > 0) + { + if(hashed_process_data->x.middle_marked == FALSE) { + + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return FALSE; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint x; + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + } else { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return FALSE; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint x; + + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + + /* Jump over draw if we are at the same x position */ + if(x == hashed_process_data->x.middle && + hashed_process_data->x.middle_used) + { + if(hashed_process_data->x.middle_marked == FALSE) { + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + /* jump */ + } else { + DrawContext draw_context; + + /* Now create the drawing context that will be used to draw + * items related to the last state. */ + draw_context.drawable = hashed_process_data->pixmap; + draw_context.gc = drawing->gc; + draw_context.pango_layout = drawing->pango_layout; + draw_context.drawinfo.start.x = hashed_process_data->x.middle; + draw_context.drawinfo.end.x = x; + + draw_context.drawinfo.y.over = 1; + draw_context.drawinfo.y.middle = (hashed_process_data->height/2); + draw_context.drawinfo.y.under = hashed_process_data->height; + + draw_context.drawinfo.start.offset.over = 0; + draw_context.drawinfo.start.offset.middle = 0; + draw_context.drawinfo.start.offset.under = 0; + draw_context.drawinfo.end.offset.over = 0; + draw_context.drawinfo.end.offset.middle = 0; + draw_context.drawinfo.end.offset.under = 0; + + { + /* Draw the line */ + PropertiesLine prop_line = prepare_s_e_line(process); + draw_line((void*)&prop_line, (void*)&draw_context); + } + + + /* become the last x position */ + hashed_process_data->x.middle = x; + hashed_process_data->x.middle_used = TRUE; + hashed_process_data->x.middle_marked = FALSE; + + /* Calculate the next good time */ + convert_pixels_to_time(width, x+1, time_window, + &hashed_process_data->next_good_time); + } + } + } else + g_warning("Cannot find pin_in in schedchange %u", pid_in); + } + tfc->target_pid = target_pid_saved; + return 0; + + + + + /* Text dump */ +#ifdef DONTSHOW + GString *string = g_string_new("");; + gboolean field_names = TRUE, state = TRUE; + + lttv_event_to_string(e, tfc->tf, string, TRUE, field_names, tfs); + g_string_append_printf(string,"\n"); + + if(state) { + g_string_append_printf(string, " %s", + g_quark_to_string(tfs->process->state->s)); + } + + g_info("%s",string->str); + + g_string_free(string, TRUE); + + /* End of text dump */ +#endif //DONTSHOW + +} + +/* after_schedchange_hook + * + * The draw after hook is called by the reading API to have a + * particular event drawn on the screen. + * @param hook_data ControlFlowData structure of the viewer. + * @param call_data Event context. + * + * This function adds items to be drawn in a queue for each process. + * + */ +int after_schedchange_hook(void *hook_data, void *call_data) +{ + LttvTraceHook *th = (LttvTraceHook*)hook_data; + EventsRequest *events_request = (EventsRequest*)th->hook_data; + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *filter = control_flow_data->filter; + LttTime evtime = ltt_event_time(e); + + /* Add process to process list (if not present) */ + LttvProcessState *process_in; + LttTime birth; + guint pl_height = 0; + HashedProcessData *hashed_process_data_in = NULL; + + ProcessList *process_list = control_flow_data->process_list; + + guint pid_in; + { + guint pid_out; + pid_out = ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 0)); + pid_in = ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 1)); + } + + tfc->target_pid = pid_in; + if(!filter || !filter->head || + lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) { + /* Find process pid_in in the list... */ + //process_in = lttv_state_find_process(ts, ANY_CPU, pid_in); + //process_in = tfs->process; + guint cpu = tfs->cpu; + guint trace_num = ts->parent.index; + process_in = ts->running_process[cpu]; + /* It should exist, because we are after the state update. */ +#ifdef EXTRA_CHECK + g_assert(process_in != NULL); +#endif //EXTRA_CHECK + birth = process_in->creation_time; + + hashed_process_data_in = processlist_get_process_data(process_list, + pid_in, + process_in->cpu, + &birth, + trace_num); + if(hashed_process_data_in == NULL) + { + g_assert(pid_in == 0 || pid_in != process_in->ppid); + ProcessInfo *process_info; + Drawing_t *drawing = control_flow_data->drawing; + /* Process not present */ + processlist_add(process_list, + drawing, + pid_in, + process_in->tgid, + process_in->cpu, + process_in->ppid, + &birth, + trace_num, + process_in->name, + process_in->brand, + &pl_height, + &process_info, + &hashed_process_data_in); + gtk_widget_set_size_request(drawing->drawing_area, + -1, + pl_height); + gtk_widget_queue_draw(drawing->drawing_area); + } + /* Set the current process */ + process_list->current_hash_data[trace_num][process_in->cpu] = + hashed_process_data_in; + + if(ltt_time_compare(hashed_process_data_in->next_good_time, + evtime) <= 0) + { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return FALSE; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint new_x; + + convert_time_to_pixels( + time_window, + evtime, + width, + &new_x); + + if(hashed_process_data_in->x.middle != new_x) { + hashed_process_data_in->x.middle = new_x; + hashed_process_data_in->x.middle_used = FALSE; + hashed_process_data_in->x.middle_marked = FALSE; + } + } + } + + return 0; +} + + + + +/* before_execmode_hook + * + * This function basically draw lines and icons. Two types of lines are drawn : + * one small (3 pixels?) representing the state of the process and the second + * type is thicker (10 pixels?) representing on which CPU a process is running + * (and this only in running state). + * + * Extremums of the lines : + * x_min : time of the last event context for this process kept in memory. + * x_max : time of the current event. + * y : middle of the process in the process list. The process is found in the + * list, therefore is it's position in pixels. + * + * The choice of lines'color is defined by the context of the last event for this + * process. + */ + + +int before_execmode_hook(void *hook_data, void *call_data) +{ + LttvTraceHook *th = (LttvTraceHook*)hook_data; + EventsRequest *events_request = (EventsRequest*)th->hook_data; + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *filter = control_flow_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + LttTime evtime = ltt_event_time(e); + + /* we are in a execmode, before the state update. We must draw the + * items corresponding to the state before it changes : now is the right + * time to do it. + */ + /* For the pid */ + //LttvProcessState *process = tfs->process; + guint cpu = tfs->cpu; + guint trace_num = ts->parent.index; + LttvProcessState *process = ts->running_process[cpu]; + g_assert(process != NULL); + + guint pid = process->pid; + + /* Well, the process_out existed : we must get it in the process hash + * or add it, and draw its items. + */ + /* Add process to process list (if not present) */ + guint pl_height = 0; + HashedProcessData *hashed_process_data = NULL; + ProcessList *process_list = control_flow_data->process_list; + LttTime birth = process->creation_time; + + if(likely(process_list->current_hash_data[trace_num][cpu] != NULL)) { + hashed_process_data = process_list->current_hash_data[trace_num][cpu]; + } else { + hashed_process_data = processlist_get_process_data(process_list, + pid, + process->cpu, + &birth, + trace_num); + if(unlikely(hashed_process_data == NULL)) + { + g_assert(pid == 0 || pid != process->ppid); + ProcessInfo *process_info; + /* Process not present */ + Drawing_t *drawing = control_flow_data->drawing; + processlist_add(process_list, + drawing, + pid, + process->tgid, + process->cpu, + process->ppid, + &birth, + trace_num, + process->name, + process->brand, + &pl_height, + &process_info, + &hashed_process_data); + gtk_widget_set_size_request(drawing->drawing_area, + -1, + pl_height); + gtk_widget_queue_draw(drawing->drawing_area); + } + /* Set the current process */ + process_list->current_hash_data[trace_num][process->cpu] = + hashed_process_data; + } + + /* Now, the process is in the state hash and our own process hash. + * We definitely can draw the items related to the ending state. + */ + + if(likely(ltt_time_compare(hashed_process_data->next_good_time, + evtime) > 0)) + { + if(unlikely(hashed_process_data->x.middle_marked == FALSE)) { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return FALSE; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint x; + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + } else { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return FALSE; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint x; + + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + + /* Jump over draw if we are at the same x position */ + if(unlikely(x == hashed_process_data->x.middle && + hashed_process_data->x.middle_used)) + { + if(unlikely(hashed_process_data->x.middle_marked == FALSE)) { + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + /* jump */ + } else { + + DrawContext draw_context; + /* Now create the drawing context that will be used to draw + * items related to the last state. */ + draw_context.drawable = hashed_process_data->pixmap; + draw_context.gc = drawing->gc; + draw_context.pango_layout = drawing->pango_layout; + draw_context.drawinfo.start.x = hashed_process_data->x.middle; + draw_context.drawinfo.end.x = x; + + draw_context.drawinfo.y.over = 1; + draw_context.drawinfo.y.middle = (hashed_process_data->height/2); + draw_context.drawinfo.y.under = hashed_process_data->height; + + draw_context.drawinfo.start.offset.over = 0; + draw_context.drawinfo.start.offset.middle = 0; + draw_context.drawinfo.start.offset.under = 0; + draw_context.drawinfo.end.offset.over = 0; + draw_context.drawinfo.end.offset.middle = 0; + draw_context.drawinfo.end.offset.under = 0; + + { + /* Draw the line */ + PropertiesLine prop_line = prepare_s_e_line(process); + draw_line((void*)&prop_line, (void*)&draw_context); + + } + /* become the last x position */ + hashed_process_data->x.middle = x; + hashed_process_data->x.middle_used = TRUE; + hashed_process_data->x.middle_marked = FALSE; + + /* Calculate the next good time */ + convert_pixels_to_time(width, x+1, time_window, + &hashed_process_data->next_good_time); + } + } + + return 0; +} + +/* before_process_exit_hook + * + * Draw lines for process event. + * + * @param hook_data ControlFlowData structure of the viewer. + * @param call_data Event context. + * + * This function adds items to be drawn in a queue for each process. + * + */ + + +int before_process_exit_hook(void *hook_data, void *call_data) +{ + LttvTraceHook *th = (LttvTraceHook*)hook_data; + EventsRequest *events_request = (EventsRequest*)th->hook_data; + + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *filter = control_flow_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + LttTime evtime = ltt_event_time(e); + + /* Add process to process list (if not present) */ + //LttvProcessState *process = tfs->process; + guint cpu = tfs->cpu; + guint trace_num = ts->parent.index; + LttvProcessState *process = ts->running_process[cpu]; + guint pid = process->pid; + LttTime birth; + guint pl_height = 0; + HashedProcessData *hashed_process_data = NULL; + + ProcessList *process_list = control_flow_data->process_list; + + g_assert(process != NULL); + + birth = process->creation_time; + + if(likely(process_list->current_hash_data[trace_num][cpu] != NULL)) { + hashed_process_data = process_list->current_hash_data[trace_num][cpu]; + } else { + hashed_process_data = processlist_get_process_data(process_list, + pid, + process->cpu, + &birth, + trace_num); + if(unlikely(hashed_process_data == NULL)) + { + g_assert(pid == 0 || pid != process->ppid); + /* Process not present */ + Drawing_t *drawing = control_flow_data->drawing; + ProcessInfo *process_info; + processlist_add(process_list, + drawing, + pid, + process->tgid, + process->cpu, + process->ppid, + &birth, + trace_num, + process->name, + process->brand, + &pl_height, + &process_info, + &hashed_process_data); + gtk_widget_set_size_request(drawing->drawing_area, + -1, + pl_height); + gtk_widget_queue_draw(drawing->drawing_area); + } + } + + /* Now, the process is in the state hash and our own process hash. + * We definitely can draw the items related to the ending state. + */ + + if(likely(ltt_time_compare(hashed_process_data->next_good_time, + evtime) > 0)) + { + if(unlikely(hashed_process_data->x.middle_marked == FALSE)) { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return FALSE; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint x; + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + } else { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return FALSE; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint x; + + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + + /* Jump over draw if we are at the same x position */ + if(unlikely(x == hashed_process_data->x.middle && + hashed_process_data->x.middle_used)) + { + if(unlikely(hashed_process_data->x.middle_marked == FALSE)) { + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + /* jump */ + } else { + DrawContext draw_context; + + /* Now create the drawing context that will be used to draw + * items related to the last state. */ + draw_context.drawable = hashed_process_data->pixmap; + draw_context.gc = drawing->gc; + draw_context.pango_layout = drawing->pango_layout; + draw_context.drawinfo.start.x = hashed_process_data->x.middle; + draw_context.drawinfo.end.x = x; + + draw_context.drawinfo.y.over = 1; + draw_context.drawinfo.y.middle = (hashed_process_data->height/2); + draw_context.drawinfo.y.under = hashed_process_data->height; + + draw_context.drawinfo.start.offset.over = 0; + draw_context.drawinfo.start.offset.middle = 0; + draw_context.drawinfo.start.offset.under = 0; + draw_context.drawinfo.end.offset.over = 0; + draw_context.drawinfo.end.offset.middle = 0; + draw_context.drawinfo.end.offset.under = 0; + + { + /* Draw the line */ + PropertiesLine prop_line = prepare_s_e_line(process); + draw_line((void*)&prop_line, (void*)&draw_context); + + } + /* become the last x position */ + hashed_process_data->x.middle = x; + hashed_process_data->x.middle_used = TRUE; + hashed_process_data->x.middle_marked = FALSE; + + /* Calculate the next good time */ + convert_pixels_to_time(width, x+1, time_window, + &hashed_process_data->next_good_time); + } + } + + return 0; + +} + + + +/* before_process_release_hook + * + * Draw lines for process event. + * + * @param hook_data ControlFlowData structure of the viewer. + * @param call_data Event context. + * + * This function adds items to be drawn in a queue for each process. + * + */ + + +int before_process_release_hook(void *hook_data, void *call_data) +{ + LttvTraceHook *th = (LttvTraceHook*)hook_data; + EventsRequest *events_request = (EventsRequest*)th->hook_data; + + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *filter = control_flow_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + LttTime evtime = ltt_event_time(e); + + guint trace_num = ts->parent.index; + + guint pid; + { + pid = ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 0)); + } + + /* Add process to process list (if not present) */ + /* Don't care about the process if it's not in the state hash already : + * that means a process that has never done anything in the trace and + * unknown suddently gets destroyed : no state meaningful to show. */ + LttvProcessState *process = lttv_state_find_process(ts, ANY_CPU, pid); + + if(process != NULL) { + LttTime birth; + guint pl_height = 0; + HashedProcessData *hashed_process_data = NULL; + + ProcessList *process_list = control_flow_data->process_list; + + birth = process->creation_time; + + /* Cannot use current process : this event happens on another process, + * action done by the parent. */ + hashed_process_data = processlist_get_process_data(process_list, + pid, + process->cpu, + &birth, + trace_num); + if(unlikely(hashed_process_data == NULL)) + /* + * Process already been scheduled out EXIT_DEAD, not in the process list + * anymore. Just return. + */ + return FALSE; + + /* Now, the process is in the state hash and our own process hash. + * We definitely can draw the items related to the ending state. + */ + + if(likely(ltt_time_compare(hashed_process_data->next_good_time, + evtime) > 0)) + { + if(unlikely(hashed_process_data->x.middle_marked == FALSE)) { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return FALSE; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint x; + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + } else { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return FALSE; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint x; + + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + + /* Jump over draw if we are at the same x position */ + if(unlikely(x == hashed_process_data->x.middle && + hashed_process_data->x.middle_used)) + { + if(unlikely(hashed_process_data->x.middle_marked == FALSE)) { + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + /* jump */ + } else { + DrawContext draw_context; + + /* Now create the drawing context that will be used to draw + * items related to the last state. */ + draw_context.drawable = hashed_process_data->pixmap; + draw_context.gc = drawing->gc; + draw_context.pango_layout = drawing->pango_layout; + draw_context.drawinfo.start.x = hashed_process_data->x.middle; + draw_context.drawinfo.end.x = x; + + draw_context.drawinfo.y.over = 1; + draw_context.drawinfo.y.middle = (hashed_process_data->height/2); + draw_context.drawinfo.y.under = hashed_process_data->height; + + draw_context.drawinfo.start.offset.over = 0; + draw_context.drawinfo.start.offset.middle = 0; + draw_context.drawinfo.start.offset.under = 0; + draw_context.drawinfo.end.offset.over = 0; + draw_context.drawinfo.end.offset.middle = 0; + draw_context.drawinfo.end.offset.under = 0; + + { + /* Draw the line */ + PropertiesLine prop_line = prepare_s_e_line(process); + draw_line((void*)&prop_line, (void*)&draw_context); + + } + /* become the last x position */ + hashed_process_data->x.middle = x; + hashed_process_data->x.middle_used = TRUE; + hashed_process_data->x.middle_marked = FALSE; + + /* Calculate the next good time */ + convert_pixels_to_time(width, x+1, time_window, + &hashed_process_data->next_good_time); + } + } + } + + return 0; +} + + + + + +/* after_process_fork_hook + * + * Create the processlist entry for the child process. Put the last + * position in x at the current time value. + * + * @param hook_data ControlFlowData structure of the viewer. + * @param call_data Event context. + * + * This function adds items to be drawn in a queue for each process. + * + */ +int after_process_fork_hook(void *hook_data, void *call_data) +{ + LttvTraceHook *th = (LttvTraceHook*)hook_data; + EventsRequest *events_request = (EventsRequest*)th->hook_data; + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *filter = control_flow_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + LttTime evtime = ltt_event_time(e); + + guint child_pid; + { + child_pid = ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 1)); + } + + /* Add process to process list (if not present) */ + LttvProcessState *process_child; + LttTime birth; + guint pl_height = 0; + HashedProcessData *hashed_process_data_child = NULL; + + ProcessList *process_list = control_flow_data->process_list; + + /* Find child in the list... */ + process_child = lttv_state_find_process(ts, ANY_CPU, child_pid); + /* It should exist, because we are after the state update. */ + g_assert(process_child != NULL); + + birth = process_child->creation_time; + guint trace_num = ts->parent.index; + + /* Cannot use current process, because this action is done by the parent + * on its child. */ + hashed_process_data_child = processlist_get_process_data(process_list, + child_pid, + process_child->cpu, + &birth, + trace_num); + if(likely(hashed_process_data_child == NULL)) + { + g_assert(child_pid == 0 || child_pid != process_child->ppid); + /* Process not present */ + Drawing_t *drawing = control_flow_data->drawing; + ProcessInfo *process_info; + processlist_add(process_list, + drawing, + child_pid, + process_child->tgid, + process_child->cpu, + process_child->ppid, + &birth, + trace_num, + process_child->name, + process_child->brand, + &pl_height, + &process_info, + &hashed_process_data_child); + gtk_widget_set_size_request(drawing->drawing_area, + -1, + pl_height); + gtk_widget_queue_draw(drawing->drawing_area); + } else { + processlist_set_ppid(process_list, process_child->ppid, + hashed_process_data_child); + processlist_set_tgid(process_list, process_child->tgid, + hashed_process_data_child); + } + + + if(likely(ltt_time_compare(hashed_process_data_child->next_good_time, + evtime) <= 0)) + { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return FALSE; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint new_x; + convert_time_to_pixels( + time_window, + evtime, + width, + &new_x); + + if(likely(hashed_process_data_child->x.over != new_x)) { + hashed_process_data_child->x.over = new_x; + hashed_process_data_child->x.over_used = FALSE; + hashed_process_data_child->x.over_marked = FALSE; + } + if(likely(hashed_process_data_child->x.middle != new_x)) { + hashed_process_data_child->x.middle = new_x; + hashed_process_data_child->x.middle_used = FALSE; + hashed_process_data_child->x.middle_marked = FALSE; + } + if(likely(hashed_process_data_child->x.under != new_x)) { + hashed_process_data_child->x.under = new_x; + hashed_process_data_child->x.under_used = FALSE; + hashed_process_data_child->x.under_marked = FALSE; + } + } + return FALSE; +} + + + +/* after_process_exit_hook + * + * Create the processlist entry for the child process. Put the last + * position in x at the current time value. + * + * @param hook_data ControlFlowData structure of the viewer. + * @param call_data Event context. + * + * This function adds items to be drawn in a queue for each process. + * + */ +int after_process_exit_hook(void *hook_data, void *call_data) +{ + LttvTraceHook *th = (LttvTraceHook*)hook_data; + EventsRequest *events_request = (EventsRequest*)th->hook_data; + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *filter = control_flow_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + LttTime evtime = ltt_event_time(e); + + /* Add process to process list (if not present) */ + //LttvProcessState *process = tfs->process; + guint cpu = tfs->cpu; + guint trace_num = ts->parent.index; + LttvProcessState *process = ts->running_process[cpu]; + + /* It should exist, because we are after the state update. */ + g_assert(process != NULL); + + guint pid = process->pid; + LttTime birth; + guint pl_height = 0; + HashedProcessData *hashed_process_data = NULL; + + ProcessList *process_list = control_flow_data->process_list; + + birth = process->creation_time; + + if(likely(process_list->current_hash_data[trace_num][cpu] != NULL) ){ + hashed_process_data = process_list->current_hash_data[trace_num][cpu]; + } else { + hashed_process_data = processlist_get_process_data(process_list, + pid, + process->cpu, + &birth, + trace_num); + if(unlikely(hashed_process_data == NULL)) + { + g_assert(pid == 0 || pid != process->ppid); + /* Process not present */ + Drawing_t *drawing = control_flow_data->drawing; + ProcessInfo *process_info; + processlist_add(process_list, + drawing, + pid, + process->tgid, + process->cpu, + process->ppid, + &birth, + trace_num, + process->name, + process->brand, + &pl_height, + &process_info, + &hashed_process_data); + gtk_widget_set_size_request(drawing->drawing_area, + -1, + pl_height); + gtk_widget_queue_draw(drawing->drawing_area); + } + + /* Set the current process */ + process_list->current_hash_data[trace_num][process->cpu] = + hashed_process_data; + } + + if(unlikely(ltt_time_compare(hashed_process_data->next_good_time, + evtime) <= 0)) + { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return FALSE; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint new_x; + convert_time_to_pixels( + time_window, + evtime, + width, + &new_x); + if(unlikely(hashed_process_data->x.middle != new_x)) { + hashed_process_data->x.middle = new_x; + hashed_process_data->x.middle_used = FALSE; + hashed_process_data->x.middle_marked = FALSE; + } + } + + return FALSE; +} + + +/* Get the filename of the process to print */ +int after_fs_exec_hook(void *hook_data, void *call_data) +{ + LttvTraceHook *th = (LttvTraceHook*)hook_data; + EventsRequest *events_request = (EventsRequest*)th->hook_data; + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *filter = control_flow_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + guint cpu = tfs->cpu; + guint trace_num = ts->parent.index; + LttvProcessState *process = ts->running_process[cpu]; + g_assert(process != NULL); + + guint pid = process->pid; + + /* Well, the process_out existed : we must get it in the process hash + * or add it, and draw its items. + */ + /* Add process to process list (if not present) */ + guint pl_height = 0; + HashedProcessData *hashed_process_data = NULL; + ProcessList *process_list = control_flow_data->process_list; + LttTime birth = process->creation_time; + + if(likely(process_list->current_hash_data[trace_num][cpu] != NULL)) { + hashed_process_data = process_list->current_hash_data[trace_num][cpu]; + } else { + hashed_process_data = processlist_get_process_data(process_list, + pid, + process->cpu, + &birth, + trace_num); + if(unlikely(hashed_process_data == NULL)) + { + g_assert(pid == 0 || pid != process->ppid); + ProcessInfo *process_info; + /* Process not present */ + Drawing_t *drawing = control_flow_data->drawing; + processlist_add(process_list, + drawing, + pid, + process->tgid, + process->cpu, + process->ppid, + &birth, + trace_num, + process->name, + process->brand, + &pl_height, + &process_info, + &hashed_process_data); + gtk_widget_set_size_request(drawing->drawing_area, + -1, + pl_height); + gtk_widget_queue_draw(drawing->drawing_area); + } + /* Set the current process */ + process_list->current_hash_data[trace_num][process->cpu] = + hashed_process_data; + } + + processlist_set_name(process_list, process->name, hashed_process_data); + + return 0; + +} + +/* Get the filename of the process to print */ +int after_user_generic_thread_brand_hook(void *hook_data, void *call_data) +{ + LttvTraceHook *th = (LttvTraceHook*)hook_data; + EventsRequest *events_request = (EventsRequest*)th->hook_data; + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *filter = control_flow_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + guint cpu = tfs->cpu; + guint trace_num = ts->parent.index; + LttvProcessState *process = ts->running_process[cpu]; + g_assert(process != NULL); + + guint pid = process->pid; + + /* Well, the process_out existed : we must get it in the process hash + * or add it, and draw its items. + */ + /* Add process to process list (if not present) */ + guint pl_height = 0; + HashedProcessData *hashed_process_data = NULL; + ProcessList *process_list = control_flow_data->process_list; + LttTime birth = process->creation_time; + + if(likely(process_list->current_hash_data[trace_num][cpu] != NULL)) { + hashed_process_data = process_list->current_hash_data[trace_num][cpu]; + } else { + hashed_process_data = processlist_get_process_data(process_list, + pid, + process->cpu, + &birth, + trace_num); + if(unlikely(hashed_process_data == NULL)) + { + g_assert(pid == 0 || pid != process->ppid); + ProcessInfo *process_info; + /* Process not present */ + Drawing_t *drawing = control_flow_data->drawing; + processlist_add(process_list, + drawing, + pid, + process->tgid, + process->cpu, + process->ppid, + &birth, + trace_num, + process->name, + process->brand, + &pl_height, + &process_info, + &hashed_process_data); + gtk_widget_set_size_request(drawing->drawing_area, + -1, + pl_height); + gtk_widget_queue_draw(drawing->drawing_area); + } + /* Set the current process */ + process_list->current_hash_data[trace_num][process->cpu] = + hashed_process_data; + } + + processlist_set_brand(process_list, process->brand, hashed_process_data); + + return 0; + +} + + +/* after_event_enum_process_hook + * + * Create the processlist entry for the child process. Put the last + * position in x at the current time value. + * + * @param hook_data ControlFlowData structure of the viewer. + * @param call_data Event context. + * + * This function adds items to be drawn in a queue for each process. + * + */ +int after_event_enum_process_hook(void *hook_data, void *call_data) +{ + LttvTraceHook *th = (LttvTraceHook*)hook_data; + EventsRequest *events_request = (EventsRequest*)th->hook_data; + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + guint first_cpu, nb_cpus, cpu; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *filter = control_flow_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + LttTime evtime = ltt_event_time(e); + + /* Add process to process list (if not present) */ + LttvProcessState *process_in; + LttTime birth; + guint pl_height = 0; + HashedProcessData *hashed_process_data_in = NULL; + + ProcessList *process_list = control_flow_data->process_list; + guint trace_num = ts->parent.index; + + guint pid_in; + { + pid_in = ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 0)); + } + + if(pid_in == 0) { + first_cpu = 0; + nb_cpus = ltt_trace_get_num_cpu(ts->parent.t); + } else { + first_cpu = ANY_CPU; + nb_cpus = ANY_CPU+1; + } + + for(cpu = first_cpu; cpu < nb_cpus; cpu++) { + /* Find process pid_in in the list... */ + process_in = lttv_state_find_process(ts, cpu, pid_in); + //process_in = tfs->process; + //guint cpu = tfs->cpu; + //guint trace_num = ts->parent.index; + //process_in = ts->running_process[cpu]; + /* It should exist, because we are after the state update. */ + #ifdef EXTRA_CHECK + //g_assert(process_in != NULL); + #endif //EXTRA_CHECK + birth = process_in->creation_time; + + hashed_process_data_in = processlist_get_process_data(process_list, + pid_in, + process_in->cpu, + &birth, + trace_num); + if(hashed_process_data_in == NULL) + { + if(pid_in != 0 && pid_in == process_in->ppid) + g_critical("TEST %u , %u", pid_in, process_in->ppid); + g_assert(pid_in == 0 || pid_in != process_in->ppid); + ProcessInfo *process_info; + Drawing_t *drawing = control_flow_data->drawing; + /* Process not present */ + processlist_add(process_list, + drawing, + pid_in, + process_in->tgid, + process_in->cpu, + process_in->ppid, + &birth, + trace_num, + process_in->name, + process_in->brand, + &pl_height, + &process_info, + &hashed_process_data_in); + gtk_widget_set_size_request(drawing->drawing_area, + -1, + pl_height); + gtk_widget_queue_draw(drawing->drawing_area); + } else { + processlist_set_name(process_list, process_in->name, + hashed_process_data_in); + processlist_set_ppid(process_list, process_in->ppid, + hashed_process_data_in); + processlist_set_tgid(process_list, process_in->tgid, + hashed_process_data_in); + } + } + return 0; +} + + +gint update_time_window_hook(void *hook_data, void *call_data) +{ + ControlFlowData *control_flow_data = (ControlFlowData*) hook_data; + Drawing_t *drawing = control_flow_data->drawing; + ProcessList *process_list = control_flow_data->process_list; + + const TimeWindowNotifyData *time_window_nofify_data = + ((const TimeWindowNotifyData *)call_data); + + TimeWindow *old_time_window = + time_window_nofify_data->old_time_window; + TimeWindow *new_time_window = + time_window_nofify_data->new_time_window; + + /* Update the ruler */ + drawing_update_ruler(control_flow_data->drawing, + new_time_window); + + + /* Two cases : zoom in/out or scrolling */ + + /* In order to make sure we can reuse the old drawing, the scale must + * be the same and the new time interval being partly located in the + * currently shown time interval. (reuse is only for scrolling) + */ + + g_info("Old time window HOOK : %lu, %lu to %lu, %lu", + old_time_window->start_time.tv_sec, + old_time_window->start_time.tv_nsec, + old_time_window->time_width.tv_sec, + old_time_window->time_width.tv_nsec); + + g_info("New time window HOOK : %lu, %lu to %lu, %lu", + new_time_window->start_time.tv_sec, + new_time_window->start_time.tv_nsec, + new_time_window->time_width.tv_sec, + new_time_window->time_width.tv_nsec); + + if( new_time_window->time_width.tv_sec == old_time_window->time_width.tv_sec + && new_time_window->time_width.tv_nsec == old_time_window->time_width.tv_nsec) + { + /* Same scale (scrolling) */ + g_info("scrolling"); + LttTime *ns = &new_time_window->start_time; + LttTime *nw = &new_time_window->time_width; + LttTime *os = &old_time_window->start_time; + LttTime *ow = &old_time_window->time_width; + LttTime old_end = old_time_window->end_time; + LttTime new_end = new_time_window->end_time; + //if(nsdrawing->width; + convert_time_to_pixels( + *old_time_window, + *ns, + width, + &x); + + /* Copy old data to new location */ + copy_pixmap_region(process_list, + NULL, + control_flow_data->drawing->drawing_area->style->black_gc, + NULL, + x, 0, + 0, 0, + control_flow_data->drawing->width-x+SAFETY, -1); + + if(drawing->damage_begin == drawing->damage_end) + drawing->damage_begin = control_flow_data->drawing->width-x; + else + drawing->damage_begin = 0; + + drawing->damage_end = control_flow_data->drawing->width; + + /* Clear the data request background, but not SAFETY */ + rectangle_pixmap(process_list, + control_flow_data->drawing->drawing_area->style->black_gc, + TRUE, + drawing->damage_begin+SAFETY, 0, + drawing->damage_end - drawing->damage_begin, // do not overlap + -1); + gtk_widget_queue_draw(drawing->drawing_area); + //gtk_widget_queue_draw_area (drawing->drawing_area, + // 0,0, + // control_flow_data->drawing->width, + // control_flow_data->drawing->height); + + /* Get new data for the rest. */ + drawing_data_request(control_flow_data->drawing, + drawing->damage_begin, 0, + drawing->damage_end - drawing->damage_begin, + control_flow_data->drawing->height); + } else { + //if(nsdrawing->width; + convert_time_to_pixels( + *new_time_window, + *os, + width, + &x); + + /* Copy old data to new location */ + copy_pixmap_region (process_list, + NULL, + control_flow_data->drawing->drawing_area->style->black_gc, + NULL, + 0, 0, + x, 0, + -1, -1); + + if(drawing->damage_begin == drawing->damage_end) + drawing->damage_end = x; + else + drawing->damage_end = + control_flow_data->drawing->width; + + drawing->damage_begin = 0; + + rectangle_pixmap (process_list, + control_flow_data->drawing->drawing_area->style->black_gc, + TRUE, + drawing->damage_begin, 0, + drawing->damage_end - drawing->damage_begin, // do not overlap + -1); + + gtk_widget_queue_draw(drawing->drawing_area); + //gtk_widget_queue_draw_area (drawing->drawing_area, + // 0,0, + // control_flow_data->drawing->width, + // control_flow_data->drawing->height); + + + /* Get new data for the rest. */ + drawing_data_request(control_flow_data->drawing, + drawing->damage_begin, 0, + drawing->damage_end - drawing->damage_begin, + control_flow_data->drawing->height); + + } else { + if(ltt_time_compare(*ns,*os) == 0) + { + g_info("not scrolling"); + } else { + g_info("scrolling far"); + /* Cannot reuse any part of the screen : far jump */ + + + rectangle_pixmap (process_list, + control_flow_data->drawing->drawing_area->style->black_gc, + TRUE, + 0, 0, + control_flow_data->drawing->width+SAFETY, // do not overlap + -1); + + //gtk_widget_queue_draw_area (drawing->drawing_area, + // 0,0, + // control_flow_data->drawing->width, + // control_flow_data->drawing->height); + gtk_widget_queue_draw(drawing->drawing_area); + + drawing->damage_begin = 0; + drawing->damage_end = control_flow_data->drawing->width; + + drawing_data_request(control_flow_data->drawing, + 0, 0, + control_flow_data->drawing->width, + control_flow_data->drawing->height); + + } + } + } + } else { + /* Different scale (zoom) */ + g_info("zoom"); + + rectangle_pixmap (process_list, + control_flow_data->drawing->drawing_area->style->black_gc, + TRUE, + 0, 0, + control_flow_data->drawing->width+SAFETY, // do not overlap + -1); + + //gtk_widget_queue_draw_area (drawing->drawing_area, + // 0,0, + // control_flow_data->drawing->width, + // control_flow_data->drawing->height); + gtk_widget_queue_draw(drawing->drawing_area); + + drawing->damage_begin = 0; + drawing->damage_end = control_flow_data->drawing->width; + + drawing_data_request(control_flow_data->drawing, + 0, 0, + control_flow_data->drawing->width, + control_flow_data->drawing->height); + } + + /* Update directly when scrolling */ + gdk_window_process_updates(control_flow_data->drawing->drawing_area->window, + TRUE); + + return 0; +} + +gint traceset_notify(void *hook_data, void *call_data) +{ + ControlFlowData *control_flow_data = (ControlFlowData*) hook_data; + Drawing_t *drawing = control_flow_data->drawing; + + if(unlikely(drawing->gc == NULL)) { + return FALSE; + } + if(drawing->dotted_gc == NULL) { + return FALSE; + } + + drawing_clear(control_flow_data->drawing); + processlist_clear(control_flow_data->process_list); + gtk_widget_set_size_request( + control_flow_data->drawing->drawing_area, + -1, processlist_get_height(control_flow_data->process_list)); + redraw_notify(control_flow_data, NULL); + + request_background_data(control_flow_data); + + return FALSE; +} + +gint redraw_notify(void *hook_data, void *call_data) +{ + ControlFlowData *control_flow_data = (ControlFlowData*) hook_data; + Drawing_t *drawing = control_flow_data->drawing; + GtkWidget *widget = drawing->drawing_area; + + drawing->damage_begin = 0; + drawing->damage_end = drawing->width; + + /* fun feature, to be separated someday... */ + drawing_clear(control_flow_data->drawing); + processlist_clear(control_flow_data->process_list); + gtk_widget_set_size_request( + control_flow_data->drawing->drawing_area, + -1, processlist_get_height(control_flow_data->process_list)); + // Clear the images + rectangle_pixmap (control_flow_data->process_list, + widget->style->black_gc, + TRUE, + 0, 0, + drawing->alloc_width, + -1); + + gtk_widget_queue_draw(drawing->drawing_area); + + if(drawing->damage_begin < drawing->damage_end) + { + drawing_data_request(drawing, + drawing->damage_begin, + 0, + drawing->damage_end-drawing->damage_begin, + drawing->height); + } + + //gtk_widget_queue_draw_area(drawing->drawing_area, + // 0,0, + // drawing->width, + // drawing->height); + return FALSE; + +} + + +gint continue_notify(void *hook_data, void *call_data) +{ + ControlFlowData *control_flow_data = (ControlFlowData*) hook_data; + Drawing_t *drawing = control_flow_data->drawing; + + //g_assert(widget->allocation.width == drawing->damage_end); + + if(drawing->damage_begin < drawing->damage_end) + { + drawing_data_request(drawing, + drawing->damage_begin, + 0, + drawing->damage_end-drawing->damage_begin, + drawing->height); + } + + return FALSE; +} + + +gint update_current_time_hook(void *hook_data, void *call_data) +{ + ControlFlowData *control_flow_data = (ControlFlowData*)hook_data; + Drawing_t *drawing = control_flow_data->drawing; + + LttTime current_time = *((LttTime*)call_data); + + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + + LttTime time_begin = time_window.start_time; + LttTime width = time_window.time_width; + LttTime half_width; + { + guint64 time_ll = ltt_time_to_uint64(width); + time_ll = time_ll >> 1; /* divide by two */ + half_width = ltt_time_from_uint64(time_ll); + } + LttTime time_end = ltt_time_add(time_begin, width); + + LttvTracesetContext * tsc = + lttvwindow_get_traceset_context(control_flow_data->tab); + + LttTime trace_start = tsc->time_span.start_time; + LttTime trace_end = tsc->time_span.end_time; + + g_info("New current time HOOK : %lu, %lu", current_time.tv_sec, + current_time.tv_nsec); + + + + /* If current time is inside time interval, just move the highlight + * bar */ + + /* Else, we have to change the time interval. We have to tell it + * to the main window. */ + /* The time interval change will take care of placing the current + * time at the center of the visible area, or nearest possible if we are + * at one end of the trace. */ + + + if(ltt_time_compare(current_time, time_begin) < 0) + { + TimeWindow new_time_window; + + if(ltt_time_compare(current_time, + ltt_time_add(trace_start,half_width)) < 0) + time_begin = trace_start; + else + time_begin = ltt_time_sub(current_time,half_width); + + new_time_window.start_time = time_begin; + new_time_window.time_width = width; + new_time_window.time_width_double = ltt_time_to_double(width); + new_time_window.end_time = ltt_time_add(time_begin, width); + + lttvwindow_report_time_window(control_flow_data->tab, new_time_window); + } + else if(ltt_time_compare(current_time, time_end) > 0) + { + TimeWindow new_time_window; + + if(ltt_time_compare(current_time, ltt_time_sub(trace_end, half_width)) > 0) + time_begin = ltt_time_sub(trace_end,width); + else + time_begin = ltt_time_sub(current_time,half_width); + + new_time_window.start_time = time_begin; + new_time_window.time_width = width; + new_time_window.time_width_double = ltt_time_to_double(width); + new_time_window.end_time = ltt_time_add(time_begin, width); + + lttvwindow_report_time_window(control_flow_data->tab, new_time_window); + + } + gtk_widget_queue_draw(control_flow_data->drawing->drawing_area); + + /* Update directly when scrolling */ + gdk_window_process_updates(control_flow_data->drawing->drawing_area->window, + TRUE); + + return 0; +} + +typedef struct _ClosureData { + EventsRequest *events_request; + LttvTracesetState *tss; + LttTime end_time; + guint x_end; +} ClosureData; + + +void draw_closure(gpointer key, gpointer value, gpointer user_data) +{ + ProcessInfo *process_info = (ProcessInfo*)key; + HashedProcessData *hashed_process_data = (HashedProcessData*)value; + ClosureData *closure_data = (ClosureData*)user_data; + + EventsRequest *events_request = closure_data->events_request; + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracesetState *tss = closure_data->tss; + LttvTracesetContext *tsc = (LttvTracesetContext*)tss; + + LttTime evtime = closure_data->end_time; + + gboolean dodraw = TRUE; + + { + /* For the process */ + /* First, check if the current process is in the state computation + * process list. If it is there, that means we must add it right now and + * draw items from the beginning of the read for it. If it is not + * present, it's a new process and it was not present : it will + * be added after the state update. */ +#ifdef EXTRA_CHECK + g_assert(lttv_traceset_number(tsc->ts) > 0); +#endif //EXTRA_CHECK + LttvTraceContext *tc = tsc->traces[process_info->trace_num]; + LttvTraceState *ts = (LttvTraceState*)tc; + +#if 0 + //FIXME : optimize data structures. + LttvTracefileState *tfs; + LttvTracefileContext *tfc; + guint i; + for(i=0;itracefiles->len;i++) { + tfc = g_array_index(tc->tracefiles, LttvTracefileContext*, i); + if(ltt_tracefile_name(tfc->tf) == LTT_NAME_CPU + && tfs->cpu == process_info->cpu) + break; + + } + g_assert(itracefiles->len); + tfs = LTTV_TRACEFILE_STATE(tfc); +#endif //0 + // LttvTracefileState *tfs = + // (LttvTracefileState*)tsc->traces[process_info->trace_num]-> + // tracefiles[process_info->cpu]; + + LttvProcessState *process; + process = lttv_state_find_process(ts, process_info->cpu, + process_info->pid); + + if(unlikely(process != NULL)) { + + LttvFilter *filter = control_flow_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,NULL,NULL, + tc->t,NULL,process,tc)) + dodraw = FALSE; + + /* Only draw for processes that are currently in the trace states */ + + ProcessList *process_list = control_flow_data->process_list; +#ifdef EXTRA_CHECK + /* Should be alike when background info is ready */ + if(control_flow_data->background_info_waiting==0) + g_assert(ltt_time_compare(process->creation_time, + process_info->birth) == 0); +#endif //EXTRA_CHECK + + /* Now, the process is in the state hash and our own process hash. + * We definitely can draw the items related to the ending state. + */ + + if(unlikely(ltt_time_compare(hashed_process_data->next_good_time, + evtime) <= 0)) + { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + + guint x = closure_data->x_end; + + DrawContext draw_context; + + /* Now create the drawing context that will be used to draw + * items related to the last state. */ + draw_context.drawable = hashed_process_data->pixmap; + draw_context.gc = drawing->gc; + draw_context.pango_layout = drawing->pango_layout; + draw_context.drawinfo.end.x = x; + + draw_context.drawinfo.y.over = 1; + draw_context.drawinfo.y.middle = (hashed_process_data->height/2); + draw_context.drawinfo.y.under = hashed_process_data->height; + + draw_context.drawinfo.start.offset.over = 0; + draw_context.drawinfo.start.offset.middle = 0; + draw_context.drawinfo.start.offset.under = 0; + draw_context.drawinfo.end.offset.over = 0; + draw_context.drawinfo.end.offset.middle = 0; + draw_context.drawinfo.end.offset.under = 0; +#if 0 + /* Jump over draw if we are at the same x position */ + if(x == hashed_process_data->x.over) + { + /* jump */ + } else { + draw_context.drawinfo.start.x = hashed_process_data->x.over; + /* Draw the line */ + PropertiesLine prop_line = prepare_execmode_line(process); + draw_line((void*)&prop_line, (void*)&draw_context); + + hashed_process_data->x.over = x; + } +#endif //0 + + if(unlikely(x == hashed_process_data->x.middle && + hashed_process_data->x.middle_used)) { +#if 0 /* do not mark closure : not missing information */ + if(hashed_process_data->x.middle_marked == FALSE) { + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(drawing->pixmap, + drawing->gc, + x, + y+(height/2)-3); + hashed_process_data->x.middle_marked = TRUE; + } +#endif //0 + /* Jump */ + } else { + draw_context.drawinfo.start.x = hashed_process_data->x.middle; + /* Draw the line */ + if(dodraw) { + PropertiesLine prop_line = prepare_s_e_line(process); + draw_line((void*)&prop_line, (void*)&draw_context); + } + + /* become the last x position */ + if(likely(x != hashed_process_data->x.middle)) { + hashed_process_data->x.middle = x; + /* but don't use the pixel */ + hashed_process_data->x.middle_used = FALSE; + + /* Calculate the next good time */ + convert_pixels_to_time(width, x+1, time_window, + &hashed_process_data->next_good_time); + } + } + } + } + } + return; +} + +int before_chunk(void *hook_data, void *call_data) +{ + EventsRequest *events_request = (EventsRequest*)hook_data; + LttvTracesetState *tss = (LttvTracesetState*)call_data; + ControlFlowData *cfd = (ControlFlowData*)events_request->viewer_data; +#if 0 + /* Desactivate sort */ + gtk_tree_sortable_set_sort_column_id( + GTK_TREE_SORTABLE(cfd->process_list->list_store), + TRACE_COLUMN, + GTK_SORT_ASCENDING); +#endif //0 + drawing_chunk_begin(events_request, tss); + + return 0; +} + +int before_request(void *hook_data, void *call_data) +{ + EventsRequest *events_request = (EventsRequest*)hook_data; + LttvTracesetState *tss = (LttvTracesetState*)call_data; + + drawing_data_request_begin(events_request, tss); + + return 0; +} + + +/* + * after request is necessary in addition of after chunk in order to draw + * lines until the end of the screen. after chunk just draws lines until + * the last event. + * + * for each process + * draw closing line + * expose + */ +int after_request(void *hook_data, void *call_data) +{ + EventsRequest *events_request = (EventsRequest*)hook_data; + ControlFlowData *control_flow_data = events_request->viewer_data; + LttvTracesetState *tss = (LttvTracesetState*)call_data; + + ProcessList *process_list = control_flow_data->process_list; + LttTime end_time = events_request->end_time; + + ClosureData closure_data; + closure_data.events_request = (EventsRequest*)hook_data; + closure_data.tss = tss; + closure_data.end_time = end_time; + + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + guint width = control_flow_data->drawing->width; + convert_time_to_pixels( + time_window, + end_time, + width, + &closure_data.x_end); + + + /* Draw last items */ + g_hash_table_foreach(process_list->process_hash, draw_closure, + (void*)&closure_data); + + + /* Request expose */ + drawing_request_expose(events_request, tss, end_time); + return 0; +} + +/* + * for each process + * draw closing line + * expose + */ +int after_chunk(void *hook_data, void *call_data) +{ + EventsRequest *events_request = (EventsRequest*)hook_data; + ControlFlowData *control_flow_data = events_request->viewer_data; + LttvTracesetState *tss = (LttvTracesetState*)call_data; + LttvTracesetContext *tsc = (LttvTracesetContext*)call_data; + LttvTracefileContext *tfc = lttv_traceset_context_get_current_tfc(tsc); + LttTime end_time; + + ProcessList *process_list = control_flow_data->process_list; + guint i; + LttvTraceset *traceset = tsc->ts; + guint nb_trace = lttv_traceset_number(traceset); + + /* Only execute when called for the first trace's events request */ + if(!process_list->current_hash_data) return; + + for(i = 0 ; i < nb_trace ; i++) { + g_free(process_list->current_hash_data[i]); + } + g_free(process_list->current_hash_data); + process_list->current_hash_data = NULL; + + if(tfc != NULL) + end_time = LTT_TIME_MIN(tfc->timestamp, events_request->end_time); + else /* end of traceset, or position now out of request : end */ + end_time = events_request->end_time; + + ClosureData closure_data; + closure_data.events_request = (EventsRequest*)hook_data; + closure_data.tss = tss; + closure_data.end_time = end_time; + + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + guint width = control_flow_data->drawing->width; + convert_time_to_pixels( + time_window, + end_time, + width, + &closure_data.x_end); + + /* Draw last items */ + g_hash_table_foreach(process_list->process_hash, draw_closure, + (void*)&closure_data); +#if 0 + /* Reactivate sort */ + gtk_tree_sortable_set_sort_column_id( + GTK_TREE_SORTABLE(control_flow_data->process_list->list_store), + GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, + GTK_SORT_ASCENDING); + + update_index_to_pixmap(control_flow_data->process_list); + /* Request a full expose : drawing scrambled */ + gtk_widget_queue_draw(control_flow_data->drawing->drawing_area); +#endif //0 + /* Request expose (updates damages zone also) */ + drawing_request_expose(events_request, tss, end_time); + + return 0; +} + +/* after_statedump_end + * + * @param hook_data ControlFlowData structure of the viewer. + * @param call_data Event context. + * + * This function adds items to be drawn in a queue for each process. + * + */ +int before_statedump_end(void *hook_data, void *call_data) +{ + LttvTraceHook *th = (LttvTraceHook*)hook_data; + EventsRequest *events_request = (EventsRequest*)th->hook_data; + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttvTracesetState *tss = (LttvTracesetState*)tfc->t_context->ts_context; + ProcessList *process_list = control_flow_data->process_list; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *filter = control_flow_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + LttTime evtime = ltt_event_time(e); + + ClosureData closure_data; + closure_data.events_request = events_request; + closure_data.tss = tss; + closure_data.end_time = evtime; + + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + guint width = control_flow_data->drawing->width; + convert_time_to_pixels( + time_window, + evtime, + width, + &closure_data.x_end); + + /* Draw last items */ + g_hash_table_foreach(process_list->process_hash, draw_closure, + (void*)&closure_data); +#if 0 + /* Reactivate sort */ + gtk_tree_sortable_set_sort_column_id( + GTK_TREE_SORTABLE(control_flow_data->process_list->list_store), + GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, + GTK_SORT_ASCENDING); + + update_index_to_pixmap(control_flow_data->process_list); + /* Request a full expose : drawing scrambled */ + gtk_widget_queue_draw(control_flow_data->drawing->drawing_area); +#endif //0 + /* Request expose (updates damages zone also) */ + drawing_request_expose(events_request, tss, evtime); + + return 0; +} diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/eventhooks.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/eventhooks.h new file mode 100644 index 00000000..88376123 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/eventhooks.h @@ -0,0 +1,123 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + + +/* eventhooks.h defines the hooks that are given to processTrace as parameter. + * These hooks call the drawing API to draw the information on the screen, + * using information from Context, but mostly state (running, waiting...). + */ + + +#ifndef _EVENT_HOOKS_H +#define _EVENT_HOOKS_H + +#include +#include +#include + +#include "processlist.h" +#include "drawing.h" +#include "cfv.h" + + +/* Structure used to store and use information relative to one events refresh + * request. Typically filled in by the expose event callback, then passed to the + * library call, then used by the drawing hooks. Then, once all the events are + * sent, it is freed by the hook called after the reading. + */ +//typedef struct _EventRequest +//{ +// ControlFlowData *control_flow_data; +// LttTime time_begin, time_end; +// gint x_begin, x_end; + /* Fill the Events_Context during the initial expose, before calling for + * events. + */ + //GArray Events_Context; //FIXME +//} EventRequest ; + + + + + +void send_test_data(ProcessList *process_list, Drawing_t *drawing); + +GtkWidget *h_guicontrolflow(LttvPlugin *plugin); + +GtkWidget *h_legend(LttvPlugin *plugin); + +int event_selected_hook(void *hook_data, void *call_data); + +/* + * The draw event hook is called by the reading API to have a + * particular event drawn on the screen. + * @param hook_data ControlFlowData structure of the viewer. + * @param call_data Event context with state. + * + * This function basically draw lines and icons. Two types of lines are drawn : + * one small (3 pixels?) representing the state of the process and the second + * type is thicker (10 pixels?) representing on which CPU a process is running + * (and this only in running state). + * + * Extremums of the lines : + * x_min : time of the last event context for this process kept in memory. + * x_max : time of the current event. + * y : middle of the process in the process list. The process is found in the + * list, therefore is it's position in pixels. + * + * The choice of lines'color is defined by the context of the last event for this + * process. + */ +int before_schedchange_hook(void *hook_data, void *call_data); +int after_schedchange_hook(void *hook_data, void *call_data); +int before_execmode_hook(void *hook_data, void *call_data); +int after_execmode_hook(void *hook_data, void *call_data); + + +int before_process_exit_hook(void *hook_data, void *call_data); +int before_process_release_hook(void *hook_data, void *call_data); +int after_process_exit_hook(void *hook_data, void *call_data); +int after_process_fork_hook(void *hook_data, void *call_data); +int after_fs_exec_hook(void *hook_data, void *call_data); +int after_user_generic_thread_brand_hook(void *hook_data, void *call_data); +int after_event_enum_process_hook(void *hook_data, void *call_data); + +#if 0 +int before_process_hook(void *hook_data, void *call_data); +int after_process_hook(void *hook_data, void *call_data); +#endif //0 + +void draw_closure(gpointer key, gpointer value, gpointer user_data); + +int before_chunk(void *hook_data, void *call_data); +int after_chunk(void *hook_data, void *call_data); +int before_request(void *hook_data, void *call_data); +int after_request(void *hook_data, void *call_data); +int before_statedump_end(void *hook_data, void *call_data); + + + +gint update_time_window_hook(void *hook_data, void *call_data); +gint update_current_time_hook(void *hook_data, void *call_data); +gint traceset_notify(void *hook_data, void *call_data); +gint redraw_notify(void *hook_data, void *call_data); +gint continue_notify(void *hook_data, void *call_data); + +void legend_destructor(GtkWindow *legend); + +#endif // _EVENT_HOOKS_H diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/hGuiControlFlowInsert.xpm b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/hGuiControlFlowInsert.xpm new file mode 100644 index 00000000..db4b7275 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/hGuiControlFlowInsert.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static char * hGuiControlFlowInsert_xpm[] = { +"22 22 3 1", +" c None", +". c #0DF904", +"+ c #F90404", +" ", +" . ", +" .. ", +" ... ", +" .... ", +" ... ", +" .. ", +" . ", +" ", +"++++++++++............", +"++++++++++............", +" ", +" ++++++ ", +" ++++++ ", +" ++++++ ", +" ++++++ ", +" ++++++ ", +" ++++++ ", +" ", +"..........++++++++++++", +"..........++++++++++++", +" "}; diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/lttv_plugin_cfv.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/lttv_plugin_cfv.c new file mode 100644 index 00000000..d8b005ab --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/lttv_plugin_cfv.c @@ -0,0 +1,84 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2006 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 "lttv_plugin_cfv.h" +#include +#include "drawing.h" + +/* + * forward definitions + */ + +/* + * Implementation + */ + +static void cfv_update_filter(LttvPlugin *parent, LttvFilter *filter) +{ + LttvPluginCFV *self = LTTV_PLUGIN_CFV(parent); + g_message("In CFV update filter."); + lttv_filter_destroy(self->cfd->filter); + self->cfd->filter = filter; + redraw_notify(self->cfd, NULL); +} + + +static void +lttv_plugin_cfv_class_init (LttvPluginCFVClass *klass) +{ + LttvPluginClass *parent_klass; + parent_klass = &klass->parent; + parent_klass->update_filter = cfv_update_filter; + g_type_class_add_private (klass, sizeof (ControlFlowData)); +} + + +static void +lttv_plugin_cfv_init (GTypeInstance *instance, gpointer g_class) +{ + LttvPluginCFV *self = LTTV_PLUGIN_CFV (instance); + self->cfd = G_TYPE_INSTANCE_GET_PRIVATE (self, + LTTV_TYPE_PLUGIN_CFV, ControlFlowData); +} + + +GType +lttv_plugin_cfv_get_type (void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (LttvPluginCFVClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + lttv_plugin_cfv_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (LttvPluginCFV), + 0, /* n_preallocs */ + lttv_plugin_cfv_init /* instance_init */ + }; + type = g_type_register_static (G_TYPE_OBJECT, + "LttvPluginCFVType", + &info, 0); + } + return type; +} + + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/lttv_plugin_cfv.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/lttv_plugin_cfv.h new file mode 100644 index 00000000..45445da9 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/lttv_plugin_cfv.h @@ -0,0 +1,63 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2006 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 LTTV_PLUGIN_CFV_H +#define LTTV_PLUGIN_CFV_H + +#include +#include +#include "cfv.h" + +/* + * Type macros. + */ + +#define LTTV_TYPE_PLUGIN_CFV (lttv_plugin_cfv_get_type ()) +#define LTTV_PLUGIN_CFV(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LTTV_TYPE_PLUGIN_CFV, LttvPluginCFV)) +#define LTTV_PLUGIN_CFV_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), LTTV_TYPE_PLUGIN_CFV, LttvPluginCFVClass)) +#define LTTV_IS_PLUGIN_CFV(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LTTV_TYPE_PLUGIN_CFV)) +#define LTTV_IS_PLUGIN_CFV_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), LTTV_TYPE_PLUGIN_CFV)) +#define LTTV_PLUGIN_CFV_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), LTTV_TYPE_PLUGIN_CFV, LttvPluginCFVClass)) + +typedef struct _LttvPluginCFV LttvPluginCFV; +typedef struct _LttvPluginCFVClass LttvPluginCFVClass; + +struct _LttvPluginCFV { + LttvPlugin parent; + + /* instance members */ + ControlFlowData *cfd; + + /* private */ +}; + +struct _LttvPluginCFVClass { + LttvPluginClass parent; + + /* class members */ +}; + +/* used by LTTV_PLUGIN_TAB_TYPE */ +GType lttv_plugin_cfv_get_type (void); + +/* + * Method definitions. + */ + + +#endif diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/module.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/module.c new file mode 100644 index 00000000..1ad14c16 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/module.c @@ -0,0 +1,113 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + + + +/*! \defgroup guiEvents libguiControlFlow: The GUI ControlFlow display plugin */ +/*\@{*/ + +/*! \file guiControlFlow.c + * \brief Graphical plugin for showing control flow of a trace. + * + * This plugin adds a Control Flow Viewer functionnality to Linux TraceToolkit + * GUI when this plugin is loaded. The init and destroy functions add the + * viewer's insertion menu item and toolbar icon by calling viewer.h's + * API functions. Then, when a viewer's object is created, the constructor + * creates ans register through API functions what is needed to interact + * with the TraceSet window. + * + * This plugin uses the gdk library to draw the events and gtk to interact + * with the user. + * + * Author : Mathieu Desnoyers, June 2003 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "cfv.h" +#include "lttv_plugin_cfv.h" +#include "eventhooks.h" + +#include "hGuiControlFlowInsert.xpm" + +GQuark LTT_NAME_CPU; + +/** Array containing instanced objects. Used when module is unloaded */ +GSList *g_control_flow_data_list = NULL ; + +/***************************************************************************** + * Functions for module loading/unloading * + *****************************************************************************/ +/** + * plugin's init function + * + * This function initializes the Control Flow Viewer functionnality through the + * gtkTraceSet API. + */ +static void init() { + + g_info("GUI ControlFlow Viewer init()"); + + /* Register the toolbar insert button and menu entry*/ + lttvwindow_register_constructor("guicontrolflow", + "/", + "Insert Control Flow Viewer", + hGuiControlFlowInsert_xpm, + "Insert Control Flow Viewer", + h_guicontrolflow); + + LTT_NAME_CPU = g_quark_from_string("/cpu"); +} + +void destroy_walk(gpointer data, gpointer user_data) +{ + g_info("Walk destroy GUI Control Flow Viewer"); + guicontrolflow_destructor_full((LttvPluginCFV*)data); +} + + +/** + * plugin's destroy function + * + * This function releases the memory reserved by the module and unregisters + * everything that has been registered in the gtkTraceSet API. + */ +static void destroy() { + g_info("GUI Control Flow Viewer destroy()"); + + g_slist_foreach(g_control_flow_data_list, destroy_walk, NULL ); + + g_slist_free(g_control_flow_data_list); + + g_slist_free(g_control_flow_data_list); + + /* Unregister the toolbar insert button and menu entry */ + lttvwindow_unregister_constructor(h_guicontrolflow); +} + + +LTTV_MODULE("guicontrolflow", "Control flow viewer", \ + "Graphical module to view processes state and control flow", \ + init, destroy, "lttvwindow") diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/processlist.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/processlist.c new file mode 100644 index 00000000..383c3e8e --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/processlist.c @@ -0,0 +1,811 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "processlist.h" +#include "drawing.h" +#include "drawitem.h" + +#define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format) + +/* Preallocated Size of the index_to_pixmap array */ +#define ALLOCATE_PROCESSES 1000 + +/***************************************************************************** + * Methods to synchronize process list * + *****************************************************************************/ + + +gint process_sort_func ( GtkTreeModel *model, + GtkTreeIter *it_a, + GtkTreeIter *it_b, + gpointer user_data) +{ + gchar *a_name; + gchar *a_brand; + guint a_pid, a_tgid, a_ppid, a_cpu; + gulong a_birth_s, a_birth_ns; + guint a_trace; + + gchar *b_name; + gchar *b_brand; + guint b_pid, b_tgid, b_ppid, b_cpu; + gulong b_birth_s, b_birth_ns; + guint b_trace; + + gtk_tree_model_get(model, + it_a, + PROCESS_COLUMN, &a_name, + BRAND_COLUMN, &a_brand, + PID_COLUMN, &a_pid, + TGID_COLUMN, &a_tgid, + PPID_COLUMN, &a_ppid, + CPU_COLUMN, &a_cpu, + BIRTH_S_COLUMN, &a_birth_s, + BIRTH_NS_COLUMN, &a_birth_ns, + TRACE_COLUMN, &a_trace, + -1); + + gtk_tree_model_get(model, + it_b, + PROCESS_COLUMN, &b_name, + BRAND_COLUMN, &b_brand, + PID_COLUMN, &b_pid, + TGID_COLUMN, &b_tgid, + PPID_COLUMN, &b_ppid, + CPU_COLUMN, &b_cpu, + BIRTH_S_COLUMN, &b_birth_s, + BIRTH_NS_COLUMN, &b_birth_ns, + TRACE_COLUMN, &b_trace, + -1); + + + /* Order by PID */ + if(a_pid == 0 && b_pid == 0) { + /* If 0, order by CPU */ + if(a_cpu > b_cpu) return 1; + if(a_cpu < b_cpu) return -1; + + } else { /* if not 0, order by pid */ + + if(a_pid > b_pid) return 1; + if(a_pid < b_pid) return -1; + } + + /* Order by birth second */ + + if(a_birth_s > b_birth_s) return 1; + if(a_birth_s < b_birth_s) return -1; + + + /* Order by birth nanosecond */ + if(a_birth_ns > b_birth_ns) return 1; + if(a_birth_ns < b_birth_ns) return -1; + + /* Order by trace_num */ + if(a_trace > b_trace) return 1; + if(a_trace < b_trace) return -1; + + return 0; + +} + +static guint process_list_hash_fct(gconstpointer key) +{ + guint pid = ((const ProcessInfo*)key)->pid; + return ((pid>>8 ^ pid>>4 ^ pid>>2 ^ pid) ^ ((const ProcessInfo*)key)->cpu); +} + +/* If hash is good, should be different */ +static gboolean process_list_equ_fct(gconstpointer a, gconstpointer b) +{ + const ProcessInfo *pa = (const ProcessInfo*)a; + const ProcessInfo *pb = (const ProcessInfo*)b; + + gboolean ret = TRUE; + + if(likely(pa->pid != pb->pid)) + ret = FALSE; + if(likely((pa->pid == 0 && (pa->cpu != pb->cpu)))) + ret = FALSE; + if(unlikely(ltt_time_compare(pa->birth, pb->birth) != 0)) + ret = FALSE; + if(unlikely(pa->trace_num != pb->trace_num)) + ret = FALSE; + + return ret; +} + +void destroy_hash_key(gpointer key); + +void destroy_hash_data(gpointer data); + + +gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, gpointer data) +{ + ControlFlowData *control_flow_data = + (ControlFlowData*)g_object_get_data( + G_OBJECT(widget), + "control_flow_data"); + Drawing_t *drawing = control_flow_data->drawing; + unsigned int cell_height = + get_cell_height(GTK_TREE_VIEW(control_flow_data->process_list->process_list_widget)); + + switch(event->direction) { + case GDK_SCROLL_UP: + gtk_adjustment_set_value(control_flow_data->v_adjust, + gtk_adjustment_get_value(control_flow_data->v_adjust) - cell_height); + break; + case GDK_SCROLL_DOWN: + gtk_adjustment_set_value(control_flow_data->v_adjust, + gtk_adjustment_get_value(control_flow_data->v_adjust) + cell_height); + break; + default: + g_error("should only scroll up and down."); + } + return TRUE; +} + + +static void update_index_to_pixmap_each(ProcessInfo *key, + HashedProcessData *value, + ProcessList *process_list) +{ + guint array_index = processlist_get_index_from_data(process_list, value); + + g_assert(array_index < process_list->index_to_pixmap->len); + + GdkPixmap **pixmap = + (GdkPixmap**)&g_ptr_array_index(process_list->index_to_pixmap, array_index); + + *pixmap = value->pixmap; +} + + +void update_index_to_pixmap(ProcessList *process_list) +{ + g_ptr_array_set_size(process_list->index_to_pixmap, + g_hash_table_size(process_list->process_hash)); + g_hash_table_foreach(process_list->process_hash, + (GHFunc)update_index_to_pixmap_each, + process_list); +} + + +static void update_pixmap_size_each(ProcessInfo *key, + HashedProcessData *value, + guint width) +{ + GdkPixmap *old_pixmap = value->pixmap; + + value->pixmap = + gdk_pixmap_new(old_pixmap, + width, + value->height, + -1); + + gdk_pixmap_unref(old_pixmap); +} + + +void update_pixmap_size(ProcessList *process_list, guint width) +{ + g_hash_table_foreach(process_list->process_hash, + (GHFunc)update_pixmap_size_each, + (gpointer)width); +} + + +typedef struct _CopyPixmap { + GdkDrawable *dest; + GdkGC *gc; + GdkDrawable *src; + gint xsrc, ysrc, xdest, ydest, width, height; +} CopyPixmap; + +static void copy_pixmap_region_each(ProcessInfo *key, + HashedProcessData *value, + CopyPixmap *cp) +{ + GdkPixmap *src = cp->src; + GdkPixmap *dest = cp->dest; + + if(dest == NULL) + dest = value->pixmap; + if(src == NULL) + src = value->pixmap; + + gdk_draw_drawable (dest, + cp->gc, + src, + cp->xsrc, cp->ysrc, + cp->xdest, cp->ydest, + cp->width, cp->height); +} + + + + +void copy_pixmap_region(ProcessList *process_list, GdkDrawable *dest, + GdkGC *gc, GdkDrawable *src, + gint xsrc, gint ysrc, + gint xdest, gint ydest, gint width, gint height) +{ + CopyPixmap cp = { dest, gc, src, xsrc, ysrc, xdest, ydest, width, height }; + + g_hash_table_foreach(process_list->process_hash, + (GHFunc)copy_pixmap_region_each, + &cp); +} + + + +typedef struct _RectanglePixmap { + gboolean filled; + gint x, y, width, height; + GdkGC *gc; +} RectanglePixmap; + +static void rectangle_pixmap_each(ProcessInfo *key, + HashedProcessData *value, + RectanglePixmap *rp) +{ + if(rp->height == -1) + rp->height = value->height; + + gdk_draw_rectangle (value->pixmap, + rp->gc, + rp->filled, + rp->x, rp->y, + rp->width, rp->height); +} + + + + +void rectangle_pixmap(ProcessList *process_list, GdkGC *gc, + gboolean filled, gint x, gint y, gint width, gint height) +{ + RectanglePixmap rp = { filled, x, y, width, height, gc }; + + g_hash_table_foreach(process_list->process_hash, + (GHFunc)rectangle_pixmap_each, + &rp); +} + + +/* Renders each pixmaps into on big drawable */ +void copy_pixmap_to_screen(ProcessList *process_list, + GdkDrawable *dest, + GdkGC *gc, + gint x, gint y, + gint width, gint height) +{ + if(process_list->index_to_pixmap->len == 0) return; + guint cell_height = process_list->cell_height; + + /* Get indexes */ + gint begin = floor(y/(double)cell_height); + gint end = MIN(ceil((y+height)/(double)cell_height), + process_list->index_to_pixmap->len); + gint i; + + for(i=begin; iindex_to_pixmap->len); + /* Render the pixmap to the screen */ + GdkPixmap *pixmap = + //(GdkPixmap*)g_ptr_array_index(process_list->index_to_pixmap, i); + GDK_PIXMAP(g_ptr_array_index(process_list->index_to_pixmap, i)); + + gdk_draw_drawable (dest, + gc, + pixmap, + x, 0, + x, i*cell_height, + width, cell_height); + + } + + +} + + + + + + + + + +ProcessList *processlist_construct(void) +{ + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + ProcessList* process_list = g_new(ProcessList,1); + + process_list->number_of_process = 0; + + process_list->current_hash_data = NULL; + + /* Create the Process list */ + process_list->list_store = gtk_list_store_new ( N_COLUMNS, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_UINT, + G_TYPE_UINT, + G_TYPE_UINT, + G_TYPE_UINT, + G_TYPE_ULONG, + G_TYPE_ULONG, + G_TYPE_UINT); + + + process_list->process_list_widget = + gtk_tree_view_new_with_model + (GTK_TREE_MODEL (process_list->list_store)); + + g_object_unref (G_OBJECT (process_list->list_store)); + + gtk_tree_sortable_set_default_sort_func( + GTK_TREE_SORTABLE(process_list->list_store), + process_sort_func, + NULL, + NULL); + + + gtk_tree_sortable_set_sort_column_id( + GTK_TREE_SORTABLE(process_list->list_store), + GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, + GTK_SORT_ASCENDING); + + + process_list->process_hash = g_hash_table_new_full( + process_list_hash_fct, process_list_equ_fct, + destroy_hash_key, destroy_hash_data + ); + + + gtk_tree_view_set_headers_visible( + GTK_TREE_VIEW(process_list->process_list_widget), TRUE); + + /* Create a column, associating the "text" attribute of the + * cell_renderer to the first column of the model */ + /* Columns alignment : 0.0 : Left 0.5 : Center 1.0 : Right */ + renderer = gtk_cell_renderer_text_new (); + process_list->renderer = renderer; + + /* Add a temporary row to the model to get the cell size when the first + * real process is added. */ + GtkTreeIter iter; + GtkTreePath *path; + path = gtk_tree_path_new_first(); + gtk_tree_model_get_iter (gtk_tree_view_get_model(GTK_TREE_VIEW(process_list->process_list_widget)), &iter, path); + gtk_list_store_append(process_list->list_store, &iter); + gtk_tree_path_free(path); + + process_list->cell_height = 0; // not ready to get size yet. + + column = gtk_tree_view_column_new_with_attributes ( "Process", + renderer, + "text", + PROCESS_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column ( + GTK_TREE_VIEW (process_list->process_list_widget), column); + + process_list->button = column->button; + + column = gtk_tree_view_column_new_with_attributes ( "Brand", + renderer, + "text", + BRAND_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column ( + GTK_TREE_VIEW (process_list->process_list_widget), column); + + column = gtk_tree_view_column_new_with_attributes ( "PID", + renderer, + "text", + PID_COLUMN, + NULL); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column ( + GTK_TREE_VIEW (process_list->process_list_widget), column); + + column = gtk_tree_view_column_new_with_attributes ( "TGID", + renderer, + "text", + TGID_COLUMN, + NULL); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column ( + GTK_TREE_VIEW (process_list->process_list_widget), column); + + column = gtk_tree_view_column_new_with_attributes ( "PPID", + renderer, + "text", + PPID_COLUMN, + NULL); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column ( + GTK_TREE_VIEW (process_list->process_list_widget), column); + + column = gtk_tree_view_column_new_with_attributes ( "CPU", + renderer, + "text", + CPU_COLUMN, + NULL); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column ( + GTK_TREE_VIEW (process_list->process_list_widget), column); + + column = gtk_tree_view_column_new_with_attributes ( "Birth sec", + renderer, + "text", + BIRTH_S_COLUMN, + NULL); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column ( + GTK_TREE_VIEW (process_list->process_list_widget), column); + + //gtk_tree_view_column_set_visible(column, 0); + // + column = gtk_tree_view_column_new_with_attributes ( "Birth nsec", + renderer, + "text", + BIRTH_NS_COLUMN, + NULL); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column ( + GTK_TREE_VIEW (process_list->process_list_widget), column); + + column = gtk_tree_view_column_new_with_attributes ( "TRACE", + renderer, + "text", + TRACE_COLUMN, + NULL); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column ( + GTK_TREE_VIEW (process_list->process_list_widget), column); + + + //gtk_tree_view_column_set_visible(column, 0); + + g_object_set_data_full( + G_OBJECT(process_list->process_list_widget), + "process_list_Data", + process_list, + (GDestroyNotify)processlist_destroy); + + process_list->index_to_pixmap = g_ptr_array_sized_new(ALLOCATE_PROCESSES); + + return process_list; +} + +void processlist_destroy(ProcessList *process_list) +{ + g_debug("processlist_destroy %p", process_list); + g_hash_table_destroy(process_list->process_hash); + process_list->process_hash = NULL; + g_ptr_array_free(process_list->index_to_pixmap, TRUE); + + g_free(process_list); + g_debug("processlist_destroy end"); +} + +static gboolean remove_hash_item(ProcessInfo *process_info, + HashedProcessData *hashed_process_data, + ProcessList *process_list) +{ + GtkTreeIter iter; + + iter = hashed_process_data->y_iter; + + gtk_list_store_remove (process_list->list_store, &iter); + gdk_pixmap_unref(hashed_process_data->pixmap); + + if(likely(process_list->current_hash_data != NULL)) { + if(likely(hashed_process_data == + process_list->current_hash_data[process_info->trace_num][process_info->cpu])) + process_list->current_hash_data[process_info->trace_num][process_info->cpu] = NULL; + } + return TRUE; /* remove the element from the hash table */ +} + +void processlist_clear(ProcessList *process_list) +{ + g_info("processlist_clear %p", process_list); + + g_hash_table_foreach_remove(process_list->process_hash, + (GHRFunc)remove_hash_item, + (gpointer)process_list); + process_list->number_of_process = 0; + update_index_to_pixmap(process_list); +} + + +GtkWidget *processlist_get_widget(ProcessList *process_list) +{ + return process_list->process_list_widget; +} + + +void destroy_hash_key(gpointer key) +{ + g_free(key); +} + +void destroy_hash_data(gpointer data) +{ + g_free(data); +} + + +void processlist_set_name(ProcessList *process_list, + GQuark name, + HashedProcessData *hashed_process_data) +{ + gtk_list_store_set ( process_list->list_store, &hashed_process_data->y_iter, + PROCESS_COLUMN, g_quark_to_string(name), + -1); +} + +void processlist_set_brand(ProcessList *process_list, + GQuark brand, + HashedProcessData *hashed_process_data) +{ + gtk_list_store_set ( process_list->list_store, &hashed_process_data->y_iter, + BRAND_COLUMN, g_quark_to_string(brand), + -1); +} + +void processlist_set_tgid(ProcessList *process_list, + guint tgid, + HashedProcessData *hashed_process_data) +{ + gtk_list_store_set ( process_list->list_store, &hashed_process_data->y_iter, + TGID_COLUMN, tgid, + -1); +} + +void processlist_set_ppid(ProcessList *process_list, + guint ppid, + HashedProcessData *hashed_process_data) +{ + gtk_list_store_set ( process_list->list_store, &hashed_process_data->y_iter, + PPID_COLUMN, ppid, + -1); +} + + +int processlist_add( ProcessList *process_list, + Drawing_t *drawing, + guint pid, + guint tgid, + guint cpu, + guint ppid, + LttTime *birth, + guint trace_num, + GQuark name, + GQuark brand, + guint *height, + ProcessInfo **pm_process_info, + HashedProcessData **pm_hashed_process_data) +{ + ProcessInfo *Process_Info = g_new(ProcessInfo, 1); + HashedProcessData *hashed_process_data = g_new(HashedProcessData, 1); + *pm_hashed_process_data = hashed_process_data; + *pm_process_info = Process_Info; + + Process_Info->pid = pid; + Process_Info->tgid = tgid; + if(pid == 0) + Process_Info->cpu = cpu; + else + Process_Info->cpu = 0; + Process_Info->ppid = ppid; + Process_Info->birth = *birth; + Process_Info->trace_num = trace_num; + + /* When we create it from before state update, we are sure that the + * last event occured before the beginning of the global area. + * + * If it is created after state update, this value (0) will be + * overriden by the new state before anything is drawn. + */ + hashed_process_data->x.over = 0; + hashed_process_data->x.over_used = FALSE; + hashed_process_data->x.over_marked = FALSE; + hashed_process_data->x.middle = 0; + hashed_process_data->x.middle_used = FALSE; + hashed_process_data->x.middle_marked = FALSE; + hashed_process_data->x.under = 0; + hashed_process_data->x.under_used = FALSE; + hashed_process_data->x.under_marked = FALSE; + hashed_process_data->next_good_time = ltt_time_zero; + + if (process_list->cell_height == 0) { + GtkTreePath *path; + GdkRectangle rect; + GtkTreeIter iter; + + path = gtk_tree_path_new_first(); + gtk_tree_model_get_iter (gtk_tree_view_get_model(GTK_TREE_VIEW(process_list->process_list_widget)), &iter, path); + gtk_tree_view_get_background_area( + GTK_TREE_VIEW(process_list->process_list_widget), + path, NULL, &rect); + gtk_list_store_remove(process_list->list_store, &iter); + gtk_tree_path_free (path); + process_list->cell_height = rect.height; + } + + /* Add a new row to the model */ + gtk_list_store_append ( process_list->list_store, + &hashed_process_data->y_iter); + + gtk_list_store_set ( process_list->list_store, &hashed_process_data->y_iter, + PROCESS_COLUMN, g_quark_to_string(name), + BRAND_COLUMN, g_quark_to_string(brand), + PID_COLUMN, pid, + TGID_COLUMN, tgid, + PPID_COLUMN, ppid, + CPU_COLUMN, cpu, + BIRTH_S_COLUMN, birth->tv_sec, + BIRTH_NS_COLUMN, birth->tv_nsec, + TRACE_COLUMN, trace_num, + -1); + //gtk_tree_view_set_model(GTK_TREE_VIEW(process_list->process_list_widget), + // GTK_TREE_MODEL(process_list->list_store)); + //gtk_container_resize_children(GTK_CONTAINER(process_list->process_list_widget)); + + g_hash_table_insert(process_list->process_hash, + (gpointer)Process_Info, + (gpointer)hashed_process_data); + + process_list->number_of_process++; +#if 0 + GtkTreePath *path; + GdkRectangle rect; + gtk_widget_queue_draw(process_list->process_list_widget); + path = gtk_tree_path_new_first(); + gtk_tree_view_get_background_area(GTK_TREE_VIEW(process_list->process_list_widget), + path, NULL, &rect); + gtk_tree_path_free (path); + process_list->cell_height = rect.height; +#endif //0 + + + hashed_process_data->height = process_list->cell_height; + g_assert(hashed_process_data->height != 0); + + *height = hashed_process_data->height * process_list->number_of_process; + + hashed_process_data->pixmap = + gdk_pixmap_new(drawing->drawing_area->window, + drawing->alloc_width, + hashed_process_data->height, + -1); + + // Clear the image + gdk_draw_rectangle (hashed_process_data->pixmap, + drawing->drawing_area->style->black_gc, + TRUE, + 0, 0, + drawing->alloc_width, + hashed_process_data->height); + + update_index_to_pixmap(process_list); + + + return 0; +} + +int processlist_remove( ProcessList *process_list, + guint pid, + guint cpu, + LttTime *birth, + guint trace_num) +{ + ProcessInfo process_info; + HashedProcessData *hashed_process_data; + GtkTreeIter iter; + + process_info.pid = pid; + if(pid == 0) + process_info.cpu = cpu; + else + process_info.cpu = 0; + process_info.birth = *birth; + process_info.trace_num = trace_num; + + + hashed_process_data = + (HashedProcessData*)g_hash_table_lookup( + process_list->process_hash, + &process_info); + if(likely(hashed_process_data != NULL)) + { + iter = hashed_process_data->y_iter; + + gtk_list_store_remove (process_list->list_store, &iter); + + g_hash_table_remove(process_list->process_hash, + &process_info); + + if(likely(process_list->current_hash_data != NULL)) { + if(likely(hashed_process_data == process_list->current_hash_data[trace_num][cpu])) { + process_list->current_hash_data[trace_num][cpu] = NULL; + } + } + + gdk_pixmap_unref(hashed_process_data->pixmap); + + update_index_to_pixmap(process_list); + + process_list->number_of_process--; + + return 0; + } else { + return 1; + } +} + + +#if 0 +static inline guint get_cpu_number_from_name(GQuark name) +{ + const gchar *string; + char *begin; + guint cpu; + + string = g_quark_to_string(name); + + begin = strrchr(string, '/'); + begin++; + + g_assert(begin != '\0'); + + cpu = strtoul(begin, NULL, 10); + + return cpu; +} +#endif //0 diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/processlist.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/processlist.h new file mode 100644 index 00000000..d9484603 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/processlist.h @@ -0,0 +1,271 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 _PROCESS_LIST_H +#define _PROCESS_LIST_H + +#include +#include +#include +#include + +#include "drawitem.h" + +/* The process list + * + * Tasks : + * Create a process list + * contains the data for the process list + * tells the height of the process list widget + * provides methods to add/remove process from the list + * note : the sync with drawing is left to the caller. + * provides helper function to convert a process unique identifier to + * pixels (in height). + * + */ + + +/* Enumeration of the columns */ +enum +{ + PROCESS_COLUMN, + BRAND_COLUMN, + PID_COLUMN, + TGID_COLUMN, + PPID_COLUMN, + CPU_COLUMN, + BIRTH_S_COLUMN, + BIRTH_NS_COLUMN, + TRACE_COLUMN, + N_COLUMNS +}; + + +typedef struct _ProcessInfo { + + guint pid; + guint tgid; + guint cpu; + guint ppid; + LttTime birth; + guint trace_num; + + // gint height_cache; + +} ProcessInfo; + +typedef struct _HashedProcessData { + + GdkPixmap *pixmap; // Pixmap slice containing drawing buffer for the PID + gint height; // height of the pixmap + GtkTreeIter y_iter; // Access quickly to y pos. + // DrawContext *draw_context; + /* Information on current drawing */ + struct { + guint over; + gboolean over_used; /* inform the user that information is incomplete */ + gboolean over_marked; /* inform the user that information is incomplete */ + guint middle; + gboolean middle_used; /* inform the user that information is incomplete */ + gboolean middle_marked;/* inform the user that information is incomplete */ + guint under; + gboolean under_used; /* inform the user that information is incomplete */ + gboolean under_marked; /* inform the user that information is incomplete */ + } x; /* last x position saved by after state update */ + + LttTime next_good_time; /* precalculate the next time where the next + pixel is.*/ + +} HashedProcessData; + +struct _ProcessList { + + GtkWidget *process_list_widget; + GtkListStore *list_store; + GtkWidget *button; /* one button of the tree view */ + GtkCellRenderer *renderer; + + /* A hash table by PID to speed up process position find in the list */ + GHashTable *process_hash; + + guint number_of_process; + gint cell_height; + + /* Current process pointer, one per cpu, one per trace */ + HashedProcessData ***current_hash_data; + + /* Array containing index -> pixmap correspondance. Must be updated + * every time the process list is reordered, process added or removed */ + GPtrArray * index_to_pixmap; + +}; + + +typedef struct _ProcessList ProcessList; + + +#ifndef TYPE_DRAWING_T_DEFINED +#define TYPE_DRAWING_T_DEFINED +typedef struct _Drawing_t Drawing_t; +#endif //TYPE_DRAWING_T_DEFINED + +ProcessList *processlist_construct(void); +void processlist_destroy(ProcessList *process_list); +GtkWidget *processlist_get_widget(ProcessList *process_list); + +void processlist_clear(ProcessList *process_list); + +// out : success (0) and height +/* CPU num is only used for PID 0 */ +int processlist_add(ProcessList *process_list, Drawing_t * drawing, + guint pid, guint tgid, guint cpu, guint ppid, + LttTime *birth, guint trace_num, GQuark name, GQuark brand, guint *height, + ProcessInfo **process_info, + HashedProcessData **hashed_process_data); +// out : success (0) and height +int processlist_remove(ProcessList *process_list, guint pid, guint cpu, + LttTime *birth, guint trace_num); + +/* Set the name of a process */ +void processlist_set_name(ProcessList *process_list, + GQuark name, + HashedProcessData *hashed_process_data); + +void processlist_set_brand(ProcessList *process_list, + GQuark brand, + HashedProcessData *hashed_process_data); + +/* Set the ppid of a process */ +void processlist_set_tgid(ProcessList *process_list, + guint tgid, + HashedProcessData *hashed_process_data); +void processlist_set_ppid(ProcessList *process_list, + guint ppid, + HashedProcessData *hashed_process_data); + + +/* Synchronize the list at the left and the drawing */ +void update_index_to_pixmap(ProcessList *process_list); + +/* Update the width of each pixmap buffer for each process */ +void update_pixmap_size(ProcessList *process_list, guint width); + + +/* Put src and/or dest to NULL to copy from/to the each PID specific pixmap */ +void copy_pixmap_region(ProcessList *process_list, GdkDrawable *dest, + GdkGC *gc, GdkDrawable *src, + gint xsrc, gint ysrc, + gint xdest, gint ydest, gint width, gint height); + +/* If height is -1, the height of each pixmap is used */ +void rectangle_pixmap(ProcessList *process_list, GdkGC *gc, + gboolean filled, gint x, gint y, gint width, gint height); + +/* Renders each pixmaps into on big drawable */ +void copy_pixmap_to_screen(ProcessList *process_list, + GdkDrawable *dest, + GdkGC *gc, + gint x, gint y, + gint width, gint height); + + +static inline gint get_cell_height(GtkTreeView *TreeView) +{ + gint height; + GtkTreeViewColumn *column = gtk_tree_view_get_column(TreeView, 0); + + gtk_tree_view_column_cell_get_size(column, NULL, NULL, NULL, NULL, &height); + + gint vertical_separator; + gtk_widget_style_get (GTK_WIDGET (TreeView), + "vertical-separator", &vertical_separator, + NULL); + height += vertical_separator; + + return height; +} + +static inline guint processlist_get_height(ProcessList *process_list) +{ + return process_list->cell_height * process_list->number_of_process ; +} + + +static inline HashedProcessData *processlist_get_process_data( + ProcessList *process_list, + guint pid, guint cpu, LttTime *birth, guint trace_num) +{ + ProcessInfo process_info; + + process_info.pid = pid; + if(pid == 0) + process_info.cpu = cpu; + else + process_info.cpu = ANY_CPU; + process_info.birth = *birth; + process_info.trace_num = trace_num; + + return (HashedProcessData*)g_hash_table_lookup( + process_list->process_hash, + &process_info); +} + + +static inline gint processlist_get_pixels_from_data( ProcessList *process_list, + HashedProcessData *hashed_process_data, + guint *y, + guint *height) +{ + gint *path_indices; + GtkTreePath *tree_path; + + tree_path = gtk_tree_model_get_path((GtkTreeModel*)process_list->list_store, + &hashed_process_data->y_iter); + path_indices = gtk_tree_path_get_indices (tree_path); + + *height = get_cell_height((GtkTreeView*)process_list->process_list_widget); + *y = *height * path_indices[0]; + gtk_tree_path_free(tree_path); + + return 0; + +} + +static inline guint processlist_get_index_from_data(ProcessList *process_list, + HashedProcessData *hashed_process_data) +{ + gint *path_indices; + GtkTreePath *tree_path; + guint ret; + + tree_path = gtk_tree_model_get_path((GtkTreeModel*)process_list->list_store, + &hashed_process_data->y_iter); + path_indices = gtk_tree_path_get_indices (tree_path); + + ret = path_indices[0]; + + gtk_tree_path_free(tree_path); + + return ret; +} + + + +#endif // _PROCESS_LIST_H diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/test.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/test.c new file mode 100644 index 00000000..8a71a52e --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/controlflow/test.c @@ -0,0 +1,578 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +static void destroy_cb( GtkWidget *widget, + gpointer data ) +{ + gtk_main_quit (); +} + + + +int main(int argc, char **argv) +{ + GtkWidget *Window; + GtkWidget *CF_Viewer; + GtkWidget *VBox_V; + GtkWidget *HScroll_VC; + ControlFlowData *control_flow_data; + guint ev_sel = 444 ; + /* Horizontal scrollbar and it's adjustment */ + GtkWidget *VScroll_VC; + GtkAdjustment *v_adjust ; + + /* Initialize i18n support */ + gtk_set_locale (); + + /* Initialize the widget set */ + gtk_init (&argc, &argv); + + init(); + + Window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (Window), ("Test Window")); + + g_signal_connect (G_OBJECT (Window), "destroy", + G_CALLBACK (destroy_cb), NULL); + + + VBox_V = gtk_vbox_new(0, 0); + gtk_container_add (GTK_CONTAINER (Window), VBox_V); + + //ListViewer = hGuiEvents(Window); + //gtk_box_pack_start(GTK_BOX(VBox_V), ListViewer, TRUE, TRUE, 0); + + //ListViewer = hGuiEvents(Window); + //gtk_box_pack_start(GTK_BOX(VBox_V), ListViewer, FALSE, TRUE, 0); + + control_flow_data = guicontrolflow(); + CF_Viewer = control_flow_data->scrolled_window; + gtk_box_pack_start(GTK_BOX(VBox_V), CF_Viewer, TRUE, TRUE, 0); + + /* Create horizontal scrollbar and pack it */ + HScroll_VC = gtk_hscrollbar_new(NULL); + gtk_box_pack_start(GTK_BOX(VBox_V), HScroll_VC, FALSE, TRUE, 0); + + + gtk_widget_show (HScroll_VC); + gtk_widget_show (VBox_V); + gtk_widget_show (Window); + + //Event_Selected_Hook(control_flow_data, &ev_sel); + + gtk_main (); + + g_critical("main loop finished"); + + //h_guievents_destructor(ListViewer); + + //g_critical("GuiEvents Destructor finished"); + destroy(); + + return 0; +} + + + +void add_test_process(ControlFlowData *control_flow_data) +{ + GtkTreeIter iter; + int i; + gchar *process[] = { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" }; + + for(i=0; inumber_of_process; i++) + { + /* Add a new row to the model */ + gtk_list_store_append (control_flow_data->list_store, &iter); + gtk_list_store_set ( control_flow_data->list_store, &iter, + PROCESS_COLUMN, process[i], + -1); + } + +} + + + + + + +void test_draw(ControlFlowData *control_flow_data) +{ + /* Draw event states using available height, Number of process, cell height + * (don't forget to remove two pixels at beginning and end). + * For horizontal : use width, Time_Begin, Time_End. + * This function calls the reading library to get the draw_hook called + * for the desired period of time. */ + + drawingAreaInfo *drawing_Area_Info = &control_flow_data->drawing_Area_Info; + + +} + +#ifdef DEBUG +void test_draw() { + gint cell_height = get_cell_height(GTK_TREE_VIEW(control_flow_data->process_list_widget)); + GdkGC *GC = gdk_gc_new(widget->window); + GdkColor color = CF_Colors[GREEN]; + + gdk_color_alloc (gdk_colormap_get_system () , &color); + + g_critical("expose"); + + /* When redrawing, use widget->allocation.width to get the width of + * drawable area. */ + control_flow_data->drawing_Area_Info.width = widget->allocation.width; + + test_draw(control_flow_data); + + gdk_gc_copy(GC,widget->style->black_gc); + gdk_gc_set_foreground(GC,&color); + + //gdk_draw_arc (widget->window, + // widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + // TRUE, + // //0, 0, widget->allocation.width, widget->allocation.height, + // 0, 0, widget->allocation.width, + // control_flow_data->drawing_Area_Info.height, + // 0, 64 * 360); + + + //drawing_Area_Init(control_flow_data); + + // 2 pixels for the box around the drawing area, 1 pixel for off-by-one + // (starting from 0) + //gdk_gc_copy (&GC, widget->style->fg_gc[GTK_WIDGET_STATE (widget)]); + + gdk_gc_set_line_attributes(GC,12, GDK_LINE_SOLID, GDK_CAP_NOT_LAST,GDK_JOIN_MITER); + + gdk_draw_line (widget->window, + GC, + 0, (cell_height-1)/2, + widget->allocation.width, (cell_height-1)/2); + + color = CF_Colors[BLUE]; + + gdk_color_alloc (gdk_colormap_get_system () , &color); + + gdk_gc_set_foreground(GC,&color); + + + gdk_gc_set_line_attributes(GC,3, GDK_LINE_SOLID, GDK_CAP_NOT_LAST,GDK_JOIN_MITER); + + gdk_draw_line (widget->window, + GC, + 0, (cell_height-1)/2, + widget->allocation.width,(cell_height-1)/2); + + + + + + + g_object_unref(GC); + + //gdk_colormap_alloc_colors(gdk_colormap_get_system(), TRUE, + + //gdk_gc_set_line_attributes(GC,5, GDK_LINE_SOLID, GDK_CAP_NOT_LAST,GDK_JOIN_MITER); + //gdk_gc_set_foreground(GC, + + //gdk_draw_line (widget->window, + // GC, + // 0, (2*cell_height)-2-1, + // 50, (2*cell_height)-2-1); + +} +#endif //DEBUG + + +/* Event_Hook.c tests */ + +void test_draw_item(Drawing_t *drawing, + GdkPixmap *pixmap) +{ + PropertiesIcon properties_icon; + DrawContext draw_context; + + DrawInfo current, previous; + ItemInfo over, middle, under, modify_over, modify_middle, modify_under; + + int i=0,j=0; + + //for(i=0; i<1024;i=i+15) + { + // for(j=0;j<768;j=j+15) + { + over.x = i; + over.y = j; + + current.modify_over = &over; + + draw_context.drawable = pixmap; + draw_context.gc = drawing->drawing_area->style->black_gc; + + draw_context.current = ¤t; + draw_context.previous = NULL; + + properties_icon.icon_name = g_new(char, MAX_PATH_LEN); + strncpy(properties_icon.icon_name, + "/home/compudj/local/share/LinuxTraceToolkit/pixmaps/mini-display.xpm", + MAX_PATH_LEN); + properties_icon.width = -1; + properties_icon.height = -1; + properties_icon.position = OVER; + draw_icon(&properties_icon, &draw_context); + g_free(properties_icon.icon_name); + } + } + +} + +#ifdef NOTUSE +/* NOTE : no drawing data should be sent there, since the drawing widget + * has not been initialized */ +void send_test_drawing(ProcessList *process_list, + Drawing_t *drawing, + GdkPixmap *pixmap, + gint x, gint y, // y not used here? + gint width, + gint height) // height won't be used here ? +{ + int i,j; + ProcessInfo Process_Info = {10000, 12000, 55600}; + //ProcessInfo Process_Info = {156, 14000, 55500}; + GtkTreeRowReference *row_ref; + PangoContext *context; + PangoLayout *layout; + PangoFontDescription *FontDesc;// = pango_font_description_new(); + gint Font_Size; + + //icon + //GdkBitmap *mask = g_new(GdkBitmap, 1); + //GdkPixmap *icon_pixmap = g_new(GdkPixmap, 1); + GdkGC * gc; + // rectangle + GdkColor color = { 0, 0xffff, 0x0000, 0x0000 }; + + gc = gdk_gc_new(pixmap); + /* Sent text data */ + layout = gtk_widget_create_pango_layout(drawing->drawing_area, + NULL); + context = pango_layout_get_context(layout); + FontDesc = pango_context_get_font_description(context); + Font_Size = pango_font_description_get_size(FontDesc); + pango_font_description_set_size(FontDesc, Font_Size-3*PANGO_SCALE); + + + + + LttTime birth; + birth.tv_sec = 12000; + birth.tv_nsec = 55500; + g_info("we have : x : %u, y : %u, width : %u, height : %u", x, y, width, height); + processlist_get_process_pixels(process_list, + 1, + &birth, + &y, + &height); + + g_info("we draw : x : %u, y : %u, width : %u, height : %u", x, y, width, height); + drawing_draw_line( + drawing, pixmap, x, + y+(height/2), x + width, y+(height/2), + drawing->drawing_area->style->black_gc); + + pango_layout_set_text(layout, "Test", -1); + gdk_draw_layout(pixmap, drawing->drawing_area->style->black_gc, + 0, y+height, layout); + + birth.tv_sec = 14000; + birth.tv_nsec = 55500; + + processlist_get_process_pixels(process_list, + 156, + &birth, + &y, + &height); + + + drawing_draw_line( + drawing, pixmap, x, + y+(height/2), x + width, y+(height/2), + drawing->drawing_area->style->black_gc); + + g_info("y : %u, height : %u", y, height); + + + + birth.tv_sec = 12000; + birth.tv_nsec = 55700; + + processlist_get_process_pixels(process_list, + 10, + &birth, + &y, + &height); + + /* Draw rectangle (background color) */ + gdk_gc_copy(gc, drawing->drawing_area->style->black_gc); + gdk_gc_set_rgb_fg_color(gc, &color); + gdk_draw_rectangle(pixmap, gc, + TRUE, + x, y, width, height); + + drawing_draw_line( + drawing, pixmap, x, + y+(height/2), x + width, y+(height/2), + drawing->drawing_area->style->black_gc); + + + /* Draw arc */ + gdk_draw_arc(pixmap, drawing->drawing_area->style->black_gc, + TRUE, 100, y, height/2, height/2, 0, 360*64); + + g_info("y : %u, height : %u", y, height); + + for(i=0; i<10; i++) + { + birth.tv_sec = i*12000; + birth.tv_nsec = i*55700; + + processlist_get_process_pixels(process_list, + i, + &birth, + &y, + &height); + + + drawing_draw_line( + drawing, pixmap, x, + y+(height/2), x + width, y+(height/2), + drawing->drawing_area->style->black_gc); + + g_critical("y : %u, height : %u", y, height); + + } + + birth.tv_sec = 12000; + birth.tv_nsec = 55600; + + processlist_get_process_pixels(process_list, + 10, + &birth, + &y, + &height); + + + drawing_draw_line( + drawing, pixmap, x, + y+(height/2), x + width, y+(height/2), + drawing->drawing_area->style->black_gc); + + g_info("y : %u, height : %u", y, height); + + + /* IMPORTANT : This action uses the cpu heavily! */ + //icon_pixmap = gdk_pixmap_create_from_xpm(pixmap, &mask, NULL, +// "/home/compudj/local/share/LinuxTraceToolkit/pixmaps/move_message.xpm"); + // "/home/compudj/local/share/LinuxTraceToolkit/pixmaps/mini-display.xpm"); + + // gdk_gc_set_clip_mask(drawing->drawing_area->style->black_gc, mask); + +// for(i=x;idrawing_area->style->black_gc); +// gdk_gc_set_clip_origin(drawing->drawing_area->style->black_gc, i, j); +// gdk_draw_drawable(pixmap, +// drawing->drawing_area->style->black_gc, +// icon_pixmap, +// 0, 0, i, j, -1, -1); + +// } +// } + + test_draw_item(drawing,pixmap); + + //gdk_gc_set_clip_origin(drawing->drawing_area->style->black_gc, 0, 0); + //gdk_gc_set_clip_mask(drawing->drawing_area->style->black_gc, NULL); + + //g_free(icon_pixmap); + //g_free(mask); + + + + + + + pango_font_description_set_size(FontDesc, Font_Size); + g_object_unref(layout); + g_free(gc); +} + +void send_test_process(ProcessList *process_list, Drawing_t *drawing) +{ + guint height, y; + int i; + ProcessInfo Process_Info = {10000, 12000, 55600}; + //ProcessInfo Process_Info = {156, 14000, 55500}; + GtkTreeRowReference *row_ref; + + LttTime birth; + + if(process_list->Test_Process_Sent) return; + + birth.tv_sec = 12000; + birth.tv_nsec = 55500; + + processlist_add(process_list, + 1, + &birth, + &y); + processlist_get_process_pixels(process_list, + 1, + &birth, + &y, + &height); + drawing_insert_square( drawing, y, height); + + //g_critical("y : %u, height : %u", y, height); + + birth.tv_sec = 14000; + birth.tv_nsec = 55500; + + processlist_add(process_list, + 156, + &birth, + &y); + processlist_get_process_pixels(process_list, + 156, + &birth, + &y, + &height); + drawing_insert_square( drawing, y, height); + + //g_critical("y : %u, height : %u", y, height); + + birth.tv_sec = 12000; + birth.tv_nsec = 55700; + + processlist_add(process_list, + 10, + &birth, + &height); + processlist_get_process_pixels(process_list, + 10, + &birth, + &y, + &height); + drawing_insert_square( drawing, y, height); + + //g_critical("y : %u, height : %u", y, height); + + //drawing_insert_square( drawing, height, 5); + + for(i=0; i<10; i++) + { + birth.tv_sec = i*12000; + birth.tv_nsec = i*55700; + + processlist_add(process_list, + i, + &birth, + &height); + processlist_get_process_pixels(process_list, + i, + &birth, + &y, + &height); + drawing_insert_square( drawing, y, height); + + // g_critical("y : %u, height : %u", y, height); + + } + //g_critical("height : %u", height); + + birth.tv_sec = 12000; + birth.tv_nsec = 55600; + + processlist_add(process_list, + 10, + &birth, + &y); + processlist_get_process_pixels(process_list, + 10, + &birth, + &y, + &height); + drawing_insert_square( drawing, y, height); + + //g_critical("y : %u, height : %u", y, height); + + processlist_add(process_list, + 10000, + &birth, + &height); + processlist_get_process_pixels(process_list, + 10000, + &birth, + &y, + &height); + drawing_insert_square( drawing, y, height); + + //g_critical("y : %u, height : %u", y, height); + + //drawing_insert_square( drawing, height, 5); + //g_critical("height : %u", height); + + + processlist_get_process_pixels(process_list, + 10000, + &birth, + &y, &height); + processlist_remove( process_list, + 10000, + &birth); + + drawing_remove_square( drawing, y, height); + + if(row_ref = + (GtkTreeRowReference*)g_hash_table_lookup( + process_list->process_hash, + &Process_Info)) + { + g_critical("key found"); + g_critical("position in the list : %s", + gtk_tree_path_to_string ( + gtk_tree_row_reference_get_path( + (GtkTreeRowReference*)row_ref) + )); + + } + + process_list->Test_Process_Sent = TRUE; + +} +#endif//NOTUSE + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/detailedevents/Makefile.am b/tags/lttv-0.11.3-23102008/lttv/modules/gui/detailedevents/Makefile.am new file mode 100644 index 00000000..20cfacef --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/detailedevents/Makefile.am @@ -0,0 +1,41 @@ +# This file is part of the Linux Trace Toolkit viewer +# Copyright (C) 2003-2004 Mathieu Desnoyers +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License Version 2 as +# published by the Free Software Foundation; +# +# 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. + + + +# +# Makefile for LTT New generation user interface : plugins. +# +# Created by Mathieu Desnoyers on May 6, 2003 +# + +AM_CFLAGS = $(GLIB_CFLAGS) +AM_CFLAGS += $(GTK_CFLAGS) +AM_CFLAGS += -fvisibility=hidden +LIBS += $(GLIB_LIBS) +LIBS += $(GTK_LIBS) -L${top_builddir}/lttv/modules/gui/lttvwindow/lttvwindow -llttvwindow + +libdir = ${lttvplugindir} + +lib_LTLIBRARIES = libguievents.la +libguievents_la_LDFLAGS = -module -avoid-version +libguievents_la_SOURCES = events.c lttv_plugin_evd.c + +noinst_HEADERS = lttv_plugin_evd.h events.h + +EXTRA_DIST = \ + hGuiEventsInsert.xpm diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/detailedevents/events.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/detailedevents/events.c new file mode 100644 index 00000000..f1cca3c7 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/detailedevents/events.c @@ -0,0 +1,1945 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers and XangXiu Yang + * 2005 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + + +//*! \defgroup GuiEvents libGuiEvents: The GUI Events display plugin */ +/*\@{*/ + +/*! \file GuiEvents.c + * \brief Graphical plugin for showing events. + * + * This plugin lists all the events contained in the current time interval + * in a list. + * + * This plugin adds a Events Viewer functionnality to Linux TraceToolkit + * GUI when this plugin is loaded. The init and destroy functions add the + * viewer's insertion menu item and toolbar icon by calling viewer.h's + * API functions. Then, when a viewer's object is created, the constructor + * creates ans register through API functions what is needed to interact + * with the lttvwindow. + * + * Authors : Mathieu Desnoyers and XangXiu Yang, June to December 2003 + * Inspired from original LTT, made by Karim Yaghmour + * + * Mostly rewritten by Mathieu Desnoyers, August 2005. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lttv_plugin_evd.h" + +#include "events.h" +#include "hGuiEventsInsert.xpm" + +#define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format) + +#ifndef g_debug +#define g_debug(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format) +#endif + +#define abs(a) (((a)<0)?(-a):(a)) +#define max(a,b) ((a)>(b)?(a):(b)) +#define min(a,b) ((a)<(b)?(a):(b)) + +/** Array containing instanced objects. Used when module is unloaded */ +static GSList *g_event_viewer_data_list = NULL ; + +typedef enum _ScrollDirection{ + SCROLL_STEP_UP, + SCROLL_STEP_DOWN, + SCROLL_PAGE_UP, + SCROLL_PAGE_DOWN, + SCROLL_JUMP, + SCROLL_NONE +} ScrollDirection; + +/** hook functions for update time interval, current time ... */ +gboolean update_current_time(void * hook_data, void * call_data); +gboolean update_current_position(void * hook_data, void * call_data); +//gboolean show_event_detail(void * hook_data, void * call_data); +gboolean traceset_changed(void * hook_data, void * call_data); +gboolean filter_changed(void * hook_data, void * call_data); + +static void request_background_data(EventViewerData *event_viewer_data); + +//! Event Viewer's constructor hook +GtkWidget *h_gui_events(LttvPlugin *plugin); +//! Event Viewer's constructor +EventViewerData *gui_events(LttvPluginTab *ptab); +//! Event Viewer's destructor +void gui_events_destructor(gpointer data); +void gui_events_free(gpointer data); + +static gboolean +header_size_allocate(GtkWidget *widget, + GtkAllocation *allocation, + gpointer user_data); + +void tree_v_set_cursor(EventViewerData *event_viewer_data); +void tree_v_get_cursor(EventViewerData *event_viewer_data); + +/* Prototype for selection handler callback */ +static void tree_selection_changed_cb (GtkTreeSelection *selection, + gpointer data); +static void v_scroll_cb (GtkAdjustment *adjustment, gpointer data); +static void tree_v_size_allocate_cb (GtkWidget *widget, GtkAllocation *alloc, + gpointer data); +static void tree_v_size_request_cb (GtkWidget *widget, + GtkRequisition *requisition, gpointer data); +static void tree_v_cursor_changed_cb (GtkWidget *widget, gpointer data); +static void tree_v_move_cursor_cb (GtkWidget *widget, GtkMovementStep arg1, + gint arg2, gpointer data); +static void filter_button (GtkToolButton *toolbutton, + gpointer user_data); +static gboolean tree_v_scroll_handler (GtkWidget *widget, GdkEventScroll *event, gpointer data); +static gboolean key_handler(GtkWidget *widget, GdkEventKey *event, + gpointer user_data); + +static void get_events(double time, EventViewerData *event_viewer_data); + +int event_hook(void *hook_data, void *call_data); + +/* Enumeration of the columns */ +enum +{ + TRACE_NAME_COLUMN, + TRACEFILE_NAME_COLUMN, + CPUID_COLUMN, + EVENT_COLUMN, + TIME_S_COLUMN, + TIME_NS_COLUMN, + PID_COLUMN, + EVENT_DESCR_COLUMN, + POSITION_COLUMN, + N_COLUMNS +}; + +/** + * Event Viewer's constructor hook + * + * This constructor is given as a parameter to the menuitem and toolbar button + * registration. It creates the list. + * @param parent_window A pointer to the parent window. + * @return The widget created. + */ +GtkWidget * +h_gui_events(LttvPlugin *plugin) +{ + LttvPluginTab *ptab = LTTV_PLUGIN_TAB(plugin); + EventViewerData* event_viewer_data = gui_events(ptab) ; + if(event_viewer_data) + return event_viewer_data->top_widget; + else return NULL; + +} + +/** + * Event Viewer's constructor + * + * This constructor is used to create EventViewerData data structure. + * @return The Event viewer data created. + */ +EventViewerData * +gui_events(LttvPluginTab *ptab) +{ + LttTime end; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + EventViewerData* event_viewer_data = g_new(EventViewerData,1); + LttvPluginEVD *plugin_evd = g_object_new(LTTV_TYPE_PLUGIN_EVD, NULL); + GtkTooltips *tooltips = gtk_tooltips_new(); + plugin_evd->evd = event_viewer_data; + Tab *tab = ptab->tab; + event_viewer_data->tab = tab; + event_viewer_data->ptab = ptab; + GtkWidget *tmp_toolbar_icon; + + LttvTracesetContext * tsc = + lttvwindow_get_traceset_context(event_viewer_data->tab); + + + event_viewer_data->event_hooks = lttv_hooks_new(); + lttv_hooks_add(event_viewer_data->event_hooks, + event_hook, + event_viewer_data, + LTTV_PRIO_DEFAULT); + + lttvwindow_register_current_time_notify(tab, + update_current_time,event_viewer_data); + lttvwindow_register_current_position_notify(tab, + update_current_position,event_viewer_data); + lttvwindow_register_traceset_notify(tab, + traceset_changed,event_viewer_data); + lttvwindow_register_filter_notify(tab, + filter_changed, event_viewer_data); + lttvwindow_register_redraw_notify(tab, + evd_redraw_notify, event_viewer_data); + + + event_viewer_data->scroll_win = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (event_viewer_data->scroll_win); + gtk_scrolled_window_set_policy( + GTK_SCROLLED_WINDOW(event_viewer_data->scroll_win), + GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); + + event_viewer_data->currently_selected_position = + lttv_traceset_context_position_new(tsc); + event_viewer_data->first_event = + lttv_traceset_context_position_new(tsc); + event_viewer_data->last_event = + lttv_traceset_context_position_new(tsc); + + event_viewer_data->main_win_filter = lttvwindow_get_filter(tab); + + event_viewer_data->update_cursor = TRUE; + event_viewer_data->report_position = TRUE; + + event_viewer_data->last_tree_update_time = 0; + + event_viewer_data->init_done = 0; + + /* Create a model for storing the data list */ + event_viewer_data->store_m = gtk_list_store_new ( + N_COLUMNS, /* Total number of columns */ + G_TYPE_STRING, /* Trace name */ + G_TYPE_STRING, /* Tracefile name */ + G_TYPE_UINT, /* CPUID */ + G_TYPE_STRING, /* Event */ + G_TYPE_UINT, /* Time s */ + G_TYPE_UINT, /* Time ns */ + G_TYPE_INT, /* PID */ + G_TYPE_STRING, /* Event's description */ + G_TYPE_POINTER);/* Position (not shown) */ + + event_viewer_data->pos = g_ptr_array_sized_new(10); + + /* Create the viewer widget for the columned list */ + event_viewer_data->tree_v = + gtk_tree_view_new_with_model (GTK_TREE_MODEL (event_viewer_data->store_m)); + + g_signal_connect (G_OBJECT (event_viewer_data->tree_v), "size-allocate", + G_CALLBACK (tree_v_size_allocate_cb), + event_viewer_data); + g_signal_connect (G_OBJECT (event_viewer_data->tree_v), "size-request", + G_CALLBACK (tree_v_size_request_cb), + event_viewer_data); + + g_signal_connect (G_OBJECT (event_viewer_data->tree_v), "cursor-changed", + G_CALLBACK (tree_v_cursor_changed_cb), + event_viewer_data); + + g_signal_connect (G_OBJECT (event_viewer_data->tree_v), "move-cursor", + G_CALLBACK (tree_v_move_cursor_cb), + event_viewer_data); + + g_signal_connect (G_OBJECT(event_viewer_data->tree_v), "key-press-event", + G_CALLBACK(key_handler), + event_viewer_data); + + g_signal_connect (G_OBJECT(event_viewer_data->tree_v), "scroll-event", + G_CALLBACK(tree_v_scroll_handler), + event_viewer_data); + + + + // Use on each column! + //gtk_tree_view_column_set_sizing(event_viewer_data->tree_v, + //GTK_TREE_VIEW_COLUMN_FIXED); + + /* The view now holds a reference. We can get rid of our own + * reference */ + g_object_unref (G_OBJECT (event_viewer_data->store_m)); + + + /* Create a column, associating the "text" attribute of the + * cell_renderer to the first column of the model */ + /* Columns alignment : 0.0 : Left 0.5 : Center 1.0 : Right */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Trace", + renderer, + "text", TRACE_NAME_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_fixed_width (column, 120); + gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->tree_v), + column); + + event_viewer_data->button = column->button; + + g_signal_connect (G_OBJECT(event_viewer_data->button), + "size-allocate", + G_CALLBACK(header_size_allocate), + (gpointer)event_viewer_data); + + + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Tracefile", + renderer, + "text", TRACEFILE_NAME_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_fixed_width (column, 120); + gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->tree_v), + column); + + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("CPUID", + renderer, + "text", CPUID_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_fixed_width (column, 45); + gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->tree_v), + column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Event", + renderer, + "text", EVENT_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_fixed_width (column, 120); + gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->tree_v), + column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Time (s)", + renderer, + "text", TIME_S_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 1.0); + gtk_tree_view_column_set_fixed_width (column, 120); + gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->tree_v), + column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Time (ns)", + renderer, + "text", TIME_NS_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 1.0); + gtk_tree_view_column_set_fixed_width (column, 120); + gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->tree_v), + column); + + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("PID", + renderer, + "text", PID_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 1.0); + gtk_tree_view_column_set_fixed_width (column, 45); + gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->tree_v), + column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Event Description", + renderer, + "text", EVENT_DESCR_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->tree_v), + column); + + + /* Setup the selection handler */ + event_viewer_data->select_c = + gtk_tree_view_get_selection (GTK_TREE_VIEW (event_viewer_data->tree_v)); + gtk_tree_selection_set_mode (event_viewer_data->select_c, + GTK_SELECTION_SINGLE); + g_signal_connect (G_OBJECT (event_viewer_data->select_c), "changed", + G_CALLBACK (tree_selection_changed_cb), + event_viewer_data); + + gtk_container_add (GTK_CONTAINER (event_viewer_data->scroll_win), + event_viewer_data->tree_v); + + event_viewer_data->hbox_v = gtk_hbox_new(0, 0); + event_viewer_data->top_widget = event_viewer_data->hbox_v; + plugin_evd->parent.top_widget = event_viewer_data->hbox_v; + + event_viewer_data->toolbar = gtk_toolbar_new(); + gtk_toolbar_set_orientation(GTK_TOOLBAR(event_viewer_data->toolbar), + GTK_ORIENTATION_VERTICAL); + + tmp_toolbar_icon = create_pixmap (main_window_get_widget(tab), + "guifilter16x16.png"); + gtk_widget_show(tmp_toolbar_icon); + event_viewer_data->button_filter = gtk_tool_button_new(tmp_toolbar_icon, + "Filter"); + g_signal_connect (G_OBJECT(event_viewer_data->button_filter), + "clicked", + G_CALLBACK (filter_button), + (gpointer)plugin_evd); + gtk_tool_item_set_tooltip(GTK_TOOL_ITEM(event_viewer_data->button_filter), + tooltips, "Open the filter window", NULL); + gtk_toolbar_insert(GTK_TOOLBAR(event_viewer_data->toolbar), + event_viewer_data->button_filter, + 0); + gtk_toolbar_set_style(GTK_TOOLBAR(event_viewer_data->toolbar), + GTK_TOOLBAR_ICONS); + gtk_box_pack_start(GTK_BOX(event_viewer_data->hbox_v), + event_viewer_data->toolbar, FALSE, FALSE, 0); + event_viewer_data->filter = NULL; + + gtk_box_pack_start(GTK_BOX(event_viewer_data->hbox_v), + event_viewer_data->scroll_win, TRUE, TRUE, 0); + + gtk_container_set_border_width(GTK_CONTAINER(event_viewer_data->hbox_v), 1); + + /* Create vertical scrollbar and pack it */ + event_viewer_data->vscroll_vc = gtk_vscrollbar_new(NULL); + gtk_range_set_update_policy (GTK_RANGE(event_viewer_data->vscroll_vc), + GTK_UPDATE_CONTINUOUS); + // Changed by MD : more user friendly :) + //GTK_UPDATE_DISCONTINUOUS); + gtk_box_pack_start(GTK_BOX(event_viewer_data->hbox_v), + event_viewer_data->vscroll_vc, FALSE, TRUE, 0); + + /* Get the vertical scrollbar's adjustment */ + event_viewer_data->vadjust_c = + gtk_range_get_adjustment(GTK_RANGE(event_viewer_data->vscroll_vc)); + event_viewer_data->vtree_adjust_c = gtk_tree_view_get_vadjustment( + GTK_TREE_VIEW (event_viewer_data->tree_v)); + + g_signal_connect (G_OBJECT (event_viewer_data->vadjust_c), "value-changed", + G_CALLBACK (v_scroll_cb), + event_viewer_data); + /* Set the upper bound to the last event number */ + event_viewer_data->previous_value = 0; + event_viewer_data->vadjust_c->lower = 0.0; + //event_viewer_data->vadjust_c->upper = event_viewer_data->number_of_events; + LttTime time = lttvwindow_get_current_time(tab); + time = ltt_time_sub(time, tsc->time_span.start_time); + event_viewer_data->vadjust_c->value = ltt_time_to_double(time); + event_viewer_data->vadjust_c->value = 0.0; + event_viewer_data->vadjust_c->step_increment = 1.0; + event_viewer_data->vadjust_c->page_increment = 2.0; + // event_viewer_data->vtree_adjust_c->upper; + event_viewer_data->vadjust_c->page_size = 2.0; + // event_viewer_data->vtree_adjust_c->upper; + /* Raw event trace */ + gtk_widget_show(GTK_WIDGET(event_viewer_data->button_filter)); + gtk_widget_show(event_viewer_data->toolbar); + gtk_widget_show(event_viewer_data->hbox_v); + gtk_widget_show(event_viewer_data->tree_v); + gtk_widget_show(event_viewer_data->vscroll_vc); + + /* Add the object's information to the module's array */ + g_event_viewer_data_list = g_slist_append(g_event_viewer_data_list, + plugin_evd); + + event_viewer_data->num_visible_events = 1; + + //get the life span of the traceset and set the upper of the scroll bar + + TimeInterval time_span = tsc->time_span; + end = ltt_time_sub(time_span.end_time, time_span.start_time); + + event_viewer_data->vadjust_c->upper = + ltt_time_to_double(end); + + /* Set the Selected Event */ + // tree_v_set_cursor(event_viewer_data); + + // event_viewer_data->current_time_updated = FALSE; + // + g_object_set_data_full( + G_OBJECT(event_viewer_data->hbox_v), + "plugin_data", + plugin_evd, + (GDestroyNotify)gui_events_free); + + g_object_set_data( + G_OBJECT(event_viewer_data->hbox_v), + "event_viewer_data", + event_viewer_data); + + event_viewer_data->background_info_waiting = 0; + + + request_background_data(event_viewer_data); + + return event_viewer_data; +} + + + +static gint background_ready(void *hook_data, void *call_data) +{ + EventViewerData *event_viewer_data = (EventViewerData *)hook_data; + LttvTrace *trace = (LttvTrace*)call_data; + + event_viewer_data->background_info_waiting--; + + if(event_viewer_data->background_info_waiting == 0) { + g_message("event viewer : background computation data ready."); + + evd_redraw_notify(event_viewer_data, NULL); + } + + return 0; +} + + +static void request_background_data(EventViewerData *event_viewer_data) +{ + LttvTracesetContext * tsc = + lttvwindow_get_traceset_context(event_viewer_data->tab); + gint num_traces = lttv_traceset_number(tsc->ts); + gint i; + LttvTrace *trace; + LttvTraceState *tstate; + + LttvHooks *background_ready_hook = + lttv_hooks_new(); + lttv_hooks_add(background_ready_hook, background_ready, event_viewer_data, + LTTV_PRIO_DEFAULT); + event_viewer_data->background_info_waiting = 0; + + for(i=0;its, i); + tstate = LTTV_TRACE_STATE(tsc->traces[i]); + + if(lttvwindowtraces_get_ready(g_quark_from_string("state"),trace)==FALSE + && !tstate->has_precomputed_states) { + + if(lttvwindowtraces_get_in_progress(g_quark_from_string("state"), + trace) == FALSE) { + /* We first remove requests that could have been done for the same + * information. Happens when two viewers ask for it before servicing + * starts. + */ + if(!lttvwindowtraces_background_request_find(trace, "state")) + lttvwindowtraces_background_request_queue( + main_window_get_widget(event_viewer_data->tab), trace, "state"); + lttvwindowtraces_background_notify_queue(event_viewer_data, + trace, + ltt_time_infinite, + NULL, + background_ready_hook); + event_viewer_data->background_info_waiting++; + } else { /* in progress */ + + lttvwindowtraces_background_notify_current(event_viewer_data, + trace, + ltt_time_infinite, + NULL, + background_ready_hook); + event_viewer_data->background_info_waiting++; + } + } else { + /* Data ready. By its nature, this viewer doesn't need to have + * its data ready hook called there, because a background + * request is always linked with a redraw. + */ + } + + } + + lttv_hooks_destroy(background_ready_hook); + +} + +static gboolean +header_size_allocate(GtkWidget *widget, + GtkAllocation *allocation, + gpointer user_data) +{ + EventViewerData *event_viewer_data = (EventViewerData*)user_data; + + event_viewer_data->header_height = allocation->height; + + return 0; +} + + +void tree_v_set_cursor(EventViewerData *event_viewer_data) +{ + GtkTreePath *path; + + g_debug("set cursor cb"); + +#if 0 + if(event_viewer_data->currently_selected_event != -1) + { + path = gtk_tree_path_new_from_indices( + event_viewer_data->currently_selected_event, + -1); + + gtk_tree_view_set_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), + path, NULL, FALSE); + gtk_tree_path_free(path); + } +#endif //0 +} + +void tree_v_get_cursor(EventViewerData *event_viewer_data) +{ + GtkTreePath *path; + gint *indices; + + g_debug("get cursor cb"); + + +#if 0 + gtk_tree_view_get_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), + &path, NULL); + indices = gtk_tree_path_get_indices(path); + + if(indices != NULL) + event_viewer_data->currently_selected_event = indices[0]; + else + event_viewer_data->currently_selected_event = -1; + + gtk_tree_path_free(path); +#endif //0 +} + +/* Filter out the key repeats that come too fast */ +static gboolean key_handler(GtkWidget *widget, GdkEventKey *event, + gpointer user_data) +{ + EventViewerData *evd = (EventViewerData *)user_data; + + g_debug("event time : %u , last time : %u", event->time, + evd->last_tree_update_time); + + if(guint32_before(event->time, evd->last_tree_update_time)) + return TRUE; + else + return FALSE; +} + +void tree_v_move_cursor_cb (GtkWidget *widget, + GtkMovementStep arg1, + gint arg2, + gpointer data) +{ + GtkTreePath *path; // = gtk_tree_path_new(); + gint *indices; + gdouble value; + EventViewerData *event_viewer_data = (EventViewerData*)data; + + g_debug("move cursor cb"); + //gtk_tree_view_get_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), + // &path, NULL); + //if(path == NULL) + //{ + /* No prior cursor, put it at beginning of page + * and let the execution do */ + // path = gtk_tree_path_new_from_indices(0, -1); + // gtk_tree_view_set_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), + // path, NULL, FALSE); + //} + + //indices = gtk_tree_path_get_indices(path); + + //value = gtk_adjustment_get_value(event_viewer_data->vadjust_c); + + /* If events request pending, do nothing*/ + if(lttvwindow_events_request_pending(event_viewer_data->tab)) return; + + /* If no prior position... */ +#if 0 + if(ltt_time_compare( + lttv_traceset_context_position_get_time( + event_viewer_data->currently_selected_position), + ltt_time_infinite) == 0) { + + path = gtk_tree_path_new_from_indices(0, -1); + gtk_tree_view_set_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), + path, NULL, FALSE); + + gtk_tree_path_free(path); + return; + + } +#endif //0 + + g_debug("tree view move cursor : arg1 is %u and arg2 is %d", + (guint)arg1, arg2); + + switch(arg1) { + case GTK_MOVEMENT_DISPLAY_LINES: + if(arg2 == 1) { + /* Move one line down */ + if(event_viewer_data->pos->len > 0) { + LttvTracesetContextPosition *end_pos = + (LttvTracesetContextPosition*)g_ptr_array_index( + event_viewer_data->pos, + event_viewer_data->pos->len-1); + if(lttv_traceset_context_pos_pos_compare(end_pos, + event_viewer_data->currently_selected_position) == 0) { + /* Must get down one event and select the last one */ + gtk_tree_selection_unselect_all(gtk_tree_view_get_selection( + GTK_TREE_VIEW(event_viewer_data->tree_v))); + event_viewer_data->update_cursor = FALSE; + gtk_adjustment_set_value(event_viewer_data->vadjust_c, + gtk_adjustment_get_value(event_viewer_data->vadjust_c) + 1); + event_viewer_data->update_cursor = TRUE; + if(event_viewer_data->pos->len > 0) { + path = gtk_tree_path_new_from_indices( + max(0, event_viewer_data->pos->len - 1), -1); + if(path) { + gtk_tree_view_set_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), + path, NULL, FALSE); + gtk_tree_path_free(path); + } + } + } + } else { + /* Must get down one event and select the last one */ + gtk_tree_selection_unselect_all(gtk_tree_view_get_selection( + GTK_TREE_VIEW(event_viewer_data->tree_v))); + event_viewer_data->update_cursor = FALSE; + gtk_adjustment_set_value(event_viewer_data->vadjust_c, + gtk_adjustment_get_value(event_viewer_data->vadjust_c) + 1); + event_viewer_data->update_cursor = TRUE; + if(event_viewer_data->pos->len > 0) { + path = gtk_tree_path_new_from_indices( + max(0, event_viewer_data->pos->len - 1), -1); + if(path) { + gtk_tree_view_set_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), + path, NULL, FALSE); + gtk_tree_path_free(path); + } + } + } + + } else { + if(event_viewer_data->pos->len > 0) { + /* Move one line up */ + LttvTracesetContextPosition *begin_pos = + (LttvTracesetContextPosition*)g_ptr_array_index( + event_viewer_data->pos, + 0); + if(lttv_traceset_context_pos_pos_compare(begin_pos, + event_viewer_data->currently_selected_position) == 0) { + /* Must get up one event and select the first one */ + gtk_tree_selection_unselect_all(gtk_tree_view_get_selection( + GTK_TREE_VIEW(event_viewer_data->tree_v))); + event_viewer_data->update_cursor = FALSE; + gtk_adjustment_set_value(event_viewer_data->vadjust_c, + gtk_adjustment_get_value(event_viewer_data->vadjust_c) - 1); + event_viewer_data->update_cursor = TRUE; + if(event_viewer_data->pos->len > 0) { + path = gtk_tree_path_new_from_indices( + 0, -1); + if(path) { + gtk_tree_view_set_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), + path, NULL, FALSE); + gtk_tree_path_free(path); + } + } + } + } else { + /* Must get up one event and select the first one */ + gtk_tree_selection_unselect_all(gtk_tree_view_get_selection( + GTK_TREE_VIEW(event_viewer_data->tree_v))); + event_viewer_data->update_cursor = FALSE; + gtk_adjustment_set_value(event_viewer_data->vadjust_c, + gtk_adjustment_get_value(event_viewer_data->vadjust_c) - 1); + event_viewer_data->update_cursor = TRUE; + if(event_viewer_data->pos->len > 0) { + path = gtk_tree_path_new_from_indices( + 0, -1); + if(path) { + gtk_tree_view_set_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), + path, NULL, FALSE); + gtk_tree_path_free(path); + } + } + } + } + break; + case GTK_MOVEMENT_PAGES: + if(arg2 == 1) { + /* Move one page down */ + if(event_viewer_data->pos->len > 0) { + LttvTracesetContextPosition *end_pos = + (LttvTracesetContextPosition*)g_ptr_array_index( + event_viewer_data->pos, + event_viewer_data->pos->len-1); + if(lttv_traceset_context_pos_pos_compare(end_pos, + event_viewer_data->currently_selected_position) == 0) { + /* Must get down one page and select the last one */ + gtk_tree_selection_unselect_all(gtk_tree_view_get_selection( + GTK_TREE_VIEW(event_viewer_data->tree_v))); + event_viewer_data->update_cursor = FALSE; + gtk_adjustment_set_value(event_viewer_data->vadjust_c, + gtk_adjustment_get_value(event_viewer_data->vadjust_c) + 2); + event_viewer_data->update_cursor = TRUE; + if(event_viewer_data->pos->len > 0) { + path = gtk_tree_path_new_from_indices( + event_viewer_data->pos->len - 1, -1); + if(path) { + gtk_tree_view_set_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), + path, NULL, FALSE); + gtk_tree_path_free(path); + } + } + } + } else { + /* Must get down one page and select the last one */ + gtk_tree_selection_unselect_all(gtk_tree_view_get_selection( + GTK_TREE_VIEW(event_viewer_data->tree_v))); + event_viewer_data->update_cursor = FALSE; + gtk_adjustment_set_value(event_viewer_data->vadjust_c, + gtk_adjustment_get_value(event_viewer_data->vadjust_c) + 2); + event_viewer_data->update_cursor = TRUE; + if(event_viewer_data->pos->len > 0) { + path = gtk_tree_path_new_from_indices( + event_viewer_data->pos->len - 1, -1); + if(path) { + gtk_tree_view_set_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), + path, NULL, FALSE); + gtk_tree_path_free(path); + } + } + } + } else { + /* Move one page up */ + if(event_viewer_data->pos->len > 0) { + LttvTracesetContextPosition *begin_pos = + (LttvTracesetContextPosition*)g_ptr_array_index( + event_viewer_data->pos, + 0); + if(lttv_traceset_context_pos_pos_compare(begin_pos, + event_viewer_data->currently_selected_position) == 0) { + /* Must get up one page and select the first one */ + gtk_tree_selection_unselect_all(gtk_tree_view_get_selection( + GTK_TREE_VIEW(event_viewer_data->tree_v))); + event_viewer_data->update_cursor = FALSE; + gtk_adjustment_set_value(event_viewer_data->vadjust_c, + gtk_adjustment_get_value(event_viewer_data->vadjust_c) - 2); + event_viewer_data->update_cursor = TRUE; + if(event_viewer_data->pos->len > 0) { + path = gtk_tree_path_new_from_indices( + 0, -1); + if(path) { + gtk_tree_view_set_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), + path, NULL, FALSE); + gtk_tree_path_free(path); + } + } + } + } else { + /* Must get up one page and select the first one */ + gtk_tree_selection_unselect_all(gtk_tree_view_get_selection( + GTK_TREE_VIEW(event_viewer_data->tree_v))); + event_viewer_data->update_cursor = FALSE; + gtk_adjustment_set_value(event_viewer_data->vadjust_c, + gtk_adjustment_get_value(event_viewer_data->vadjust_c) - 2); + event_viewer_data->update_cursor = TRUE; + if(event_viewer_data->pos->len > 0) { + path = gtk_tree_path_new_from_indices( + 0, -1); + if(path) { + gtk_tree_view_set_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), + path, NULL, FALSE); + gtk_tree_path_free(path); + } + } + } + } + break; + default: + break; + } + + //gtk_tree_path_free(path); + +#if 0 + if(arg1 == GTK_MOVEMENT_DISPLAY_LINES) + { + /* Move one line */ + if(arg2 == 1) + { + /* move one line down */ + if(indices[0]) // Do we need an empty field here (before first)? + { + if(value + event_viewer_data->num_visible_events <= + event_viewer_data->number_of_events -1) + { + event_viewer_data->currently_selected_event += 1; + // gtk_adjustment_set_value(event_viewer_data->vadjust_c, value+1); + //gtk_tree_path_free(path); + //path = gtk_tree_path_new_from_indices(event_viewer_data->num_visible_events-1, -1); + //gtk_tree_view_set_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), path, NULL, FALSE); + g_signal_stop_emission_by_name(G_OBJECT(widget), "move-cursor"); + } + } + } else { + /* Move one line up */ + if(indices[0] == 0) + { + if(value - 1 >= 0 ) + { + event_viewer_data->currently_selected_event -= 1; + // gtk_adjustment_set_value(event_viewer_data->vadjust_c, value-1); + //gtk_tree_path_free(path); + //path = gtk_tree_path_new_from_indices(0, -1); + //gtk_tree_view_set_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), path, NULL, FALSE); + g_signal_stop_emission_by_name(G_OBJECT(widget), "move-cursor"); + } + } + } + } + + if(arg1 == GTK_MOVEMENT_PAGES) + { + /* Move one page */ + if(arg2 == 1) + { + if(event_viewer_data->num_visible_events == 1) + value += 1 ; + /* move one page down */ + if(value + event_viewer_data->num_visible_events-1 <= + event_viewer_data->number_of_events ) + { + event_viewer_data->currently_selected_event += + event_viewer_data->num_visible_events-1; + // gtk_adjustment_set_value(event_viewer_data->vadjust_c, + // value+(event_viewer_data->num_visible_events-1)); + //gtk_tree_path_free(path); + //path = gtk_tree_path_new_from_indices(0, -1); + //gtk_tree_view_set_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), path, NULL, FALSE); + g_signal_stop_emission_by_name(G_OBJECT(widget), "move-cursor"); + } + } else { + /* Move one page up */ + if(event_viewer_data->num_visible_events == 1) + value -= 1 ; + + if(indices[0] < event_viewer_data->num_visible_events - 2 ) + { + if(value - (event_viewer_data->num_visible_events-1) >= 0) + { + event_viewer_data->currently_selected_event -= + event_viewer_data->num_visible_events-1; + + // gtk_adjustment_set_value(event_viewer_data->vadjust_c, + // value-(event_viewer_data->num_visible_events-1)); + //gtk_tree_path_free(path); + //path = gtk_tree_path_new_from_indices(0, -1); + //gtk_tree_view_set_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), path, NULL, FALSE); + g_signal_stop_emission_by_name(G_OBJECT(widget), "move-cursor"); + + } else { + /* Go to first Event */ + event_viewer_data->currently_selected_event == 0 ; + // gtk_adjustment_set_value(event_viewer_data->vadjust_c, + // 0); + //gtk_tree_path_free(path); + //path = gtk_tree_path_new_from_indices(0, -1); + //gtk_tree_view_set_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), path, NULL, FALSE); + g_signal_stop_emission_by_name(G_OBJECT(widget), "move-cursor"); + + } + } + } + } + + if(arg1 == GTK_MOVEMENT_BUFFER_ENDS) + { + /* Move to the ends of the buffer */ + if(arg2 == 1) + { + /* move end of buffer */ + event_viewer_data->currently_selected_event = + event_viewer_data->number_of_events-1 ; + // gtk_adjustment_set_value(event_viewer_data->vadjust_c, + // event_viewer_data->number_of_events - + // event_viewer_data->num_visible_events); + //gtk_tree_path_free(path); + //path = gtk_tree_path_new_from_indices(event_viewer_data->num_visible_events-1, -1); + //gtk_tree_view_set_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), path, NULL, FALSE); + g_signal_stop_emission_by_name(G_OBJECT(widget), "move-cursor"); + } else { + /* Move beginning of buffer */ + event_viewer_data->currently_selected_event = 0 ; + // gtk_adjustment_set_value(event_viewer_data->vadjust_c, 0); + //gtk_tree_path_free(path); + //path = gtk_tree_path_new_from_indices(0, -1); + //gtk_tree_view_set_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), path, NULL, FALSE); + g_signal_stop_emission_by_name(G_OBJECT(widget), "move-cursor"); + } + } +#endif //0 +} + +static void filter_button (GtkToolButton *toolbutton, + gpointer user_data) +{ + LttvPluginEVD *plugin_evd = (LttvPluginEVD*)user_data; + LttvAttribute *attribute; + LttvAttributeValue value; + gboolean ret; + g_printf("Filter button clicked\n"); + + attribute = LTTV_ATTRIBUTE(lttv_iattribute_find_subdir( + LTTV_IATTRIBUTE(lttv_global_attributes()), + LTTV_VIEWER_CONSTRUCTORS)); + g_assert(attribute); + + ret = lttv_iattribute_find_by_path(LTTV_IATTRIBUTE(attribute), + "guifilter", LTTV_POINTER, &value); + g_assert(ret); + lttvwindow_viewer_constructor constructor = + (lttvwindow_viewer_constructor)*(value.v_pointer); + if(constructor) constructor(&plugin_evd->parent); + else g_warning("Filter module not loaded."); + + //FIXME : viewer returned. +} + +gboolean tree_v_scroll_handler (GtkWidget *widget, GdkEventScroll *event, gpointer data) +{ + EventViewerData *event_viewer_data = (EventViewerData*) data; + Tab *tab = event_viewer_data->tab; + + switch(event->direction) { + case GDK_SCROLL_UP: + gtk_adjustment_set_value(event_viewer_data->vadjust_c, + gtk_adjustment_get_value(event_viewer_data->vadjust_c) - 1); + break; + case GDK_SCROLL_DOWN: + gtk_adjustment_set_value(event_viewer_data->vadjust_c, + gtk_adjustment_get_value(event_viewer_data->vadjust_c) + 1); + break; + default: + g_error("Only scroll up and down expected"); + } + return TRUE; +} + +void tree_v_cursor_changed_cb (GtkWidget *widget, gpointer data) +{ + EventViewerData *event_viewer_data = (EventViewerData*) data; + Tab *tab = event_viewer_data->tab; + GtkTreeIter iter; + GtkTreeModel* model = GTK_TREE_MODEL(event_viewer_data->store_m); + GtkTreePath *path; + LttvTracesetContextPosition *pos; + + g_debug("cursor changed cb"); + + /* On cursor change, modify the currently selected event by calling + * the right API function */ + if(event_viewer_data->report_position) { + if(event_viewer_data->pos->len > 0) { + gtk_tree_view_get_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), + &path, NULL); + if(path) { + if(gtk_tree_model_get_iter(model,&iter,path)){ + gtk_tree_model_get(model, &iter, POSITION_COLUMN, &pos, -1); + + if(lttv_traceset_context_pos_pos_compare(pos, + event_viewer_data->currently_selected_position) != 0) + lttvwindow_report_current_position(tab, pos); + }else{ + g_warning("Can not get iter\n"); + } + gtk_tree_path_free(path); + } + } + } +} + + +static void tree_selection_changed_cb (GtkTreeSelection *selection, + gpointer data) +{ + g_debug("tree sel changed cb"); + EventViewerData *event_viewer_data = (EventViewerData*) data; + +#if 0 + /* Set the cursor to currently selected event */ + GtkTreeModel* model = GTK_TREE_MODEL(event_viewer_data->store_m); + GtkTreeIter iter; + LttvTracesetContextPosition *pos; + guint i; + GtkTreePath *tree_path; + + for(i=0;inum_visible_events;i++) { + tree_path = gtk_tree_path_new_from_indices( + i, + -1); + if(gtk_tree_model_get_iter(model,&iter,tree_path)){ + gtk_tree_model_get(model, &iter, POSITION_COLUMN, &pos, -1); + + if(lttv_traceset_context_pos_pos_compare(pos, + event_viewer_data->currently_selected_position) == 0) { + /* Match! */ + gtk_tree_view_set_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), + tree_path, NULL, FALSE); + break; + } + + }else{ + g_warning("Can not get iter\n"); + } + gtk_tree_path_free(tree_path); + } +#endif //0 +} + +#if 0 +static gint key_snooper(GtkWidget *grab_widget, GdkEventKey *event, + gpointer func_data) +{ + return TRUE; +} +#endif //0 + +/* This callback may be recalled after a step up/down, but we don't want to lose + * the exact position : what we do is that we only set the value if it has + * changed : a step up/down that doesn't change the time value of the first + * event won't trigger a scrollbar change. */ + +void v_scroll_cb (GtkAdjustment *adjustment, gpointer data) +{ + EventViewerData *event_viewer_data = (EventViewerData*)data; + LttvTracesetStats *tss = + lttvwindow_get_traceset_stats(event_viewer_data->tab); + LttvTracesetContext *tsc = (LttvTracesetContext*)tss; + g_debug("SCROLL begin"); + g_debug("SCROLL values : %g , %g, %g", + adjustment->value, event_viewer_data->previous_value, + (adjustment->value - event_viewer_data->previous_value)); + + LttTime new_time_off = ltt_time_from_double(adjustment->value); + LttTime old_time_off = ltt_time_from_double(event_viewer_data->previous_value); + g_debug("SCROLL time values %lu.%lu, %lu.%lu", new_time_off.tv_sec, + new_time_off.tv_nsec, old_time_off.tv_sec, old_time_off.tv_nsec); + /* If same value : nothing to update */ + if(ltt_time_compare(new_time_off, old_time_off) == 0) + return; + + //LttTime old_time = event_viewer_data->first_event; + + + //gint snoop = gtk_key_snooper_install(key_snooper, NULL); + + get_events(adjustment->value, event_viewer_data); + + //gtk_key_snooper_remove(snoop); +#if 0 + LttTime time = ltt_time_sub(event_viewer_data->first_event, + tsc->time_span.start_time); + double value = ltt_time_to_double(time); + gtk_adjustment_set_value(event_viewer_data->vadjust_c, value); + + if(event_viewer_data->currently_selected_event != -1) { + + tree_path = gtk_tree_path_new_from_indices( + event_viewer_data->currently_selected_event, + -1); + + // gtk_tree_view_set_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), tree_path, + // NULL, FALSE); + gtk_tree_path_free(tree_path); + } +#endif //0 + g_debug("SCROLL end"); +} + +static __inline gint get_cell_height(GtkTreeView *TreeView) +{ + gint height; + GtkTreeViewColumn *column = gtk_tree_view_get_column(TreeView, 0); + + gtk_tree_view_column_cell_get_size(column, NULL, NULL, NULL, NULL, &height); + + gint vertical_separator; + gtk_widget_style_get (GTK_WIDGET (TreeView), + "vertical-separator", &vertical_separator, + NULL); + + height += vertical_separator; + + return height; +} + +void tree_v_size_allocate_cb (GtkWidget *widget, GtkAllocation *alloc, gpointer data) +{ + EventViewerData *event_viewer_data = (EventViewerData*)data; + gint cell_height = get_cell_height(GTK_TREE_VIEW(event_viewer_data->tree_v)); + gint last_num_visible_events = event_viewer_data->num_visible_events; + gdouble exact_num_visible; + + exact_num_visible = ( alloc->height - + event_viewer_data->header_height ) + / (double)cell_height ; + + event_viewer_data->num_visible_events = ceil(exact_num_visible) ; + +/* + event_viewer_data->vadjust_c->page_increment = + floor(exact_num_visible); + event_viewer_data->vadjust_c->page_size = + floor(exact_num_visible); +*/ + + g_debug("size allocate %p : last_num_visible_events : %d", + event_viewer_data, last_num_visible_events); + g_debug("num_visible_events : %d, value %lu", + event_viewer_data->num_visible_events, + event_viewer_data->vadjust_c->value); + + if(event_viewer_data->num_visible_events != last_num_visible_events) { + get_events(event_viewer_data->vadjust_c->value, event_viewer_data); + } + + +} + +void tree_v_size_request_cb (GtkWidget *widget, GtkRequisition *requisition, gpointer data) +{ + gint h; + EventViewerData *event_viewer_data = (EventViewerData*)data; + gint cell_height = get_cell_height(GTK_TREE_VIEW(event_viewer_data->tree_v)); + + h = cell_height + event_viewer_data->header_height; + requisition->height = h; + +} + +#if 0 +gboolean show_event_detail(void * hook_data, void * call_data) +{ + EventViewerData *event_viewer_data = (EventViewerData*) hook_data; + LttvTracesetContext * tsc = lttvwindow_get_traceset_context(event_viewer_data->tab); + + if(event_viewer_data->event_fields_queue_tmp->length == 0 && + event_viewer_data->event_fields_queue->length == 0){ + event_viewer_data->shown = FALSE; + return FALSE; + } + + if(event_viewer_data->shown == FALSE){ + event_viewer_data->shown = TRUE; + update_raw_data_array(event_viewer_data, + event_viewer_data->event_fields_queue_tmp->length); + + get_data(event_viewer_data->vadjust_c->value, + event_viewer_data->num_visible_events, + event_viewer_data); + + remove_context_hooks(event_viewer_data,tsc); + } + + return FALSE; +} +#endif //0 + +static gboolean events_check_handler(guint count, gboolean *stop_flag, + gpointer data) +{ + EventViewerData *evd = (EventViewerData*)data; + if(count % CHECK_GDK_INTERVAL == 0) { + GdkEvent *event; + GtkWidget *widget; + while((event = gdk_event_get()) != NULL) { + widget = gtk_get_event_widget(event); + if(widget == + lookup_widget(main_window_get_widget(evd->tab), + "StopProcessingButton") + || widget == evd->vscroll_vc) { + gtk_main_do_event(event); + gdk_window_process_all_updates(); + } + gdk_event_free(event); + } + if(*stop_flag) + return TRUE; + else + return FALSE; + } else return FALSE; +} + +static void get_events(double new_value, EventViewerData *event_viewer_data) +{ + GtkTreePath *tree_path; + LttvTracesetStats *tss = + lttvwindow_get_traceset_stats(event_viewer_data->tab); + LttvTracesetContext *tsc = (LttvTracesetContext*)tss; + guint i; + gboolean seek_by_time; + + if(lttvwindow_preempt_count > 0) return; + + double value = new_value - event_viewer_data->previous_value; + + /* Set stop button status for foreground processing */ + event_viewer_data->tab->stop_foreground = FALSE; + lttvwindow_events_request_disable(); + + /* See where we have to scroll... */ + ScrollDirection direction; + gint relative_position; + + if(value < -0.8) { + if(value >= -1.0) direction = SCROLL_STEP_UP; + else { + if(value >= -2.0) direction = SCROLL_PAGE_UP; + else direction = SCROLL_JUMP; + } + } else if(value > 0.8) { + if(value <= 1.0) direction = SCROLL_STEP_DOWN; + else { + if(value <= 2.0) direction = SCROLL_PAGE_DOWN; + else direction = SCROLL_JUMP; + } + } else direction = SCROLL_NONE; /* 0.0 */ + + + switch(direction) { + case SCROLL_STEP_UP: + g_debug("get_events : SCROLL_STEP_UP"); + relative_position = -1; + seek_by_time = 0; + break; + case SCROLL_STEP_DOWN: + g_debug("get_events : SCROLL_STEP_DOWN"); + relative_position = 1; + seek_by_time = 0; + break; + case SCROLL_PAGE_UP: + g_debug("get_events : SCROLL_PAGE_UP"); + relative_position = -(event_viewer_data->num_visible_events); + seek_by_time = 0; + break; + case SCROLL_PAGE_DOWN: + g_debug("get_events : SCROLL_PAGE_DOWN"); + relative_position = event_viewer_data->num_visible_events; + seek_by_time = 0; + break; + case SCROLL_JUMP: + g_debug("get_events : SCROLL_JUMP"); + seek_by_time = 1; + break; + case SCROLL_NONE: + g_debug("get_events : SCROLL_NONE"); + relative_position = 0; + seek_by_time = 0; + break; + } + + LttTime time = ltt_time_from_double(new_value); + time = ltt_time_add(tsc->time_span.start_time, time); + + if(!seek_by_time) { + + LttvTracesetContextPosition *pos = + lttv_traceset_context_position_new(tsc); + + /* Remember the beginning position */ + if(event_viewer_data->pos->len > 0) { + LttvTracesetContextPosition *first_pos = + (LttvTracesetContextPosition*)g_ptr_array_index( + event_viewer_data->pos, + 0); + lttv_traceset_context_position_copy(pos, first_pos); + + if(relative_position >= 0) { + LttTime first_event_time = + lttv_traceset_context_position_get_time( + pos); + lttv_state_traceset_seek_time_closest((LttvTracesetState*)tsc, + first_event_time); + lttv_process_traceset_middle(tsc, ltt_time_infinite, + G_MAXUINT, + pos); + + } else if(relative_position < 0) { + g_assert(lttv_process_traceset_seek_position(tsc, pos) == 0); + } + } else { + /* There is nothing in the list : simply seek to the time value. */ + lttv_state_traceset_seek_time_closest((LttvTracesetState*)tsc, + time); + lttv_process_traceset_middle(tsc, time, G_MAXUINT, + NULL); + } + + /* Note that, as we mess with the tsc position, this function CANNOT be called + * from a hook inside the lttv_process_traceset_middle. */ + /* As the lttvwindow API keeps a sync_position inside the tsc to go back at + * the right spot after being interrupted, it's ok to change the tsc position, + * as long as we do not touch the sync_position. */ + + /* Get the beginning position of the read (with seek backward or seek forward) + */ + if(relative_position > 0) { + guint count; + count += lttv_process_traceset_seek_n_forward(tsc, relative_position, + events_check_handler, + &event_viewer_data->tab->stop_foreground, + event_viewer_data->main_win_filter, + event_viewer_data->filter, NULL, event_viewer_data); + } else if(relative_position < 0) { + guint count; + + /* Get an idea of currently shown event dispersion */ + LttTime first_event_time = + lttv_traceset_context_position_get_time(event_viewer_data->first_event); + LttTime last_event_time = + lttv_traceset_context_position_get_time(event_viewer_data->last_event); + LttTime time_diff = ltt_time_sub(last_event_time, first_event_time); + if(ltt_time_compare(time_diff, ltt_time_zero) == 0) + time_diff = seek_back_default_offset; + + count = lttv_process_traceset_seek_n_backward(tsc, + abs(relative_position), + time_diff, + (seek_time_fct)lttv_state_traceset_seek_time_closest, + events_check_handler, + &event_viewer_data->tab->stop_foreground, + event_viewer_data->main_win_filter, + event_viewer_data->filter, NULL, event_viewer_data); + } /* else 0 : do nothing : we are already at the beginning position */ + + lttv_traceset_context_position_destroy(pos); + + /* Save the first event position */ + lttv_traceset_context_position_save(tsc, event_viewer_data->first_event); + + time = lttv_traceset_context_position_get_time( + event_viewer_data->first_event); + //if(ltt_time_compare(time, tsc->time_span.end_time) > 0) + // time = tsc->time_span.end_time; + + LttTime time_val = ltt_time_sub(time, + tsc->time_span.start_time); + event_viewer_data->previous_value = ltt_time_to_double(time_val); + + lttv_state_traceset_seek_time_closest((LttvTracesetState*)tsc, time); + lttv_process_traceset_middle(tsc, ltt_time_infinite, G_MAXUINT, + event_viewer_data->first_event); + + } else { + /* Seek by time */ + lttv_state_traceset_seek_time_closest((LttvTracesetState*)tsc, + time); + lttv_process_traceset_middle(tsc, time, G_MAXUINT, + NULL); + LttTime time_val = ltt_time_sub(time, + tsc->time_span.start_time); + event_viewer_data->previous_value = ltt_time_to_double(time_val); + lttv_traceset_context_position_save(tsc, event_viewer_data->first_event); + } + + /* Clear the model (don't forget to free the TCS positions!) */ + gtk_list_store_clear(event_viewer_data->store_m); + for(i=0;ipos->len;i++) { + LttvTracesetContextPosition *cur_pos = + (LttvTracesetContextPosition*)g_ptr_array_index(event_viewer_data->pos, + i); + lttv_traceset_context_position_destroy(cur_pos); + } + g_ptr_array_set_size(event_viewer_data->pos, 0); + + + /* Mathieu : + * I make the choice not to use the mainwindow lttvwindow API here : the idle + * loop might have a too low priority, and we want good update while + * scrolling. However, we call the gdk loop to get events periodically so the + * processing can be stopped. + */ + + lttv_process_traceset_begin(tsc, + NULL, NULL, NULL, event_viewer_data->event_hooks, NULL); + + event_viewer_data->num_events = 0; + + lttv_process_traceset_middle(tsc, ltt_time_infinite, G_MAXUINT, NULL); + + lttv_process_traceset_end(tsc, + NULL, NULL, NULL, event_viewer_data->event_hooks, NULL); + + /* Get the end position */ + if(event_viewer_data->pos->len > 0) { + LttvTracesetContextPosition *cur_pos = + (LttvTracesetContextPosition*)g_ptr_array_index(event_viewer_data->pos, + event_viewer_data->pos->len - 1); + lttv_traceset_context_position_copy(event_viewer_data->last_event, + cur_pos); + } else + lttv_traceset_context_position_save(tsc, event_viewer_data->last_event); + + gtk_adjustment_set_value(event_viewer_data->vadjust_c, + event_viewer_data->previous_value); + + //g_signal_emit_by_name(G_OBJECT (event_viewer_data->select_c), + // "changed"); + + event_viewer_data->last_tree_update_time = + gdk_x11_get_server_time( + gtk_widget_get_parent_window(event_viewer_data->tree_v)); + + lttvwindow_events_request_enable(); + + return; +} + + +int event_hook(void *hook_data, void *call_data) +{ + EventViewerData *event_viewer_data = (EventViewerData*)hook_data; + LttvTracefileContext *tfc = (LttvTracefileContext*)call_data; + LttvTracefileState *tfs = (LttvTracefileState*)call_data; + LttEvent *e = ltt_tracefile_get_event(tfc->tf); + + if(event_viewer_data->num_events % CHECK_GDK_INTERVAL == 0) { + GdkEvent *event; + GtkWidget *widget; + while((event = gdk_event_get()) != NULL) { + widget = gtk_get_event_widget(event); + if(widget == + lookup_widget(main_window_get_widget(event_viewer_data->tab), + "StopProcessingButton") + || widget == event_viewer_data->vscroll_vc) { + gtk_main_do_event(event); + gdk_window_process_all_updates(); + } + gdk_event_free(event); + } + //gtk_main_iteration_do(FALSE); + if(event_viewer_data->tab->stop_foreground) + return TRUE; + } + event_viewer_data->num_events++; + + LttvFilter *filter = event_viewer_data->main_win_filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + filter = event_viewer_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + +// LttFacility *facility = ltt_event_facility(e); +// LttEventType *event_type = ltt_event_eventtype(e); + LttTime time = ltt_event_time(e); + + guint cpu = tfs->cpu; + LttvTraceState *ts = (LttvTraceState*)tfc->t_context; + LttvProcessState *process = ts->running_process[cpu]; + + GtkTreeIter iter; + + GString *desc = g_string_new(""); + + LttvTracesetContextPosition *pos = + lttv_traceset_context_position_new(tfc->t_context->ts_context); + + lttv_traceset_context_position_save(tfc->t_context->ts_context, pos); + + lttv_event_to_string(e, desc, TRUE, TRUE, (LttvTracefileState*)tfc); + + g_info("detail : %s", desc->str); + + gtk_list_store_append (event_viewer_data->store_m, &iter); + gtk_list_store_set (event_viewer_data->store_m, &iter, + TRACE_NAME_COLUMN, g_quark_to_string(ltt_trace_name(tfc->t_context->t)), + TRACEFILE_NAME_COLUMN, g_quark_to_string(ltt_tracefile_name(tfc->tf)), + CPUID_COLUMN, cpu, + EVENT_COLUMN, g_quark_to_string(marker_get_info_from_id(tfc->t_context->t, e->event_id)->name), + TIME_S_COLUMN, time.tv_sec, + TIME_NS_COLUMN, time.tv_nsec, + PID_COLUMN, process->pid, + EVENT_DESCR_COLUMN, desc->str, + POSITION_COLUMN, pos, + -1); + + g_ptr_array_add(event_viewer_data->pos, pos); + + g_string_free(desc, TRUE); + + if(event_viewer_data->update_cursor) { + if(lttv_traceset_context_pos_pos_compare(pos, + event_viewer_data->currently_selected_position) == 0) { + GtkTreePath *path = gtk_tree_path_new_from_indices( + event_viewer_data->pos->len - 1, -1); + if(path) { + gtk_tree_view_set_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), + path, NULL, FALSE); + gtk_tree_path_free(path); + } + } + } + + if(event_viewer_data->pos->len >= event_viewer_data->num_visible_events ) + return TRUE; + else + return FALSE; +} + + + +static void event_update_selection(EventViewerData *event_viewer_data) +{ + guint i; + GPtrArray *positions = event_viewer_data->pos; + g_info("event_update_selection"); + + for(i=0;ilen;i++) { + LttvTracesetContextPosition *cur_pos = + (LttvTracesetContextPosition*)g_ptr_array_index(positions, i); + if(lttv_traceset_context_pos_pos_compare(cur_pos, + event_viewer_data->currently_selected_position) == 0) { + GtkTreePath *path = gtk_tree_path_new_from_indices(i, -1); + if(path) { + gtk_tree_view_set_cursor(GTK_TREE_VIEW(event_viewer_data->tree_v), + path, NULL, FALSE); + gtk_tree_path_free(path); + } + } + } +} + +static int current_time_get_first_event_hook(void *hook_data, void *call_data) +{ + EventViewerData *event_viewer_data = (EventViewerData*)hook_data; + LttvTracefileContext *tfc = (LttvTracefileContext*)call_data; + LttEvent *e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *filter = event_viewer_data->main_win_filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + filter = event_viewer_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + lttv_traceset_context_position_save(tfc->t_context->ts_context, + event_viewer_data->current_time_get_first); + return TRUE; +} + + +gboolean update_current_time(void * hook_data, void * call_data) +{ + g_info("update_current_time"); + EventViewerData *event_viewer_data = (EventViewerData*) hook_data; + const LttTime * current_time = (LttTime*)call_data; + LttvTracesetContext * tsc = + lttvwindow_get_traceset_context(event_viewer_data->tab); + GtkTreePath *path; + + /* If the currently selected event time != current time, set the first event + * with this time as currently selected. */ + LttTime pos_time = lttv_traceset_context_position_get_time( + event_viewer_data->currently_selected_position); + if(ltt_time_compare(pos_time, *current_time) != 0) { + + lttv_state_traceset_seek_time_closest((LttvTracesetState*)tsc, + *current_time); + lttv_process_traceset_middle(tsc, *current_time, G_MAXUINT, + NULL); + + /* Get the first event that passes in the filter */ + event_viewer_data->current_time_get_first = + lttv_traceset_context_position_new(tsc); + LttvHooks *hooks = lttv_hooks_new(); + lttv_hooks_add(hooks, + current_time_get_first_event_hook, + event_viewer_data, + LTTV_PRIO_DEFAULT); + + lttv_process_traceset_begin(tsc, + NULL, NULL, NULL, hooks, NULL); + + lttv_process_traceset_middle(tsc, ltt_time_infinite, G_MAXUINT, NULL); + + lttv_process_traceset_end(tsc, + NULL, NULL, NULL, hooks, NULL); + + lttv_hooks_destroy(hooks); + + lttv_traceset_context_position_copy( + event_viewer_data->currently_selected_position, + event_viewer_data->current_time_get_first); + lttv_traceset_context_position_destroy( + event_viewer_data->current_time_get_first); + pos_time = lttv_traceset_context_position_get_time( + event_viewer_data->currently_selected_position); + } + + LttTime time = ltt_time_sub(pos_time, tsc->time_span.start_time); + double new_value = ltt_time_to_double(time); + + event_viewer_data->report_position = FALSE; + /* Change the viewed area if does not match */ + if(lttv_traceset_context_pos_pos_compare( + event_viewer_data->currently_selected_position, + event_viewer_data->first_event) < 0 + || + lttv_traceset_context_pos_pos_compare( + event_viewer_data->currently_selected_position, + event_viewer_data->last_event) > 0) { + gtk_adjustment_set_value(event_viewer_data->vadjust_c, new_value); + } else { + /* Simply update the current time : it is in the list */ + event_update_selection(event_viewer_data); + } + event_viewer_data->report_position = TRUE; + + return FALSE; +} + +gboolean update_current_position(void * hook_data, void * call_data) +{ + g_info("update_current_position"); + EventViewerData *event_viewer_data = (EventViewerData*) hook_data; + const LttvTracesetContextPosition *current_pos = + (LttvTracesetContextPosition*)call_data; + LttvTracesetContext * tsc = + lttvwindow_get_traceset_context(event_viewer_data->tab); + + if(lttv_traceset_context_pos_pos_compare( + event_viewer_data->currently_selected_position, current_pos) != 0) { + lttv_traceset_context_position_copy( + event_viewer_data->currently_selected_position, current_pos); + + /* Change the viewed area if does not match */ + if(lttv_traceset_context_pos_pos_compare( + event_viewer_data->currently_selected_position, + event_viewer_data->first_event) < 0 + || + lttv_traceset_context_pos_pos_compare( + event_viewer_data->currently_selected_position, + event_viewer_data->last_event) > 0) { + LttTime time = lttv_traceset_context_position_get_time(current_pos); + time = ltt_time_sub(time, tsc->time_span.start_time); + double new_value = ltt_time_to_double(time); + gtk_adjustment_set_value(event_viewer_data->vadjust_c, new_value); + } else { + /* Simply update the current time : it is in the list */ + event_update_selection(event_viewer_data); + } + + } + + + return FALSE; +} + + + +gboolean traceset_changed(void * hook_data, void * call_data) +{ + EventViewerData *event_viewer_data = (EventViewerData*) hook_data; + LttvTracesetContext * tsc = + lttvwindow_get_traceset_context(event_viewer_data->tab); + TimeInterval time_span = tsc->time_span; + + LttTime end; + gtk_list_store_clear(event_viewer_data->store_m); + g_ptr_array_set_size(event_viewer_data->pos, 0); + + end = ltt_time_sub(time_span.end_time, time_span.start_time); + event_viewer_data->vadjust_c->upper = ltt_time_to_double(end); + + /* Reset the positions */ + lttv_traceset_context_position_destroy( + event_viewer_data->currently_selected_position); + lttv_traceset_context_position_destroy( + event_viewer_data->first_event); + lttv_traceset_context_position_destroy( + event_viewer_data->last_event); + + event_viewer_data->currently_selected_position = + lttv_traceset_context_position_new(tsc); + event_viewer_data->first_event = + lttv_traceset_context_position_new(tsc); + event_viewer_data->last_event = + lttv_traceset_context_position_new(tsc); + + get_events(event_viewer_data->vadjust_c->value, event_viewer_data); + // event_viewer_data->vadjust_c->value = 0; + + request_background_data(event_viewer_data); + + return FALSE; +} + +gboolean filter_changed(void * hook_data, void * call_data) +{ + EventViewerData *event_viewer_data = (EventViewerData*) hook_data; + LttvTracesetContext * tsc = + lttvwindow_get_traceset_context(event_viewer_data->tab); + + event_viewer_data->main_win_filter = + (LttvFilter*)call_data; + get_events(event_viewer_data->vadjust_c->value, event_viewer_data); + + return FALSE; +} + + +gint evd_redraw_notify(void *hook_data, void *call_data) +{ + EventViewerData *event_viewer_data = (EventViewerData*) hook_data; + + get_events(event_viewer_data->vadjust_c->value, event_viewer_data); + return 0; +} + +void gui_events_free(gpointer data) +{ + LttvPluginEVD *plugin_evd = (LttvPluginEVD*)data; + Tab *tab = plugin_evd->evd->tab; + EventViewerData *event_viewer_data = plugin_evd->evd; + guint i; + + lttv_filter_destroy(plugin_evd->evd->filter); + + if(tab != NULL){ + lttv_hooks_remove(event_viewer_data->event_hooks,event_hook); + lttv_hooks_destroy(event_viewer_data->event_hooks); + + for(i=0;ipos->len;i++) { + LttvTracesetContextPosition *cur_pos = + (LttvTracesetContextPosition*)g_ptr_array_index(event_viewer_data->pos, + i); + lttv_traceset_context_position_destroy(cur_pos); + } + lttv_traceset_context_position_destroy( + event_viewer_data->currently_selected_position); + lttv_traceset_context_position_destroy( + event_viewer_data->first_event); + lttv_traceset_context_position_destroy( + event_viewer_data->last_event); + g_ptr_array_free(event_viewer_data->pos, TRUE); + + lttvwindow_unregister_current_time_notify(tab, + update_current_time, event_viewer_data); + lttvwindow_unregister_current_position_notify(tab, + update_current_position, event_viewer_data); + //lttvwindow_unregister_show_notify(tab, + // show_event_detail, event_viewer_data); + lttvwindow_unregister_traceset_notify(tab, + traceset_changed, event_viewer_data); + lttvwindow_unregister_filter_notify(tab, + filter_changed, event_viewer_data); + lttvwindow_unregister_redraw_notify(tab, + evd_redraw_notify, event_viewer_data); + + } + lttvwindowtraces_background_notify_remove(event_viewer_data); + + g_event_viewer_data_list = g_slist_remove(g_event_viewer_data_list, + event_viewer_data); + //g_free(event_viewer_data); + g_object_unref(plugin_evd); +} + + + +void gui_events_destructor(gpointer data) +{ + LttvPluginEVD *plugin_evd = (LttvPluginEVD*)data; + /* May already been done by GTK window closing */ + if(GTK_IS_WIDGET(plugin_evd->parent.top_widget)){ + gtk_widget_destroy(plugin_evd->parent.top_widget); + } +} + + + +/** + * plugin's init function + * + * This function initializes the Event Viewer functionnality through the + * gtkTraceSet API. + */ +static void init() { + + lttvwindow_register_constructor("guievents", + "/", + "Insert Event Viewer", + hGuiEventsInsert_xpm, + "Insert Event Viewer", + h_gui_events); +} + +void event_destroy_walk(gpointer data, gpointer user_data) +{ + gui_events_destructor((LttvPluginEVD*)data); +} + +/** + * plugin's destroy function + * + * This function releases the memory reserved by the module and unregisters + * everything that has been registered in the gtkTraceSet API. + */ +static void destroy() { + + g_slist_foreach(g_event_viewer_data_list, event_destroy_walk, NULL ); + g_slist_free(g_event_viewer_data_list); + + lttvwindow_unregister_constructor(h_gui_events); + +} + + + + +LTTV_MODULE("guievents", "Detailed events view", \ + "Graphical module to display a detailed event list", \ + init, destroy, "lttvwindow", "print") diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/detailedevents/events.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/detailedevents/events.h new file mode 100644 index 00000000..93b0df2e --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/detailedevents/events.h @@ -0,0 +1,74 @@ +#ifndef _EVENTS_H +#define _EVENTS_H + +#include +#include + +typedef struct _EventViewerData EventViewerData; + +struct _EventViewerData { + + Tab * tab; + LttvPluginTab *ptab; + LttvHooks * event_hooks; + + /* previous value is used to determine if it is a page up/down or + * step up/down, in which case we move of a certain amount of events (one or + * the number of events shown on the screen) instead of changing begin time. + */ + double previous_value; + + //scroll window containing Tree View + GtkWidget * scroll_win; + + /* Model containing list data */ + GtkListStore *store_m; + + GPtrArray *pos; /* Array of LttvTracesetContextPosition * */ + + GtkWidget *top_widget; + GtkWidget *hbox_v; + /* Widget to display the data in a columned list */ + GtkWidget *tree_v; + GtkAdjustment *vtree_adjust_c ; + GtkWidget *button; /* a button of the header, used to get the header_height */ + gint header_height; + + /* Vertical scrollbar and its adjustment */ + GtkWidget *vscroll_vc; + GtkAdjustment *vadjust_c; + + /* Selection handler */ + GtkTreeSelection *select_c; + + gint num_visible_events; + + LttvTracesetContextPosition *currently_selected_position; + gboolean update_cursor; /* Speed optimisation : do not update cursor when + unnecessary */ + gboolean report_position; /* do not report position when in current_time + update */ + + LttvTracesetContextPosition *first_event; /* Time of the first event shown */ + LttvTracesetContextPosition *last_event; /* Time of the first event shown */ + + LttvTracesetContextPosition *current_time_get_first; + + LttvFilter *main_win_filter; + + gint background_info_waiting; + + guint32 last_tree_update_time; /* To filter out repeat keys */ + + guint num_events; /* Number of events processed */ + + LttvFilter *filter; + GtkWidget *toolbar; + GtkToolItem *button_filter; + + guint init_done; +}; + +extern gint evd_redraw_notify(void *hook_data, void *call_data); + +#endif //EVENTS_H diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/detailedevents/hGuiEventsInsert.xpm b/tags/lttv-0.11.3-23102008/lttv/modules/gui/detailedevents/hGuiEventsInsert.xpm new file mode 100644 index 00000000..159209bf --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/detailedevents/hGuiEventsInsert.xpm @@ -0,0 +1,230 @@ +/* XPM */ +static char * hGuiEventsInsert_xpm[] = { +"22 22 205 2", +" c None", +". c #3995E5", +"+ c #449DE8", +"@ c #4BA0EA", +"# c #479BE7", +"$ c #4395E5", +"% c #3F8FE2", +"& c #3A89E0", +"* c #3683DD", +"= c #327DDB", +"- c #2D77D8", +"; c #2971D6", +"> c #246BD3", +", c #2065D1", +"' c #1B5FCE", +") c #1759CC", +"! c #1253C9", +"~ c #0E4DC7", +"{ c #0A47C4", +"] c #0742C2", +"^ c #053FBF", +"/ c #0238B2", +"( c #0032A4", +"_ c #8BCFFF", +": c #84C9FF", +"< c #7DC1FF", +"[ c #75BAFF", +"} c #6DB2FF", +"| c #65AAFF", +"1 c #5DA1FF", +"2 c #5499FF", +"3 c #4B90FF", +"4 c #4287FF", +"5 c #397FFF", +"6 c #3076FF", +"7 c #276DFF", +"8 c #1E64FF", +"9 c #155BFF", +"0 c #0C52FF", +"a c #044AFF", +"b c #0349FF", +"c c #0340F1", +"d c #0032AB", +"e c #4DA3EC", +"f c #84C8FF", +"g c #7EC2FF", +"h c #77BBFF", +"i c #70B4FF", +"j c #68ADFF", +"k c #60A5FF", +"l c #589DFF", +"m c #5095FF", +"n c #488DFF", +"o c #3F84FF", +"p c #367BFF", +"q c #2D73FF", +"r c #246AFF", +"s c #1C61FF", +"t c #1358FF", +"u c #0A4FFF", +"v c #0343F6", +"w c #0132B1", +"x c #5BA8EB", +"y c #BFE1FF", +"z c #BCDEFF", +"A c #B9DBFF", +"B c #B5D7FF", +"C c #B1D4FF", +"D c #AED0FF", +"E c #AACCFF", +"F c #A6C8FF", +"G c #A2C4FF", +"H c #9DC0FF", +"I c #99BCFF", +"J c #95B8FF", +"K c #90B3FF", +"L c #8CAFFF", +"M c #87AAFF", +"N c #82A5FE", +"O c #7FA2FE", +"P c #7EA1FD", +"Q c #7DA0FD", +"R c #7A9AF5", +"S c #1D48B3", +"T c #6AAFEB", +"U c #FFFFFF", +"V c #FEFEFF", +"W c #FDFDFE", +"X c #FBFBFC", +"Y c #F9F9FB", +"Z c #F7F7FA", +"` c #F6F6F9", +" . c #F0EFF4", +".. c #385EB5", +"+. c #69AEEA", +"@. c #F4F4F8", +"#. c #EEEDF3", +"$. c #385DB4", +"%. c #69ACE9", +"&. c #848484", +"*. c #7F7F7F", +"=. c #808080", +"-. c #7E7E7F", +";. c #7D7D7E", +">. c #7C7C7D", +",. c #7B7B7D", +"'. c #7B7B7C", +"). c #7A7A7C", +"!. c #F2F2F7", +"~. c #ECEBF2", +"{. c #375CB4", +"]. c #68AAE8", +"^. c #FBFBFD", +"/. c #F1F1F6", +"(. c #E9E9F0", +"_. c #365BB3", +":. c #67A9E7", +"<. c #F8F8FB", +"[. c #F3F3F7", +"}. c #EFEFF5", +"|. c #E7E7EE", +"1. c #355AB2", +"2. c #66A7E6", +"3. c #858585", +"4. c #7C7C7E", +"5. c #79797B", +"6. c #78787B", +"7. c #7B7B7E", +"8. c #EDEDF4", +"9. c #E5E5ED", +"0. c #3459B2", +"a. c #65A6E5", +"b. c #F9F9FC", +"c. c #ECECF3", +"d. c #E3E3EC", +"e. c #3358B1", +"f. c #64A5E4", +"g. c #F6F6FA", +"h. c #EAEAF2", +"i. c #E1E1EA", +"j. c #3358B0", +"k. c #63A3E3", +"l. c #8D8D8D", +"m. c #8A8A8C", +"n. c #77777A", +"o. c #76767A", +"p. c #767679", +"q. c #808085", +"r. c #E8E8F1", +"s. c #DFDFE9", +"t. c #3257B0", +"u. c #62A2E2", +"v. c #FAFAFC", +"w. c #F4F4F9", +"x. c #EEEEF4", +"y. c #E7E7F0", +"z. c #DEDDE8", +"A. c #62A0E1", +"B. c #E5E5EF", +"C. c #DCDCE7", +"D. c #619FE1", +"E. c #8A8A8B", +"F. c #87878A", +"G. c #757579", +"H. c #747478", +"I. c #737378", +"J. c #7E7E83", +"K. c #E3E3EE", +"L. c #DBDBE6", +"M. c #609DDF", +"N. c #F3F3F8", +"O. c #F1F1F7", +"P. c #E2E2ED", +"Q. c #DAD9E6", +"R. c #5E9ADE", +"S. c #FAFAFB", +"T. c #F9F9FA", +"U. c #F7F6F9", +"V. c #F5F4F7", +"W. c #F2F2F6", +"X. c #F1F0F5", +"Y. c #EFEEF3", +"Z. c #EDECF2", +"`. c #EBEAF1", +" + c #E9E8EF", +".+ c #E7E6EE", +"++ c #E4E4ED", +"@+ c #E3E2EB", +"#+ c #E1E0EA", +"$+ c #D4D3E0", +"%+ c #357FD4", +"&+ c #5791D7", +"*+ c #548CD4", +"=+ c #5188D1", +"-+ c #4E83CE", +";+ c #4B7ECA", +">+ c #487AC7", +",+ c #4575C4", +"'+ c #4271C1", +")+ c #406CBE", +"!+ c #3D68BB", +"~+ c #3A63B8", +"{+ c #375FB5", +"]+ c #345AB2", +"^+ c #0C3BA7", +" ", +". + @ # $ % & * = - ; > , ' ) ! ~ { ] ^ / ( ", +"+ _ : < [ } | 1 2 3 4 5 6 7 8 9 0 a b b c d ", +"e f g h i j k l m n o p q r s t u b b b v w ", +"x y z A B C D E F G H I J K L M N O P Q R S ", +"T U U U U U U U U U U U U U V W X Y Z ` ...", +"+.U U U U U U U U U U U U V W X Y Z ` @.#.$.", +"%.U U &.*.*.*.U U =.=.*.*.-.;.>.,.'.).!.~.{.", +"].U U U U U U U U U U V W ^.Y Z ` @.!./.(._.", +":.U U U U U U U U U V W ^.Y <.` @.[./.}.|.1.", +"2.U U 3.*.*.*.U U *.=.;.4.>.'.).5.6.7.8.9.0.", +"a.U U U U U U U U W ^.b.<.` @.[./.}.8.c.d.e.", +"f.U U U U U U U W ^.b.<.g.@.[./.}.8.c.h.i.j.", +"k.U U *.*.*.l.W ^.4.m.,.).5.6.n.o.p.q.r.s.t.", +"u.U U U U U W ^.v.<.g.w.[./.}.x.c.h.r.y.z.t.", +"A.U U U U W ^.v.<.g.w.[./.}.x.c.h.r.y.B.C.t.", +"D.U U *.-.;.E.<.g.).F.6.n.n.p.G.H.I.J.K.L.t.", +"M.U U W ^.v.<.g.w.N.O.}.x.c.h.r.y.B.K.P.Q.t.", +"R.S.T.U.V.W.X.Y.Z.`. +.+++@+#+s.z.C.L.Q.$+t.", +"%+&+*+=+-+;+>+,+'+)+!+~+{+]+t.t.t.t.t.t.t.^+", +" ", +" "}; diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/detailedevents/lttv_plugin_evd.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/detailedevents/lttv_plugin_evd.c new file mode 100644 index 00000000..ae93579a --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/detailedevents/lttv_plugin_evd.c @@ -0,0 +1,84 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2006 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 "lttv_plugin_evd.h" +#include +#include "events.h" + +/* + * forward definitions + */ + +/* + * Implementation + */ + +static void evd_update_filter(LttvPlugin *parent, LttvFilter *filter) +{ + LttvPluginEVD *self = LTTV_PLUGIN_EVD(parent); + g_message("In EVD update filter."); + lttv_filter_destroy(self->evd->filter); + self->evd->filter = filter; + evd_redraw_notify(self->evd, NULL); +} + + +static void +lttv_plugin_evd_class_init (LttvPluginEVDClass *klass) +{ + LttvPluginClass *parent_klass; + parent_klass = &klass->parent; + parent_klass->update_filter = evd_update_filter; + g_type_class_add_private (klass, sizeof (EventViewerData)); +} + + +static void +lttv_plugin_evd_init (GTypeInstance *instance, gpointer g_class) +{ + LttvPluginEVD *self = LTTV_PLUGIN_EVD (instance); + self->evd = G_TYPE_INSTANCE_GET_PRIVATE (self, + LTTV_TYPE_PLUGIN_EVD, EventViewerData); +} + + +GType +lttv_plugin_evd_get_type (void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (LttvPluginEVDClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + lttv_plugin_evd_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (LttvPluginEVD), + 0, /* n_preallocs */ + lttv_plugin_evd_init /* instance_init */ + }; + type = g_type_register_static (G_TYPE_OBJECT, + "LttvPluginEVDType", + &info, 0); + } + return type; +} + + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/detailedevents/lttv_plugin_evd.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/detailedevents/lttv_plugin_evd.h new file mode 100644 index 00000000..fe9eb100 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/detailedevents/lttv_plugin_evd.h @@ -0,0 +1,63 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2006 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 LTTV_PLUGIN_EVD_H +#define LTTV_PLUGIN_EVD_H + +#include +#include +#include "events.h" + +/* + * Type macros. + */ + +#define LTTV_TYPE_PLUGIN_EVD (lttv_plugin_evd_get_type ()) +#define LTTV_PLUGIN_EVD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LTTV_TYPE_PLUGIN_EVD, LttvPluginEVD)) +#define LTTV_PLUGIN_EVD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), LTTV_TYPE_PLUGIN_EVD, LttvPluginEVDClass)) +#define LTTV_IS_PLUGIN_EVD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LTTV_TYPE_PLUGIN_EVD)) +#define LTTV_IS_PLUGIN_EVD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), LTTV_TYPE_PLUGIN_EVD)) +#define LTTV_PLUGIN_EVD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), LTTV_TYPE_PLUGIN_EVD, LttvPluginEVDClass)) + +typedef struct _LttvPluginEVD LttvPluginEVD; +typedef struct _LttvPluginEVDClass LttvPluginEVDClass; + +struct _LttvPluginEVD { + LttvPlugin parent; + + /* instance members */ + EventViewerData *evd; + + /* private */ +}; + +struct _LttvPluginEVDClass { + LttvPluginClass parent; + + /* class members */ +}; + +/* used by LTTV_PLUGIN_TAB_TYPE */ +GType lttv_plugin_evd_get_type (void); + +/* + * Method definitions. + */ + + +#endif diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/diskperformance/Makefile.am b/tags/lttv-0.11.3-23102008/lttv/modules/gui/diskperformance/Makefile.am new file mode 100644 index 00000000..572c1740 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/diskperformance/Makefile.am @@ -0,0 +1,39 @@ +# This file is part of the Linux Trace Toolkit viewer +# Copyright (C) 2003-2004 Mathieu Desnoyers +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License Version 2 as +# published by the Free Software Foundation; +# +# 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. + + + +# +# Makefile for LTT New generation user interface : plugins. +# +# Created by Mathieu Desnoyers on May 6, 2003 +# + +AM_CFLAGS = $(GLIB_CFLAGS) +AM_CFLAGS += $(GTK_CFLAGS) +AM_CFLAGS += -fvisibility=hidden +LIBS += $(GLIB_LIBS) +LIBS += $(GTK_LIBS) -L${top_builddir}/lttv/modules/gui/lttvwindow/lttvwindow -llttvwindow + +libdir = ${lttvplugindir} + +lib_LTLIBRARIES = libdiskperformance.la +libdiskperformance_la_LDFLAGS = -module -avoid-version +libdiskperformance_la_SOURCES = diskperformance.c + +EXTRA_DIST = \ + hDiskPerformanceInsert.xpm diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/diskperformance/block.xml b/tags/lttv-0.11.3-23102008/lttv/modules/gui/diskperformance/block.xml new file mode 100644 index 00000000..c296bd89 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/diskperformance/block.xml @@ -0,0 +1,19 @@ + + block facility has events related to block read and block written. + + + block read for disk data + major number of the device + minor number of the device + number of bytes read + + + + Block write event + major number of the device + minor number of the device + number of bytes written + + + + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/diskperformance/diskperformance.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/diskperformance/diskperformance.c new file mode 100644 index 00000000..d09da7e6 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/diskperformance/diskperformance.c @@ -0,0 +1,1270 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2005 Peter Ho + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hDiskPerformanceInsert.xpm" + + +#define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format) +#define g_debug(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format) +// fixed #define TRACE_NUMBER 0 +#define NO_ITEMS 0 + +enum{ + DISKNAME_COLUMN, + BYTES_RD_COLUMN, + BYTES_RD_SEC_COLUMN, + NUM_RD_COLUMN, + BYTES_WR_COLUMN, + BYTES_WR_SEC_COLUMN, + NUM_WR_COLUMN, + N_COLUMNS +}; + +enum operation_t { + LTTV_READ_OPERATION = 1, + LTTV_WRITE_OPERATION +}; + +typedef struct _DiskPerformanceData { + + Tab * tab; + + LttvPluginTab *ptab; + + LttvHooks * hooks_trace_after; + + LttvHooks * hooks_trace_before; + /* time window */ + TimeWindow time_window; + + GtkWidget * scroll_win; + + /* Model containing list data */ + GtkListStore *store_m; + + GtkWidget *hbox_v; + + /* Widget to display the data in a columned list */ + GtkWidget *tree_v; + + /* Selection handler */ + GtkTreeSelection *select_c; + + GArray *disk_array; + + LttvHooksById * event_by_id_hooks; + +} DiskPerformanceData; + + +typedef struct _lttv_block { + guint major_number; + guint minor_number; + guint size; +} lttv_block; + +typedef struct _lttv_total_block { + char diskname[10]; + guint64 total_bytes_read; + guint num_read_operations; + guint64 total_bytes_written; + guint num_write_operations; + +} lttv_total_block; + +GSList *g_disk_data_list = NULL ; + + + +/* facility */ +GQuark LTT_FACILITY_BLOCK; + +/* events */ +GQuark LTT_EVENT_BLOCK_READ; +GQuark LTT_EVENT_BLOCK_WRITE; + +static DiskPerformanceData *disk_performance_data(LttvPluginTab *ptab); +static void disk_destroy_walk(gpointer data, gpointer user_data); +static gboolean disk_show(void *hook_data, void *call_data); +static gboolean trace_header(void *hook_data, void *call_data); +static gboolean disk_update_time_window(void * hook_data, void * call_data); +static void request_event( DiskPerformanceData *disk_performance); +void gui_disperformance_free(DiskPerformanceData *event_viewer_data); +static void get_event_detail(LttEvent *e, lttv_block* disk_data); +static char * major_minor_to_diskname( lttv_block* disk_data); +static void sum_data(char* diskname, guint size, enum operation_t opt, GArray *disk_array); +static GtkWidget *disk_performance(LttvPlugin *plugin); + +static gboolean block_read_callback(void *hook_data, void *call_data); + +static gboolean block_write_callback(void *hook_data, void *call_data); + + +static gboolean disk_show(void *hook_data, void *call_data){ + + guint i; + lttv_total_block element; + GtkTreeIter iter; + LttTime time_interval; + guint64 time_interval_64; + guint64 temp_variable; + guint64 bytes_read_per_sec, bytes_written_per_sec; + g_info(" diskperformance: disk_show() \n"); + DiskPerformanceData *disk_performance = (DiskPerformanceData *)hook_data; + GArray *disk_array = disk_performance->disk_array; + time_interval = ltt_time_sub(disk_performance->time_window.end_time, disk_performance->time_window.start_time); + + time_interval_64 = time_interval.tv_sec; + time_interval_64 *= NANOSECONDS_PER_SECOND; + time_interval_64 += time_interval.tv_nsec; + gtk_list_store_clear(disk_performance->store_m); + for(i = 0; i < disk_array->len; i++){ + + element = g_array_index(disk_array,lttv_total_block,i); + temp_variable = element.total_bytes_read * NANOSECONDS_PER_SECOND; + bytes_read_per_sec = (guint64) temp_variable / time_interval_64; + + temp_variable = element.total_bytes_written * NANOSECONDS_PER_SECOND; + bytes_written_per_sec = (guint64) temp_variable / time_interval_64; + + gtk_list_store_append (disk_performance->store_m, &iter); + gtk_list_store_set (disk_performance->store_m, &iter, + DISKNAME_COLUMN, element.diskname, + BYTES_RD_COLUMN, element.total_bytes_read, + BYTES_RD_SEC_COLUMN,bytes_read_per_sec, + NUM_RD_COLUMN, element.num_read_operations, + BYTES_WR_COLUMN, element.total_bytes_written, + BYTES_WR_SEC_COLUMN, bytes_written_per_sec, + NUM_WR_COLUMN, element.num_write_operations, + -1); + + } + if(disk_performance->disk_array->len) + g_array_remove_range (disk_performance->disk_array,0,disk_performance->disk_array->len); + return FALSE; +} + +static gboolean trace_header(void *hook_data, void *call_data){ + return FALSE; +} + + +static gboolean disk_update_time_window(void * hook_data, void * call_data){ + + DiskPerformanceData *disk_performance = (DiskPerformanceData *) hook_data; + const TimeWindowNotifyData *time_window_nofify_data = ((const TimeWindowNotifyData *)call_data); + disk_performance->time_window = *time_window_nofify_data->new_time_window; + Tab *tab = disk_performance->tab; + lttvwindow_events_request_remove_all(tab, disk_performance); + request_event( disk_performance); + + + return FALSE; +} + +void gui_disperformance_free(DiskPerformanceData *eventdata){ + Tab *tab = eventdata->tab; + g_info("disperformance.c : gui_disperformance_free, %p", eventdata); + g_info("%p, %p", eventdata, tab); + if(tab != NULL) + { + g_array_free (eventdata->disk_array, TRUE); + + lttvwindow_unregister_time_window_notify(tab, + disk_update_time_window, + eventdata); + + lttvwindow_events_request_remove_all(eventdata->tab, + eventdata); + g_disk_data_list = g_slist_remove(g_disk_data_list, eventdata); + } + g_free(eventdata); + g_info("disperformance.c : gui_disperformance_free end, %p", eventdata); +} + + + + + + +void disk_destructor_full(DiskPerformanceData *disk_data) +{ + + if(GTK_IS_WIDGET(disk_data->hbox_v)) + gtk_widget_destroy(disk_data->hbox_v); + +} + +static void disk_destroy_walk(gpointer data, gpointer user_data) +{ + g_info("Walk destroy GUI disk performance Viewer"); + disk_destructor_full((DiskPerformanceData*)data); +} +/** + * init function + * + * + * This is the entry point of the viewer. + * + */ +static void init() +{ + + g_info("Init diskPerformance.c"); + + LTT_FACILITY_BLOCK = g_quark_from_string("block"); + LTT_EVENT_BLOCK_READ = g_quark_from_string("read"); + LTT_EVENT_BLOCK_WRITE = g_quark_from_string("write"); + + lttvwindow_register_constructor("diskperformance", + "/", + "Insert Disk Performance", + hDiskPerformanceInsert_xpm, + "Insert Disk Performance", + disk_performance); +} + +/** + * Constructor hook + * + */ +GtkWidget *disk_performance(LttvPlugin *plugin) +{ + LttvPluginTab *ptab = LTTV_PLUGIN_TAB(plugin); + DiskPerformanceData* disk_data = disk_performance_data(ptab); + if(disk_data) + return disk_data->hbox_v; + else + return NULL; +} + +/** + * This function initializes the Event Viewer functionnality through the + * GTK API. + */ +DiskPerformanceData *disk_performance_data(LttvPluginTab *ptab) +{ + LttTime end; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + DiskPerformanceData* disk_data = g_new(DiskPerformanceData,1) ; + + g_info("enter disk_performance_data \n"); + Tab *tab = ptab->tab; + disk_data->tab = tab; + disk_data->ptab = ptab; + disk_data->time_window = lttvwindow_get_time_window(tab); + + disk_data->disk_array = g_array_new(FALSE, FALSE, sizeof(lttv_total_block )); + + lttvwindow_register_time_window_notify(tab, + disk_update_time_window, + disk_data); + + disk_data->scroll_win = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (disk_data->scroll_win); + gtk_scrolled_window_set_policy( + GTK_SCROLLED_WINDOW(disk_data->scroll_win), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + + /* Create a model for storing the data list */ + disk_data->store_m = gtk_list_store_new ( + N_COLUMNS, /* Total number of columns */ + G_TYPE_STRING, /* Diskname */ + G_TYPE_INT64, /* Bytes read */ + G_TYPE_INT64, /* Bytes read/sec */ + G_TYPE_INT, + G_TYPE_INT64, /* bytes written */ + G_TYPE_INT64, /* bytes written/sec */ + G_TYPE_INT + ); + + disk_data->tree_v = gtk_tree_view_new_with_model (GTK_TREE_MODEL (disk_data->store_m)); + + g_object_unref (G_OBJECT (disk_data->store_m)); + + renderer = gtk_cell_renderer_text_new (); + + column = gtk_tree_view_column_new_with_attributes ("DiskName", + renderer, + "text", DISKNAME_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_fixed_width (column, 45); + gtk_tree_view_append_column (GTK_TREE_VIEW (disk_data->tree_v), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("BytesRead", + renderer, + "text", BYTES_RD_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_fixed_width (column, 220); + gtk_tree_view_append_column (GTK_TREE_VIEW (disk_data->tree_v), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("BytesRead/sec", + renderer, + "text", BYTES_RD_SEC_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 1.0); + gtk_tree_view_column_set_fixed_width (column, 220); + gtk_tree_view_append_column (GTK_TREE_VIEW (disk_data->tree_v), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("NumReadOperations", + renderer, + "text",NUM_RD_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 1.0); + gtk_tree_view_column_set_fixed_width (column, 220); + gtk_tree_view_append_column (GTK_TREE_VIEW (disk_data->tree_v), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("BytesWritten", + renderer, + "text", BYTES_WR_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_fixed_width (column, 145); + gtk_tree_view_append_column (GTK_TREE_VIEW (disk_data->tree_v), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("BytesWritten/sec", + renderer, + "text", BYTES_WR_SEC_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 1.0); + gtk_tree_view_column_set_fixed_width (column, 220); + gtk_tree_view_append_column (GTK_TREE_VIEW (disk_data->tree_v), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("NumWriteOperations", + renderer, + "text",NUM_WR_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_fixed_width (column, 145); + gtk_tree_view_append_column (GTK_TREE_VIEW (disk_data->tree_v), column); + + disk_data->select_c = gtk_tree_view_get_selection (GTK_TREE_VIEW (disk_data->tree_v)); + gtk_tree_selection_set_mode (disk_data->select_c, GTK_SELECTION_SINGLE); + + gtk_container_add (GTK_CONTAINER (disk_data->scroll_win), disk_data->tree_v); + + disk_data->hbox_v = gtk_hbox_new(0, 0); + gtk_box_pack_start(GTK_BOX(disk_data->hbox_v), disk_data->scroll_win, TRUE, TRUE, 0); + + gtk_widget_show(disk_data->hbox_v); + gtk_widget_show(disk_data->tree_v); + + + g_disk_data_list = g_slist_append(g_disk_data_list, disk_data); + g_object_set_data_full(G_OBJECT(disk_data->hbox_v), + "disk_data", + disk_data, + (GDestroyNotify)gui_disperformance_free); + + request_event(disk_data); + return disk_data; +} + +/** + * + * For each trace in the traceset, this function: + * - calls lttv_trace_find_hook() & registers a hook function to event_by_id_hooks + * - registers a callback function to each hook + * - calls lttvwindow_events_request() to request data in a specific + * time interval to the main window + * + */ +static void request_event(DiskPerformanceData *disk_performance) +{ + guint i, k, l, nb_trace; + + GArray *hooks; + + guint ret; + + LttvTraceHook *hook; + + LttvTraceState *ts; + + LttvTraceHookByFacility *thf; + + LttvTracesetContext *tsc = lttvwindow_get_traceset_context(disk_performance->tab); + /* Get the traceset */ + LttvTraceset *traceset = tsc->ts; + + nb_trace = lttv_traceset_number(traceset); + + //for(i = 0; itraces[i]; + + disk_performance->event_by_id_hooks = lttv_hooks_by_id_new(); + /* Register event_by_id_hooks with a callback function */ + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_BLOCK, LTT_EVENT_BLOCK_READ, + 0, 0, 0, + block_read_callback, + disk_performance, + &g_array_index(hooks, LttvTraceHook, 0)); + + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_BLOCK, LTT_EVENT_BLOCK_WRITE, + 0, 0, 0, + block_write_callback, + disk_performance, + &g_array_index(hooks, LttvTraceHook, 1)); + + g_assert(!ret); + + /*iterate through the facility list*/ + for(k = 0 ; k < hooks->len; k++) + { + hook = &g_array_index(hooks, LttvTraceHook, k); + for(l=0; lfac_list->len; l++) + { + thf = g_array_index(hook->fac_list, LttvTraceHookByFacility*, l); + lttv_hooks_add(lttv_hooks_by_id_find(disk_performance->event_by_id_hooks, thf->id), + thf->h, + disk_performance, + LTTV_PRIO_DEFAULT); + + } + } + + disk_performance->hooks_trace_after = lttv_hooks_new(); + /* Registers a hook function */ + lttv_hooks_add(disk_performance->hooks_trace_after, disk_show, disk_performance, LTTV_PRIO_DEFAULT); + + disk_performance->hooks_trace_before = lttv_hooks_new(); + /* Registers a hook function */ + lttv_hooks_add(disk_performance->hooks_trace_before, trace_header, disk_performance, LTTV_PRIO_DEFAULT); + + /* Initalize the EventsRequest structure */ + events_request->owner = disk_performance; + events_request->viewer_data = disk_performance; + events_request->servicing = FALSE; + events_request->start_time = disk_performance->time_window.start_time; + events_request->start_position = NULL; + events_request->stop_flag = FALSE; + events_request->end_time = disk_performance->time_window.end_time; + events_request->num_events = G_MAXUINT; + events_request->end_position = NULL; + events_request->trace = i; + events_request->hooks = hooks; + events_request->before_chunk_traceset = NULL; + events_request->before_chunk_trace = disk_performance->hooks_trace_before; + events_request->before_chunk_tracefile= NULL; + events_request->event = NULL; + events_request->event_by_id = disk_performance->event_by_id_hooks; + events_request->after_chunk_tracefile = NULL; + events_request->after_chunk_trace = NULL; + events_request->after_chunk_traceset = NULL; + events_request->before_request = NULL; + events_request->after_request = disk_performance->hooks_trace_after; + + lttvwindow_events_request(disk_performance->tab, events_request); + } + +} + +/** + * This function is called whenever a read event occurs. + * + */ +static gboolean block_read_callback(void *hook_data, void *call_data) +{ + LttEvent *e; + LttTime event_time; + unsigned cpu_id; + lttv_block block_read; + char *diskname; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + DiskPerformanceData *disk_performance = (DiskPerformanceData *)hook_data; + GArray *disk_array = disk_performance->disk_array; + e = ltt_tracefile_get_event(tfc->tf); + event_time = ltt_event_time(e); + cpu_id = ltt_event_cpu_id(e); + + get_event_detail(e, &block_read); + diskname = major_minor_to_diskname(&block_read); + sum_data(diskname, block_read.size,LTTV_READ_OPERATION, disk_array); + + return FALSE; +} + +/** + * This function is called whenever a write event occurs. + * + */ +static gboolean block_write_callback(void *hook_data, void *call_data) +{ + LttEvent *e; + LttTime event_time; + unsigned cpu_id; + lttv_block block_write; + char *diskname; + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + DiskPerformanceData *disk_performance = (DiskPerformanceData *)hook_data; + GArray *disk_array = disk_performance->disk_array; + e = ltt_tracefile_get_event(tfc->tf); + event_time = ltt_event_time(e); + cpu_id = ltt_event_cpu_id(e); + + get_event_detail(e, &block_write); + diskname = major_minor_to_diskname(&block_write); + sum_data(diskname, block_write.size,LTTV_WRITE_OPERATION, disk_array); + + return FALSE; +} + +/** + * This function extracts the major, minor and size + * + */ +static void get_event_detail(LttEvent *e, lttv_block* disk_data) +{ + guint i, num_fields; + LttEventType *event_type; + LttField *element; + LttField *field; + event_type = ltt_event_eventtype(e); + num_fields = ltt_eventtype_num_fields(event_type); + + for(i = 0 ; i < num_fields ; i++) + { + element = ltt_eventtype_field(event_type,i); + switch(i) + { + case 0: + disk_data->major_number = ltt_event_get_long_unsigned(e, element); + break; + + case 1: + disk_data->minor_number = ltt_event_get_long_unsigned(e, element); + break; + case 2: + disk_data->size = ltt_event_get_long_unsigned(e, element); + break; + } + + } + +} + + + +/** + * This function calculates: the number of operations, the total bytes read or written, + * the average number of bytes read or written by sec. + */ +static void sum_data(char* diskname, guint size, enum operation_t operation, GArray *disk_array) +{ + + lttv_total_block data; + lttv_total_block *element; + guint i; + gboolean notFound = FALSE; + + memset ((void*)&data, 0,sizeof(lttv_total_block)); + + if(disk_array->len == NO_ITEMS){ + strcpy(data.diskname, diskname); + if(operation == LTTV_READ_OPERATION){ + data.total_bytes_read = size; + data.num_read_operations++; + } + else{ + data.total_bytes_written = size; + data.num_write_operations ++; + } + g_array_append_val (disk_array, data); + } + else{ + for(i = 0; i < disk_array->len; i++){ + element = &g_array_index(disk_array,lttv_total_block,i); + if(strcmp(element->diskname,diskname) == 0){ + if(operation == LTTV_READ_OPERATION){ + element->num_read_operations++; + element->total_bytes_read += size; + } + else{ + element->num_write_operations ++; + element->total_bytes_written += size; + } + notFound = TRUE; + } + } + if(!notFound){ + strcpy(data.diskname, diskname); + if(operation == LTTV_READ_OPERATION){ + data.total_bytes_read = size; + data.num_read_operations ++; + } + else{ + data.total_bytes_written = size; + data.num_write_operations ++; + } + g_array_append_val (disk_array, data); + } + } +} + + +static void destroy() +{ + g_info("Destroy diskPerformance"); + g_slist_foreach(g_disk_data_list, disk_destroy_walk, NULL ); + g_slist_free(g_disk_data_list); + + lttvwindow_unregister_constructor(disk_performance); + +} + +/** + * This function convert the major and minor number to the corresponding disk. + * Data taken from Documentation/devices.txt of the kernel tree. + */ +static char * major_minor_to_diskname( lttv_block* disk_data) +{ + switch(disk_data->major_number) + { + /* IDE Disks */ + case 3: /* First MFM, RLL and IDE hard disk/CD-ROM interface */ + if( (disk_data->minor_number >= 0) && (disk_data->minor_number < 64)) + return "/dev/hda"; + if((disk_data->minor_number >= 64) && (disk_data->minor_number < 128)) + return "/dev/hdb"; + break; + + case 22: /*Second IDE hard disk/CD-ROM interface */ + if( (disk_data->minor_number >= 0) && (disk_data->minor_number < 64)) + return "/dev/hdc"; + if((disk_data->minor_number >= 64) && (disk_data->minor_number < 128)) + return "/dev/hdd"; + break; + + case 33: /* Third IDE hard disk/CD-ROM interface */ + if( (disk_data->minor_number >= 0) && (disk_data->minor_number < 64)) + return "/dev/hde"; + if((disk_data->minor_number >= 64) && (disk_data->minor_number < 128)) + return "/dev/hdf"; + break; + + case 34: /* Fourth IDE hard disk/CD-ROM interface */ + if( (disk_data->minor_number >= 0) && (disk_data->minor_number < 64)) + return "/dev/hdg"; + if((disk_data->minor_number >= 64) && (disk_data->minor_number < 128)) + return "/dev/hdh"; + break; + + case 56: /* Fifth IDE hard disk/CD-ROM interface */ + if( (disk_data->minor_number >= 0) && (disk_data->minor_number < 64)) + return "/dev/hdi"; + if((disk_data->minor_number >= 64) && (disk_data->minor_number < 128)) + return "/dev/hdj"; + break; + + case 57: /* Sixth IDE hard disk/CD-ROM interface */ + if( (disk_data->minor_number >= 0) && (disk_data->minor_number < 64)) + return "/dev/hdk"; + if((disk_data->minor_number >= 64) && (disk_data->minor_number < 128)) + return "/dev/hdl"; + break; + + case 88: /* Seventh IDE hard disk/CD-ROM interface */ + if( (disk_data->minor_number >= 0) && (disk_data->minor_number < 64)) + return "/dev/hdm"; + if((disk_data->minor_number >= 64) && (disk_data->minor_number < 128)) + return "/dev/hdn"; + break; + + case 89: /* Eighth IDE hard disk/CD-ROM interface */ + if( (disk_data->minor_number >= 0) && (disk_data->minor_number < 64)) + return "/dev/hdo"; + if((disk_data->minor_number >= 64) && (disk_data->minor_number < 128)) + return "/dev/hdp"; + break; + + case 90: /* Ninth IDE hard disk/CD-ROM interface */ + if( (disk_data->minor_number >= 0) && (disk_data->minor_number < 64)) + return "/dev/hdq"; + if((disk_data->minor_number >= 64) && (disk_data->minor_number < 128)) + return "/dev/hdr"; + break; + + case 91: /* Tenth IDE hard disk/CD-ROM interface */ + if( (disk_data->minor_number >= 0) && (disk_data->minor_number < 64)) + return "/dev/hds"; + if((disk_data->minor_number >= 64) && (disk_data->minor_number < 128)) + return "/dev/hdt"; + break; + + /* SCSI Disks */ + case 8: /* SCSI disk devices */ + if( (disk_data->minor_number >= 0) && (disk_data->minor_number < 16)) + return "/dev/sda"; // First SCSI disk whole disk + + if((disk_data->minor_number >= 16) && (disk_data->minor_number < 32)) + return "/dev/sdb";// Second SCSI disk whole disk + + if((disk_data->minor_number >= 32) && (disk_data->minor_number < 48)) + return "/dev/sdc";// Third SCSI disk whole disk + + if((disk_data->minor_number >= 48) && (disk_data->minor_number < 64)) + return "/dev/sdd";// Fourth SCSI disk whole disk + + if((disk_data->minor_number >= 64) && (disk_data->minor_number < 80)) + return "/dev/sde";// Fifth SCSI disk whole disk + + if((disk_data->minor_number >= 80) && (disk_data->minor_number < 96)) + return "/dev/sdf";// Sixth SCSI disk whole disk + + if((disk_data->minor_number >= 96) && (disk_data->minor_number < 112)) + return "/dev/sdg";// seventh SCSI disk whole disk + + if((disk_data->minor_number >= 112) && (disk_data->minor_number < 128)) + return "/dev/sdh";// eighth SCSI disk whole disk + + if((disk_data->minor_number >= 128) && (disk_data->minor_number < 144)) + return "/dev/sdi";// 9th SCSI disk whole disk + + if((disk_data->minor_number >= 144) && (disk_data->minor_number < 160)) + return "/dev/sdj";// 10th SCSI disk whole disk + + if((disk_data->minor_number >= 160) && (disk_data->minor_number < 176)) + return "/dev/sdk";// 11th SCSI disk whole disk + + if((disk_data->minor_number >= 176) && (disk_data->minor_number < 192)) + return "/dev/sdl";// 12th SCSI disk whole disk + + if((disk_data->minor_number >= 192) && (disk_data->minor_number < 208)) + return "/dev/sdm";// 13th SCSI disk whole disk + + if((disk_data->minor_number >= 208) && (disk_data->minor_number < 224)) + return "/dev/sdn";// 14th SCSI disk whole disk + + if((disk_data->minor_number >= 224) && (disk_data->minor_number < 240)) + return "/dev/sdo";// 15th SCSI disk whole disk + + if((disk_data->minor_number >= 240) && (disk_data->minor_number < 256)) + return "/dev/sdp";// 16th SCSI disk whole disk + break; + + case 65: /* SCSI disk devices */ + if( (disk_data->minor_number >= 0) && (disk_data->minor_number < 16)) + return "/dev/sdq"; + + if((disk_data->minor_number >= 16) && (disk_data->minor_number < 32)) + return "/dev/sdr"; + + if((disk_data->minor_number >= 32) && (disk_data->minor_number < 48)) + return "/dev/sds"; + + if((disk_data->minor_number >= 48) && (disk_data->minor_number < 64)) + return "/dev/sdt"; + + if((disk_data->minor_number >= 64) && (disk_data->minor_number < 80)) + return "/dev/sdu"; + + if((disk_data->minor_number >= 80) && (disk_data->minor_number < 96)) + return "/dev/sdv"; + + if((disk_data->minor_number >= 96) && (disk_data->minor_number < 112)) + return "/dev/sdw"; + + if((disk_data->minor_number >= 112) && (disk_data->minor_number < 128)) + return "/dev/sdy"; + + if((disk_data->minor_number >= 128) && (disk_data->minor_number < 144)) + return "/dev/sdx"; + + if((disk_data->minor_number >= 144) && (disk_data->minor_number < 160)) + return "/dev/sdz"; + + if((disk_data->minor_number >= 160) && (disk_data->minor_number < 176)) + return "/dev/sdaa"; + + if((disk_data->minor_number >= 176) && (disk_data->minor_number < 192)) + return "/dev/sdab"; + + if((disk_data->minor_number >= 192) && (disk_data->minor_number < 208)) + return "/dev/sdac"; + + if((disk_data->minor_number >= 208) && (disk_data->minor_number < 224)) + return "/dev/sdad"; + + if((disk_data->minor_number >= 224) && (disk_data->minor_number < 240)) + return "/dev/sdae"; + + if((disk_data->minor_number >= 240) && (disk_data->minor_number < 256)) + return "/dev/sdaf"; + break; + + case 66: /* SCSI disk devices */ + if( (disk_data->minor_number >= 0) && (disk_data->minor_number < 16)) + return "/dev/sdag"; + + if((disk_data->minor_number >= 16) && (disk_data->minor_number < 32)) + return "/dev/sdah"; + + if((disk_data->minor_number >= 32) && (disk_data->minor_number < 48)) + return "/dev/sdai"; + + if((disk_data->minor_number >= 48) && (disk_data->minor_number < 64)) + return "/dev/sdaj"; + + if((disk_data->minor_number >= 64) && (disk_data->minor_number < 80)) + return "/dev/sdak"; + + if((disk_data->minor_number >= 80) && (disk_data->minor_number < 96)) + return "/dev/sdal"; + + if((disk_data->minor_number >= 96) && (disk_data->minor_number < 112)) + return "/dev/sdam"; + + if((disk_data->minor_number >= 112) && (disk_data->minor_number < 128)) + return "/dev/sdan"; + + if((disk_data->minor_number >= 128) && (disk_data->minor_number < 144)) + return "/dev/sdao"; + + if((disk_data->minor_number >= 144) && (disk_data->minor_number < 160)) + return "/dev/sdap"; + + if((disk_data->minor_number >= 160) && (disk_data->minor_number < 176)) + return "/dev/sdaq"; + + if((disk_data->minor_number >= 176) && (disk_data->minor_number < 192)) + return "/dev/sdar"; + + if((disk_data->minor_number >= 192) && (disk_data->minor_number < 208)) + return "/dev/sdas"; + + if((disk_data->minor_number >= 208) && (disk_data->minor_number < 224)) + return "/dev/sdat"; + + if((disk_data->minor_number >= 224) && (disk_data->minor_number < 240)) + return "/dev/sdau"; + + if((disk_data->minor_number >= 240) && (disk_data->minor_number < 256)) + return "/dev/sdav"; + break; + + + case 67: /* SCSI disk devices */ + if( (disk_data->minor_number >= 0) && (disk_data->minor_number < 16)) + return "/dev/sdaw"; + + if((disk_data->minor_number >= 16) && (disk_data->minor_number < 32)) + return "/dev/sdax"; + + if((disk_data->minor_number >= 32) && (disk_data->minor_number < 48)) + return "/dev/sday"; + + if((disk_data->minor_number >= 48) && (disk_data->minor_number < 64)) + return "/dev/sdaz"; + + if((disk_data->minor_number >= 64) && (disk_data->minor_number < 80)) + return "/dev/sdba"; + + if((disk_data->minor_number >= 80) && (disk_data->minor_number < 96)) + return "/dev/sdbb"; + + if((disk_data->minor_number >= 96) && (disk_data->minor_number < 112)) + return "/dev/sdbc"; + + if((disk_data->minor_number >= 112) && (disk_data->minor_number < 128)) + return "/dev/sdbd"; + + if((disk_data->minor_number >= 128) && (disk_data->minor_number < 144)) + return "/dev/sdbe"; + + if((disk_data->minor_number >= 144) && (disk_data->minor_number < 160)) + return "/dev/sdbf"; + + if((disk_data->minor_number >= 160) && (disk_data->minor_number < 176)) + return "/dev/sdbg"; + + if((disk_data->minor_number >= 176) && (disk_data->minor_number < 192)) + return "/dev/sdbh"; + + if((disk_data->minor_number >= 192) && (disk_data->minor_number < 208)) + return "/dev/sdbi"; + + if((disk_data->minor_number >= 208) && (disk_data->minor_number < 224)) + return "/dev/sdbj"; + + if((disk_data->minor_number >= 224) && (disk_data->minor_number < 240)) + return "/dev/sdbk"; + + if((disk_data->minor_number >= 240) && (disk_data->minor_number < 256)) + return "/dev/sdbl"; + break; + + case 68 : /* SCSI disk devices */ + if( (disk_data->minor_number >= 0) && (disk_data->minor_number < 16)) + return "/dev/sdbm"; + + if((disk_data->minor_number >= 16) && (disk_data->minor_number < 32)) + return "/dev/sdbm"; + + if((disk_data->minor_number >= 32) && (disk_data->minor_number < 48)) + return "/dev/sdbo"; + + if((disk_data->minor_number >= 48) && (disk_data->minor_number < 64)) + return "/dev/sdbp"; + + if((disk_data->minor_number >= 64) && (disk_data->minor_number < 80)) + return "/dev/sdbq"; + + if((disk_data->minor_number >= 80) && (disk_data->minor_number < 96)) + return "/dev/sdbr"; + + if((disk_data->minor_number >= 96) && (disk_data->minor_number < 112)) + return "/dev/sdbs"; + + if((disk_data->minor_number >= 112) && (disk_data->minor_number < 128)) + return "/dev/sdbt"; + + if((disk_data->minor_number >= 128) && (disk_data->minor_number < 144)) + return "/dev/sdbu"; + + if((disk_data->minor_number >= 144) && (disk_data->minor_number < 160)) + return "/dev/sdbv"; + + if((disk_data->minor_number >= 160) && (disk_data->minor_number < 176)) + return "/dev/sdbw"; + + if((disk_data->minor_number >= 176) && (disk_data->minor_number < 192)) + return "/dev/sdbx"; + + if((disk_data->minor_number >= 192) && (disk_data->minor_number < 208)) + return "/dev/sdby"; + + if((disk_data->minor_number >= 208) && (disk_data->minor_number < 224)) + return "/dev/sdbz"; + + if((disk_data->minor_number >= 224) && (disk_data->minor_number < 240)) + return "/dev/sdca"; + + if((disk_data->minor_number >= 240) && (disk_data->minor_number < 256)) + return "/dev/sdcb"; + break; + case 69 : /* SCSI disk devices */ + if( (disk_data->minor_number >= 0) && (disk_data->minor_number < 16)) + return "/dev/sdcc"; + + if((disk_data->minor_number >= 16) && (disk_data->minor_number < 32)) + return "/dev/sdcd"; + + if((disk_data->minor_number >= 32) && (disk_data->minor_number < 48)) + return "/dev/sdce"; + + if((disk_data->minor_number >= 48) && (disk_data->minor_number < 64)) + return "/dev/sdcf"; + + if((disk_data->minor_number >= 64) && (disk_data->minor_number < 80)) + return "/dev/sdcg"; + + if((disk_data->minor_number >= 80) && (disk_data->minor_number < 96)) + return "/dev/sdch"; + + if((disk_data->minor_number >= 96) && (disk_data->minor_number < 112)) + return "/dev/sdci"; + + if((disk_data->minor_number >= 112) && (disk_data->minor_number < 128)) + return "/dev/sdcj"; + + if((disk_data->minor_number >= 128) && (disk_data->minor_number < 144)) + return "/dev/sdck"; + + if((disk_data->minor_number >= 144) && (disk_data->minor_number < 160)) + return "/dev/sdcl"; + + if((disk_data->minor_number >= 160) && (disk_data->minor_number < 176)) + return "/dev/sdcm"; + + if((disk_data->minor_number >= 176) && (disk_data->minor_number < 192)) + return "/dev/sdcn"; + + if((disk_data->minor_number >= 192) && (disk_data->minor_number < 208)) + return "/dev/sdco"; + + if((disk_data->minor_number >= 208) && (disk_data->minor_number < 224)) + return "/dev/sdcp"; + + if((disk_data->minor_number >= 224) && (disk_data->minor_number < 240)) + return "/dev/sdcq"; + + if((disk_data->minor_number >= 240) && (disk_data->minor_number < 256)) + return "/dev/sdcr"; + break; + + + case 70 : /* SCSI disk devices */ + if( (disk_data->minor_number >= 0) && (disk_data->minor_number < 16)) + return "/dev/sdcs"; + + if((disk_data->minor_number >= 16) && (disk_data->minor_number < 32)) + return "/dev/sdct"; + + if((disk_data->minor_number >= 32) && (disk_data->minor_number < 48)) + return "/dev/sdcu"; + + if((disk_data->minor_number >= 48) && (disk_data->minor_number < 64)) + return "/dev/sdcv"; + + if((disk_data->minor_number >= 64) && (disk_data->minor_number < 80)) + return "/dev/sdcw"; + + if((disk_data->minor_number >= 80) && (disk_data->minor_number < 96)) + return "/dev/sdcx"; + + if((disk_data->minor_number >= 96) && (disk_data->minor_number < 112)) + return "/dev/sdcy"; + + if((disk_data->minor_number >= 112) && (disk_data->minor_number < 128)) + return "/dev/sdcz"; + + if((disk_data->minor_number >= 128) && (disk_data->minor_number < 144)) + return "/dev/sdda"; + + if((disk_data->minor_number >= 144) && (disk_data->minor_number < 160)) + return "/dev/sddb"; + + if((disk_data->minor_number >= 160) && (disk_data->minor_number < 176)) + return "/dev/sddc"; + + if((disk_data->minor_number >= 176) && (disk_data->minor_number < 192)) + return "/dev/sddd"; + + if((disk_data->minor_number >= 192) && (disk_data->minor_number < 208)) + return "/dev/sdde"; + + if((disk_data->minor_number >= 208) && (disk_data->minor_number < 224)) + return "/dev/sddf"; + + if((disk_data->minor_number >= 224) && (disk_data->minor_number < 240)) + return "/dev/sddg"; + + if((disk_data->minor_number >= 240) && (disk_data->minor_number < 256)) + return "/dev/sddh"; + break; + + case 71: /* SCSI disk devices */ + if( (disk_data->minor_number >= 0) && (disk_data->minor_number < 16)) + return "/dev/sddi"; + + if((disk_data->minor_number >= 16) && (disk_data->minor_number < 32)) + return "/dev/sddj"; + + if((disk_data->minor_number >= 32) && (disk_data->minor_number < 48)) + return "/dev/sddk"; + + if((disk_data->minor_number >= 48) && (disk_data->minor_number < 64)) + return "/dev/sddl"; + + if((disk_data->minor_number >= 64) && (disk_data->minor_number < 80)) + return "/dev/sddm"; + + if((disk_data->minor_number >= 80) && (disk_data->minor_number < 96)) + return "/dev/sddn"; + + if((disk_data->minor_number >= 96) && (disk_data->minor_number < 112)) + return "/dev/sddo"; + + if((disk_data->minor_number >= 112) && (disk_data->minor_number < 128)) + return "/dev/sddp"; + + if((disk_data->minor_number >= 128) && (disk_data->minor_number < 144)) + return "/dev/sddq"; + + if((disk_data->minor_number >= 144) && (disk_data->minor_number < 160)) + return "/dev/sddr"; + + if((disk_data->minor_number >= 160) && (disk_data->minor_number < 176)) + return "/dev/sdds"; + + if((disk_data->minor_number >= 176) && (disk_data->minor_number < 192)) + return "/dev/sddt"; + + if((disk_data->minor_number >= 192) && (disk_data->minor_number < 208)) + return "/dev/sddu"; + + if((disk_data->minor_number >= 208) && (disk_data->minor_number < 224)) + return "/dev/sddv"; + + if((disk_data->minor_number >= 224) && (disk_data->minor_number < 240)) + return "/dev/sddw"; + + if((disk_data->minor_number >= 240) && (disk_data->minor_number < 256)) + return "/dev/sddx"; + break; + + case 128: /* SCSI disk devices */ + if( (disk_data->minor_number >= 0) && (disk_data->minor_number < 16)) + return "/dev/sddy"; + + if((disk_data->minor_number >= 16) && (disk_data->minor_number < 32)) + return "/dev/sddz"; + + if((disk_data->minor_number >= 32) && (disk_data->minor_number < 48)) + return "/dev/sdea"; + + if((disk_data->minor_number >= 48) && (disk_data->minor_number < 64)) + return "/dev/sdeb"; + + if((disk_data->minor_number >= 64) && (disk_data->minor_number < 80)) + return "/dev/sdec"; + + if((disk_data->minor_number >= 80) && (disk_data->minor_number < 96)) + return "/dev/sded"; + + if((disk_data->minor_number >= 96) && (disk_data->minor_number < 112)) + return "/dev/sdee"; + + if((disk_data->minor_number >= 112) && (disk_data->minor_number < 128)) + return "/dev/sdef"; + + if((disk_data->minor_number >= 128) && (disk_data->minor_number < 144)) + return "/dev/sdeg"; + + if((disk_data->minor_number >= 144) && (disk_data->minor_number < 160)) + return "/dev/sdeh"; + + if((disk_data->minor_number >= 160) && (disk_data->minor_number < 176)) + return "/dev/sdei"; + + if((disk_data->minor_number >= 176) && (disk_data->minor_number < 192)) + return "/dev/sdej"; + + if((disk_data->minor_number >= 192) && (disk_data->minor_number < 208)) + return "/dev/sdek"; + + if((disk_data->minor_number >= 208) && (disk_data->minor_number < 224)) + return "/dev/sdel"; + + if((disk_data->minor_number >= 224) && (disk_data->minor_number < 240)) + return "/dev/sdem"; + + if((disk_data->minor_number >= 240) && (disk_data->minor_number < 256)) + return "/dev/sden"; + break; + + case 129: /* SCSI disk devices */ + if( (disk_data->minor_number >= 0) && (disk_data->minor_number < 16)) + return "/dev/sdeo"; + + if((disk_data->minor_number >= 16) && (disk_data->minor_number < 32)) + return "/dev/sdep"; + + if((disk_data->minor_number >= 32) && (disk_data->minor_number < 48)) + return "/dev/sdeq"; + + if((disk_data->minor_number >= 48) && (disk_data->minor_number < 64)) + return "/dev/sder"; + + if((disk_data->minor_number >= 64) && (disk_data->minor_number < 80)) + return "/dev/sdes"; + + if((disk_data->minor_number >= 80) && (disk_data->minor_number < 96)) + return "/dev/sdet"; + + if((disk_data->minor_number >= 96) && (disk_data->minor_number < 112)) + return "/dev/sdeu"; + + if((disk_data->minor_number >= 112) && (disk_data->minor_number < 128)) + return "/dev/sdev"; + + if((disk_data->minor_number >= 128) && (disk_data->minor_number < 144)) + return "/dev/sdew"; + + if((disk_data->minor_number >= 144) && (disk_data->minor_number < 160)) + return "/dev/sdez"; + + if((disk_data->minor_number >= 160) && (disk_data->minor_number < 176)) + return "/dev/sdey"; + + if((disk_data->minor_number >= 176) && (disk_data->minor_number < 192)) + return "/dev/sdez"; + + if((disk_data->minor_number >= 192) && (disk_data->minor_number < 208)) + return "/dev/sdfa"; + + if((disk_data->minor_number >= 208) && (disk_data->minor_number < 224)) + return "/dev/sdfb"; + + if((disk_data->minor_number >= 224) && (disk_data->minor_number < 240)) + return "/dev/sdfc"; + + if((disk_data->minor_number >= 240) && (disk_data->minor_number < 256)) + return "/dev/sdfd"; + break; + /*USB block devices*/ + case 180: + if( (disk_data->minor_number >= 0) && (disk_data->minor_number < 8)) + return "/dev/uba"; + if( (disk_data->minor_number >= 8) && (disk_data->minor_number < 16)) + return "/dev/ubb"; + if( (disk_data->minor_number >= 16) && (disk_data->minor_number < 24)) + return "/dev/ubc"; + break; + + } + + +} +LTTV_MODULE("diskperformance", "disk info view", \ + "Produce disk I/O performance", \ + init, destroy, "lttvwindow") + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/diskperformance/hDiskPerformanceInsert.xpm b/tags/lttv-0.11.3-23102008/lttv/modules/gui/diskperformance/hDiskPerformanceInsert.xpm new file mode 100644 index 00000000..5eb65b51 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/diskperformance/hDiskPerformanceInsert.xpm @@ -0,0 +1,27 @@ +/* XPM */ +static char * hDiskPerformanceInsert_xpm[] = { +"22 22 2 1", +" c None", +". c #800080", +" ", +" ", +" .......... ", +" ............. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" ............. ", +" .......... ", +" ", +" "}; diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/diskperformance/liste.txt b/tags/lttv-0.11.3-23102008/lttv/modules/gui/diskperformance/liste.txt new file mode 100644 index 00000000..20bb40b0 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/diskperformance/liste.txt @@ -0,0 +1,3 @@ +3 0 hda +4 0 hdb +6 0 hdc diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/filter/Makefile.am b/tags/lttv-0.11.3-23102008/lttv/modules/gui/filter/Makefile.am new file mode 100644 index 00000000..89819a25 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/filter/Makefile.am @@ -0,0 +1,20 @@ +# +# Makefile for LTT New generation user interface : plugins. +# +# Created by Mathieu Desnoyers on May 6, 2003 +# + +AM_CFLAGS = $(GLIB_CFLAGS) +AM_CFLAGS += $(GTK_CFLAGS) +AM_CFLAGS += -fvisibility=hidden +LIBS += $(GLIB_LIBS) +LIBS += $(GTK_LIBS) -L${top_builddir}/lttv/modules/gui/lttvwindow/lttvwindow -llttvwindow + +libdir = ${lttvplugindir} + +lib_LTLIBRARIES = libguifilter.la +libguifilter_la_LDFLAGS = -module -avoid-version +libguifilter_la_SOURCES = filter.c + +EXTRA_DIST = \ + hGuiFilterInsert.xpm diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/filter/filter.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/filter/filter.c new file mode 100644 index 00000000..66108652 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/filter/filter.c @@ -0,0 +1,708 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2005 Simon Bouvier-Zappa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "hGuiFilterInsert.xpm" + + +GSList *g_filter_list = NULL ; + +/*! \file lttv/modules/gui/filter/filter.c + * \brief Graphic filter interface. + * + * The gui filter facility gives the user an easy to use + * basic interface to construct and modify at will a filter + * expression. User may either decide to write it himself in + * expression text entry or add simple expressions using the + * many choices boxes. + * + * \note The version of gtk-2.0 currently installed on + * my desktop misses some function of the newer + * gtk+ api. For the time being, I'll use the older api + * to keep compatibility with most systems. + */ + +typedef struct _FilterViewerData FilterViewerData; +typedef struct _FilterViewerDataLine FilterViewerDataLine; + +/* + * Prototypes + */ +GtkWidget *guifilter_get_widget(FilterViewerData *fvd); +FilterViewerData *gui_filter(LttvPlugin *plugin); +void gui_filter_destructor(FilterViewerData *fvd); +FilterViewerDataLine* gui_filter_add_line(FilterViewerData *fvd); +void gui_filter_line_set_visible(FilterViewerDataLine *fvdl, gboolean v); +void gui_filter_line_reset(FilterViewerDataLine *fvdl); +GtkWidget* h_guifilter(LttvPlugin *plugin); +void filter_destroy_walk(gpointer data, gpointer user_data); + +/* + * Callback functions + */ +void callback_process_button(GtkWidget *widget, gpointer data); +gboolean callback_enter_check(GtkWidget *widget, + GdkEventKey *event, + gpointer user_data); +void callback_add_button(GtkWidget *widget, gpointer data); +void callback_logical_op_box(GtkWidget *widget, gpointer data); +void callback_expression_field(GtkWidget *widget, gpointer data); +void callback_cancel_button(GtkWidget *widget, gpointer data); + +/** + * @struct _FilterViewerDataLine + * + * @brief Defines a simple expression + * This structures defines a simple + * expression whithin the main filter + * viewer data + */ +struct _FilterViewerDataLine { + int row; /**< row number */ + gboolean visible; /**< visible state */ + GtkWidget *f_not_op_box; /**< '!' operator (GtkComboBox) */ + GtkWidget *f_logical_op_box; /**< '&,|,^' operators (GtkComboBox) */ + GtkWidget *f_field_box; /**< field types (GtkComboBox) */ + GtkWidget *f_math_op_box; /**< '>,>=,<,<=,=,!=' operators (GtkComboBox) */ + GtkWidget *f_value_field; /**< expression's value (GtkComboBox) */ +}; + +/** + * @struct _FilterViewerData + * + * @brief Main structure of gui filter + * Main struct for the filter gui module + */ +struct _FilterViewerData { + LttvPlugin *plugin; /**< Plugin on which we interact. */ + + GtkWidget *f_window; /**< filter window */ + + GtkWidget *f_main_box; /**< main container */ + + GtkWidget *f_expression_field; /**< entire expression (GtkEntry) */ + GtkWidget *f_process_button; /**< process expression button (GtkButton) */ + + GtkWidget *f_logical_op_junction_box; /**< linking operator box (GtkComboBox) */ + + int rows; /**< number of rows */ + GPtrArray *f_lines; /**< array of FilterViewerDataLine */ + + GPtrArray *f_not_op_options; /**< array of operators types for not_op box */ + GPtrArray *f_logical_op_options; /**< array of operators types for logical_op box */ + GPtrArray *f_field_options; /**< array of field types for field box */ + GPtrArray *f_math_op_options; /**< array of operators types for math_op box */ + + GtkWidget *f_add_button; /**< add expression to current expression (GtkButton) */ + GtkWidget *f_cancel_button; /**< cancel and close dialog (GtkButton) */ + +}; + +/** + * @fn GtkWidget* guifilter_get_widget(FilterViewerData*) + * + * This function returns the current main widget + * used by this module + * @param fvd the module struct + * @return The main widget + */ +GtkWidget* +guifilter_get_widget(FilterViewerData *fvd) +{ + return fvd->f_window; +} + +/** + * @fn FilterViewerData* gui_filter(Tab*) + * + * Constructor is used to create FilterViewerData data structure. + * @param tab The tab structure used by the widget + * @return The Filter viewer data created. + */ +FilterViewerData* +gui_filter(LttvPlugin *plugin) +{ + g_debug("filter::gui_filter()"); + + unsigned i; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + FilterViewerData* fvd = g_new(FilterViewerData,1); + + fvd->plugin = plugin; + +// lttvwindow_register_traceset_notify(fvd->tab, +// filter_traceset_changed, +// filter_viewer_data); +// request_background_data(filter_viewer_data); + + /* + * Initiating items for + * combo boxes + */ + fvd->f_not_op_options = g_ptr_array_new(); + g_ptr_array_add(fvd->f_not_op_options,(gpointer) g_string_new("")); + g_ptr_array_add(fvd->f_not_op_options,(gpointer) g_string_new("!")); + + fvd->f_logical_op_options = g_ptr_array_new(); //g_array_new(FALSE,FALSE,sizeof(gchar)); + g_ptr_array_add(fvd->f_logical_op_options,(gpointer) g_string_new("")); + g_ptr_array_add(fvd->f_logical_op_options,(gpointer) g_string_new("&")); + g_ptr_array_add(fvd->f_logical_op_options,(gpointer) g_string_new("|")); + g_ptr_array_add(fvd->f_logical_op_options,(gpointer) g_string_new("!")); + g_ptr_array_add(fvd->f_logical_op_options,(gpointer) g_string_new("^")); + + fvd->f_field_options = g_ptr_array_new(); //g_array_new(FALSE,FALSE,16); + g_ptr_array_add(fvd->f_field_options,(gpointer) g_string_new("")); + g_ptr_array_add(fvd->f_field_options,(gpointer) g_string_new("event.name")); + g_ptr_array_add(fvd->f_field_options,(gpointer) g_string_new("event.facility")); + g_ptr_array_add(fvd->f_field_options,(gpointer) g_string_new("event.category")); + g_ptr_array_add(fvd->f_field_options,(gpointer) g_string_new("event.time")); + g_ptr_array_add(fvd->f_field_options,(gpointer) g_string_new("event.tsc")); + g_ptr_array_add(fvd->f_field_options,(gpointer) g_string_new("event.target_pid")); + /* + * TODO: Add core.xml fields here ! + */ + g_ptr_array_add(fvd->f_field_options,(gpointer) g_string_new("tracefile.name")); + g_ptr_array_add(fvd->f_field_options,(gpointer) g_string_new("trace.name")); + g_ptr_array_add(fvd->f_field_options,(gpointer) g_string_new("state.process_name")); + g_ptr_array_add(fvd->f_field_options,(gpointer) g_string_new("state.thread_brand")); + g_ptr_array_add(fvd->f_field_options,(gpointer) g_string_new("state.pid")); + g_ptr_array_add(fvd->f_field_options,(gpointer) g_string_new("state.ppid")); + g_ptr_array_add(fvd->f_field_options,(gpointer) g_string_new("state.creation_time")); + g_ptr_array_add(fvd->f_field_options,(gpointer) g_string_new("state.insertion_time")); + g_ptr_array_add(fvd->f_field_options,(gpointer) g_string_new("state.execution_mode")); + g_ptr_array_add(fvd->f_field_options,(gpointer) g_string_new("state.execution_submode")); + g_ptr_array_add(fvd->f_field_options,(gpointer) g_string_new("state.process_status")); + g_ptr_array_add(fvd->f_field_options,(gpointer) g_string_new("state.cpu")); + + fvd->f_math_op_options = g_ptr_array_new(); //g_array_new(FALSE,FALSE,7); + g_ptr_array_add(fvd->f_math_op_options,(gpointer) g_string_new("")); + g_ptr_array_add(fvd->f_math_op_options,(gpointer) g_string_new("=")); + g_ptr_array_add(fvd->f_math_op_options,(gpointer) g_string_new("!=")); + g_ptr_array_add(fvd->f_math_op_options,(gpointer) g_string_new("<")); + g_ptr_array_add(fvd->f_math_op_options,(gpointer) g_string_new("<=")); + g_ptr_array_add(fvd->f_math_op_options,(gpointer) g_string_new(">")); + g_ptr_array_add(fvd->f_math_op_options,(gpointer) g_string_new(">=")); + + + fvd->f_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(fvd->f_window), "LTTV Filter"); + gtk_window_set_transient_for(GTK_WINDOW(fvd->f_window), + GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(fvd->plugin->top_widget)))); + gtk_window_set_destroy_with_parent(GTK_WINDOW(fvd->f_window), TRUE); + gtk_window_set_resizable(GTK_WINDOW(fvd->f_window), FALSE); + + /* + * Initiating GtkTable layout + * starts with 2 rows and 5 columns and + * expands when expressions added + */ + fvd->f_main_box = gtk_table_new(3,7,FALSE); + gtk_table_set_row_spacings(GTK_TABLE(fvd->f_main_box),5); + gtk_table_set_col_spacings(GTK_TABLE(fvd->f_main_box),5); + + gtk_container_add(GTK_CONTAINER(fvd->f_window), GTK_WIDGET(fvd->f_main_box)); + + /* + * First half of the filter window + * - textual entry of filter expression + * - processing button + */ + fvd->f_expression_field = gtk_entry_new(); //gtk_scrolled_window_new (NULL, NULL); + g_signal_connect (G_OBJECT(fvd->f_expression_field), + "key-press-event", G_CALLBACK (callback_enter_check), (gpointer)fvd); +// gtk_entry_set_text(GTK_ENTRY(fvd->f_expression_field),"state.cpu>0"); + gtk_widget_show (fvd->f_expression_field); + + g_signal_connect (G_OBJECT (fvd->f_expression_field), "changed", + G_CALLBACK (callback_expression_field), (gpointer) fvd); + + fvd->f_process_button = gtk_button_new_with_label("Process"); + gtk_widget_show (fvd->f_process_button); + + g_signal_connect (G_OBJECT (fvd->f_process_button), "clicked", + G_CALLBACK (callback_process_button), (gpointer) fvd); + + gtk_table_attach( GTK_TABLE(fvd->f_main_box),fvd->f_expression_field,0,6,0,1,GTK_FILL,GTK_FILL,0,0); + gtk_table_attach( GTK_TABLE(fvd->f_main_box),fvd->f_process_button,6,7,0,1,GTK_FILL,GTK_FILL,0,0); + + + + /* + * Second half of the filter window + * - combo boxes featuring filtering options added to the expression + */ + fvd->f_add_button = gtk_button_new_with_label("Add Expression"); + gtk_widget_show (fvd->f_add_button); + + g_signal_connect (G_OBJECT (fvd->f_add_button), "clicked", + G_CALLBACK (callback_add_button), (gpointer) fvd); + + gtk_table_attach( GTK_TABLE(fvd->f_main_box),fvd->f_add_button,6,7,2,3,GTK_FILL,GTK_FILL,0,0); + + fvd->f_cancel_button = gtk_button_new_with_label("Cancel"); + gtk_widget_show (fvd->f_cancel_button); + g_signal_connect (G_OBJECT (fvd->f_cancel_button), "clicked", + G_CALLBACK (callback_cancel_button), (gpointer) fvd); + + gtk_table_attach( GTK_TABLE(fvd->f_main_box),fvd->f_cancel_button,6,7,1,2,GTK_FILL,GTK_FILL,0,0); + + fvd->f_logical_op_junction_box = gtk_combo_box_new_text(); + for(i=0;if_logical_op_options->len;i++) { + GString* s = g_ptr_array_index(fvd->f_logical_op_options,i); + gtk_combo_box_append_text(GTK_COMBO_BOX(fvd->f_logical_op_junction_box), s->str); + } + gtk_combo_box_set_active(GTK_COMBO_BOX(fvd->f_logical_op_junction_box),0); + + //gtk_widget_show(fvd->f_logical_op_box); + + gtk_table_attach( GTK_TABLE(fvd->f_main_box),fvd->f_logical_op_junction_box,0,1,1,2,GTK_SHRINK,GTK_FILL,0,0); + + gtk_container_set_border_width(GTK_CONTAINER(fvd->f_main_box), 1); + + /* initialize a new line */ + fvd->f_lines = g_ptr_array_new(); + fvd->rows = 1; + FilterViewerDataLine* fvdl = gui_filter_add_line(fvd); + g_ptr_array_add(fvd->f_lines,(gpointer) fvdl); + + /* + * show main container + */ + gtk_widget_show(fvd->f_main_box); + gtk_widget_show(fvd->f_window); + + + g_object_set_data_full( + G_OBJECT(guifilter_get_widget(fvd)), + "filter_viewer_data", + fvd, + (GDestroyNotify)gui_filter_destructor); + + g_filter_list = g_slist_append( + g_filter_list, + fvd); + + return fvd; +} + +/** + * @fn FilterViewerDataLine* gui_filter_add_line(FilterViewerData*) + * + * Adds a filter option line on the module tab + * @param fvd The filter module structure + * @return The line structure + */ +FilterViewerDataLine* +gui_filter_add_line(FilterViewerData* fvd) { + + FilterViewerDataLine* fvdl = g_new(FilterViewerDataLine,1); + + unsigned i; + fvdl->row = fvd->rows; + fvdl->visible = TRUE; + + fvdl->f_not_op_box = gtk_combo_box_new_text(); + for(i=0;if_not_op_options->len;i++) { + GString* s = g_ptr_array_index(fvd->f_not_op_options,i); + gtk_combo_box_append_text(GTK_COMBO_BOX(fvdl->f_not_op_box), s->str); + } + + fvdl->f_field_box = gtk_combo_box_new_text(); + for(i=0;if_field_options->len;i++) { + GString* s = g_ptr_array_index(fvd->f_field_options,i); +// g_print("String field: %s\n",s->str); + gtk_combo_box_append_text(GTK_COMBO_BOX(fvdl->f_field_box), s->str); + } + + fvdl->f_math_op_box = gtk_combo_box_new_text(); + for(i=0;if_math_op_options->len;i++) { + GString* s = g_ptr_array_index(fvd->f_math_op_options,i); + gtk_combo_box_append_text(GTK_COMBO_BOX(fvdl->f_math_op_box), s->str); + } + + fvdl->f_value_field = gtk_entry_new(); + + fvdl->f_logical_op_box = gtk_combo_box_new_text(); + for(i=0;if_logical_op_options->len;i++) { + GString* s = g_ptr_array_index(fvd->f_logical_op_options,i); + gtk_combo_box_append_text(GTK_COMBO_BOX(fvdl->f_logical_op_box), s->str); + } + gtk_widget_set_events(fvdl->f_logical_op_box, + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK | + GDK_FOCUS_CHANGE_MASK); + + g_signal_connect (G_OBJECT (fvdl->f_logical_op_box), "changed", + G_CALLBACK (callback_logical_op_box), (gpointer) fvd); + + gui_filter_line_reset(fvdl); + gui_filter_line_set_visible(fvdl,TRUE); + + gtk_table_attach( GTK_TABLE(fvd->f_main_box),fvdl->f_not_op_box,0,1,fvd->rows+1,fvd->rows+2,GTK_SHRINK,GTK_FILL,0,0); + gtk_table_attach( GTK_TABLE(fvd->f_main_box),fvdl->f_field_box,1,3,fvd->rows+1,fvd->rows+2,GTK_SHRINK,GTK_FILL,0,0); + gtk_table_attach( GTK_TABLE(fvd->f_main_box),fvdl->f_math_op_box,3,4,fvd->rows+1,fvd->rows+2,GTK_SHRINK,GTK_FILL,0,0); + gtk_table_attach( GTK_TABLE(fvd->f_main_box),fvdl->f_value_field,4,5,fvd->rows+1,fvd->rows+2,GTK_SHRINK,GTK_FILL,0,0); + gtk_table_attach( GTK_TABLE(fvd->f_main_box),fvdl->f_logical_op_box,5,6,fvd->rows+1,fvd->rows+2,GTK_SHRINK,GTK_FILL,0,0); + + return fvdl; +} + +/** + * @fn void gui_filter_line_set_visible(FilterViewerDataLine*,gboolean) + * + * Change visible state of current FilterViewerDataLine + * @param fvdl pointer to the current FilterViewerDataLine + * @param v TRUE: sets visible, FALSE: sets invisible + */ +void +gui_filter_line_set_visible(FilterViewerDataLine *fvdl, gboolean v) { + + fvdl->visible = v; + if(v) { + gtk_widget_show(fvdl->f_not_op_box); + gtk_widget_show(fvdl->f_field_box); + gtk_widget_show(fvdl->f_math_op_box); + gtk_widget_show(fvdl->f_value_field); + gtk_widget_show(fvdl->f_logical_op_box); + } else { + gtk_widget_hide(fvdl->f_not_op_box); + gtk_widget_hide(fvdl->f_field_box); + gtk_widget_hide(fvdl->f_math_op_box); + gtk_widget_hide(fvdl->f_value_field); + gtk_widget_hide(fvdl->f_logical_op_box); + } + +} + +/** + * @fn void gui_filter_line_reset(FilterViewerDataLine*) + * + * Sets selections of all boxes in current FilterViewerDataLine + * to default value (0) + * @param fvdl pointer to current FilterViewerDataLine + */ +void +gui_filter_line_reset(FilterViewerDataLine *fvdl) { + + gtk_combo_box_set_active(GTK_COMBO_BOX(fvdl->f_not_op_box),0); + gtk_combo_box_set_active(GTK_COMBO_BOX(fvdl->f_field_box),0); + gtk_combo_box_set_active(GTK_COMBO_BOX(fvdl->f_math_op_box),0); + gtk_entry_set_text(GTK_ENTRY(fvdl->f_value_field),""); + gtk_combo_box_set_active(GTK_COMBO_BOX(fvdl->f_logical_op_box),0); +} + +/** + * @fn void gui_filter_destructor(FilterViewerData*) + * + * Destructor for the filter gui module + * @param fvd The module structure + */ +void +gui_filter_destructor(FilterViewerData *fvd) +{ + /* May already been done by GTK window closing */ + if(GTK_IS_WIDGET(guifilter_get_widget(fvd))){ + g_info("widget still exists"); + } +// if(tab != NULL) { +// lttvwindow_unregister_traceset_notify(fvd->tab, +// filter_traceset_changed, +// filter_viewer_data); +// } + lttvwindowtraces_background_notify_remove(fvd); + + g_filter_list = g_slist_remove(g_filter_list, fvd); + + g_free(fvd); +} + + +/** + * @fn GtkWidget* h_guifilter(Tab*) + * + * Filter Module's constructor hook + * + * This constructor is given as a parameter to the menuitem and toolbar button + * registration. It creates the list. + * @param obj Object to interact with. + * @return The widget created. + */ +GtkWidget * +h_guifilter(LttvPlugin *plugin) +{ + FilterViewerData* f = gui_filter(plugin) ; + + return NULL; +} + +/** + * @fn static void init() + * + * This function initializes the Filter Viewer functionnality through the + * gtkTraceSet API. + */ +static void init() { + + lttvwindow_register_constructor("guifilter", + "/", + "Insert Filter Module", + hGuiFilterInsert_xpm, + "Insert Filter Module", + h_guifilter); +} + +/** + * @fn void filter_destroy_walk(gpointer,gpointer) + * + * Initiate the destruction of the current gui module + * on the GTK Interface + */ +void +filter_destroy_walk(gpointer data, gpointer user_data) +{ + FilterViewerData *fvd = (FilterViewerData*)data; + + g_debug("CFV.c : filter_destroy_walk, %p", fvd); + + /* May already have been done by GTK window closing */ + if(GTK_IS_WIDGET(guifilter_get_widget(fvd))) + gtk_widget_destroy(guifilter_get_widget(fvd)); +} + +/** + * @fn static void destroy() + * @brief plugin's destroy function + * + * This function releases the memory reserved by the module and unregisters + * everything that has been registered in the gtkTraceSet API. + */ +static void destroy() { + + g_slist_foreach(g_filter_list, filter_destroy_walk, NULL ); + + lttvwindow_unregister_constructor(h_guifilter); + +} + +/** + * @fn void callback_process_button(GtkWidget*,gpointer) + * + * The Process Button callback function + * @param widget The Button widget passed to the callback function + * @param data Data sent along with the callback function + */ +void +callback_process_button(GtkWidget *widget, gpointer data) { + + g_debug("callback_process_button(): Processing expression"); + + FilterViewerData *fvd = (FilterViewerData*)data; + LttvFilter* filter; + + if(strlen(gtk_entry_get_text(GTK_ENTRY(fvd->f_expression_field))) !=0) { + filter = lttv_filter_new(); + GString* s = g_string_new(gtk_entry_get_text(GTK_ENTRY(fvd->f_expression_field))); + lttv_filter_append_expression(filter,s->str); + g_string_free(s,TRUE); + } else { + filter = NULL; + } + /* Remove the old filter if present */ + //g_object_set_data_full(fvd->obj, "filter", filter, + // (GDestroyNotify)lttv_filter_destroy); + //g_object_notify(fvd->obj, "filter"); + lttv_plugin_update_filter(fvd->plugin, filter); +} + +/** + * @fn void callback_cancel_button(GtkWidget*,gpointer) + * + * The Cancel Button callback function + * @param widget The Button widget passed to the callback function + * @param data Data sent along with the callback function + */ +void +callback_cancel_button(GtkWidget *widget, gpointer data) { + + FilterViewerData *fvd = (FilterViewerData*)data; + gtk_widget_destroy(fvd->f_window); +} + +gboolean callback_enter_check(GtkWidget *widget, + GdkEventKey *event, + gpointer user_data) +{ + g_debug("typed : %x", event->keyval); + switch(event->keyval) { + case GDK_Return: + case GDK_KP_Enter: + case GDK_ISO_Enter: + case GDK_3270_Enter: + callback_process_button(widget, user_data); + break; + default: + break; + } + return FALSE; +} + +/** + * @fn void callback_expression_field(GtkWidget*,gpointer) + * + * The Add Button callback function + * @param widget The Button widget passed to the callback function + * @param data Data sent along with the callback function + */ +void +callback_expression_field(GtkWidget *widget, gpointer data) { + + FilterViewerData *fvd = (FilterViewerData*)data; + + if(strlen(gtk_entry_get_text(GTK_ENTRY(fvd->f_expression_field))) !=0) { + gtk_widget_show(fvd->f_logical_op_junction_box); + } else { + gtk_widget_hide(fvd->f_logical_op_junction_box); + } +} + + +/** + * @fn void callback_add_button(GtkWidget*,gpointer) + * + * The Add Button callback function + * @param widget The Button widget passed to the callback function + * @param data Data sent along with the callback function + */ +void +callback_add_button(GtkWidget *widget, gpointer data) { + + g_debug("callback_add_button(): processing simple expressions"); + + unsigned i; + + FilterViewerData *fvd = (FilterViewerData*)data; + FilterViewerDataLine *fvdl = NULL; + GString* a_filter_string = g_string_new(""); + + /* + * adding linking operator to + * string + */ + GString* s; + s = g_ptr_array_index(fvd->f_logical_op_options,gtk_combo_box_get_active(GTK_COMBO_BOX(fvd->f_logical_op_junction_box))); + a_filter_string = g_string_append(a_filter_string,s->str); + gtk_combo_box_set_active(GTK_COMBO_BOX(fvd->f_logical_op_junction_box),0); + + /* begin expression */ + a_filter_string = g_string_append_c(a_filter_string,'('); + + /* + * For each simple expression, add the resulting string + * to the filter string + * + * Each simple expression takes the following schema + * [not operator '!',' '] [field type] [math operator '<','<=','>','>=','=','!='] [value] + */ + for(i=0;if_lines->len;i++) { + fvdl = (FilterViewerDataLine*)g_ptr_array_index(fvd->f_lines,i); + + s = g_ptr_array_index(fvd->f_not_op_options,gtk_combo_box_get_active(GTK_COMBO_BOX(fvdl->f_not_op_box))); + a_filter_string = g_string_append(a_filter_string,s->str); + + s = g_ptr_array_index(fvd->f_field_options,gtk_combo_box_get_active(GTK_COMBO_BOX(fvdl->f_field_box))); + a_filter_string = g_string_append(a_filter_string,s->str); + + s = g_ptr_array_index(fvd->f_math_op_options,gtk_combo_box_get_active(GTK_COMBO_BOX(fvdl->f_math_op_box))); + a_filter_string = g_string_append(a_filter_string,s->str); + + a_filter_string = g_string_append(a_filter_string,gtk_entry_get_text(GTK_ENTRY(fvdl->f_value_field))); + + s = g_ptr_array_index(fvd->f_logical_op_options,gtk_combo_box_get_active(GTK_COMBO_BOX(fvdl->f_logical_op_box))); + a_filter_string = g_string_append(a_filter_string,s->str); + + /* + * resetting simple expression lines + */ + gui_filter_line_reset(fvdl); + if(i) gui_filter_line_set_visible(fvdl,FALSE); // Only keep the first line + } + + /* end expression */ + a_filter_string = g_string_append_c(a_filter_string,')'); + + g_string_prepend(a_filter_string,gtk_entry_get_text(GTK_ENTRY(fvd->f_expression_field))); + gtk_entry_set_text(GTK_ENTRY(fvd->f_expression_field),a_filter_string->str); + +} + +/** + * @fn void callback_logical_op_box(GtkWidget*,gpointer) + * + * The logical op box callback function + * @param widget The Button widget passed to the callback function + * @param data Data sent along with the callback function + */ +void +callback_logical_op_box(GtkWidget *widget, gpointer data) { + + g_debug("callback_logical_op_box(): adding new simple expression"); + + FilterViewerData *fvd = (FilterViewerData*)data; + FilterViewerDataLine *fvdl = NULL; + + int i; + for(i=0;if_lines->len;i++) { + fvdl = (FilterViewerDataLine*)g_ptr_array_index(fvd->f_lines,i); + if(fvdl->f_logical_op_box == widget) { + if(gtk_combo_box_get_active(GTK_COMBO_BOX(fvdl->f_logical_op_box)) == 0) return; + if(i==fvd->f_lines->len-1) { /* create a new line */ + fvd->rows++; + FilterViewerDataLine* fvdl2 = gui_filter_add_line(fvd); + g_ptr_array_add(fvd->f_lines,(gpointer) fvdl2); + } else { + FilterViewerDataLine *fvdl2 = (FilterViewerDataLine*)g_ptr_array_index(fvd->f_lines,i+1); + if(!fvdl2->visible) gui_filter_line_set_visible(fvdl2,TRUE); + } + } + } + +} + +LTTV_MODULE("guifilter", "Filter window", \ + "Graphical module that let user specify their filtering options", \ + init, destroy, "lttvwindow") + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/filter/hGuiFilterInsert.xpm b/tags/lttv-0.11.3-23102008/lttv/modules/gui/filter/hGuiFilterInsert.xpm new file mode 100644 index 00000000..2b3e7f85 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/filter/hGuiFilterInsert.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static char * hGuiFilterInsert_xpm[] = { +"22 22 3 1", +" c None", +". c #000000", +"r c #FF0000", +" ", +" ", +" ..... ", +" .. .. ", +" .. .. ", +" .. .. ", +" rrr.. .. ", +" rrr.. . ", +" .. .. ", +" .. .. ", +" .. ..rrr ", +" .. ..rrr ", +" .. .. ", +" .. .. ", +" rrr.. . ", +" rrr.. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" ..... ", +" ", +" "}; diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/Makefile.am b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/Makefile.am new file mode 100644 index 00000000..3cb65bb9 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/Makefile.am @@ -0,0 +1,44 @@ +# This file is part of the Linux Trace Toolkit viewer +# Copyright (C) 2003-2004 Mathieu Desnoyers +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License Version 2 as +# published by the Free Software Foundation; +# +# 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. + + + +# +# Makefile for LTT New generation user interface : plugins. +# +# Created by Mathieu Desnoyers on May 6, 2003 +# + +AM_CFLAGS = $(GLIB_CFLAGS) +AM_CFLAGS += $(GTK_CFLAGS) +AM_CFLAGS += -fvisibility=hidden +LIBS += $(GLIB_LIBS) +LIBS += $(GTK_LIBS) -L${top_builddir}/lttv/modules/gui/lttvwindow/lttvwindow -llttvwindow + +libdir = ${lttvplugindir} + +lib_LTLIBRARIES = libguihistogram.la +libguihistogram_la_LDFLAGS = -module -avoid-version +libguihistogram_la_SOURCES = histomodule.c histoeventhooks.c histocfv.c \ + histobuttonwidget.c histodrawing.c histodrawitem.c + +noinst_HEADERS = histoeventhooks.h histocfv.h \ + histobuttonwidget.h histodrawing.h histodrawitem.h + +EXTRA_DIST = \ + hHistogramInsert.xpm stock_zoom_in_24.xpm stock_zoom_out_24.xpm \ + stock_zoom_fit_24.xpm diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/hHistogramInsert.xpm b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/hHistogramInsert.xpm new file mode 100644 index 00000000..ba18fed1 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/hHistogramInsert.xpm @@ -0,0 +1,35 @@ +/* XPM */ +static char *hHistogramInsert_xpm[]={ +"22 22 10 1", +"b c None", +". c None", +"f c #0000c0", +"e c #0000ff", +"g c #004000", +"d c #008080", +"a c #00ff00", +"c c #800000", +"# c #808000", +"h c #ffff00", +"......................", +"####aaaaaaaaaaaaaaaaaa", +".....bbbbbb####b......", +"...ccccbbbccccb#b#..##", +"....cc.bbbbcc.b.#.#.#.", +"....cc..bbbcc..ddd.#..", +"....cc..bbbcc.dd.dd...", +"....cc..bbbcc.d...d...", +"....cc..bbbcceeee.dfff", +"....ccccccccc.d...d...", +"....cc..bbbccd....dd..", +"....cc..bbbcc...eeee..", +"....cc..bbbcc......dd.", +"...#cc#####cc.......dd", +"....cc.bbbbcc..bbbbb..", +"...cccc.bbcccc.bbbbb..", +"....bbggbbbbb.ggbbbb..", +"hhhh.ghhghbbbghhghbbhh", +"g.bbbgbbgbbbbgbbg..b..", +"gbbbbgbbgbbbbgbbg..bbg", +".gbhhhhbbghhghb..ghhgb", +"bbgg...b..gg......gg.."}; diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histobuttonwidget.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histobuttonwidget.c new file mode 100644 index 00000000..279be946 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histobuttonwidget.c @@ -0,0 +1,246 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2006 Parisa Heidari + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "histobuttonwidget.h" +#include "histodrawing.h" +#include "histodrawitem.h" +#include "stock_zoom_in_24.xpm" +#include "stock_zoom_out_24.xpm" +#include "stock_zoom_fit_24.xpm" + +#define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format) +#define g_debug(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format) + +/* Preallocated Size of the index_to_pixmap array */ +#define ALLOCATE_PROCESSES 1000 + +/***************************************************************************** + * * + *****************************************************************************/ +static GtkWidget *xpm_label_box( gchar *xpm_filename, + gchar *label_text ); +static gboolean gplus( GtkWidget *widget,gpointer user_data) +{ + HistoControlFlowData *histo_cfd = (HistoControlFlowData *)user_data; + //histo_cfd->vertical_ratio =histo_cfd->vertical_ratio * (1.0/2.0); + if(histo_cfd->max_height>1) + { + histo_cfd->max_height /= 2; + //just redraw.horizontal scale is not changed so Array's data are valid. + histogram_show(histo_cfd ,0,histo_cfd->number_of_process->len); + } + else + g_warning("Zoom more than 1 event is impossible"); + + histo_drawing_update_vertical_ruler(histo_cfd->drawing);//, TimeWindow *time_window); + return 0; +} + +static gboolean gMinus( GtkWidget *widget, + gpointer user_data ) +{ + HistoControlFlowData *histo_cfd = (HistoControlFlowData *)user_data; + histo_cfd->max_height *= 2; + + //just redraw.horizontal scale is not changed so Array's data are valid. + histogram_show(histo_cfd ,0,histo_cfd->number_of_process->len); + histo_drawing_update_vertical_ruler(histo_cfd->drawing);//, TimeWindow *time_window); + return 0; +} + +static gboolean gFit( GtkWidget *widget, + gpointer user_data ) +{ + /*find the maximum value and put max_height equal with this maximum*/ + HistoControlFlowData *histo_cfd = (HistoControlFlowData *)user_data; + gint i=1,x; + guint maximum; + maximum =g_array_index(histo_cfd->number_of_process,guint,i); + for (i=1; i < histo_cfd->number_of_process-> len ;i++) + { + x=g_array_index(histo_cfd->number_of_process,guint,i); + maximum=MAX(x,maximum); + } + if (maximum >0) + { + histo_cfd->max_height=maximum; + histogram_show (histo_cfd,0,histo_cfd->number_of_process->len); + } + histo_drawing_update_vertical_ruler(histo_cfd->drawing); + + return 0; +} +/* Create a new hbox with an image and a label packed into it + * and return the box. */ + +static GtkWidget *xpm_label_box( gchar* xpm_filename, + gchar *label_text ) +{ + GtkWidget *box; + GtkWidget *label; + GtkWidget *image; + + GdkPixbuf *pixbufP; + //GError **error; + /* Create box for image and label */ + box = gtk_hbox_new (FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (box), 1); + + /* Now on to the image stuff */ + + pixbufP = gdk_pixbuf_new_from_xpm_data((const char*)xpm_filename); + image = gtk_image_new_from_pixbuf(pixbufP); + + /* Create a label for the button */ + label = gtk_label_new (label_text); + + /* Pack the image and label into the box */ + gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 1); + gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 1); + + gtk_widget_show (image); + gtk_widget_show (label); + + return box; +} + +ButtonWidget *histo_buttonwidget_construct(HistoControlFlowData *histocontrol_flow_data) +{ + GtkWidget *boxPlus, *boxMinus , *boxfit;//containing text and image for each button. + + ButtonWidget *buttonwidget = g_new(ButtonWidget,1); + buttonwidget->histo_control_flow_data = histocontrol_flow_data; + /* Put + and - on the vbox and assign related functions to each button */ + buttonwidget-> vbox1 = gtk_vbox_new (FALSE, 0); + +// Add 2 buttons on the vbox +// buttonwidget ->buttonP = gtk_button_new_with_mnemonic ("+"); +// buttonwidget->buttonM = gtk_button_new_with_mnemonic ("-"); +// Instead, add 2 button with image and text: + + buttonwidget ->buttonP =gtk_button_new (); + buttonwidget ->buttonM =gtk_button_new (); + buttonwidget ->buttonFit =gtk_button_new (); + +/* This calls our box creating function */ + boxPlus = xpm_label_box (stock_zoom_in_24, "vertical"); + boxMinus = xpm_label_box (stock_zoom_out_24, "vertical"); + boxfit = xpm_label_box (stock_zoom_fit_24, "vertical"); + +/* Pack and show all widgets */ + gtk_widget_show (boxPlus); + gtk_widget_show (boxMinus); + gtk_widget_show (boxfit); + + gtk_container_add (GTK_CONTAINER (buttonwidget -> buttonP), boxPlus); + gtk_container_add (GTK_CONTAINER (buttonwidget -> buttonM), boxMinus); + gtk_container_add (GTK_CONTAINER (buttonwidget -> buttonFit), boxfit); + + gtk_box_pack_start (GTK_BOX (buttonwidget->vbox1),buttonwidget->buttonP, TRUE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (buttonwidget->vbox1),buttonwidget->buttonM, TRUE, FALSE, 0); + gtk_box_pack_end (GTK_BOX (buttonwidget->vbox1),buttonwidget->buttonFit, TRUE, FALSE, 0); + + /* When the button receives the "clicked" signal, it will call the + * function gplus() passing it NULL as its argument. The gplus() + * function is defined above . */ + + g_signal_connect (G_OBJECT (buttonwidget ->buttonP), "clicked", + G_CALLBACK (gplus), (gpointer)histocontrol_flow_data); + g_signal_connect (G_OBJECT ( buttonwidget->buttonM), "clicked", + G_CALLBACK (gMinus), (gpointer)histocontrol_flow_data); + g_signal_connect (G_OBJECT ( buttonwidget->buttonFit), "clicked", + G_CALLBACK (gFit), (gpointer)histocontrol_flow_data); + + gtk_widget_show (buttonwidget -> vbox1); + gtk_widget_show (buttonwidget ->buttonP); + gtk_widget_show (buttonwidget ->buttonM); + gtk_widget_show (buttonwidget ->buttonFit); + + return buttonwidget; +} + +void histo_buttonwidget_destroy(ButtonWidget *buttonwidget) +{ + g_debug("buttonwidget_destroy %p", buttonwidget); + + g_free(buttonwidget); + g_debug("buttonwidget_destroy end"); +} + +GtkWidget *histo_buttonwidget_get_widget(ButtonWidget *button_widget) +{ + return button_widget->vbox1; +} + + + +void histo_rectangle_pixmap (GdkGC *gc, + gboolean filled, gint x, gint y, gint width, gint height, + histoDrawing_t *value) +{ + if(height == -1) + height = value->drawing_area->allocation.height; + if(width == -1) + height = value->drawing_area->allocation.width; + gdk_draw_rectangle (value->pixmap, + gc, + filled, + x, y, + width, height); +} + +//This could be usefull if a vertical scroll bar is added to viewer: +void histo_copy_pixmap_region(histoDrawing_t *drawing,GdkDrawable *dest, + GdkGC *gc, GdkDrawable *src, + gint xsrc, gint ysrc, + gint xdest, gint ydest, gint width, gint height) +{ + + if(dest == NULL) + dest = drawing->pixmap; + if(src == NULL) + src = drawing->pixmap; + + gdk_draw_drawable (dest,gc,src,xsrc, ysrc, + xdest, ydest,width, height); +} + +void histo_update_pixmap_size(histoDrawing_t *value, + guint width) +{ + GdkPixmap *old_pixmap = value->pixmap; + + value->pixmap = + gdk_pixmap_new(old_pixmap, + width, + value->height, + -1); + + gdk_pixmap_unref(old_pixmap); +} + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histobuttonwidget.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histobuttonwidget.h new file mode 100644 index 00000000..7c2ed889 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histobuttonwidget.h @@ -0,0 +1,80 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2006 Parisa Heidari + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 _HISTOBUTTONWIDGET_H +#define _HISTOBUTTONWIDGET_H + +#include +#include +#include +#include "histocfv.h" +#include "histodrawitem.h" + + +/* The ButtonWidget + * + * Tasks : + * Create a widget + * containing 3 buttons zoomIn,zoonOut and zoomFit to change the vertical scale. + * + */ +#ifndef TYPE_ButtonWidget_DEFINED +#define TYPE_ButtonWidget_DEFINED +typedef struct _ButtonWidget ButtonWidget; +#endif //TYPE_ButtonWidget_DEFINED + +#ifndef TYPE_HistoControlFlowData_DEFINED +#define TYPE_HistoControlFlowData_DEFINED +typedef struct _HistoControlFlowData HistoControlFlowData; +#endif //TYPE_HistoControlFlowData_DEFINED + +struct _ButtonWidget { + + GtkWidget *buttonP; + GtkWidget *buttonM; + GtkWidget *buttonFit; + + GtkWidget *vbox1;//buttons are placed on this vbox + + GtkWidget *hbox;//Parent Widget containing buttons and drawing area. + HistoControlFlowData *histo_control_flow_data; + +}; + + +void histo_copy_pixmap_region(histoDrawing_t *drawing,GdkDrawable *dest, + GdkGC *gc, GdkDrawable *src, + gint xsrc, gint ysrc, + gint xdest, gint ydest, gint width, gint height); + +void histo_rectangle_pixmap (GdkGC *gc,gboolean filled, gint x, gint y, + gint width, gint height,histoDrawing_t *value); + +ButtonWidget *histo_buttonwidget_construct(HistoControlFlowData *histocontrol_flow_data); + +void histo_buttonwidget_destroy(ButtonWidget *buttonwidget); + + +static gboolean gplus( GtkWidget *widget,gpointer user_data);//assigned to zoomIn +static gboolean gMinus( GtkWidget *widget,gpointer user_data );//assigned to zoomOut +static gboolean gFit( GtkWidget *widget,gpointer user_data );//assigned to zoomFit + +GtkWidget *histo_buttonwidget_get_widget(ButtonWidget *button_widget); +void histo_update_pixmap_size(histoDrawing_t *value, + guint width); +#endif //_HISTOBUTTONWIDGET_H diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histocfv.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histocfv.c new file mode 100644 index 00000000..80c5c5ac --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histocfv.c @@ -0,0 +1,242 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2006 Parisa heidari (inspired from CFV by Mathieu Desnoyers) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "histocfv.h" +#include "histodrawing.h" +#include "histobuttonwidget.h" +#include "histoeventhooks.h" + +#define PREDEFINED_HEIGHT 5000 + +extern GSList *g_histo_control_flow_data_list; + +static gboolean +header_size_allocate(GtkWidget *widget, + GtkAllocation *allocation, + gpointer user_data) +{ + histoDrawing_t *drawing = (histoDrawing_t*)user_data; + + gtk_widget_set_size_request(drawing->ruler, -1, allocation->height); + //gtk_widget_queue_resize(drawing->padding); + //gtk_widget_queue_resize(drawing->ruler); + gtk_container_check_resize(GTK_CONTAINER(drawing->ruler_hbox)); + return 0; +} + + +/***************************************************************************** + * Histo Control Flow Viewer class implementation * + *****************************************************************************/ +/** + * Histo Control Flow Viewer's constructor + * + * This constructor is given as a parameter to the menuitem and toolbar button + * registration. It creates the drawing widget. + * @param ParentWindow A pointer to the parent window. + * @return The widget created. + */ +HistoControlFlowData * +guihistocontrolflow(LttvPluginTab *ptab) +{ + GtkWidget *button_widget, *drawing_widget, *drawing_area; + GtkWidget *buttonP,*buttonM; + histoDrawing_t *drawing; + HistoControlFlowData* histo_control_flow_data = g_new(HistoControlFlowData,1) ; + + histo_control_flow_data->ptab = ptab; + histo_control_flow_data->tab = ptab->tab; + histo_control_flow_data->max_height = PREDEFINED_HEIGHT; + + /*histo_control_flow_data->v_adjust = + GTK_ADJUSTMENT(gtk_adjustment_new( 0.0, // Value + 0.0, // Lower + 0.0, // Upper + 0.0, // Step inc. + 0.0, // Page inc. + 0.0)); // page size */ + + // Create the drawing + histo_control_flow_data->drawing = histo_drawing_construct(histo_control_flow_data); + + drawing = histo_control_flow_data->drawing; + drawing_widget = histo_drawing_get_widget(drawing); + + drawing_area = histo_drawing_get_drawing_area(drawing); + + histo_control_flow_data->number_of_process = 0; + + ///histo_control_flow_data->background_info_waiting = 0; + + // Create the Button widget + histo_control_flow_data->buttonwidget = histo_buttonwidget_construct(histo_control_flow_data); + + button_widget = histo_buttonwidget_get_widget( histo_control_flow_data-> buttonwidget); + buttonP =histo_control_flow_data-> buttonwidget->buttonP; + buttonM =histo_control_flow_data-> buttonwidget->buttonM; + + //set the size of ruler fix + gtk_widget_set_size_request(histo_control_flow_data->drawing->ruler, -1, 25); + gtk_container_check_resize(GTK_CONTAINER(histo_control_flow_data->drawing->ruler_hbox)); + +/*//or set the size of ruler by button P + g_signal_connect (G_OBJECT(buttonP), + "size-allocate", + G_CALLBACK(header_size_allocate), + (gpointer)histo_control_flow_data->drawing);*/ + + + ///histo_control_flow_data->h_paned = gtk_hpaned_new(); + + ///changed for histogram + histo_control_flow_data->box = gtk_hbox_new(FALSE, 0); + histo_control_flow_data->ev_box = gtk_event_box_new(); + + /// histo_control_flow_data->top_widget =gtk_event_box_new(); + gtk_container_add(GTK_CONTAINER(histo_control_flow_data->ev_box), + drawing_widget); + ///Now add button widget and drawing area on the top_widget. + gtk_box_pack_start (GTK_BOX (histo_control_flow_data->box), + button_widget,FALSE,FALSE, 10); + gtk_box_pack_end (GTK_BOX (histo_control_flow_data->box), + histo_control_flow_data->ev_box,TRUE,TRUE, 0); + histo_control_flow_data->top_widget = histo_control_flow_data->box; + + /*gtk_container_add(GTK_CONTAINER(histo_control_flow_data->box), + histo_control_flow_data->h_paned); + + gtk_paned_pack1(GTK_PANED(histo_control_flow_data->h_paned), + button_widget->vbox1, FALSE, TRUE); + gtk_paned_pack2(GTK_PANED(histo_control_flow_data->h_paned), + drawing_widget, TRUE, TRUE); + */ + gtk_container_set_border_width(GTK_CONTAINER(histo_control_flow_data->ev_box), 1); + + // Set the size of the drawing area + //drawing_Resize(drawing, h, w); + + // Get trace statistics + //histo_control_flow_data->Trace_Statistics = get_trace_statistics(Trace); + + gtk_widget_show(drawing_widget); + gtk_widget_show(button_widget); + /*gtk_widget_show(histo_control_flow_data->h_paned);*/ + gtk_widget_show(histo_control_flow_data->box); + gtk_widget_show(histo_control_flow_data->ev_box); + gtk_widget_show(histo_control_flow_data->top_widget); + g_object_set_data_full( + G_OBJECT(histo_control_flow_data->top_widget), + "histo_control_flow_data", + histo_control_flow_data, + (GDestroyNotify)guihistocontrolflow_destructor); + + g_object_set_data( + G_OBJECT(drawing_area), + "histo_control_flow_data", + histo_control_flow_data); + + g_histo_control_flow_data_list = g_slist_append( + g_histo_control_flow_data_list, + histo_control_flow_data); + histo_control_flow_data->number_of_process =g_array_new (FALSE, + TRUE, + sizeof(guint)); + g_array_set_size (histo_control_flow_data->number_of_process, + drawing_area->allocation.width); + + //WARNING : The widget must be + //inserted in the main window before the drawing area + //can be configured (and this must happend before sending + //data) + + return histo_control_flow_data; + +} + +/* Destroys widget also */ +void guihistocontrolflow_destructor_full(HistoControlFlowData *histo_control_flow_data) +{ + g_info("HISTOCFV.c : guihistocontrolflow_destructor_full, %p", histo_control_flow_data); + /* May already have been done by GTK window closing */ + if(GTK_IS_WIDGET(guihistocontrolflow_get_widget(histo_control_flow_data))) + gtk_widget_destroy(guihistocontrolflow_get_widget(histo_control_flow_data)); + //histo_control_flow_data->mw = NULL; + //FIXME guihistocontrolflow_destructor(histo_control_flow_data); +} + +/* When this destructor is called, the widgets are already disconnected */ +void guihistocontrolflow_destructor(HistoControlFlowData *histo_control_flow_data) +{ + Tab *tab = histo_control_flow_data->tab; + + g_info("HISTOCFV.c : guihistocontrolflow_destructor, %p", histo_control_flow_data); + g_info("%p, %p, %p", histo_update_time_window_hook, histo_control_flow_data, tab); + if(GTK_IS_WIDGET(guihistocontrolflow_get_widget(histo_control_flow_data))) + g_info("widget still exists"); + + /* ButtonWidget is removed with it's widget */ + //buttonwidget_destroy(histo_control_flow_data->buttonwidget); + if(tab != NULL) + { + // Delete reading hooks + lttvwindow_unregister_traceset_notify(tab, + histo_traceset_notify, + histo_control_flow_data); + + lttvwindow_unregister_time_window_notify(tab, + histo_update_time_window_hook, + histo_control_flow_data); + + lttvwindow_unregister_current_time_notify(tab, + histo_update_current_time_hook, + histo_control_flow_data); + + lttvwindow_unregister_redraw_notify(tab, histo_redraw_notify, histo_control_flow_data); + lttvwindow_unregister_continue_notify(tab, + histo_continue_notify, + histo_control_flow_data); + + lttvwindow_events_request_remove_all(histo_control_flow_data->tab, + histo_control_flow_data); + + lttvwindow_unregister_filter_notify(tab, + histo_filter_changed, histo_control_flow_data); + + } + lttvwindowtraces_background_notify_remove(histo_control_flow_data); + g_histo_control_flow_data_list = + g_slist_remove(g_histo_control_flow_data_list,histo_control_flow_data); + + g_array_free(histo_control_flow_data->number_of_process, TRUE); + + g_info("HISTOCFV.c : guihistocontrolflow_destructor end, %p", histo_control_flow_data); + g_free(histo_control_flow_data); + +} + + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histocfv.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histocfv.h new file mode 100644 index 00000000..6f6ba95b --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histocfv.h @@ -0,0 +1,98 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2006 Parisa heidari (inspired from CFV by Mathieu Desnoyers) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 _HISTOCFV_H +#define _HISTOCFV_H + +#include +#include +#include + +//#include "histobuttonwidget.h" + +extern GQuark LTT_NAME_CPU; + + +#ifndef TYPE_histoDrawing_t_DEFINED +#define TYPE_histoDrawing_t_DEFINED +typedef struct _histoDrawing_t histoDrawing_t; +#endif //TYPE_histoDrawing_t_DEFINED + +#ifndef TYPE_ButtonWidget_DEFINED +#define TYPE_ButtonWidget_DEFINED +typedef struct _ButtonWidget ButtonWidget; +#endif //TYPE_ButtonWidget_DEFINED + +#ifndef TYPE_HistoControlFlowData_DEFINED +#define TYPE_HistoControlFlowData_DEFINED +typedef struct _HistoControlFlowData HistoControlFlowData; +#endif //TYPE_HistoControlFlowData_DEFINED + + + +struct _HistoControlFlowData { + + GtkWidget *top_widget;//The hbox containing buttons and drawing area. + LttvPluginTab *ptab; + Tab *tab; + GtkWidget *box; + GtkWidget *ev_box;//for histogram + ButtonWidget *buttonwidget; + + histoDrawing_t *drawing; + //GtkAdjustment *v_adjust ;//may be used later for scrollbar + + /* Shown events information */ +// TimeWindow time_window; +// LttTime current_time; + + //guint currently_Selected_Event ; + GArray *number_of_process;//number of events + guint background_info_waiting; /* Number of background requests waited for + in order to have all the info ready. */ +// For histogram + guint max_height; + + LttvFilter *histo_main_win_filter; + gboolean chunk_has_begun; +} ; + +/* Control Flow Data constructor */ +HistoControlFlowData *guihistocontrolflow(LttvPluginTab *ptab); +void +guihistocontrolflow_destructor_full(HistoControlFlowData *histo_control_flow_data); +void +guihistocontrolflow_destructor(HistoControlFlowData *histo_control_flow_data); + +static inline GtkWidget *guihistocontrolflow_get_widget( + HistoControlFlowData *histo_control_flow_data) +{ + return histo_control_flow_data->top_widget ; +} + +static inline ButtonWidget *guihistocontrolflow_get_buttonwidget + (HistoControlFlowData *histo_control_flow_data) +{ + return histo_control_flow_data->buttonwidget ; +} + + + +#endif // _HISTOCFV_H diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histodrawing.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histodrawing.c new file mode 100644 index 00000000..53addbce --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histodrawing.c @@ -0,0 +1,1285 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2006 Parisa heidari (inspired from CFV by Mathieu Desnoyers) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "histodrawing.h" +#include "histoeventhooks.h" +#include "histocfv.h" + +#define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format) +#define g_debug(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format) + +//FIXME +// fixed #define TRACE_NUMBER 0 +#define EXTRA_ALLOC 1024 // pixels +#define padding_width 50 + +#if 0 /* colors for two lines representation */ +GdkColor histo_drawing_colors[NUM_COLORS] = +{ /* Pixel, R, G, B */ + { 0, 0, 0, 0 }, /* COL_BLACK */ + { 0, 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_WHITE */ + { 0, 0x0FFF, 0xFFFF, 0xFFFF }, /* COL_WAIT_FORK : pale blue */ + { 0, 0xFFFF, 0xFFFF, 0x0000 }, /* COL_WAIT_CPU : yellow */ + { 0, 0xFFFF, 0xA000, 0xFCFF }, /* COL_EXIT : pale magenta */ + { 0, 0xFFFF, 0x0000, 0xFFFF }, /* COL_ZOMBIE : purple */ + { 0, 0xFFFF, 0x0000, 0x0000 }, /* COL_WAIT : red */ + { 0, 0x0000, 0xFFFF, 0x0000 }, /* COL_RUN : green */ + { 0, 0x8800, 0xFFFF, 0x8A00 }, /* COL_USER_MODE : pale green */ + { 0, 0x09FF, 0x01FF, 0xFFFF }, /* COL_SYSCALL : blue */ + { 0, 0xF900, 0x4200, 0xFF00 }, /* COL_TRAP : pale purple */ + { 0, 0xFFFF, 0x5AFF, 0x01FF }, /* COL_IRQ : orange */ + { 0, 0xFFFF, 0xFFFF, 0xFFFF } /* COL_MODE_UNKNOWN : white */ + +}; +#endif //0 + + + GdkColor histo_drawing_colors[NUM_COLORS] = +{ /* Pixel, R, G, B */ + { 0, 0, 0, 0 }, /* COL_BLACK */ + { 0, 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_WHITE */ + { 0, 0x0000, 0xFF00, 0x0000 }, /* COL_RUN_USER_MODE : green */ + { 0, 0x0100, 0x9E00, 0xFFFF }, /* COL_RUN_SYSCALL : pale blue */ + { 0, 0xFF00, 0xFF00, 0x0100 }, /* COL_RUN_TRAP : yellow */ + { 0, 0xFFFF, 0x5E00, 0x0000 }, /* COL_RUN_IRQ : red */ + { 0, 0x6600, 0x0000, 0x0000 }, /* COL_WAIT : dark red */ + { 0, 0x7700, 0x7700, 0x0000 }, /* COL_WAIT_CPU : dark yellow */ + { 0, 0x6400, 0x0000, 0x5D00 }, /* COL_ZOMBIE : dark purple */ + { 0, 0x0700, 0x6400, 0x0000 }, /* COL_WAIT_FORK : dark green */ + { 0, 0x8900, 0x0000, 0x8400 }, /* COL_EXIT : "less dark" magenta */ + { 0, 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_MODE_UNKNOWN : white */ + { 0, 0xFFFF, 0xFFFF, 0xFFFF } /* COL_UNNAMED : white */ + +}; + +/* +RUN+USER MODE green +RUN+SYSCALL +RUN+TRAP +RUN+IRQ +WAIT+foncé +WAIT CPU + WAIT FORK vert foncé ou jaune +IRQ rouge +TRAP: orange +SYSCALL: bleu pâle + +ZOMBIE + WAIT EXIT +*/ + + +/***************************************************************************** + * drawing functions * + *****************************************************************************/ + +static gboolean +histo_expose_ruler( GtkWidget *widget, GdkEventExpose *event, gpointer user_data ); + +static gboolean +histo_expose_vertical_ruler( GtkWidget *widget, GdkEventExpose *event, gpointer user_data ); + +static gboolean +histo_motion_notify_ruler(GtkWidget *widget, GdkEventMotion *event, gpointer user_data); + +static gboolean +histo_motion_notify_vertical_ruler(GtkWidget *widget, GdkEventMotion *event, gpointer user_data); + +/* Function responsible for updating the exposed area. + * It must do an events request to the lttvwindow API to ask for this update. + * Note : this function cannot clear the background, because it may + * erase drawing already present (SAFETY). + */ +void histo_drawing_data_request(histoDrawing_t *drawing, + gint x, gint y, + gint width, + gint height) +{ + +} + + +void histo_drawing_data_request_begin(EventsRequest *events_request, LttvTracesetState *tss) +{ + g_debug("Begin of data request"); + HistoControlFlowData *cfd = events_request->viewer_data; + LttvTracesetContext *tsc = LTTV_TRACESET_CONTEXT(tss); + TimeWindow time_window = + lttvwindow_get_time_window(cfd->tab); + + guint width = cfd->drawing->width; + guint x=0; + + cfd->drawing->last_start = events_request->start_time; + + histo_convert_time_to_pixels( + time_window, + events_request->start_time, + width, + &x); + + } + +void histo_drawing_chunk_begin(EventsRequest *events_request, LttvTracesetState *tss) +{ + g_debug("Begin of chunk"); + HistoControlFlowData *cfd = events_request->viewer_data; + LttvTracesetContext *tsc = LTTV_TRACESET_CONTEXT(tss); + + if(cfd->chunk_has_begun) return; + + cfd->chunk_has_begun = TRUE; +} + + +void histo_drawing_request_expose(EventsRequest *events_request, + LttvTracesetState *tss, + LttTime end_time) +{ + HistoControlFlowData *cfd = events_request->viewer_data; + histoDrawing_t *drawing = cfd->drawing; + + gint x, x_end, width; + LttvTracesetContext *tsc = (LttvTracesetContext*)tss; + + TimeWindow time_window = + lttvwindow_get_time_window(cfd->tab); + + g_debug("histo request expose"); + + histo_convert_time_to_pixels( + time_window, + end_time, + drawing->width, + &x_end); + x = drawing->damage_begin; + + width = x_end - x; + + drawing->damage_begin = x+width; + + // FIXME ? + //changed for histogram: + gtk_widget_queue_draw_area ( drawing->drawing_area, + x, 0, + width, + drawing->/*drawing_area->allocation.*/height); + + // Update directly when scrolling + gdk_window_process_updates(drawing->drawing_area->window, + TRUE); +} + + +/* Callbacks */ + + +/* Create a new backing pixmap of the appropriate size */ +/* As the scaling will always change, it's of no use to copy old + * pixmap. + * + * Change the size if width or height changes. + * (different from control flow viewer!) + */ +static gboolean +histo_configure_event( GtkWidget *widget, GdkEventConfigure *event, + gpointer user_data) +{ + histoDrawing_t *drawing = (histoDrawing_t*)user_data; + + /* First, get the new time interval of the main window */ + /* we assume (see documentation) that the main window + * has updated the time interval before this configure gets + * executed. + */ + //lttvwindow_get_time_window(drawing->histo_control_flow_data->mw, + // &drawing->histo_control_flow_data->time_window); + + /* New pixmap, size of the configure event */ + //GdkPixmap *pixmap = gdk_pixmap_new(widget->window, + // widget->allocation.width + SAFETY, + // widget->allocation.height + SAFETY, + // -1); + g_debug("drawing configure event"); + g_debug("New alloc draw size : %i by %i",widget->allocation.width, + widget->allocation.height); + +/*modified for histogram, if width is not changed, GArray is valid so + just redraw, else recalculate all.(event request again)*/ + +//enabled again for histogram: + if(drawing->pixmap) + gdk_pixmap_unref(drawing->pixmap); + + drawing->pixmap = gdk_pixmap_new(widget->window, + widget->allocation.width ,//+ SAFETY + EXTRA_ALLOC, + widget->allocation.height + EXTRA_ALLOC, + -1); + +//end add + drawing->alloc_width = drawing->width + SAFETY + EXTRA_ALLOC; + drawing->alloc_height = drawing->height + EXTRA_ALLOC; + + //drawing->height = widget->allocation.height; + + + +// Clear the image +// gdk_draw_rectangle (drawing->pixmap, +// widget->style->black_gc, +// TRUE, +// 0, 0, +// drawing->width+SAFETY, +// drawing->height); + + //g_info("init data request"); + + + /* Initial data request */ + /* no, do initial data request in the expose event */ + // Do not need to ask for data of 1 pixel : not synchronized with + // main window time at this moment. + //histo_drawing_data_request(drawing, &drawing->pixmap, 0, 0, + // widget->allocation.width, + // widget->allocation.height); + + //drawing->width = widget->allocation.width; + //drawing->height = widget->allocation.height; + + drawing->damage_begin = 0; + drawing->damage_end = widget->allocation.width; + + if((widget->allocation.width != 1 && + widget->allocation.height != 1) + /*&& drawing->damage_begin < drawing->damage_end */) + { + + gdk_draw_rectangle (drawing->pixmap, + drawing->drawing_area->style->black_gc, + TRUE, + 0, 0, + drawing->drawing_area->allocation.width, drawing->drawing_area->allocation.height); + /* histo_drawing_data_request(drawing, + drawing->damage_begin, + 0, + drawing->damage_end - drawing->damage_begin, + drawing->height);*/ + } +//modified for histogram + + if(widget->allocation.width == drawing->width) + { + + drawing->height = widget->allocation.height; + histogram_show( drawing->histo_control_flow_data,0, + drawing->histo_control_flow_data->number_of_process->len); + } + else + { + drawing->width = widget->allocation.width; + drawing->height = widget->allocation.height; + + g_array_set_size (drawing->histo_control_flow_data->number_of_process, + widget->allocation.width); + histo_request_event( drawing->histo_control_flow_data,drawing->damage_begin + ,drawing->damage_end - drawing->damage_begin); + } + return TRUE; +} + + +/* Redraw the screen from the backing pixmap */ +static gboolean +histo_expose_event( GtkWidget *widget, GdkEventExpose *event, gpointer user_data ) +{ + histoDrawing_t *drawing = (histoDrawing_t*)user_data; + + HistoControlFlowData *histo_control_flow_data = + (HistoControlFlowData*)g_object_get_data( + G_OBJECT(widget), + "histo_control_flow_data"); +#if 0 + if(unlikely(drawing->gc == NULL)) { + drawing->gc = gdk_gc_new(drawing->drawing_area->window); + gdk_gc_copy(drawing->gc, drawing->drawing_area->style->black_gc); + } +#endif //0 + TimeWindow time_window = + lttvwindow_get_time_window(histo_control_flow_data->tab); + LttTime current_time = + lttvwindow_get_current_time(histo_control_flow_data->tab); + + guint cursor_x=0; + + LttTime window_end = time_window.end_time; + + + /* update the screen from the pixmap buffer */ +//added again for histogram: + + gdk_draw_pixmap(widget->window, + widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + drawing->pixmap, + event->area.x, event->area.y, + event->area.x, event->area.y, + event->area.width, event->area.height); + //0 + + drawing->height = drawing-> drawing_area ->allocation.height; + +#if 0 + copy_pixmap_to_screen(histo_control_flow_data->process_list, + widget->window, + widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + event->area.x, event->area.y, + event->area.width, event->area.height); +#endif //0 + + + /* //disabled for histogram + copy_pixmap_to_screen(histo_control_flow_data->process_list, + widget->window, + drawing->gc, + event->area.x, event->area.y, + event->area.width, event->area.height);*/ + + /* Erase the dotted lines left.. */ + if(widget->allocation.height > drawing->height) + { + gdk_draw_rectangle (widget->window, + drawing->drawing_area->style->black_gc, + TRUE, + event->area.x, drawing->height, + event->area.width, // do not overlap + widget->allocation.height - drawing->height); + } + if(ltt_time_compare(time_window.start_time, current_time) <= 0 && + ltt_time_compare(window_end, current_time) >= 0) + { + /* Draw the dotted lines */ + histo_convert_time_to_pixels( + time_window, + current_time, + drawing->width, + &cursor_x); + +#if 0 + if(drawing->dotted_gc == NULL) { + + drawing->dotted_gc = gdk_gc_new(drawing->drawing_area->window); + gdk_gc_copy(drawing->dotted_gc, widget->style->white_gc); + + gint8 dash_list[] = { 1, 2 }; + gdk_gc_set_line_attributes(drawing->dotted_gc, + 1, + GDK_LINE_ON_OFF_DASH, + GDK_CAP_BUTT, + GDK_JOIN_MITER); + gdk_gc_set_dashes(drawing->dotted_gc, + 0, + dash_list, + 2); + } +#endif //0 + gint height_tot = MAX(widget->allocation.height, drawing->height); + gdk_draw_line(widget->window, + drawing->dotted_gc, + cursor_x, 0, + cursor_x, height_tot); + } + + return FALSE; +} + +static gboolean +histo_after_expose_event( GtkWidget *widget, GdkEventExpose *event, gpointer user_data ) +{ + //g_assert(0); + g_debug("AFTER EXPOSE"); + + return FALSE; +} + +/* mouse click */ +static gboolean +histo_button_press_event( GtkWidget *widget, GdkEventButton *event, gpointer user_data ) +{ + HistoControlFlowData *histo_control_flow_data = + (HistoControlFlowData*)g_object_get_data( + G_OBJECT(widget), + "histo_control_flow_data"); + histoDrawing_t *drawing = histo_control_flow_data->drawing; + TimeWindow time_window = + lttvwindow_get_time_window(histo_control_flow_data->tab); + + g_debug("click"); + if(event->button == 1) + { + LttTime time; + + /* left mouse button click */ + g_debug("x click is : %f", event->x); + + histo_convert_pixels_to_time(drawing->width, (guint)event->x, + time_window, + &time); + + lttvwindow_report_current_time(histo_control_flow_data->tab, time); + ////report event->y for vertical zoom +,- + } + + return FALSE; +} +/* + //Viewer's vertical scroll bar is already omitted, not needed for histogram. +static gboolean +scrollbar_size_allocate(GtkWidget *widget, + GtkAllocation *allocation, + gpointer user_data) +{ + histoDrawing_t *drawing = (histoDrawing_t*)user_data; + + gtk_widget_set_size_request(drawing->padding, allocation->width, -1); + //gtk_widget_queue_resize(drawing->padding); + //gtk_widget_queue_resize(drawing->ruler); + gtk_container_check_resize(GTK_CONTAINER(drawing->ruler_hbox)); + return 0; +} +*/ + + +histoDrawing_t *histo_drawing_construct(HistoControlFlowData *histo_control_flow_data) +{ + histoDrawing_t *drawing = g_new(histoDrawing_t, 1); + + drawing->histo_control_flow_data = histo_control_flow_data; + + drawing->vbox = gtk_vbox_new(FALSE, 1); + + + drawing->ruler_hbox = gtk_hbox_new(FALSE, 1); + drawing->ruler = gtk_drawing_area_new (); + //gtk_widget_set_size_request(drawing->ruler, -1, 27); + + drawing->padding = gtk_drawing_area_new (); + //gtk_widget_set_size_request(drawing->padding, -1, 27); + + gtk_box_pack_start(GTK_BOX(drawing->ruler_hbox), drawing->padding,FALSE, FALSE, 0); + + gtk_box_pack_end(GTK_BOX(drawing->ruler_hbox), drawing->ruler, + TRUE, TRUE, 0); + + drawing->drawing_area = gtk_drawing_area_new (); + + drawing->gc = NULL; + /* + ///at this time not necessary for histogram + drawing->hbox = gtk_hbox_new(FALSE, 1); + + drawing->viewport = gtk_viewport_new(NULL, histo_control_flow_data->v_adjust); + drawing->scrollbar = gtk_vscrollbar_new(histo_control_flow_data->v_adjust); + gtk_box_pack_start(GTK_BOX(drawing->hbox), drawing->viewport, + TRUE, TRUE, 0); + gtk_box_pack_end(GTK_BOX(drawing->hbox), drawing->scrollbar, + FALSE, FALSE, 0); + gtk_container_add(GTK_CONTAINER(drawing->viewport), + drawing->drawing_area);*/ + + //add vertical ruler: + drawing->vruler_drawing_hbox = gtk_hbox_new(FALSE, 1); + drawing-> vertical_ruler =gtk_drawing_area_new (); + gtk_box_pack_start(GTK_BOX(drawing->vruler_drawing_hbox), drawing->vertical_ruler, + FALSE, FALSE, 0); + gtk_box_pack_end(GTK_BOX(drawing->vruler_drawing_hbox), drawing->drawing_area, + TRUE, TRUE, 1); + gtk_widget_set_size_request(drawing->vertical_ruler, padding_width, -1); + + gtk_box_pack_start(GTK_BOX(drawing->vbox), drawing->ruler_hbox, + FALSE, FALSE, 1); + gtk_box_pack_end(GTK_BOX(drawing->vbox), drawing->vruler_drawing_hbox/*drawing_area*/, + TRUE, TRUE, 1); + + drawing->pango_layout = + gtk_widget_create_pango_layout(drawing->drawing_area, NULL); + + drawing->height = 1; + drawing->width = 1; + drawing->depth = 0; + drawing->alloc_height = 1; + drawing->alloc_width = 1; + + drawing->damage_begin = 0; + drawing->damage_end = 0; + drawing->horizontal_sel = -1; + + //gtk_widget_set_size_request(drawing->drawing_area->window, 50, 50); + g_object_set_data_full( + G_OBJECT(drawing->drawing_area), + "histo_Link_drawing_Data", + drawing, + (GDestroyNotify)histo_drawing_destroy); + + g_object_set_data( + G_OBJECT(drawing->ruler), + "histo_drawing", + drawing); + + g_object_set_data( + G_OBJECT(drawing->vertical_ruler), + "histo_drawing", + drawing); + + //gtk_widget_modify_bg( drawing->drawing_area, + // GTK_STATE_NORMAL, + // &CF_Colors[BLACK]); + + //gdk_window_get_geometry(drawing->drawing_area->window, + // NULL, NULL, + // &(drawing->width), + // &(drawing->height), + // -1); + + //drawing->pixmap = gdk_pixmap_new( + // drawing->drawing_area->window, + // drawing->width, + // drawing->height, + // drawing->depth); + + drawing->pixmap = NULL; + +// drawing->pixmap = gdk_pixmap_new(drawing->drawing_area->window, +// drawing->drawing_area->allocation.width, +// drawing->drawing_area->allocation.height, +// -1); + + g_signal_connect (G_OBJECT(drawing->drawing_area), + "configure_event", + G_CALLBACK (histo_configure_event), + (gpointer)drawing); + + g_signal_connect (G_OBJECT(drawing->ruler), + "expose_event", + G_CALLBACK(histo_expose_ruler), + (gpointer)drawing); + + gtk_widget_add_events(drawing->ruler, GDK_POINTER_MOTION_MASK); + gtk_widget_add_events(drawing->vertical_ruler, GDK_POINTER_MOTION_MASK); + + g_signal_connect (G_OBJECT(drawing->ruler), + "motion-notify-event", + G_CALLBACK(histo_motion_notify_ruler), + (gpointer)drawing); + + + g_signal_connect (G_OBJECT(drawing->vertical_ruler), + "expose_event", + G_CALLBACK(histo_expose_vertical_ruler), + (gpointer)drawing); + + g_signal_connect (G_OBJECT(drawing->vertical_ruler), + "motion-notify-event", + G_CALLBACK(histo_motion_notify_vertical_ruler), + (gpointer)drawing); + +/*//not necessary for historam. + g_signal_connect (G_OBJECT(drawing->drawing_area), + "size-allocate", + G_CALLBACK(scrollbar_size_allocate), + (gpointer)drawing); */ + + + gtk_widget_set_size_request(drawing->padding, padding_width, -1);//use it for vertical ruler + + g_signal_connect (G_OBJECT(drawing->drawing_area), + "expose_event", + G_CALLBACK (histo_expose_event), + (gpointer)drawing); + + g_signal_connect_after (G_OBJECT(drawing->drawing_area), + "expose_event", + G_CALLBACK (histo_after_expose_event), + (gpointer)drawing); + + g_signal_connect (G_OBJECT(drawing->drawing_area), + "button-press-event", + G_CALLBACK (histo_button_press_event), + (gpointer)drawing); + + gtk_widget_show(drawing->ruler); + gtk_widget_show(drawing->padding); + gtk_widget_show(drawing->ruler_hbox); + gtk_widget_show(drawing->vertical_ruler); + gtk_widget_show(drawing->vruler_drawing_hbox); + gtk_widget_show(drawing->drawing_area); + + /// gtk_widget_show(drawing->viewport); + /// gtk_widget_show(drawing->scrollbar); + /// gtk_widget_show(drawing->hbox); + + /* Allocate the colors */ + GdkColormap* colormap = gdk_colormap_get_system(); + gboolean success[NUM_COLORS]; + gdk_colormap_alloc_colors(colormap, histo_drawing_colors, NUM_COLORS, FALSE, + TRUE, success); + + drawing->gc = + gdk_gc_new(GDK_DRAWABLE(main_window_get_widget(histo_control_flow_data->tab)->window)); + drawing->dotted_gc = + gdk_gc_new(GDK_DRAWABLE(main_window_get_widget(histo_control_flow_data->tab)->window)); + + gdk_gc_copy(drawing->gc, + main_window_get_widget(histo_control_flow_data->tab)->style->black_gc); + gdk_gc_copy(drawing->dotted_gc, + main_window_get_widget(histo_control_flow_data->tab)->style->white_gc); + + gint8 dash_list[] = { 1, 2 }; + gdk_gc_set_line_attributes(drawing->dotted_gc, + 1, + GDK_LINE_ON_OFF_DASH, + GDK_CAP_BUTT, + GDK_JOIN_MITER); + gdk_gc_set_dashes(drawing->dotted_gc, + 0, + dash_list, + 2); + + drawing->ruler_gc_butt = + gdk_gc_new(GDK_DRAWABLE(main_window_get_widget(histo_control_flow_data->tab)->window)); + gdk_gc_copy(drawing->ruler_gc_butt, + main_window_get_widget(histo_control_flow_data->tab)->style->black_gc); + drawing->ruler_gc_round = + gdk_gc_new(GDK_DRAWABLE(main_window_get_widget(histo_control_flow_data->tab)->window)); + gdk_gc_copy(drawing->ruler_gc_round, + main_window_get_widget(histo_control_flow_data->tab)->style->black_gc); + + + gdk_gc_set_line_attributes(drawing->ruler_gc_butt, + 2, + GDK_LINE_SOLID, + GDK_CAP_BUTT, + GDK_JOIN_MITER); + + gdk_gc_set_line_attributes(drawing->ruler_gc_round, + 2, + GDK_LINE_SOLID, + GDK_CAP_ROUND, + GDK_JOIN_ROUND); + return drawing; +} + +void histo_drawing_destroy(histoDrawing_t *drawing) +{ + g_info("histo_drawing_destroy %p", drawing); + + /* Free the colors */ + GdkColormap* colormap = gdk_colormap_get_system(); + + gdk_colormap_free_colors(colormap, histo_drawing_colors, NUM_COLORS); + + // Do not unref here, histoDrawing_t destroyed by it's widget. + //g_object_unref( G_OBJECT(drawing->drawing_area)); + if(drawing->gc != NULL) + gdk_gc_unref(drawing->gc); + + g_object_unref(drawing->pango_layout); + if(drawing->dotted_gc != NULL) gdk_gc_unref(drawing->dotted_gc); + if(drawing->ruler_gc_butt != NULL) gdk_gc_unref(drawing->ruler_gc_butt); + if(drawing->ruler_gc_round != NULL) gdk_gc_unref(drawing->ruler_gc_round); + + //added for histogram + if(drawing->pixmap) + gdk_pixmap_unref(drawing->pixmap); + g_free(drawing); + g_info("histo_drawing_destroy end"); +} + + GtkWidget *histo_drawing_get_drawing_area(histoDrawing_t *drawing) +{ + return drawing->drawing_area; +} + + GtkWidget *histo_drawing_get_widget(histoDrawing_t *drawing) +{ + return drawing->vbox; +} + + void histo_drawing_draw_line( histoDrawing_t *drawing, + GdkPixmap *pixmap, + guint x1, guint y1, + guint x2, guint y2, + GdkGC *GC) +{ + gdk_draw_line (pixmap, + GC, + x1, y1, x2, y2); +} + +void histo_drawing_clear(histoDrawing_t *drawing,guint clear_from,guint clear_to) +{ + + HistoControlFlowData *cfd = drawing->histo_control_flow_data; + guint clear_width = clear_to- clear_from; + /* + //disabled for histogram + rectangle_pixmap(cfd->process_list, + drawing->drawing_area->style->black_gc, + TRUE, + 0, 0, + drawing->alloc_width, // do not overlap + -1);*/ + //instead, this is added for histogram + + histo_rectangle_pixmap (drawing->drawing_area->style->black_gc, + TRUE, + clear_from/*0*/, 0, + clear_width/*drawing->width*/, + -1,drawing); + + + +/* gdk_draw_rectangle (drawing->pixmap, + drawing->drawing_area->style->black_gc, + TRUE, + 0,0, + drawing->drawing_area->allocation.width,drawing->drawing_area->allocation.height ); + + /* ask for the buffer to be redrawn */ +//enabled again for histogram. + gtk_widget_queue_draw_area ( drawing->drawing_area, + clear_from, 0, + clear_width, drawing->height); + gdk_window_process_updates(drawing->drawing_area->window,TRUE); +//disabled instead for histogram + //gtk_widget_queue_draw ( drawing->drawing_area); + return; +} + +#if 0 +/* Insert a square corresponding to a new process in the list */ +/* Applies to whole drawing->width */ +void drawing_insert_square(histoDrawing_t *drawing, + guint y, + guint height) +{ + //GdkRectangle update_rect; + gboolean reallocate = FALSE; + GdkPixmap *new_pixmap; + + /* Allocate a new pixmap with new height */ + if(drawing->alloc_height < drawing->height + height) { + + new_pixmap = gdk_pixmap_new(drawing->drawing_area->window, + drawing->width + SAFETY + EXTRA_ALLOC, + drawing->height + height + EXTRA_ALLOC, + -1); + drawing->alloc_width = drawing->width + SAFETY + EXTRA_ALLOC; + drawing->alloc_height = drawing->height + height + EXTRA_ALLOC; + reallocate = TRUE; + + /* Copy the high region */ + gdk_draw_pixmap (new_pixmap, + drawing->drawing_area->style->black_gc, + drawing->pixmap, + 0, 0, + 0, 0, + drawing->width + SAFETY, y); + + } else { + new_pixmap = drawing->pixmap; + } + + //GdkPixmap *pixmap = gdk_pixmap_new(drawing->drawing_area->window, + // drawing->width + SAFETY, + // drawing->height + height, + // -1); + + /* add an empty square */ + gdk_draw_rectangle (new_pixmap, + drawing->drawing_area->style->black_gc, + TRUE, + 0, y, + drawing->width + SAFETY, // do not overlap + height); + + /* copy the bottom of the region */ + gdk_draw_pixmap (new_pixmap, + drawing->drawing_area->style->black_gc, + drawing->pixmap, + 0, y, + 0, y + height, + drawing->width+SAFETY, drawing->height - y); + + + if(reallocate && likely(drawing->pixmap)) { + gdk_pixmap_unref(drawing->pixmap); + drawing->pixmap = new_pixmap; + } + + if(unlikely(drawing->height==1)) drawing->height = height; + else drawing->height += height; + + gtk_widget_set_size_request(drawing->drawing_area, + -1, + drawing->height); + gtk_widget_queue_resize_no_redraw(drawing->drawing_area); + + /* ask for the buffer to be redrawn */ + gtk_widget_queue_draw_area ( drawing->drawing_area, + 0, y, + drawing->width, drawing->height-y); +} + + +/* Remove a square corresponding to a removed process in the list */ +void drawing_remove_square(histoDrawing_t *drawing, + guint y, + guint height) +{ + GdkPixmap *pixmap; + + if(unlikely((guint)drawing->height == height)) { + //pixmap = gdk_pixmap_new( + // drawing->drawing_area->window, + // drawing->width + SAFETY, + // 1, + // -1); + pixmap = drawing->pixmap; + drawing->height=1; + } else { + /* Allocate a new pixmap with new height */ + //pixmap = gdk_pixmap_new( + // drawing->drawing_area->window, + // drawing->width + SAFETY, + // drawing->height - height, + // -1); + /* Keep the same preallocated pixmap */ + pixmap = drawing->pixmap; + + /* Copy the high region */ + gdk_draw_pixmap (pixmap, + drawing->drawing_area->style->black_gc, + drawing->pixmap, + 0, 0, + 0, 0, + drawing->width + SAFETY, y); + + /* Copy up the bottom of the region */ + gdk_draw_pixmap (pixmap, + drawing->drawing_area->style->black_gc, + drawing->pixmap, + 0, y + height, + 0, y, + drawing->width, drawing->height - y - height); + + drawing->height-=height; + } + + //if(likely(drawing->pixmap)) + // gdk_pixmap_unref(drawing->pixmap); + + //drawing->pixmap = pixmap; + + gtk_widget_set_size_request(drawing->drawing_area, + -1, + drawing->height); + gtk_widget_queue_resize_no_redraw(drawing->drawing_area); + /* ask for the buffer to be redrawn */ + gtk_widget_queue_draw_area ( drawing->drawing_area, + 0, y, + drawing->width, MAX(drawing->height-y, 1)); +} +#endif //0 + +void histo_drawing_update_ruler(histoDrawing_t *drawing, TimeWindow *time_window) +{ + GtkRequisition req; + GdkRectangle rect; + + req.width = drawing->ruler->allocation.width; + req.height = drawing->ruler->allocation.height; + + + rect.x = 0; + rect.y = 0; + rect.width = req.width; + rect.height = req.height; + + gtk_widget_queue_draw(drawing->ruler); + //gtk_widget_draw( drawing->ruler, &rect); +} + +/* Redraw the ruler */ +static gboolean +histo_expose_ruler( GtkWidget *widget, GdkEventExpose *event, gpointer user_data ) +{ + histoDrawing_t *drawing = (histoDrawing_t*)user_data; + TimeWindow time_window = lttvwindow_get_time_window(drawing->histo_control_flow_data->tab); + gchar text[255]; + + PangoContext *context; + PangoLayout *layout; + PangoFontDescription *FontDesc; + PangoRectangle ink_rect; + gint global_width=0; + GdkColor foreground = { 0, 0, 0, 0 }; + GdkColor background = { 0, 0xffff, 0xffff, 0xffff }; + + LttTime window_end = time_window.end_time; + LttTime half_width = + ltt_time_div(time_window.time_width,2.0); + LttTime window_middle = + ltt_time_add(half_width, + time_window.start_time); + g_debug("ruler expose event"); + + gdk_draw_rectangle (drawing->ruler->window, + drawing->ruler->style->white_gc, + TRUE, + event->area.x, event->area.y, + event->area.width, + event->area.height); + + gdk_draw_line (drawing->ruler->window, + drawing->ruler_gc_butt, + event->area.x, 1, + event->area.x + event->area.width, 1); + + + snprintf(text, 255, "%lus\n%luns", + time_window.start_time.tv_sec, + time_window.start_time.tv_nsec); + + layout = gtk_widget_create_pango_layout(drawing->drawing_area, NULL); + + context = pango_layout_get_context(layout); + FontDesc = pango_context_get_font_description(context); + + pango_font_description_set_size(FontDesc, 6*PANGO_SCALE); + pango_layout_context_changed(layout); + + pango_layout_set_text(layout, text, -1); + pango_layout_get_pixel_extents(layout, &ink_rect, NULL); + global_width += ink_rect.width; + + gdk_draw_layout_with_colors(drawing->ruler->window, + drawing->ruler_gc_butt, + 0, + 6, + layout, &foreground, &background); + + gdk_draw_line (drawing->ruler->window, + drawing->ruler_gc_round, + 1, 1, + 1, 7); + + + snprintf(text, 255, "%lus\n%luns", window_end.tv_sec, + window_end.tv_nsec); + + pango_layout_set_text(layout, text, -1); + pango_layout_get_pixel_extents(layout, &ink_rect, NULL); + global_width += ink_rect.width; + + if(global_width <= drawing->ruler->allocation.width) + { + gdk_draw_layout_with_colors(drawing->ruler->window, + drawing->ruler_gc_butt, + drawing->ruler->allocation.width - ink_rect.width, + 6, + layout, &foreground, &background); + + gdk_draw_line (drawing->ruler->window, + drawing->ruler_gc_butt, + drawing->ruler->allocation.width-1, 1, + drawing->ruler->allocation.width-1, 7); + } + + + snprintf(text, 255, "%lus\n%luns", window_middle.tv_sec, + window_middle.tv_nsec); + + pango_layout_set_text(layout, text, -1); + pango_layout_get_pixel_extents(layout, &ink_rect, NULL); + global_width += ink_rect.width; + + if(global_width <= drawing->ruler->allocation.width) + { + gdk_draw_layout_with_colors(drawing->ruler->window, + drawing->ruler_gc_butt, + (drawing->ruler->allocation.width - ink_rect.width)/2, + 6, + layout, &foreground, &background); + + gdk_draw_line (drawing->ruler->window, + drawing->ruler_gc_butt, + drawing->ruler->allocation.width/2, 1, + drawing->ruler->allocation.width/2, 7); + } + + g_object_unref(layout); + + return FALSE; +} + + void histo_drawing_update_vertical_ruler(histoDrawing_t *drawing)//, TimeWindow *time_window) +{ + GtkRequisition req; + GdkRectangle rect; + + req.width = drawing->vertical_ruler->allocation.width; + req.height = drawing->vertical_ruler->allocation.height; + + rect.x = 0; + rect.y = 0; + rect.width = req.width; + rect.height = req.height; + + gtk_widget_queue_draw(drawing->vertical_ruler); + //gtk_widget_draw( drawing->ruler, &rect); +} + +/* notify mouse on ruler */ +static gboolean +histo_motion_notify_ruler(GtkWidget *widget, GdkEventMotion *event, gpointer user_data) +{ + //g_debug("motion"); + //eventually follow mouse and show time here +} + +static gboolean +histo_motion_notify_vertical_ruler(GtkWidget *widget, GdkEventMotion *event, gpointer user_data) +{ + //g_debug("motion"); + //eventually follow mouse and show time here +} + + + +/* Redraw the vertical ruler */ +static gboolean +histo_expose_vertical_ruler( GtkWidget *widget, GdkEventExpose *event, gpointer user_data ) +{ + histoDrawing_t *drawing = (histoDrawing_t*)user_data; + HistoControlFlowData *histo_cfv = drawing->histo_control_flow_data; + gchar text[255]; + + PangoContext *context; + PangoLayout *layout; + PangoFontDescription *FontDesc; + PangoRectangle ink_rect; + gint global_height=0; + GdkColor foreground = { 0, 0, 0, 0 }; + GdkColor background = { 0, 0xffff, 0xffff, 0xffff }; + GdkColor red ={ 0, 0xFFFF, 0x1E00, 0x1000 }; + GdkColor magneta ={ 0, 0x8900, 0x0000, 0x8400 }; + g_debug("vertical ruler expose event"); + + gdk_draw_rectangle (drawing->vertical_ruler->window, + drawing->vertical_ruler->style->white_gc, + TRUE, + event->area.x, event->area.y, + event->area.width, + event->area.height); + + gdk_draw_line (drawing->vertical_ruler->window, + drawing->ruler_gc_butt, + padding_width-1/*event->area.width-1*/,event->area.y, + padding_width-1/*event->area.width-1*/,event->area.y + event->area.height); + + snprintf(text, 255, "%.1f", (float)histo_cfv->max_height); + + layout = gtk_widget_create_pango_layout(drawing->drawing_area, NULL); + + context = pango_layout_get_context(layout); + FontDesc = pango_context_get_font_description(context); + + pango_font_description_set_size(FontDesc, 6*PANGO_SCALE); + pango_layout_context_changed(layout); + + pango_layout_set_text(layout, text, -1); + pango_layout_get_pixel_extents(layout, &ink_rect, NULL); + global_height += ink_rect.height; + + gdk_draw_layout_with_colors(drawing->vertical_ruler->window, + drawing->ruler_gc_butt, + 1, + 1, + layout, &foreground, &background); + + gdk_draw_line (drawing->vertical_ruler->window, + drawing->ruler_gc_round, + drawing->vertical_ruler-> allocation.width-1, 1, + drawing->vertical_ruler-> allocation.width-7, 1); + + + snprintf(text, 255, "%lu",0); + + pango_layout_set_text(layout, text, -1); + pango_layout_get_pixel_extents(layout, &ink_rect, NULL); + global_height += ink_rect.height; + + if(global_height <= drawing->vertical_ruler->allocation.height) + { + gdk_draw_layout_with_colors(drawing->vertical_ruler->window, + drawing->ruler_gc_butt, + 1, + drawing->vertical_ruler->allocation.height - ink_rect.height-2, + layout, &foreground, &background); + + gdk_draw_line (drawing->vertical_ruler->window, + drawing->ruler_gc_butt, + drawing->vertical_ruler-> allocation.width-1, + drawing->vertical_ruler->allocation.height-1, + drawing->vertical_ruler-> allocation.width-7, + drawing->vertical_ruler->allocation.height-1); + } + + + snprintf(text, 255, "%.1f",(float) histo_cfv->max_height/2.0); + + pango_layout_set_text(layout, text, -1); + pango_layout_get_pixel_extents(layout, &ink_rect, NULL); + global_height += ink_rect.height; + + if(global_height <= drawing->vertical_ruler->allocation.height) + { + gdk_draw_layout_with_colors(drawing->vertical_ruler->window, + drawing->ruler_gc_butt, + 1, + (drawing->vertical_ruler->allocation.height - ink_rect.height)/2, + layout, &foreground, &background); + + gdk_draw_line (drawing->vertical_ruler->window, + drawing->ruler_gc_butt, + drawing->vertical_ruler-> allocation.width-1, + drawing->vertical_ruler-> allocation.height/2, + drawing->vertical_ruler-> allocation.width-7, + drawing->vertical_ruler->allocation.height/2); + } + + //show number of events at current time: + LttTime current_time = + lttvwindow_get_current_time(histo_cfv->tab); + TimeWindow time_window = + lttvwindow_get_time_window(histo_cfv->tab); + LttTime time_begin = time_window.start_time; + LttTime time_width = time_window.time_width; + LttTime time_end = ltt_time_add(time_begin, time_width); + if((ltt_time_compare(current_time, time_begin) >= 0)&& + (ltt_time_compare(current_time, time_end) <= 0)) + { + guint *events_at_currenttime; + guint max_height=histo_cfv ->max_height; + guint x; + histo_convert_time_to_pixels( + time_window, + current_time, + drawing->width, + &x); + // if(x_testnumber_of_process->len) + + { + events_at_currenttime = + &g_array_index(histo_cfv->number_of_process,guint,x); + + + if((*events_at_currenttime) > max_height) + { + snprintf(text, 255, "OverFlow!"); + pango_layout_set_text(layout, text, -1); + pango_layout_get_pixel_extents(layout, &ink_rect, NULL); + global_height += ink_rect.height; + gdk_draw_layout_with_colors(drawing->vertical_ruler->window, + drawing->ruler_gc_butt, + 1, + (drawing->vertical_ruler->allocation.height - ink_rect.height)/5, + layout, &red, &background); + }else + // if((*events_at_currenttime) <= max_height) + { + snprintf(text, 255, "%.1f", + (float) *events_at_currenttime); + + pango_layout_set_text(layout, text, -1); + pango_layout_get_pixel_extents(layout, &ink_rect, NULL); + global_height += ink_rect.height; + + if ((*events_at_currenttime) == 0) + { + gdk_draw_layout_with_colors(drawing->vertical_ruler->window, + drawing->ruler_gc_butt, + 1, + (drawing->vertical_ruler->allocation.height - ink_rect.height)-2, + layout, &red, &background); + } + else if ((*events_at_currenttime) == max_height) + { + gdk_draw_layout_with_colors(drawing->vertical_ruler->window, + drawing->ruler_gc_butt, + 1, + 1, + layout, &red, &background); + } + /*else if ((*events_at_currenttime) == max_height/2) + { + gdk_draw_layout_with_colors(drawing->vertical_ruler->window, + drawing->ruler_gc_butt, + 1, + (drawing->vertical_ruler->allocation.height - ink_rect.height)/2, + layout, &red, &background); + }*/ + else if ((*events_at_currenttime) > max_height/2) + { + gdk_draw_layout_with_colors(drawing->vertical_ruler->window, + drawing->ruler_gc_butt, + 1, + (drawing->vertical_ruler->allocation.height - ink_rect.height)/4, + layout, &red, &background); + } + else{ + gdk_draw_layout_with_colors(drawing->vertical_ruler->window, + drawing->ruler_gc_butt, + 1, + ((drawing->vertical_ruler->allocation.height + - ink_rect.height)*3)/4, + layout, &red, &background); + } + } + + } + } + + g_object_unref(layout); + + return FALSE; +} + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histodrawing.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histodrawing.h new file mode 100644 index 00000000..179c92e4 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histodrawing.h @@ -0,0 +1,224 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2006 Parisa heidari (inspired from CFV by Mathieu Desnoyers) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 _HISTODRAWING_H +#define _HISTODRAWING_H + +#include +#include +#include +#include +#include +#include +#include +#include "histocfv.h" +#include "histodrawitem.h" + + +#define SAFETY 50 // safety pixels at right and bottom of pixmap buffer + +typedef enum _draw_color { + COL_BLACK, + COL_WHITE, + COL_RUN_USER_MODE,/* green */ + COL_RUN_SYSCALL, /* pale blue */ + COL_RUN_TRAP, /* yellow */ + COL_RUN_IRQ, /* red */ + COL_WAIT, /* dark red */ + COL_WAIT_CPU, /* dark yellow */ + COL_ZOMBIE, /* dark purple */ + COL_WAIT_FORK, /* dark green */ + COL_EXIT, /* "less dark" magenta */ + COL_MODE_UNKNOWN, /* white */ + COL_UNNAMED, /* white */ + NUM_COLORS } draw_color; + +extern GdkColor histo_drawing_colors[NUM_COLORS]; + +/* This part of the viewer does : + * Draw horizontal lines, getting graphic context as arg. + * Copy region of the screen into another. + * Modify the boundaries to reflect a scale change. (resize) + * Refresh the physical screen with the pixmap + * A helper function is provided here to convert from time to process + * identifier to pixels and the contrary (will be useful for mouse selection). + * Insert an empty square in the drawing, moving the bottom part. + * + * Note: The last point is exactly why it would not be so easy to add the + * vertical line functionnality as in the original version of LTT. In order + * to do so, we should keep all processes in the list for the duration of + * all the trace instead of dynamically adding and removing them when we + * scroll. Another possibility is to redraw all the visible area when a new + * process is added to the list. The second solution seems more appropriate + * to me. + * + * + * The pixmap used has the width of the physical window, but the height + * of the shown processes. + */ + +#ifndef TYPE_histoDrawing_t_DEFINED +#define TYPE_histoDrawing_t_DEFINED +typedef struct _histoDrawing_t histoDrawing_t; +#endif //TYPE_DRAWING_T_DEFINED + +#ifndef TYPE_HistoControlFlowData_DEFINED +#define TYPE_HistoControlFlowData_DEFINED +typedef struct _HistoControlFlowData HistoControlFlowData; +#endif //TYPE_HistoControlFlowData_DEFINED + +struct _histoDrawing_t { + GtkWidget *vbox; + GtkWidget *drawing_area; + /* + GtkWidget *hbox; + GtkWidget *viewport; + GtkWidget *scrollbar;*///at this time,not necessary for histogram + + GtkWidget *ruler_hbox; + GtkWidget *ruler; + GtkWidget *padding; +//vertical ruler + GtkWidget *vruler_drawing_hbox; + GtkWidget *vertical_ruler; + + GdkPixmap *pixmap; + HistoControlFlowData *histo_control_flow_data; + + PangoLayout *pango_layout; + + gint height, width, depth; + /* height and width of allocated buffer pixmap */ + gint alloc_height, alloc_width; + + /* X coordinate of damaged region */ + gint damage_begin, damage_end; /* damaged region to be exposed, + updated per chunk */ + LttTime last_start; + GdkGC *dotted_gc; + GdkGC *gc; + GdkGC *ruler_gc_butt; + GdkGC *ruler_gc_round; + + /* Position of the horizontal selector, -1 for none */ + gint horizontal_sel; +}; + +histoDrawing_t *histo_drawing_construct(HistoControlFlowData *histo_control_flow_data); +void histo_drawing_destroy(histoDrawing_t *drawing); + + +void histo_drawing_data_request(histoDrawing_t *drawing, + gint x, gint y, + gint width, + gint height); + + GtkWidget *histo_drawing_get_widget(histoDrawing_t *drawing); +GtkWidget *histo_drawing_get_drawing_area(histoDrawing_t *drawing); + + void histo_drawing_draw_line(histoDrawing_t *drawing, + GdkPixmap *pixmap, + guint x1, guint y1, + guint x2, guint y2, + GdkGC *GC); + +//void drawing_copy(histoDrawing_t *drawing, +// guint xsrc, guint ysrc, +// guint xdest, guint ydest, +// guint width, guint height); + +/* Clear the drawing : make it 1xwidth. */ +void histo_drawing_clear(histoDrawing_t *drawing,guint clear_from,guint clear_to); + +/* Insert a square corresponding to a new process in the list */ +static void drawing_insert_square(histoDrawing_t *drawing, + guint y, + guint height); + +/* Remove a square corresponding to a removed process in the list */ +static void drawing_remove_square(histoDrawing_t *drawing, + guint y, + guint height); + +void histo_drawing_update_ruler(histoDrawing_t *drawing, TimeWindow *time_window); + +void histo_drawing_update_vertical_ruler(histoDrawing_t *drawing);//, TimeWindow *time_window); + +void histo_drawing_request_expose(EventsRequest *events_request, + LttvTracesetState *tss, + LttTime end_time); + +void histo_drawing_data_request_begin(EventsRequest *events_request, + LttvTracesetState *tss); +void histo_drawing_chunk_begin(EventsRequest *events_request, LttvTracesetState *tss); + + + +static void +tree_row_activated(GtkTreeModel *treemodel, + GtkTreePath *arg1, + GtkTreeViewColumn *arg2, + gpointer user_data); + + +/* histo_convert_pixels_to_time + * + * Convert from window pixel and time interval to an absolute time. + */ +static inline void histo_convert_pixels_to_time( + gint width, + guint x, + TimeWindow time_window, + LttTime *time) +{ + double time_d; + + time_d = time_window.time_width_double; + time_d = time_d / (double)width * (double)x; + *time = ltt_time_from_double(time_d); + *time = ltt_time_add(time_window.start_time, *time); +} + + +static inline void histo_convert_time_to_pixels( + TimeWindow time_window, + LttTime time, + int width, + guint *x) +{ + double time_d; +#ifdef EXTRA_CHECK + g_assert(ltt_time_compare(window_time_begin, time) <= 0 && + ltt_time_compare(window_time_end, time) >= 0); +#endif //EXTRA_CHECK + + time = ltt_time_sub(time, time_window.start_time); + + time_d = ltt_time_to_double(time); + + if(time_window.time_width_double == 0.0) { + g_assert(time_d == 0.0); + *x = 0; + } else { + *x = (guint)(time_d / time_window.time_width_double * (double)width); + } + +} + +#endif // _DRAWING_H diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histodrawitem.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histodrawitem.c new file mode 100644 index 00000000..aafa515d --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histodrawitem.c @@ -0,0 +1,464 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + + + +/****************************************************************************** + * drawitem.c + * + * This file contains methods responsible for drawing a generic type of data + * in a drawable. Doing this generically will permit user defined drawing + * behavior in a later time. + * + * This file provides an API which is meant to be reusable for all viewers that + * need to show information in line, icon, text, background or point form in + * a drawable area having time for x axis. The y axis, in the control flow + * viewer case, is corresponding to the different processes, but it can be + * reused integrally for cpu, and eventually locks, buffers, network + * interfaces... What will differ between the viewers is the precise + * information which interests us. We may think that the most useful + * information for control flow are some specific events, like schedule + * change, and processes'states. It may differ for a cpu viewer : the + * interesting information could be more the execution mode of each cpu. + * This API in meant to make viewer's writers life easier : it will become + * a simple choice of icons and line types for the precise information + * the viewer has to provide (agremented with keeping supplementary records + * and modifying slightly the DrawContext to suit the needs.) + * + * We keep each data type in attributes, keys to specific information + * being formed from the GQuark corresponding to the information received. + * (facilities / facility_name / events / eventname.) + * (cpus/cpu_name, process_states/ps_name, + * execution_modes/em_name, execution_submodes/es_name). + * The goal is then to provide a generic way to print information on the + * screen for all this different information. + * + * Information can be printed as + * + * - text (text + color + size + position (over or under line) + * - icon (icon filename, corresponding to a loaded icon, accessible through + * a GQuark. Icons are loaded statically at the guiControlFlow level during + * module initialization and can be added on the fly if not present in the + * GQuark.) The habitual place for xpm icons is in + * ${prefix}/share/LinuxTraceToolkit.) + position (over or under line) + * - line (color, width, style) + * - Arc (big points) (color, size) + * - background color (color) + * + * An item is a leaf of the attributes tree. It is, in that case, including + * all kind of events categories we can have. It then associates each category + * with one or more actions (drawing something) or nothing. + * + * Each item has an array of hooks (hook list). Each hook represents an + * operation to perform. We seek the array each time we want to + * draw an item. We execute each operation in order. An operation type + * is associated with each hook to permit user listing and modification + * of these operations. The operation type is also used to find the + * corresponding priority for the sorting. Operation type and priorities + * are enum and a static int table. + * + * The array has to be sorted by priority each time we add a task in it. + * A priority is associated with each operation type. It permits + * to perform background color selection before line or text drawing. We also + * draw lines before text, so the text appears over the lines. + * + * Executing all the arrays of operations for a specific event (which + * implies information for state, event, cpu, execution mode and submode) + * has to be done in a same DrawContext. The goal there is to keep the offset + * of the text and icons over and under the middle line, so a specific + * event could be printed as ( R Si 0 for running, scheduled in, cpu 0 ), + * text being easy to replace with icons. The DrawContext is passed as + * call_data for the operation hooks. + * + * We use the lttv global attributes to keep track of the loaded icons. + * If we need an icon, we look for it in the icons / icon name pathname. + * If found, we use the pointer to it. If not, we load the pixmap in + * memory and set the pointer to the GdkPixmap in the attributes. The + * structure pointed to contains the pixmap and the mask bitmap. + * + * Author : Mathieu Desnoyers, October 2003 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "histodrawitem.h" + + +#define MAX_PATH_LEN 256 + +/* drawing hook functions */ +gboolean histo_draw_text( void *hook_data, void *call_data) +{ + histo_PropertiesText *properties = (histo_PropertiesText*)hook_data; + histo_DrawContext *draw_context = (histo_DrawContext*)call_data; + + PangoContext *context; + PangoLayout *layout; + PangoFontDescription *font_desc;// = pango_font_description_new(); + PangoRectangle ink_rect; + + layout = draw_context->pango_layout; + + context = pango_layout_get_context(layout); + font_desc = pango_context_get_font_description(context); + + pango_font_description_set_size(font_desc, properties->size*PANGO_SCALE); + pango_layout_context_changed(layout); + + pango_layout_set_text(layout, properties->text, -1); + pango_layout_get_pixel_extents(layout, &ink_rect, NULL); + + gint x=0, y=0; + gint *offset=NULL; + gboolean enough_space = FALSE; + gint width = ink_rect.width; + + switch(properties->histo_position.x) { + case POS_START: + x = draw_context->histo_drawinfo.start.x; + switch(properties->histo_position.y) { + case OVER: + offset = &draw_context->histo_drawinfo.start.offset.over; + x += draw_context->histo_drawinfo.start.offset.over; + y = draw_context->histo_drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->histo_drawinfo.start.offset.middle; + x += draw_context->histo_drawinfo.start.offset.middle; + y = draw_context->histo_drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->histo_drawinfo.start.offset.under; + x += draw_context->histo_drawinfo.start.offset.under; + y = draw_context->histo_drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x + width <= draw_context->histo_drawinfo.end.x)) { + enough_space = TRUE; + *offset += width; + } + break; + case POS_END: + x = draw_context->histo_drawinfo.end.x; + switch(properties->histo_position.y) { + case OVER: + offset = &draw_context->histo_drawinfo.end.offset.over; + x += draw_context->histo_drawinfo.end.offset.over; + y = draw_context->histo_drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->histo_drawinfo.end.offset.middle; + x += draw_context->histo_drawinfo.end.offset.middle; + y = draw_context->histo_drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->histo_drawinfo.end.offset.under; + x += draw_context->histo_drawinfo.end.offset.under; + y = draw_context->histo_drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x - width >= draw_context->histo_drawinfo.start.x)) { + enough_space = TRUE; + *offset -= width; + } + break; + } + + if(unlikely(enough_space)) + gdk_draw_layout_with_colors(draw_context->drawable, + draw_context->gc, + x, + y, + layout, properties->foreground, properties->background); + + return 0; +} + + +/* To speed up the process, search in already loaded icons list first. Only + * load it if not present. + */ +gboolean histo_draw_icon( void *hook_data, void *call_data) +{ + histo_PropertiesIcon *properties = (histo_PropertiesIcon*)hook_data; + histo_DrawContext *draw_context = (histo_DrawContext*)call_data; + + LttvIAttribute *attributes = LTTV_IATTRIBUTE(lttv_global_attributes()); + LttvAttributeValue value; + gchar icon_name[MAX_PATH_LEN] = "icons/"; + histo_IconStruct *icon_info; + + strcat(icon_name, properties->icon_name); + + g_assert(lttv_iattribute_find_by_path(attributes, icon_name, + LTTV_POINTER, &value)); + if(unlikely(*(value.v_pointer) == NULL)) + { + *(value.v_pointer) = icon_info = g_new(histo_IconStruct,1); + + icon_info->pixmap = gdk_pixmap_create_from_xpm(draw_context->drawable, + &icon_info->mask, NULL, properties->icon_name); + } + else + { + icon_info = *(value.v_pointer); + } + + gint x=0, y=0; + gint *offset=NULL; + gboolean enough_space = FALSE; + gint width = properties->width; + + switch(properties->histo_position.x) { + case POS_START: + x = draw_context->histo_drawinfo.start.x; + switch(properties->histo_position.y) { + case OVER: + offset = &draw_context->histo_drawinfo.start.offset.over; + x += draw_context->histo_drawinfo.start.offset.over; + y = draw_context->histo_drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->histo_drawinfo.start.offset.middle; + x += draw_context->histo_drawinfo.start.offset.middle; + y = draw_context->histo_drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->histo_drawinfo.start.offset.under; + x += draw_context->histo_drawinfo.start.offset.under; + y = draw_context->histo_drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x + width <= draw_context->histo_drawinfo.end.x)) { + enough_space = TRUE; + *offset += width; + } + break; + case POS_END: + x = draw_context->histo_drawinfo.end.x; + switch(properties->histo_position.y) { + case OVER: + offset = &draw_context->histo_drawinfo.end.offset.over; + x += draw_context->histo_drawinfo.end.offset.over; + y = draw_context->histo_drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->histo_drawinfo.end.offset.middle; + x += draw_context->histo_drawinfo.end.offset.middle; + y = draw_context->histo_drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->histo_drawinfo.end.offset.under; + x += draw_context->histo_drawinfo.end.offset.under; + y = draw_context->histo_drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x - width >= draw_context->histo_drawinfo.start.x)) { + enough_space = TRUE; + *offset -= width; + } + break; + } + + if(unlikely(enough_space)) { + gdk_gc_set_clip_mask(draw_context->gc, icon_info->mask); + + gdk_gc_set_clip_origin( + draw_context->gc, + x, + y); + gdk_draw_drawable(draw_context->drawable, + draw_context->gc, + icon_info->pixmap, + 0, 0, + x, + y, + properties->width, properties->height); + + gdk_gc_set_clip_origin(draw_context->gc, 0, 0); + gdk_gc_set_clip_mask(draw_context->gc, NULL); + } + return 0; +} + +gboolean histo_draw_line( void *hook_data, void *call_data) +{ + histo_PropertiesLine *properties = (histo_PropertiesLine*)hook_data; + histo_DrawContext *draw_context = (histo_DrawContext*)call_data; + + gdk_gc_set_foreground(draw_context->gc, &properties->color); + //gdk_gc_set_rgb_fg_color(draw_context->gc, &properties->color); + gdk_gc_set_line_attributes( draw_context->gc, + properties->line_width, + properties->style, + GDK_CAP_BUTT, + GDK_JOIN_MITER); + //g_critical("DRAWING LINE : x1: %i, y1: %i, x2:%i, y2:%i", + // draw_context->previous->middle->x, + // draw_context->previous->middle->y, + // draw_context->drawinfo.middle.x, + // draw_context->drawinfo.middle.y); + + gint x_begin=0, x_end=0, y=0; + + x_begin = draw_context->histo_drawinfo.start.x; + x_end = draw_context->histo_drawinfo.end.x; + + switch(properties->y) { + case OVER: + y = draw_context->histo_drawinfo.y.over; + break; + case MIDDLE: + y = draw_context->histo_drawinfo.y.middle; + break; + case UNDER: + y = draw_context->histo_drawinfo.y.under; + break; + } + + histo_drawing_draw_line( + NULL, draw_context->drawable, + x_begin, + y, + x_end, + y, + draw_context->gc); + + return 0; +} + +gboolean histo_draw_arc( void *hook_data, void *call_data) +{ + histo_PropertiesArc *properties = (histo_PropertiesArc*)hook_data; + histo_DrawContext *draw_context = (histo_DrawContext*)call_data; + + gdk_gc_set_foreground(draw_context->gc, properties->color); + //gdk_gc_set_rgb_fg_color(draw_context->gc, properties->color); + + gint x=0, y=0; + gint *offset=NULL; + gboolean enough_space = FALSE; + gint width = properties->size; + + switch(properties->histo_position.x) { + case POS_START: + x = draw_context->histo_drawinfo.start.x; + switch(properties->histo_position.y) { + case OVER: + offset = &draw_context->histo_drawinfo.start.offset.over; + x += draw_context->histo_drawinfo.start.offset.over; + y = draw_context->histo_drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->histo_drawinfo.start.offset.middle; + x += draw_context->histo_drawinfo.start.offset.middle; + y = draw_context->histo_drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->histo_drawinfo.start.offset.under; + x += draw_context->histo_drawinfo.start.offset.under; + y = draw_context->histo_drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x + width <= draw_context->histo_drawinfo.end.x)) { + enough_space = TRUE; + *offset += width; + } + break; + case POS_END: + x = draw_context->histo_drawinfo.end.x; + switch(properties->histo_position.y) { + case OVER: + offset = &draw_context->histo_drawinfo.end.offset.over; + x += draw_context->histo_drawinfo.end.offset.over; + y = draw_context->histo_drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->histo_drawinfo.end.offset.middle; + x += draw_context->histo_drawinfo.end.offset.middle; + y = draw_context->histo_drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->histo_drawinfo.end.offset.under; + x += draw_context->histo_drawinfo.end.offset.under; + y = draw_context->histo_drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x - width >= draw_context->histo_drawinfo.start.x)) { + enough_space = TRUE; + *offset -= width; + } + break; + } + + if(unlikely(enough_space)) + gdk_draw_arc(draw_context->drawable, draw_context->gc, + properties->filled, + x, + y, + properties->size, properties->size, 0, 360*64); + + return 0; +} + +gboolean histo_draw_bg( void *hook_data, void *call_data) +{ + histo_PropertiesBG *properties = (histo_PropertiesBG*)hook_data; + histo_DrawContext *draw_context = (histo_DrawContext*)call_data; + + gdk_gc_set_foreground(draw_context->gc, properties->color); + //gdk_gc_set_rgb_fg_color(draw_context->gc, properties->color); + + //g_critical("DRAWING RECT : x: %i, y: %i, w:%i, h:%i, val1 :%i, val2:%i ", + // draw_context->previous->over->x, + // draw_context->previous->over->y, + // draw_context->drawinfo.over.x - draw_context->previous->over->x, + // draw_context->previous->under->y-draw_context->previous->over->y, + // draw_context->drawinfo.over.x, + // draw_context->previous->over->x); + gdk_draw_rectangle(draw_context->drawable, draw_context->gc, + TRUE, + draw_context->histo_drawinfo.start.x, + draw_context->histo_drawinfo.y.over, + draw_context->histo_drawinfo.end.x - draw_context->histo_drawinfo.start.x, + draw_context->histo_drawinfo.y.under - draw_context->histo_drawinfo.y.over); + + return 0; +} + + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histodrawitem.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histodrawitem.h new file mode 100644 index 00000000..909185ab --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histodrawitem.h @@ -0,0 +1,279 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 _DRAW_ITEM_H +#define _DRAW_ITEM_H + +#include + +typedef struct _histo_DrawContext histo_DrawContext; +typedef struct _histo_DrawInfo histo_DrawInfo; +typedef struct _histo_ItemInfo histo_ItemInfo; + +typedef struct _histo_IconStruct histo_IconStruct; + +typedef struct _histo_DrawOperation histo_DrawOperation; + + +typedef struct _histo_PropertiesText histo_PropertiesText; +typedef struct _histo_PropertiesIcon histo_PropertiesIcon; +typedef struct _histo_PropertiesLine histo_PropertiesLine; +typedef struct _histo_PropertiesArc histo_PropertiesArc; +typedef struct _histo_PropertiesBG histo_PropertiesBG; + +typedef enum _histo_DrawableItems histo_DrawableItems; +enum _histo_DrawableItems { + ITEM_TEXT, ITEM_ICON, ITEM_LINE, ITEM_POINT, ITEM_BACKGROUND +}; + +typedef enum _histo_RelPosX { + POS_START, POS_END +} histo_RelPosX; + +typedef enum _histo_RelPosY { + OVER, MIDDLE, UNDER +} histo_RelPosY; + + +/* The DrawContext keeps information about the current drawing position and + * the previous one, so we can use both to draw lines. + * + * over : position for drawing over the middle line. + * middle : middle line position. + * under : position for drawing under the middle line. + * + * the modify_* are used to take into account that we should go forward + * when we draw a text, an arc or an icon, while it's unneeded when we + * draw a line or background. + * + * The modify_* positions are altered by the draw item functions. + * + */ + + +struct _histo_DrawContext { + GdkDrawable *drawable; + GdkGC *gc; + PangoLayout *pango_layout; + + struct { + struct { + gint x; + struct { + gint over; + gint middle; + gint under; + } offset; + } start; + + struct { + gint x; + struct { + gint over; + gint middle; + gint under; + } offset; + } end; + + struct { + gint over; + gint middle; + gint under; + } y; + + } histo_drawinfo; +}; + + + + +/* + * Structure used to keep information about icons. + */ +struct _histo_IconStruct { + GdkPixmap *pixmap; + GdkBitmap *mask; +}; + + +/* + * The Item element is only used so the DrawOperation is modifiable by users. + * During drawing, only the Hook is needed. + */ +struct _histo_DrawOperation { + histo_DrawableItems item; + LttvHooks *hook; +}; +#if 0 +/* + * We define here each items that can be drawn, together with their + * associated priority. Many item types can have the same priority, + * it's only used for quicksorting the operations when we add a new one + * to the array of operations to perform. Lower priorities are executed + * first. So, for example, we may want to give background color a value + * of 10 while a line would have 20, so the background color, which + * is in fact a rectangle, does not hide the line. + */ + +static int Items_Priorities[] = { + 50, /* ITEM_TEXT */ + 40, /* ITEM_ICON */ + 20, /* ITEM_LINE */ + 30, /* ITEM_POINT */ + 10 /* ITEM_BACKGROUND */ +}; +#endif //0 + +/* + * Here are the different structures describing each item type that can be + * drawn. They contain the information necessary to draw the item : not the + * position (this is provided by the DrawContext), but the text, icon name, + * line width, color; all the properties of the specific items. + */ + +struct _histo_PropertiesText { + GdkColor *foreground; + GdkColor *background; + gint size; + gchar *text; + struct { + histo_RelPosX x; + histo_RelPosY y; + } histo_position; +}; + + +struct _histo_PropertiesIcon { + gchar *icon_name; + gint width; + gint height; + struct { + histo_RelPosX x; + histo_RelPosY y; + } histo_position; +}; + +struct _histo_PropertiesLine { + GdkColor color; + gint line_width; + GdkLineStyle style; + histo_RelPosY y; +}; + +struct _histo_PropertiesArc { + GdkColor *color; + gint size; /* We force circle by width = height */ + gboolean filled; + struct { + histo_RelPosX x; + histo_RelPosY y; + } histo_position; +}; + +struct _histo_PropertiesBG { + GdkColor *color; +}; + + + +void histo_draw_item( GdkDrawable *drawable, + gint x, + gint y, + LttvTraceState *ts, + LttvTracefileState *tfs, + LttvIAttribute *attributes); + +/* + * The tree of attributes used to store drawing operations goes like this : + * + * event_types/ + * "facility-event_type" + * cpus/ + * "cpu name" + * mode_types/ + * "execution mode"/ + * submodes/ + * "submode" + * process_states/ + * "state name" + * + * So if, for example, we want to add a hook to get called each time we + * receive an event that is in state LTTV_STATE_SYSCALL, we put the + * pointer to the GArray of DrawOperation in + * process_states/ "name associated with LTTV_STATE_SYSCALL" + */ + + +#if 0 +/* + * The add_operation has to do a quick sort by priority to keep the operations + * in the right order. + */ +void add_operation( LttvIAttribute *attributes, + gchar *pathname, + DrawOperation *operation); + +/* + * The del_operation seeks the array present at pathname (if any) and + * removes the DrawOperation if present. It returns 0 on success, -1 + * if it fails. + */ +gint del_operation( LttvIAttribute *attributes, + gchar *pathname, + DrawOperation *operation); + +/* + * The clean_operations removes all operations present at a pathname. + * returns 0 on success, -1 if it fails. + */ +gint clean_operations( LttvIAttribute *attributes, + gchar *pathname ); + + +/* + * The list_operations gives a pointer to the operation array associated + * with the pathname. It will be NULL if no operation is present. + */ +void list_operations( LttvIAttribute *attributes, + gchar *pathname, + GArray **operation); + + + +/* + * exec_operation executes the operations if present in the attributes, or + * do nothing if not present. + */ +void exec_operations( LttvIAttribute *attributes, + gchar *pathname); +#endif //0 + +/* + * Here follow the prototypes of the hook functions used to draw the + * different items. + */ + +gboolean histo_draw_text( void *hook_data, void *call_data); +gboolean histo_draw_icon( void *hook_data, void *call_data); +gboolean histo_draw_line( void *hook_data, void *call_data); +gboolean histo_draw_arc( void *hook_data, void *call_data); +gboolean histo_draw_bg( void *hook_data, void *call_data); + + +#endif // _DRAW_ITEM_H diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histoeventhooks.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histoeventhooks.c new file mode 100644 index 00000000..f0a61ca4 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histoeventhooks.c @@ -0,0 +1,1142 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2006 Parisa heidari (inspired from CFV by Mathieu Desnoyers) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + + +/***************************************************************************** + * Hooks to be called by the main window * + *****************************************************************************/ + + +/* Event hooks are the drawing hooks called during traceset read. They draw the + * icons, text, lines and background color corresponding to the events read. + * + * Two hooks are used for drawing : before_schedchange and after_schedchange hooks. The + * before_schedchange is called before the state update that occurs with an event and + * the after_schedchange hook is called after this state update. + * + * The before_schedchange hooks fulfill the task of drawing the visible objects that + * corresponds to the data accumulated by the after_schedchange hook. + * + * The after_schedchange hook accumulates the data that need to be shown on the screen + * (items) into a queue. Then, the next before_schedchange hook will draw what that + * queue contains. That's the Right Way (TM) of drawing items on the screen, + * because we need to draw the background first (and then add icons, text, ... + * over it), but we only know the length of a background region once the state + * corresponding to it is over, which happens to be at the next before_schedchange + * hook. + * + * We also have a hook called at the end of a chunk to draw the information left + * undrawn in each process queue. We use the current time as end of + * line/background. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +//#define PANGO_ENABLE_BACKEND +#include +#include +#include +#include +#include +#include + +//#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +#include "histoeventhooks.h" +#include "histocfv.h" +#include "histobuttonwidget.h" +#include "histodrawing.h" + + +#define MAX_PATH_LEN 256 +#define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format) +//FIXME +// fixed #define TRACE_NUMBER 0 +#define EXTRA_ALLOC 1024 // pixels + +/* Action to do when background computation completed. + * + * Wait for all the awaited computations to be over. + */ + +static gint histo_background_ready(void *hook_data, void *call_data) +{ + HistoControlFlowData *histocontrol_flow_data = (HistoControlFlowData *)hook_data; + LttvTrace *trace = (LttvTrace*)call_data; + + histoDrawing_t *drawing = histocontrol_flow_data->drawing; + histocontrol_flow_data->background_info_waiting--; + + if(histocontrol_flow_data->background_info_waiting == 0) { + g_message("Histocontrol flow viewer : background computation data ready."); + + histo_drawing_clear(drawing,0,drawing->width); + + gtk_widget_set_size_request(drawing->drawing_area, + -1, -1); + histo_redraw_notify(histocontrol_flow_data, NULL); + } + + return 0; +} + + +/* Request background computation. Verify if it is in progress or ready first. + * Only for each trace in the tab's traceset. + */ +static void histo_request_background_data(HistoControlFlowData *histocontrol_flow_data) +{ + LttvTracesetContext * tsc = + lttvwindow_get_traceset_context(histocontrol_flow_data->tab); + gint num_traces = lttv_traceset_number(tsc->ts); + gint i; + LttvTrace *trace; + LttvTraceState *tstate; + + LttvHooks *histo_background_ready_hook = + lttv_hooks_new(); + lttv_hooks_add(histo_background_ready_hook, histo_background_ready, histocontrol_flow_data, + LTTV_PRIO_DEFAULT); + histocontrol_flow_data->background_info_waiting = 0; + + for(i=0;its, i); + tstate = LTTV_TRACE_STATE(tsc->traces[i]); + + if(lttvwindowtraces_get_ready(g_quark_from_string("state"),trace)==FALSE + && !tstate->has_precomputed_states) { + + if(lttvwindowtraces_get_in_progress(g_quark_from_string("state"), + trace) == FALSE) { + /* We first remove requests that could have been done for the same + * information. Happens when two viewers ask for it before servicing + * starts. + */ + if(!lttvwindowtraces_background_request_find(trace, "state")) + lttvwindowtraces_background_request_queue( + main_window_get_widget(histocontrol_flow_data->tab), trace, "state"); + lttvwindowtraces_background_notify_queue(histocontrol_flow_data, + trace, + ltt_time_infinite, + NULL, + histo_background_ready_hook); + histocontrol_flow_data->background_info_waiting++; + } else { /* in progress */ + + lttvwindowtraces_background_notify_current(histocontrol_flow_data, + trace, + ltt_time_infinite, + NULL, + histo_background_ready_hook); + histocontrol_flow_data->background_info_waiting++; + } + } else { + /* Data ready. Be its nature, this viewer doesn't need to have + * its data ready hook called there, because a background + * request is always linked with a redraw. + */ + } + + } + + lttv_hooks_destroy(histo_background_ready_hook); +} + +/** + * Histogram Viewer's constructor hook + * + * This constructor is given as a parameter to the menuitem and toolbar button + * registration. It creates the list. + * @param tab A pointer to the parent tab. + * @return The widget created. + */ +GtkWidget * +h_guihistocontrolflow(LttvPlugin *plugin) +{ + LttvPluginTab *ptab = LTTV_PLUGIN_TAB(plugin); + g_info("h_guihistocontrolflow, %p", ptab); + HistoControlFlowData *histocontrol_flow_data = guihistocontrolflow(ptab) ; + + Tab *tab = ptab->tab; + histocontrol_flow_data->tab = tab; + + // Unreg done in the GuiHistoControlFlow_Destructor + lttvwindow_register_traceset_notify(tab, + histo_traceset_notify, + histocontrol_flow_data); + + lttvwindow_register_time_window_notify(tab, + histo_update_time_window_hook, + histocontrol_flow_data); + lttvwindow_register_current_time_notify(tab, + histo_update_current_time_hook, + histocontrol_flow_data); + lttvwindow_register_redraw_notify(tab, + histo_redraw_notify, + histocontrol_flow_data); + lttvwindow_register_continue_notify(tab, + histo_continue_notify, + histocontrol_flow_data); + //added for histogram, enable filter: + lttvwindow_register_filter_notify(tab, + histo_filter_changed,histocontrol_flow_data ); + histocontrol_flow_data->histo_main_win_filter = lttvwindow_get_filter(tab); + +// histo_request_background_data(histocontrol_flow_data); + + return guihistocontrolflow_get_widget(histocontrol_flow_data) ; + +} + + + +/// added for histogram. +void histo_request_event( HistoControlFlowData *histocontrol_flow_data, guint x, guint width) +{ + if(width < 0) return ; + + guint i, nb_trace; + Tab *tab = histocontrol_flow_data->tab; + TimeWindow time_window = lttvwindow_get_time_window( tab ); + LttTime time_start, time_end; + + LttvTraceState *ts; + + //find the tracehooks + LttvTracesetContext *tsc = lttvwindow_get_traceset_context(tab); + + LttvTraceset *traceset = tsc->ts; + nb_trace = lttv_traceset_number(traceset); + guint drawing_width= histocontrol_flow_data->drawing->width; +//start time for chunk. + histo_convert_pixels_to_time(drawing_width, /*0*/x, time_window, + &time_start); +//end time for chunk. + histo_convert_pixels_to_time(drawing_width, + /*width*/x+width,time_window, + &time_end); + time_end = ltt_time_add(time_end, ltt_time_one); // because main window + // doesn't deliver end time. + + lttvwindow_events_request_remove_all(tab, + histocontrol_flow_data); + + + // LttvHooksById *histo_event_by_id = lttv_hooks_by_id_new();//if necessary for filter! + // FIXME : eventually request for more traces + // fixed for(i = 0; itraces[i]; + // Fill the events request + histo_events_request->owner = histocontrol_flow_data; + histo_events_request->viewer_data = histocontrol_flow_data; + histo_events_request->servicing = FALSE; + histo_events_request->start_time = time_start;//time_window.start_time; + + histo_events_request->start_position = NULL; + histo_events_request->stop_flag = FALSE; + histo_events_request->end_time = time_end;//time_window.end_time; + + histo_events_request->num_events = G_MAXUINT; + histo_events_request->end_position = NULL; + histo_events_request->trace = i; + histo_events_request->hooks = NULL; + histo_events_request->before_chunk_traceset = histo_before_chunk_traceset;//NULL; + histo_events_request->before_chunk_trace = NULL; + histo_events_request->before_chunk_tracefile= NULL; + histo_events_request->event = histo_count_event_hooks; + histo_events_request->event_by_id = NULL;//histo_event_by_id;//NULL; + histo_events_request->after_chunk_tracefile = NULL; + histo_events_request->after_chunk_trace = NULL; + histo_events_request->after_chunk_traceset = histo_after_chunk_traceset;//NULL; + histo_events_request->before_request = histo_before_trace_hooks; + histo_events_request->after_request = histo_after_trace_hooks; + + lttvwindow_events_request(histocontrol_flow_data->tab, histo_events_request); + } +return; +} + +//hook,added for histogram +int histo_count_event(void *hook_data, void *call_data){ + + guint x;//time to pixel + guint i;// number of events + LttTime event_time; + LttEvent *e; + guint *element; + + EventsRequest *events_request = (EventsRequest*)hook_data; + HistoControlFlowData *histocontrol_flow_data = events_request->viewer_data; + + histoDrawing_t *drawing = histocontrol_flow_data->drawing; + int width = drawing->width; + + g_info("Histogram: count_event() \n"); + + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *histo_filter = histocontrol_flow_data->histo_main_win_filter; + if(histo_filter != NULL && histo_filter->head != NULL) + if(!lttv_filter_tree_parse(histo_filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + TimeWindow time_window = lttvwindow_get_time_window(histocontrol_flow_data->tab); + event_time = ltt_event_time(e); + + histo_convert_time_to_pixels( + time_window, + event_time, + width, + &x); + element = &g_array_index(histocontrol_flow_data->number_of_process, guint, x); + (*element)++; + + return 0; +} +///befor hook:Added for histogram +int histo_before_trace(void *hook_data, void *call_data){ + + EventsRequest *events_request = (EventsRequest*)hook_data; + HistoControlFlowData *histocontrol_flow_data = events_request->viewer_data; + + histoDrawing_t *drawing = histocontrol_flow_data->drawing; + +//in order to reset all of the array elements. + guint i,end; + end = MIN(histocontrol_flow_data->number_of_process->len,drawing->damage_end); + for(i=drawing->damage_begin/*0*/; + i < end/*histocontrol_flow_data->number_of_process->len*/;i++) + { + g_array_index(histocontrol_flow_data->number_of_process, guint, i) = 0; + } + histo_drawing_clear(drawing,drawing->damage_begin/*0*/, + drawing->damage_end - drawing->damage_begin/*drawing->width*/); + //g_array_free(histocontrol_flow_data->number_of_process,TRUE); + //histocontrol_flow_data->number_of_process =g_array_new (FALSE, + // TRUE, + // sizeof(guint));//4 byte for guint + //g_array_set_size (histocontrol_flow_data->number_of_process, + // drawing->drawing_area->allocation.width); +// gtk_widget_set_size_request(drawing->drawing_area,-1,-1); + gtk_widget_queue_draw(drawing->drawing_area); + return 0; +} +//after hook,added for histogram +int histo_after_trace(void *hook_data, void *call_data){ + + EventsRequest *events_request = (EventsRequest*)hook_data; + HistoControlFlowData *histocontrol_flow_data = events_request->viewer_data; + histoDrawing_t *drawing = histocontrol_flow_data->drawing; + guint x, x_end, width; + LttTime end_time = events_request->end_time; + TimeWindow time_window = + lttvwindow_get_time_window(histocontrol_flow_data->tab); + + g_debug("histo after trace"); + + histo_convert_time_to_pixels( + time_window, + end_time, + drawing->width, + &x_end); + x = drawing->damage_begin; + width = x_end - x; + drawing->damage_begin = x+width; + histogram_show (histocontrol_flow_data,x,x_end); + + return 0; +} + +void histogram_show(HistoControlFlowData *histocontrol_flow_data,guint draw_begin, + guint draw_end) +{ + histoDrawing_t *drawing = histocontrol_flow_data->drawing; + GtkWidget *drawingarea= histo_drawing_get_drawing_area(drawing); + guint width = drawing->width; + guint height= drawing->height;//drawingarea->allocation.height; + + /* gdk_gc_set_line_attributes(drawing->gc, + 2, + GDK_LINE_SOLID, + GDK_CAP_BUTT, + GDK_JOIN_MITER);*/ +//clean the area! + histo_drawing_clear(drawing,draw_begin,draw_end); + LttTime t1,t2; + TimeWindow time_window = + lttvwindow_get_time_window(histocontrol_flow_data->tab); + + guint val,h_val; + + guint i,line_src,line_end; + guint end_chunk=MIN(draw_end,(histocontrol_flow_data->number_of_process)->len); + + for (i=draw_begin/*0*/;inumber_of_process)->len*/;i++){ + val=g_array_index(histocontrol_flow_data->number_of_process,guint,i); + h_val= height-((height*val)/histocontrol_flow_data->max_height); + + histo_convert_pixels_to_time(width, i, + time_window, + &t1); + histo_convert_pixels_to_time(width, i+1, + time_window, + &t2); + line_src=i; + +//check if zoom in is used and more than 1 pixel correspond to each 1nsec +//used for drawing point (not line) on the screen. +/* while (ltt_time_compare(t1,t2)==0) + { + histo_convert_pixels_to_time(width, i++, + time_window, + &t1); + histo_convert_pixels_to_time(width, i+1, + time_window, + &t2); + + + }//while (t1==t2) +*/ //replaced later for lines. + + if(val > drawing->histo_control_flow_data->max_height){ + //overlimit, yellow color + gdk_gc_set_foreground(drawing->gc,&histo_drawing_colors[COL_WHITE] );//COL_RUN_TRAP + gdk_draw_line (drawing->pixmap, + drawing->gc, + i/*line_src*/,1, + i,/*1*/height); + } + else{ + gdk_gc_set_foreground(drawing->gc,&histo_drawing_colors[COL_RUN_USER_MODE] ); + gdk_draw_line (drawing->pixmap, + drawing->gc, + i/*line_src*/,h_val, + i,/*h_val*/height); + } + + while ((ltt_time_compare(t1,t2)==0)&&(i drawing->histo_control_flow_data->max_height){ + //overlimit, yellow color + gdk_gc_set_foreground(drawing->gc, + &histo_drawing_colors[COL_RUN_TRAP] ); + gdk_draw_line (drawing->pixmap, + drawing->gc, + i,1, + i,height); + } + else{ + gdk_gc_set_foreground(drawing->gc,&histo_drawing_colors[COL_RUN_USER_MODE] ); + gdk_draw_line (drawing->pixmap, + drawing->gc, + i,h_val, + i,height); + } + histo_convert_pixels_to_time(width, i, + time_window, + &t1); + if(idrawing_area, + draw_begin, 0, + draw_end-draw_begin, drawing->height); + gdk_window_process_updates(drawingarea->window,TRUE); +} + +int histo_event_selected_hook(void *hook_data, void *call_data) +{ + HistoControlFlowData *histocontrol_flow_data = (HistoControlFlowData*) hook_data; + guint *event_number = (guint*) call_data; + + g_debug("DEBUG : event selected by main window : %u", *event_number); + + return 0; +} + + + +/* histo_before_schedchange_hook + * + * This function basically draw lines and icons. Two types of lines are drawn : + * one small (3 pixels?) representing the state of the process and the second + * type is thicker (10 pixels?) representing on which CPU a process is running + * (and this only in running state). + * + * Extremums of the lines : + * x_min : time of the last event context for this process kept in memory. + * x_max : time of the current event. + * y : middle of the process in the process list. The process is found in the + * list, therefore is it's position in pixels. + * + * The choice of lines'color is defined by the context of the last event for this + * process. + */ + +/* +int histo_before_schedchange_hook(void *hook_data, void *call_data) +{ + return 0; +} +*/ + +gint histo_update_time_window_hook(void *hook_data, void *call_data) +{ + HistoControlFlowData *histocontrol_flow_data = (HistoControlFlowData*) hook_data; + histoDrawing_t *drawing = histocontrol_flow_data->drawing; + + const TimeWindowNotifyData *histo_time_window_nofify_data = + ((const TimeWindowNotifyData *)call_data); + + TimeWindow *histo_old_time_window = + histo_time_window_nofify_data->old_time_window; + TimeWindow *histo_new_time_window = + histo_time_window_nofify_data->new_time_window; + + // Update the ruler + histo_drawing_update_ruler(drawing, + histo_new_time_window); + + /* Two cases : zoom in/out or scrolling */ + + /* In order to make sure we can reuse the old drawing, the scale must + * be the same and the new time interval being partly located in the + * currently shown time interval. (reuse is only for scrolling) + */ + + g_info("Old time window HOOK : %lu, %lu to %lu, %lu", + histo_old_time_window->start_time.tv_sec, + histo_old_time_window->start_time.tv_nsec, + histo_old_time_window->time_width.tv_sec, + histo_old_time_window->time_width.tv_nsec); + + g_info("New time window HOOK : %lu, %lu to %lu, %lu", + histo_new_time_window->start_time.tv_sec, + histo_new_time_window->start_time.tv_nsec, + histo_new_time_window->time_width.tv_sec, + histo_new_time_window->time_width.tv_nsec); + + //For Histo,redraw always except if zoom fit is pushed 2 times consequently + if( histo_new_time_window->start_time.tv_sec == histo_old_time_window->start_time.tv_sec + && histo_new_time_window->start_time.tv_nsec == histo_old_time_window->start_time.tv_nsec + && histo_new_time_window->time_width.tv_sec == histo_old_time_window->time_width.tv_sec + && histo_new_time_window->time_width.tv_nsec == histo_old_time_window->time_width.tv_nsec) + { + return 0; + } + histo_rectangle_pixmap (drawing->drawing_area->style->black_gc, + TRUE, + 0, 0, + drawing->width,//+SAFETY, // do not overlap + -1,drawing); + + drawing->damage_begin = 0; + drawing->damage_end = drawing->width; + + gtk_widget_queue_draw(drawing->drawing_area); + histo_request_event(histocontrol_flow_data,drawing->damage_begin, + drawing->damage_end- drawing->damage_begin); + + gdk_window_process_updates(drawing->drawing_area->window,TRUE); + +//show number of event at current time + + histo_drawing_update_vertical_ruler(drawing); + + + +/*// if( histo_new_time_window->time_width.tv_sec == histo_old_time_window->time_width.tv_sec + && histo_new_time_window->time_width.tv_nsec == histo_old_time_window->time_width.tv_nsec) + { + // Same scale (scrolling) + g_info("scrolling"); + /* For histogram, + while scrolling no matter far or near , + right or left it's necessary to redraw whole screen!*/ +/*// LttTime *ns = &histo_new_time_window->start_time; + LttTime *nw = &histo_new_time_window->time_width; + LttTime *os = &histo_old_time_window->start_time; + LttTime *ow = &histo_old_time_window->time_width; + LttTime histo_old_end = histo_old_time_window->end_time; + LttTime histo_new_end = histo_new_time_window->end_time; + //if(nsdrawing_area); + + drawing->damage_begin = 0; + drawing->damage_end = drawing->width; + + //replaced for hisogram + histo_request_event(histocontrol_flow_data,drawing->damage_begin, + drawing->damage_end- drawing->damage_begin); +/* + if(ltt_time_compare(*ns, histo_old_end) == -1 + && ltt_time_compare(*os, *ns) == -1) + { + g_info("scrolling near right"); + // Scroll right, keep right part of the screen + guint x = 0; + guint width = drawing->width; + histo_convert_time_to_pixels( + *histo_old_time_window, + *ns, + width, + &x); + + // Copy old data to new location + //replaced for histogram: + histo_copy_pixmap_region(drawing,NULL, + drawing->drawing_area->style->black_gc,//drawing->gc, + NULL, + x, 0, + 0, 0, (drawing->width-x) + , -1); + + if(drawing->damage_begin == drawing->damage_end) + drawing->damage_begin = drawing->width-x; + else + drawing->damage_begin = 0; + + drawing->damage_end = drawing->width; + +//(histo) copy corresponding array region too: + guint i; + + for(i=0; i < histocontrol_flow_data->number_of_process->len-x;i++) + { + g_array_index(histocontrol_flow_data->number_of_process, guint, i) = + g_array_index(histocontrol_flow_data->number_of_process, guint, i+x); + } + + // Clear the data request background, but not SAFETY + + +//not necessary for histo, because in before chunk ,it clears the area +/* histo_rectangle_pixmap ( + drawing->drawing_area->style->black_gc, + TRUE, + drawing->damage_begin, 0, + drawing->damage_end - drawing->damage_begin, // do not overlap + -1,drawing); +*/ + /* gtk_widget_queue_draw(drawing->drawing_area); + //gtk_widget_queue_draw_area (drawing->drawing_area, + // 0,0, + // histocontrol_flow_data->drawing->width, + // histocontrol_flow_data->drawing->height); + + // Get new data for the rest. + //replaced for hisogram + histo_request_event(histocontrol_flow_data,drawing->damage_begin, + drawing->damage_end- drawing->damage_begin); + } else { + //if(nswidth; + histo_convert_time_to_pixels( + *histo_new_time_window, + *os, + width, + &x); + + // Copy old data to new location + //replaced for histogram + + histo_copy_pixmap_region(drawing,NULL, + drawing->drawing_area->style->black_gc,//drawing->gc, + NULL, + 0, 0, + x, 0, -1, -1); + //(histo) copy corresponding array region too: + guint i; + for(i=histocontrol_flow_data->number_of_process->len; i > x-1;i--) + { + g_array_index(histocontrol_flow_data->number_of_process, guint, i) = + g_array_index(histocontrol_flow_data->number_of_process, guint, i-x); + } + + if(drawing->damage_begin == drawing->damage_end) + drawing->damage_end = x; + else + drawing->damage_end = + drawing->width; + + drawing->damage_begin = 0; + + +//not necessary for histo, because in before chunk ,it clears the area + /* histo_rectangle_pixmap (drawing->drawing_area->style->black_gc, + TRUE, + drawing->damage_begin, 0, + drawing->damage_end - drawing->damage_begin, // do not overlap + -1,drawing); +*/ + /* gtk_widget_queue_draw(drawing->drawing_area); + //gtk_widget_queue_draw_area (drawing->drawing_area, + // 0,0, + // histocontrol_flow_data->drawing->width, + // histocontrol_flow_data->drawing->height); + + + // Get new data for the rest. + +//replaced for hisogram + histo_request_event(histocontrol_flow_data,drawing->damage_begin, + drawing->damage_end- drawing->damage_begin); + + } else { + if(ltt_time_compare(*ns,*os) == 0) + { + g_info("not scrolling"); + } else { + g_info("scrolling far"); + // Cannot reuse any part of the screen : far jump + + //not necessary for histo, because in before chunk ,it clears the area + /* histo_rectangle_pixmap (histocontrol_flow_data->drawing->drawing_area->style->black_gc, + TRUE, + 0, 0, + histocontrol_flow_data->drawing->width,//+SAFETY, // do not overlap + -1,drawing); +*/ + //gtk_widget_queue_draw_area (drawing->drawing_area, + // 0,0, + // histocontrol_flow_data->drawing->width, + // histocontrol_flow_data->drawing->height); +/* gtk_widget_queue_draw(drawing->drawing_area); + + drawing->damage_begin = 0; + drawing->damage_end = histocontrol_flow_data->drawing->width; +/* + histo_drawing_data_request(histocontrol_flow_data->drawing, + 0, 0, + histocontrol_flow_data->drawing->width, + histocontrol_flow_data->drawing->height);*/ + //replaced for hisogram + /* histo_request_event(histocontrol_flow_data,drawing->damage_begin, + drawing->damage_end- drawing->damage_begin); + } + } + } + } else { + // Different scale (zoom) + g_info("zoom"); + + //not necessary for histo, because in before chunk ,it clears the area + /* + histo_rectangle_pixmap (drawing->drawing_area->style->black_gc, + TRUE, + 0, 0, + histocontrol_flow_data->drawing->width+SAFETY, // do not overlap + -1,drawing); +*/ + //gtk_widget_queue_draw_area (drawing->drawing_area, + // 0,0, + // histocontrol_flow_data->drawing->width, + // histocontrol_flow_data->drawing->height); +/*// gtk_widget_queue_draw(drawing->drawing_area); + + drawing->damage_begin = 0; + drawing->damage_end = drawing->width; + + //replaced for hisogram + histo_request_event(histocontrol_flow_data,drawing->damage_begin, + drawing->damage_end- drawing->damage_begin); + } + + // Update directly when scrolling + gdk_window_process_updates(drawing->drawing_area->window, + TRUE); + + //show number of event at current time + + histo_drawing_update_vertical_ruler(drawing); +*/ +//disabled for histogram, always redraw whole screen. + return 0; +} + +gint histo_traceset_notify(void *hook_data, void *call_data) +{ + HistoControlFlowData *histocontrol_flow_data = (HistoControlFlowData*) hook_data; + histoDrawing_t *drawing = histocontrol_flow_data->drawing; + + if(unlikely(drawing->gc == NULL)) { + return FALSE; + } + if(drawing->dotted_gc == NULL) { + return FALSE; + } + + histo_drawing_clear(drawing,0,drawing->width); + + guint i; + for(i=0;i < histocontrol_flow_data->number_of_process->len;i++) + { + g_array_index(histocontrol_flow_data->number_of_process, guint, i) = 0; + } + gtk_widget_set_size_request( + drawing->drawing_area, + -1, -1); + histo_redraw_notify(histocontrol_flow_data, NULL); + + ///histo_request_background_data(histocontrol_flow_data); + + return FALSE; +} + +gint histo_redraw_notify(void *hook_data, void *call_data) +{ + HistoControlFlowData *histocontrol_flow_data = (HistoControlFlowData*) hook_data; + histoDrawing_t *drawing = histocontrol_flow_data->drawing; + GtkWidget *widget = drawing->drawing_area; + + drawing->damage_begin = 0; + drawing->damage_end = drawing->width; + + // fun feature, to be separated someday... + + histo_drawing_clear(drawing,0,drawing->width); + + gtk_widget_set_size_request( + drawing->drawing_area, + -1, -1); + // Clear the images + + histo_rectangle_pixmap (widget->style->black_gc, + TRUE, + 0, 0, + drawing->alloc_width, + -1,drawing); + gtk_widget_queue_draw(widget); + + + if(drawing->damage_begin < drawing->damage_end) + { + //replaced for histogram + histo_request_event(histocontrol_flow_data,0,drawing->width); + } + + + //gtk_widget_queue_draw_area(drawing->drawing_area, + // 0,0, + // drawing->width, + // drawing->height); + return FALSE; + +} + + +gint histo_continue_notify(void *hook_data, void *call_data) +{ + HistoControlFlowData *histocontrol_flow_data = (HistoControlFlowData*) hook_data; + histoDrawing_t *drawing = histocontrol_flow_data->drawing; + + //g_assert(widget->allocation.width == drawing->damage_end); + + if(drawing->damage_begin < drawing->damage_end) + { + histo_request_event(histocontrol_flow_data,drawing->damage_begin, + drawing->damage_end-drawing->damage_begin); + } + + return FALSE; +} + + +gint histo_update_current_time_hook(void *hook_data, void *call_data) +{ + HistoControlFlowData *histocontrol_flow_data = (HistoControlFlowData*)hook_data; + histoDrawing_t *drawing = histocontrol_flow_data->drawing; + + LttTime current_time = *((LttTime*)call_data); + + TimeWindow time_window = + lttvwindow_get_time_window(histocontrol_flow_data->tab); + + LttTime time_begin = time_window.start_time; + LttTime width = time_window.time_width; + LttTime half_width; + { + guint64 time_ll = ltt_time_to_uint64(width); + time_ll = time_ll >> 1; /* divide by two */ + half_width = ltt_time_from_uint64(time_ll); + } + LttTime time_end = ltt_time_add(time_begin, width); + + LttvTracesetContext * tsc = + lttvwindow_get_traceset_context(histocontrol_flow_data->tab); + + LttTime trace_start = tsc->time_span.start_time; + LttTime trace_end = tsc->time_span.end_time; + + g_info("Histogram: New current time HOOK : %lu, %lu", current_time.tv_sec, + current_time.tv_nsec); + + + + /* If current time is inside time interval, just move the highlight + * bar */ + + /* Else, we have to change the time interval. We have to tell it + * to the main window. */ + /* The time interval change will take care of placing the current + * time at the center of the visible area, or nearest possible if we are + * at one end of the trace. */ + + + if(ltt_time_compare(current_time, time_begin) < 0) + { + TimeWindow histo_new_time_window; + + if(ltt_time_compare(current_time, + ltt_time_add(trace_start,half_width)) < 0) + time_begin = trace_start; + else + time_begin = ltt_time_sub(current_time,half_width); + + histo_new_time_window.start_time = time_begin; + histo_new_time_window.time_width = width; + histo_new_time_window.time_width_double = ltt_time_to_double(width); + histo_new_time_window.end_time = ltt_time_add(time_begin, width); + + lttvwindow_report_time_window(histocontrol_flow_data->tab, histo_new_time_window); + } + else if(ltt_time_compare(current_time, time_end) > 0) + { + TimeWindow histo_new_time_window; + + if(ltt_time_compare(current_time, ltt_time_sub(trace_end, half_width)) > 0) + time_begin = ltt_time_sub(trace_end,width); + else + time_begin = ltt_time_sub(current_time,half_width); + + histo_new_time_window.start_time = time_begin; + histo_new_time_window.time_width = width; + histo_new_time_window.time_width_double = ltt_time_to_double(width); + histo_new_time_window.end_time = ltt_time_add(time_begin, width); + + lttvwindow_report_time_window(histocontrol_flow_data->tab, histo_new_time_window); + + } + gtk_widget_queue_draw(drawing->drawing_area); + + /* Update directly when scrolling */ + gdk_window_process_updates(drawing->drawing_area->window, + TRUE); + + histo_drawing_update_vertical_ruler(drawing); + + return 0; +} + +gboolean histo_filter_changed(void * hook_data, void * call_data) +{ + HistoControlFlowData *histocontrol_flow_data = (HistoControlFlowData*)hook_data; + histoDrawing_t *drawing =histocontrol_flow_data->drawing; + + LttvTracesetContext * tsc = + lttvwindow_get_traceset_context(histocontrol_flow_data->tab); + + histocontrol_flow_data->histo_main_win_filter = + (LttvFilter*)call_data; + //get_events(event_viewer_data->vadjust_c->value, event_viewer_data); + gtk_widget_set_size_request( + drawing->drawing_area, + -1, -1); + drawing->damage_begin = 0; + drawing->damage_end = drawing->width; + + /* //done in, before request! + histo_drawing_clear(drawing,0,drawing->width); + guint i; + for(i=0;i < histocontrol_flow_data->number_of_process->len;i++) + { + g_array_index(histocontrol_flow_data->number_of_process, guint, i) = 0; + }*/ + + histo_request_event(histocontrol_flow_data,0,drawing->width); + + return FALSE; +} + +typedef struct _histo_ClosureData { + EventsRequest *events_request; + LttvTracesetState *tss; + LttTime end_time; + guint x_end; +} histo_ClosureData; + + + +int histo_before_chunk(void *hook_data, void *call_data) +{ + EventsRequest *histo_events_request = (EventsRequest*)hook_data; + LttvTracesetState *histo_tss = (LttvTracesetState*)call_data; + HistoControlFlowData *histo_cfd = (HistoControlFlowData*)histo_events_request->viewer_data; +#if 0 + /* Desactivate sort */ + gtk_tree_sortable_set_sort_column_id( + GTK_TREE_SORTABLE(cfd->process_list->list_store), + TRACE_COLUMN, + GTK_SORT_ASCENDING); +#endif //0 + histo_drawing_chunk_begin(histo_events_request, histo_tss); + + return 0; +} + +/*int histo_before_request(void *hook_data, void *call_data) +{ + EventsRequest *events_request = (EventsRequest*)hook_data; + LttvTracesetState *tss = (LttvTracesetState*)call_data; + + histo_drawing_data_request_begin(events_request, tss); + + return 0; +} +*/ + + +/* + * after request is necessary in addition of after chunk in order to draw + * lines until the end of the screen. after chunk just draws lines until + * the last event. + * + * for each process + * draw closing line + * expose + */ +/*int histo_after_request(void *hook_data, void *call_data) +{ + return 0; +} +*/ +/* + * for each process + * draw closing line + * expose + */ + +int histo_after_chunk(void *hook_data, void *call_data) +{ + EventsRequest *events_request = (EventsRequest*)hook_data; + HistoControlFlowData *histocontrol_flow_data = events_request->viewer_data; + LttvTracesetState *tss = (LttvTracesetState*)call_data; + LttvTracesetContext *tsc = (LttvTracesetContext*)call_data; + LttvTracefileContext *tfc = lttv_traceset_context_get_current_tfc(tsc); + LttTime end_time; + + histoDrawing_t *drawing = histocontrol_flow_data->drawing; + + if(!histocontrol_flow_data->chunk_has_begun) return; + histocontrol_flow_data->chunk_has_begun = TRUE; + + if(tfc != NULL) + end_time = LTT_TIME_MIN(tfc->timestamp, events_request->end_time); + else /* end of traceset, or position now out of request : end */ + end_time = events_request->end_time; + + guint x, x_end, width; + + TimeWindow time_window = + lttvwindow_get_time_window(histocontrol_flow_data->tab); + + g_debug("histo after chunk"); + + histo_convert_time_to_pixels( + time_window, + end_time, + drawing->width, + &x_end); + x = drawing->damage_begin; + width = x_end - x; + drawing->damage_begin = x+width; + + histogram_show (histocontrol_flow_data,x,x_end); + + return 0; +} + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histoeventhooks.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histoeventhooks.h new file mode 100644 index 00000000..f5a76309 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histoeventhooks.h @@ -0,0 +1,128 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2006 Parisa heidari (inspired from CFV by Mathieu Desnoyers) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + + +/* eventhooks.h defines the hooks that are given to processTrace as parameter. + * These hooks call the drawing API to draw the information on the screen, + * using information from Context, but mostly state (running, waiting...). + */ + + +#ifndef _EVENT_HOOKS_H +#define _EVENT_HOOKS_H + +#include +#include +#include + +#include "histobuttonwidget.h" +#include "histodrawing.h" +#include "histocfv.h" + + +/* Structure used to store and use information relative to one events refresh + * request. Typically filled in by the expose event callback, then passed to the + * library call, then used by the drawing hooks. Then, once all the events are + * sent, it is freed by the hook called after the reading. + */ +//typedef struct _EventRequest +//{ +// ControlFlowData *control_flow_data; +// LttTime time_begin, time_end; +// gint x_begin, x_end; + /* Fill the Events_Context during the initial expose, before calling for + * events. + */ + //GArray Events_Context; //FIXME +//} EventRequest ; + + + + + +void send_test_data(ButtonWidget *buttonwidget, histoDrawing_t *drawing);//?? + +GtkWidget *h_guihistocontrolflow(LttvPlugin *plugin); + +//GtkWidget *h_legend(Tab *tab); + +int histo_event_selected_hook(void *hook_data, void *call_data); + +/* + * The draw event hook is called by the reading API to have a + * particular event drawn on the screen. + * @param hook_data ControlFlowData structure of the viewer. + * @param call_data Event context with state. + * + * This function basically draw lines and icons. Two types of lines are drawn : + * one small (3 pixels?) representing the state of the process and the second + * type is thicker (10 pixels?) representing on which CPU a process is running + * (and this only in running state). + * + * Extremums of the lines : + * x_min : time of the last event context for this process kept in memory. + * x_max : time of the current event. + * y : middle of the process in the process list. The process is found in the + * list, therefore is it's position in pixels. + * + * The choice of lines'color is defined by the context of the last event for this + * process. + */ +//int histo_before_schedchange_hook(void *hook_data, void *call_data); +int histo_after_schedchange_hook(void *hook_data, void *call_data); +int histo_before_execmode_hook(void *hook_data, void *call_data); +int histo_after_execmode_hook(void *hook_data, void *call_data); + + +int histo_before_process_exit_hook(void *hook_data, void *call_data); +int histo_before_process_release_hook(void *hook_data, void *call_data); +int histo_after_process_exit_hook(void *hook_data, void *call_data); +int histo_after_process_fork_hook(void *hook_data, void *call_data); +int histo_after_fs_exec_hook(void *hook_data, void *call_data); + + +#if 0 +int before_process_hook(void *hook_data, void *call_data); +int after_process_hook(void *hook_data, void *call_data); +#endif //0 + +//void histo_draw_closure(guint key, gpointer value, gpointer user_data); + +int histo_before_chunk(void *hook_data, void *call_data); +int histo_after_chunk(void *hook_data, void *call_data); +//int histo_before_request(void *hook_data, void *call_data); +//int histo_after_request(void *hook_data, void *call_data); + + + +gint histo_update_time_window_hook(void *hook_data, void *call_data); +gint histo_update_current_time_hook(void *hook_data, void *call_data); +gint histo_traceset_notify(void *hook_data, void *call_data); +gint histo_redraw_notify(void *hook_data, void *call_data); +gint histo_continue_notify(void *hook_data, void *call_data); + +//just for histogram +void histo_request_event( HistoControlFlowData *histocontrol_flow_data,guint x, guint width); +int histo_count_event(void *hook_data, void *call_data); +int histo_before_trace(void *hook_data, void *call_data);//replaced for histo_before_request +int histo_after_trace(void *hook_data, void *call_data);//replaced for histo_after_request + +gboolean histo_filter_changed(void * hook_data, void * call_data); + +void histogram_show(HistoControlFlowData *histocontrol_flow_data,guint draw_begin,guint draw_end); +#endif // _EVENT_HOOKS_H diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histomodule.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histomodule.c new file mode 100644 index 00000000..c9c58141 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/histomodule.c @@ -0,0 +1,93 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2006 Parisa heidari (inspired from CFV by Mathieu Desnoyers) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "histocfv.h" +#include "histoeventhooks.h" + +#include "hHistogramInsert.xpm" + + +GQuark LTT_NAME_CPU; + +/** Array containing instanced objects. Used when module is unloaded */ +GSList *g_histo_control_flow_data_list = NULL ; + +/***************************************************************************** + * Functions for module loading/unloading * + *****************************************************************************/ +/** + * plugin's init function + * + * This function initializes the Histogram Control Flow Viewer functionnality through the + * gtkTraceSet API. + */ +static void histo_init() { + + g_info("GUI ControlFlow Viewer init()"); + + /* Register the toolbar insert button and menu entry*/ + lttvwindow_register_constructor("histogram", + "/", + "Insert Histogram Viewer", + hHistogramInsert_xpm, + "Insert Histogram Viewer", + h_guihistocontrolflow); + + LTT_NAME_CPU = g_quark_from_string("/cpu"); +} + +void histo_destroy_walk(gpointer data, gpointer user_data) +{ + g_info("Walk destroy GUI Histogram Control Flow Viewer"); + guihistocontrolflow_destructor_full((HistoControlFlowData*)data); +} + + + +/** + * plugin's destroy function + * + * This function releases the memory reserved by the module and unregisters + * everything that has been registered in the gtkTraceSet API. + */ +static void histo_destroy() { + g_info("GUI Histogram Control Flow Viewer destroy()"); + + g_slist_foreach(g_histo_control_flow_data_list, histo_destroy_walk, NULL ); + + g_slist_free(g_histo_control_flow_data_list); + + /* Unregister the toolbar insert button and menu entry */ + lttvwindow_unregister_constructor(h_guihistocontrolflow); + +} + + +LTTV_MODULE("guihistogram", "Event Histogram viewer", \ + "Graphical module to view events' density histogram", \ + histo_init, histo_destroy, "lttvwindow") diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/stock_zoom_fit_24.xpm b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/stock_zoom_fit_24.xpm new file mode 100644 index 00000000..bd0b3f43 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/stock_zoom_fit_24.xpm @@ -0,0 +1,216 @@ +/* XPM */ +static char *stock_zoom_fit_24[]={ +"24 24 189 2", +"Qt c None", +"ae c #000000", +"aF c #000000", +"an c #050505", +".U c #242424", +".s c #373737", +".G c #4f4f4f", +".h c #5d5d5d", +"aS c #000000", +"a6 c #000000", +"at c #000000", +"aQ c #000000", +"ad c #000000", +"au c #030303", +"#8 c #0c0c0c", +"#j c #121212", +".F c #232323", +"#. c #303030", +".g c #353535", +".t c #404040", +".# c #494949", +"aE c #000000", +"#7 c #000000", +"#s c #090909", +"#1 c #0a0a0a", +"#k c #1b1b1b", +".f c #262626", +".a c #343434", +"am c #000000", +"aG c #010101", +"af c #060606", +".9 c #0f0f0f", +".r c #1c1c1c", +".V c #212121", +".i c #2e2e2e", +"aR c #000000", +"#0 c #020202", +"#A c #060606", +"#V c #0b0b0b", +"#t c #141414", +".e c #262626", +".b c #2d2d2d", +"aN c #000000", +"a4 c #000000", +"aW c #000000", +"aO c #000000", +"aC c #000000", +"aD c #000000", +"aP c #000000", +"aT c #202020", +"a1 c #000000", +"aU c #202020", +"aV c #000000", +"aY c #343434", +"a2 c #000000", +"as c #000000", +"av c #030303", +"#U c #050505", +"#J c #060606", +"#K c #0e0e0e", +".T c #101010", +"#B c #111111", +".E c #131313", +"a3 c #1f1f1f", +".H c #222222", +".d c #262626", +".u c #272727", +".c c #292929", +"aX c #686868", +"a0 c #3a3a3a", +"a5 c #000000", +"aZ c #797979", +"aM c #000000", +"ac c #020202", +"aH c #030303", +"#9 c #0a0a0a", +"#i c #0e0e0e", +"## c #1f1f1f", +".q c #202020", +"aB c #2b2b2b", +"al c #2e2e2e", +".j c #303030", +"aw c #313131", +"ag c #3b3b3b", +"aL c #3c3c3c", +"aj c #414141", +".8 c #434343", +"ai c #444444", +"aa c #454545", +".Y c #464646", +"a# c #474747", +"#5 c #484848", +"a. c #494949", +".6 c #4a4a4a", +".Z c #4b4b4b", +".D c #4c4c4c", +"#f c #4d4d4d", +"#p c #4e4e4e", +".5 c #4f4f4f", +".0 c #505050", +".4 c #515151", +".1 c #525252", +"#c c #535353", +"#r c #575757", +".W c #5b5b5b", +".S c #5e5e5e", +"#2 c #5f5f5f", +".v c #616161", +".p c #676767", +"#l c #6d6d6d", +".k c #737373", +".I c #757575", +"aK c #777777", +"aI c #7a7a7a", +"aA c #909090", +"#Z c #939393", +"ax c #999999", +"aJ c #9a9a9a", +"#z c #9b9b9b", +".o c #9d9d9d", +".l c #a1a1a1", +"#W c #a5a5a5", +".C c #a7a7a7", +"#u c #a8a8a8", +"#h c #ababab", +".n c #b2b2b2", +".m c #b4b4b4", +"az c #b6b6b6", +"ar c #b8b8b8", +"#a c #b9b9b9", +"#T c #bababa", +"ay c #bbbbbb", +".R c #bcbcbc", +"ak c #bdbdbd", +"#I c #bfbfbf", +".B c #c1c1c1", +".7 c #c2c2c2", +"#C c #c5c5c5", +"#L c #c6c6c6", +"ao c #c7c7c7", +"aq c #c8c8c8", +".A c #c9c9c9", +".w c #cacaca", +"ab c #cbcbcb", +".X c #cccccc", +"ap c #cdcdcd", +".z c #cecece", +".x c #cfcfcf", +".y c #d0d0d0", +"#Y c #d1d1d1", +".Q c #d2d2d2", +".J c #d3d3d3", +"#6 c #d4d4d4", +"#b c #d6d6d6", +"#S c #d7d7d7", +"#v c #d8d8d8", +"#3 c #d9d9d9", +"ah c #dadada", +".K c #dbdbdb", +"#y c #dcdcdc", +"#D c #dfdfdf", +"#R c #e0e0e0", +"#m c #e1e1e1", +"#M c #e3e3e3", +"#g c #e4e4e4", +".P c #e5e5e5", +"#q c #e6e6e6", +".L c #e7e7e7", +"#4 c #e9e9e9", +"#Q c #eaeaea", +"#N c #ececec", +"#H c #ededed", +"#w c #eeeeee", +".M c #efefef", +"#P c #f0f0f0", +".O c #f1f1f1", +"#G c #f2f2f2", +".N c #f3f3f3", +"#O c #f4f4f4", +"#E c #f5f5f5", +"#X c #f6f6f6", +"#F c #f7f7f7", +"#x c #f8f8f8", +"#o c #f9f9f9", +".3 c #fafafa", +".2 c #fbfbfb", +"#e c #fcfcfc", +"#d c #fdfdfd", +"#n c #fefefe", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQt.#.a.b.c.d.e.f.gQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQt.h.i.j.k.l.m.n.o.p.q.r.sQtQtQtQtQtQtQtQt", +"QtQtQt.t.u.v.m.w.x.y.z.A.B.C.D.E.FQtQtQtQtQtQtQt", +"QtQt.G.H.I.w.J.K.L.M.N.O.P.Q.R.S.T.UQtQtQtQtQtQt", +"QtQt.V.W.X.Y.Z.0.1.2.3.4.0.5.6.7.8.9QtQtQtQtQtQt", +"Qt#.###a#b.D.1.1#c#d#e.1.4.5#f#g#h#i#jQtQtQtQtQt", +"Qt#k#l.Q#m.0.1#d#n#n#d#e#o.0#p#q.K#r#sQtQtQtQtQt", +"Qt#t#u#v#w.4.1#e#n#n#d.2#x.0#p#q#y#z#AQtQtQtQtQt", +"Qt#B#C#D.M#E#x.2#d#d#e.3#F#G#H.P.K#I#JQtQtQtQtQt", +"Qt#K#L#M#N#G#E#x.3.3.3#F#O#P#Q#R#S#T#UQtQtQtQtQt", +"Qt#V#W#m.L#p.5#O#X#X#E.N.M#f.Z.K#Y#Z#0QtQtQtQtQt", +"Qt#1#2#3#m.Z#f#w.M.M#w#H#4.Z#5#6.w.D#7QtQtQtQtQt", +"Qt#8#9.m#v#5.6.Z.Z#q.L.6a.a#aaab.lacadQtQtQtQtQt", +"Qtaeafag.Xaaaaa##5ah.Ka#.YaiajakalamQtQtQtQtQtQt", +"QtQtan#U.4.7aoab.Xapababaq.7araaasatQtQtQtQtQtQt", +"QtQtaeauavawaxayak.R.R.RazaAaBasaCaDaEQtQtQtQtQt", +"QtQtQtaeaFaGaHajaIaJaJaKaLaMamaFaNaOaPaNaQQtQtQt", +"QtQtQtQtQtaead#7aRasasaR#7adaSaeaeaTaUaVaCaSQtQt", +"QtQtQtQtQtQtQtaeaeaeaeaeaeaeQtQtaeaWaXaYaDaVaeQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaeaPaZa0aPa1ae", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaeaea2aXa3a1ae", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaeaea4a5a6ae", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaeaeaeQtQt"}; diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/stock_zoom_in_24.xpm b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/stock_zoom_in_24.xpm new file mode 100644 index 00000000..a23a4442 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/stock_zoom_in_24.xpm @@ -0,0 +1,169 @@ +/* XPM */ +static char *stock_zoom_in_24[]={ +"24 24 142 2", +"Qt c None", +"#V c #000000", +"ae c #010101", +"#O c #020202", +"#9 c #030303", +"#J c #050505", +"#v c #060606", +"#n c #090909", +"#P c #0a0a0a", +"#K c #0b0b0b", +"#W c #0c0c0c", +"#e c #0e0e0e", +".5 c #0f0f0f", +".R c #101010", +"#w c #111111", +"#f c #121212", +".C c #131313", +"#o c #141414", +"#g c #1b1b1b", +".p c #1c1c1c", +".6 c #1f1f1f", +".o c #202020", +".T c #212121", +".F c #222222", +".D c #232323", +".S c #242424", +".d c #262626", +".s c #272727", +".c c #292929", +"ad c #2b2b2b", +".b c #2d2d2d", +".g c #2e2e2e", +".h c #303030", +"a. c #313131", +".a c #343434", +".e c #353535", +".q c #373737", +"al c #3a3a3a", +"#1 c #3b3b3b", +"ai c #3c3c3c", +"#Y c #3f3f3f", +".r c #404040", +"#F c #414141", +"#A c #424242", +".4 c #434343", +"#z c #444444", +"## c #454545", +".# c #494949", +".B c #4c4c4c", +".E c #4f4f4f", +"#5 c #515151", +"#m c #575757", +".U c #5b5b5b", +".f c #5d5d5d", +".Q c #5e5e5e", +"#Q c #5f5f5f", +".t c #616161", +".n c #676767", +"aj c #686868", +"#h c #6d6d6d", +".i c #737373", +".G c #757575", +"ah c #777777", +"ak c #797979", +"af c #7a7a7a", +"ac c #909090", +"#N c #939393", +"a# c #999999", +"ag c #9a9a9a", +"#u c #9b9b9b", +".m c #9d9d9d", +".j c #a1a1a1", +"#L c #a5a5a5", +".A c #a7a7a7", +"#p c #a8a8a8", +"#d c #ababab", +".l c #b2b2b2", +".k c #b4b4b4", +"ab c #b6b6b6", +"#8 c #b8b8b8", +".7 c #b9b9b9", +"#I c #bababa", +"aa c #bbbbbb", +".P c #bcbcbc", +"#4 c #bdbdbd", +"#C c #bfbfbf", +".z c #c1c1c1", +".3 c #c2c2c2", +"#x c #c5c5c5", +"#D c #c6c6c6", +"#6 c #c7c7c7", +"#3 c #c8c8c8", +".y c #c9c9c9", +".u c #cacaca", +"#0 c #cbcbcb", +".V c #cccccc", +"#7 c #cdcdcd", +".x c #cecece", +".v c #cfcfcf", +".w c #d0d0d0", +"#M c #d1d1d1", +".O c #d2d2d2", +".H c #d3d3d3", +"#U c #d4d4d4", +"#2 c #d5d5d5", +".8 c #d6d6d6", +".W c #d7d7d7", +"#q c #d8d8d8", +"#R c #d9d9d9", +"#Z c #dadada", +".I c #dbdbdb", +"#t c #dcdcdc", +"#y c #dfdfdf", +"#H c #e0e0e0", +".2 c #e1e1e1", +"#X c #e2e2e2", +"#E c #e3e3e3", +"#c c #e4e4e4", +".N c #e5e5e5", +"#l c #e6e6e6", +".J c #e7e7e7", +"#T c #e9e9e9", +"#G c #eaeaea", +"#S c #ebebeb", +"#b c #ececec", +"#B c #ededed", +"#k c #eeeeee", +".K c #efefef", +".1 c #f0f0f0", +".M c #f1f1f1", +"#a c #f2f2f2", +".L c #f3f3f3", +"#s c #f4f4f4", +".X c #f5f5f5", +"#r c #f6f6f6", +".0 c #f8f8f8", +".9 c #f9f9f9", +".Y c #fafafa", +".Z c #fbfbfb", +"#j c #fcfcfc", +"#. c #fdfdfd", +"#i c #fefefe", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQt.#.a.b.c.d.d.d.eQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQt.f.g.h.i.j.k.l.m.n.o.p.qQtQtQtQtQtQtQtQt", +"QtQtQt.r.s.t.k.u.v.w.x.y.z.A.B.C.DQtQtQtQtQtQtQt", +"QtQt.E.F.G.u.H.I.J.K.L.M.N.O.P.Q.R.SQtQtQtQtQtQt", +"QtQt.T.U.V.W.N.X.Y.Z.Y.0.X.1.2.3.4.5QtQtQtQtQtQt", +"Qt.h.6.7.8.J.9.Z#.####.Z.0#a#b#c#d#e#fQtQtQtQtQt", +"Qt#g#h.O.2.X.Y#.#i#####j.9.X#k#l.I#m#nQtQtQtQtQt", +"Qt#o#p#q#k#r.9#j#i####.Z.0#s#k#l#t#u#vQtQtQtQtQt", +"Qt#w#x#y.K.X#z#z#######z.4#A#B.N.I#C#vQtQtQtQtQt", +"Qt#e#D#E#b#a.4#z#z#z#z.4#A#F#G#H.W#I#JQtQtQtQtQt", +"Qt#K#L.2.J#B.M#s#r.4.4.L.K#b#c.I#M#N#OQtQtQtQtQt", +"Qt#P#Q#R.2#l#S#k.K#F#F#B#T#c.I#U.u.B#VQtQtQtQtQt", +"Qt#W#P.k#q#t#X.N#l#Y#Y#E#y#Z#U#0.j#O#VQtQtQtQtQt", +"Qt#V#v#1.V.O#U#Z.I#Z.I#q#2.w#3#4.g#VQtQtQtQtQtQt", +"QtQt#J#J#5.3#6#0.V#7#0#0#3.3#8###V#VQtQtQtQtQtQt", +"QtQt#V#9#9a.a#aa#4.P.P.Pabacad#V#V#V#VQtQtQtQtQt", +"QtQtQt#V#Vae#9#Fafagagahai#V#V#V#V#V#V#V#VQtQtQt", +"QtQtQtQtQt#V#V#V#V#V#V#V#V#V#V#V#V.o.o#V#V#VQtQt", +"QtQtQtQtQtQtQt#V#V#V#V#V#V#VQtQt#V#Vaj.a#V#V#VQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#V#Vakal#V#V#V", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#V#V#Vaj.6#V#V", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#V#V#V#V#V#V", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#V#V#VQtQt"}; diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/stock_zoom_out_24.xpm b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/stock_zoom_out_24.xpm new file mode 100644 index 00000000..61121991 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/histogram/stock_zoom_out_24.xpm @@ -0,0 +1,207 @@ +/* XPM */ +static char *stock_zoom_out_24[]={ +"24 24 180 2", +"Qt c None", +"#8 c #000000", +"aw c #000000", +"ae c #050505", +".U c #242424", +".s c #373737", +".G c #4f4f4f", +".h c #5d5d5d", +"aJ c #000000", +"aX c #000000", +"ak c #000000", +"aH c #000000", +"#7 c #000000", +"al c #030303", +"#1 c #0c0c0c", +"#i c #121212", +".F c #232323", +".8 c #303030", +".g c #353535", +".t c #404040", +".# c #494949", +"av c #000000", +"#0 c #000000", +"#p c #090909", +"#U c #0a0a0a", +"#j c #1b1b1b", +".f c #262626", +".a c #343434", +"ad c #000000", +"ax c #010101", +"#9 c #060606", +".7 c #0f0f0f", +".r c #1c1c1c", +".V c #212121", +".i c #2e2e2e", +"aI c #000000", +"#T c #020202", +"#x c #060606", +"#P c #0b0b0b", +"#q c #141414", +".e c #262626", +".b c #2d2d2d", +"aE c #000000", +"aV c #000000", +"aN c #000000", +"aF c #000000", +"at c #000000", +"au c #000000", +"aG c #000000", +"aK c #202020", +"aS c #000000", +"aL c #202020", +"aM c #000000", +"aP c #343434", +"aT c #000000", +"aj c #000000", +"am c #030303", +"#O c #050505", +"#G c #060606", +"#H c #0e0e0e", +".T c #101010", +"#y c #111111", +".E c #131313", +"aU c #1f1f1f", +".H c #222222", +".d c #262626", +".u c #272727", +".c c #292929", +"aO c #686868", +"aR c #3a3a3a", +"aW c #000000", +"aQ c #797979", +"aD c #000000", +"#6 c #020202", +"ay c #030303", +"#2 c #0a0a0a", +"#h c #0e0e0e", +".9 c #1f1f1f", +".q c #202020", +"as c #2b2b2b", +"ac c #2e2e2e", +".j c #303030", +"an c #313131", +"a. c #3b3b3b", +"aC c #3c3c3c", +"#K c #414141", +"#D c #424242", +".6 c #434343", +"#B c #444444", +"#C c #454545", +".D c #4c4c4c", +"af c #515151", +"#o c #575757", +".W c #5b5b5b", +".S c #5e5e5e", +"#V c #5f5f5f", +".v c #616161", +".p c #676767", +"#k c #6d6d6d", +".k c #737373", +".I c #757575", +"aB c #777777", +"az c #7a7a7a", +"ar c #909090", +"#S c #939393", +"ao c #999999", +"aA c #9a9a9a", +"#w c #9b9b9b", +".o c #9d9d9d", +".l c #a1a1a1", +"#Q c #a5a5a5", +".C c #a7a7a7", +"#r c #a8a8a8", +"#g c #ababab", +".n c #b2b2b2", +".m c #b4b4b4", +"aq c #b6b6b6", +"ai c #b8b8b8", +"#. c #b9b9b9", +"#N c #bababa", +"ap c #bbbbbb", +".R c #bcbcbc", +"ab c #bdbdbd", +"#F c #bfbfbf", +".B c #c1c1c1", +".5 c #c2c2c2", +"#z c #c5c5c5", +"#I c #c6c6c6", +"ag c #c7c7c7", +"aa c #c8c8c8", +".A c #c9c9c9", +".w c #cacaca", +"#5 c #cbcbcb", +".X c #cccccc", +"ah c #cdcdcd", +".z c #cecece", +".x c #cfcfcf", +".y c #d0d0d0", +"#R c #d1d1d1", +".Q c #d2d2d2", +".J c #d3d3d3", +"#Z c #d4d4d4", +"a# c #d5d5d5", +"## c #d6d6d6", +".Y c #d7d7d7", +"#s c #d8d8d8", +"#W c #d9d9d9", +"#4 c #dadada", +".K c #dbdbdb", +"#v c #dcdcdc", +"#A c #dfdfdf", +"#M c #e0e0e0", +".4 c #e1e1e1", +"#3 c #e2e2e2", +"#J c #e3e3e3", +"#f c #e4e4e4", +".P c #e5e5e5", +"#n c #e6e6e6", +".L c #e7e7e7", +"#Y c #e9e9e9", +"#L c #eaeaea", +"#X c #ebebeb", +"#e c #ececec", +"#E c #ededed", +"#m c #eeeeee", +".M c #efefef", +".3 c #f0f0f0", +".O c #f1f1f1", +"#d c #f2f2f2", +".N c #f3f3f3", +"#u c #f4f4f4", +".Z c #f5f5f5", +"#t c #f6f6f6", +".2 c #f8f8f8", +"#a c #f9f9f9", +".0 c #fafafa", +".1 c #fbfbfb", +"#c c #fcfcfc", +"#b c #fdfdfd", +"#l c #fefefe", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQt.#.a.b.c.d.e.f.gQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQt.h.i.j.k.l.m.n.o.p.q.r.sQtQtQtQtQtQtQtQt", +"QtQtQt.t.u.v.m.w.x.y.z.A.B.C.D.E.FQtQtQtQtQtQtQt", +"QtQt.G.H.I.w.J.K.L.M.N.O.P.Q.R.S.T.UQtQtQtQtQtQt", +"QtQt.V.W.X.Y.P.Z.0.1.0.2.Z.3.4.5.6.7QtQtQtQtQtQt", +"Qt.8.9#.##.L#a.1#b#b#c.1.2#d#e#f#g#h#iQtQtQtQtQt", +"Qt#j#k.Q.4.Z.0#b#l#l#b#c#a.Z#m#n.K#o#pQtQtQtQtQt", +"Qt#q#r#s#m#t#a#c#l#l#b.1.2#u#m#n#v#w#xQtQtQtQtQt", +"Qt#y#z#A.M.Z#B#B#C#C#C#B.6#D#E.P.K#F#GQtQtQtQtQt", +"Qt#H#I#J#e#d.6#B#B#B#B.6#D#K#L#M.Y#N#OQtQtQtQtQt", +"Qt#P#Q.4.L#E.O#u#t#t.Z.N.M#e#f.K#R#S#TQtQtQtQtQt", +"Qt#U#V#W.4#n#X#m.M.M#m#E#Y#f.K#Z.w.D#0QtQtQtQtQt", +"Qt#1#2.m#s#v#3.P#n#n.L#J#A#4#Z#5.l#6#7QtQtQtQtQt", +"Qt#8#9a..X.Q#Z#4.K#4.K#sa#.yaaabacadQtQtQtQtQtQt", +"QtQtae#Oaf.5ag#5.Xah#5#5aa.5ai#CajakQtQtQtQtQtQt", +"QtQt#8alamanaoapab.R.R.RaqarasajatauavQtQtQtQtQt", +"QtQtQt#8awaxay#KazaAaAaBaCaDadawaEaFaGaEaHQtQtQt", +"QtQtQtQtQt#8#7#0aIajajaI#0#7aJ#8#8aKaLaMataJQtQt", +"QtQtQtQtQtQtQt#8#8#8#8#8#8#8QtQt#8aNaOaPauaM#8Qt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#8aGaQaRaGaS#8", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#8#8aTaOaUaS#8", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#8#8aVaWaX#8", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#8#8#8QtQt"}; diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/interrupts/Makefile.am b/tags/lttv-0.11.3-23102008/lttv/modules/gui/interrupts/Makefile.am new file mode 100644 index 00000000..00c1d8f7 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/interrupts/Makefile.am @@ -0,0 +1,39 @@ +# This file is part of the Linux Trace Toolkit viewer +# Copyright (C) 2003-2004 Mathieu Desnoyers +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License Version 2 as +# published by the Free Software Foundation; +# +# 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. + + + +# +# Makefile for LTT New generation user interface : plugins. +# +# Created by Mathieu Desnoyers on May 6, 2003 +# + +AM_CFLAGS = $(GLIB_CFLAGS) +AM_CFLAGS += $(GTK_CFLAGS) +AM_CFLAGS += -fvisibility=hidden +LIBS += $(GLIB_LIBS) +LIBS += $(GTK_LIBS) -L${top_builddir}/lttv/modules/gui/lttvwindow/lttvwindow -llttvwindow + +libdir = ${lttvplugindir} + +lib_LTLIBRARIES = libinterrupts.la +libinterrupts_la_LDFLAGS = -module -avoid-version +libinterrupts_la_SOURCES = interrupts.c + +EXTRA_DIST = \ + hInterruptsInsert.xpm diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/interrupts/README b/tags/lttv-0.11.3-23102008/lttv/modules/gui/interrupts/README new file mode 100644 index 00000000..b0d55698 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/interrupts/README @@ -0,0 +1,24 @@ + +CPUID: processor ID + +IrqId: IRQ ID + +Frequency (Hz): the number of interruptions per second (Hz) + +Total Duration (nsec): the sum of each interrupt duration in nsec + +Duration standard deviation (nsec): taken from +http://en.wikipedia.org/wiki/Standard_deviation +Duration Standard_deviation = sqrt(1/N Sum ((xi -Xa)^2)) where +N: the total number of interrupts +xi: the duration of an interrupt +Xa: the average duration is the total duration divided by the total number of interrupts (N) + +Max IRQ handler duration (nsec) [time interval]: the longest IRQ handler duration in nsec. + +Average period (nsec): 1/Frequency + +Period Standard_deviation = sqrt(1/N Sum ((xi -Xa)^2)) where +N: number of interrupts +xi: duration of an interrupt +Xa: Average period in nsec diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/interrupts/hInterruptsInsert.xpm b/tags/lttv-0.11.3-23102008/lttv/modules/gui/interrupts/hInterruptsInsert.xpm new file mode 100644 index 00000000..838bb3fa --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/interrupts/hInterruptsInsert.xpm @@ -0,0 +1,27 @@ +/* XPM */ +static char * hInterruptsInsert_xpm[] = { +"22 22 2 1", +" c None", +". c #800080", +" ", +" ", +" .......... ", +" ............. ", +" ..... ", +" ..... ", +" ..... ", +" ..... ", +" ..... ", +" ..... ", +" ..... ", +" ..... ", +" ..... ", +" ..... ", +" ..... ", +" ..... ", +" ..... ", +" ..... ", +" ............. ", +" .......... ", +" ", +" "}; diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/interrupts/interrupts.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/interrupts/interrupts.c new file mode 100644 index 00000000..608e2b27 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/interrupts/interrupts.c @@ -0,0 +1,1300 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2005 Peter Ho + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + + /****************************************************************** + +Each field of the interrupt viewer is summarized as follows: + +- CPUID: processor ID + +- IrqId: IRQ ID + +- Frequency (Hz): the number of interrupts per second (Hz). + We compute the total number of interrupts. Then + we divide it by the time interval. + +- Total Duration (nsec): the sum of each interrupt duration in nsec. + For a given Irq ID, we sum the duration of each interrupt + to give us the total duration + +- Duration Standard_deviation = sqrt(1/N Sum ((xi -Xa)^2)) where + N: number of interrupts + xi: duration of an interrupt (nsec) + Xa: average duration (nsec) + The formula is taken from wikipedia: http://en.wikipedia.org/wiki/Standard_deviation. + To calculate the duration standard deviation, we make two EventsRequest passes to the main window. + In the first EventsRequest pass, we calculate the total number of interrupts to compute for + the average Xa. In the second EventsRequest pass, calculate the standard deviation. + + +- Max IRQ handler duration (nsec) [time interval]: the longest IRQ handler duration in nsec. + +- Min IRQ handler duration (nsec) [time interval]: the shortest IRQ handler duration in nsec. + +- Average period (nsec): 1/Frequency(in HZ). The frequency is computed above. + +-Period Standard_deviation = sqrt(1/N Sum ((xi -Xa)^2)) where +N: number of interrupts +xi: duration of an interrupt +Xa: Period = 1/Frequency (in Hz) + +-Frequency Standard_deviation = sqrt(1/N Sum ((xi -Xa)^2)) +N: number of interrupts +xi: duration of an interrupt +Xa: Frequency (Hz) + + +*******************************************************************/ + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hInterruptsInsert.xpm" + +#define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format) +#define NO_ITEMS 0 + +typedef struct +{ + LttTime duration; + LttTime start_time; + LttTime end_time; +}IrqDuration; + +typedef struct { + guint cpu_id; + guint id; + guint TotalNumberOfInterrupts; + LttTime total_duration; + guint average_duration; + IrqDuration max_irq_handler; + IrqDuration min_irq_handler; +}Irq; + +typedef struct { + guint id; + guint cpu_id; + LttTime event_time; +}irq_entry; + + +typedef struct +{ + guint irqId; + guint TotalNumberOfInterrupts;//frequency;// + guint64 sumOfDurations; // to store the Sum ((xi -Xa)^2) of the duration Standard deviation + guint64 sumOfPeriods; // to store the Sum ((xi -Xa)^2) of the period Standard deviation + guint64 sumOfFrequencies;// to store the Sum ((xi -Xa)^2) of the frequency Standard deviation + +}SumId; + +enum type_t { + IRQ_ENTRY, + IRQ_EXIT +}; + +/** Array containing instanced objects. Used when module is unloaded */ +static GSList *interrupt_data_list = NULL ; + + +//fixed #define TRACE_NUMBER 0 + +typedef struct _InterruptEventData { + + /*Graphical Widgets */ + GtkWidget * ScrollWindow; + GtkListStore *ListStore; + GtkWidget *Hbox; + GtkWidget *TreeView; + GtkTreeSelection *SelectionTree; + + Tab * tab; /* tab that contains this plug-in*/ + LttvPluginTab *ptab; + LttvHooks * event_hooks; + LttvHooks * hooks_trace_after; + LttvHooks * hooks_trace_before; + TimeWindow time_window; + LttvHooksById * event_by_id_hooks; + GArray *FirstRequestIrqExit; + GArray *FirstRequestIrqEntry; + GArray *SecondRequestIrqEntry; + GArray *SecondRequestIrqExit; + GArray *SumArray; + +} InterruptEventData ; + + +/* Function prototypes */ + +static gboolean interrupt_update_time_window(void * hook_data, void * call_data); +static GtkWidget *interrupts(LttvPlugin *plugin); +static InterruptEventData *system_info(LttvPluginTab *ptab); +void interrupt_destructor(InterruptEventData *event_viewer_data); +static void FirstRequest(InterruptEventData *event_data ); +static gboolean trace_header(void *hook_data, void *call_data); +static gboolean DisplayViewer (void *hook_data, void *call_data); +static void CalculateData(LttTime time_exit, guint cpu_id, InterruptEventData *event_data); +static void CalculateTotalDurationAndMaxIrqDurationAndMinIrqDuration(irq_entry *e, LttTime time_exit, GArray *FirstRequestIrqExit); +static gboolean FirstRequestIrqEntryCallback(void *hook_data, void *call_data); +static gboolean FirstRequestIrqExitCallback(void *hook_data, void *call_data); +static gboolean SecondRequest(void *hook_data, void *call_data); +static void CalculateAverageDurationForEachIrqId(InterruptEventData *event_data); +static gboolean SecondRequestIrqEntryCallback(void *hook_data, void *call_data); +static gboolean SecondRequestIrqExitCallback(void *hook_data, void *call_data); +static void CalculateXi(LttEvent *event, InterruptEventData *event_data, guint cpu_id); +static void SumItems(gint irq_id, LttTime Xi, InterruptEventData *event_data); +static int CalculateDurationStandardDeviation(gint id, InterruptEventData *event_data); +static int CalculatePeriodStandardDeviation(gint id, InterruptEventData *event_data); +static int FrequencyInHZ(gint NumberOfInterruptions, TimeWindow time_window); +static guint64 CalculatePeriodInnerPart(guint Xi, guint FrequencyHZ); +static guint64 CalculateFrequencyInnerPart(guint Xi_in_ns, guint FrequencyHZ); +static void InterruptFree(InterruptEventData *event_viewer_data); +static int CalculateFrequencyStandardDeviation(gint id, InterruptEventData *event_data); + +/* Enumeration of the columns */ +enum{ + CPUID_COLUMN, + IRQ_ID_COLUMN, + FREQUENCY_COLUMN, + DURATION_COLUMN, + DURATION_STANDARD_DEV_COLUMN, + MAX_IRQ_HANDLER_COLUMN, + MIN_IRQ_HANDLER_COLUMN, + AVERAGE_PERIOD, + PERIOD_STANDARD_DEV_COLUMN, + FREQUENCY_STANDARD_DEV_COLUMN, + N_COLUMNS +}; + + + +/** + * init function + * + * + * This is the entry point of the viewer. + * + */ +static void init() { + g_info("interrupts: init()"); + lttvwindow_register_constructor("interrupts", + "/", + "Insert Interrupts View", + hInterruptsInsert_xpm, + "Insert Interrupts View", + interrupts); + +} + + +/** + * Constructor hook + * + */ +static GtkWidget *interrupts(LttvPlugin *plugin) +{ + LttvPluginTab *ptab = LTTV_PLUGIN_TAB(plugin); + InterruptEventData* event_data = system_info(ptab) ; + if(event_data) + return event_data->Hbox; + else + return NULL; +} + +/** + * This function initializes the Event Viewer functionnality through the + * GTK API. + */ +InterruptEventData *system_info(LttvPluginTab *ptab) +{ + + LttTime end; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + InterruptEventData* event_viewer_data = g_new(InterruptEventData,1) ; + Tab *tab = ptab->tab; + event_viewer_data->ptab = ptab; + event_viewer_data->tab = tab; + + /*Get the current time frame from the main window */ + event_viewer_data->time_window = lttvwindow_get_time_window(tab); + + event_viewer_data->FirstRequestIrqExit = g_array_new(FALSE, FALSE, sizeof(Irq)); + event_viewer_data->FirstRequestIrqEntry = g_array_new(FALSE, FALSE, sizeof(irq_entry)); + + event_viewer_data->SecondRequestIrqEntry = g_array_new(FALSE, FALSE, sizeof(irq_entry)); + event_viewer_data->SecondRequestIrqExit = g_array_new(FALSE, FALSE, sizeof(Irq)); + + event_viewer_data->SumArray = g_array_new(FALSE, FALSE, sizeof(SumId)); + + + /*Create tha main window for the viewer */ + event_viewer_data->ScrollWindow = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (event_viewer_data->ScrollWindow); + gtk_scrolled_window_set_policy( + GTK_SCROLLED_WINDOW(event_viewer_data->ScrollWindow), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC/*GTK_POLICY_NEVER*/); + + /* Create a model for storing the data list */ + event_viewer_data->ListStore = gtk_list_store_new ( + N_COLUMNS, /* Total number of columns */ + G_TYPE_INT, /* CPUID */ + G_TYPE_INT, /* IRQ_ID */ + G_TYPE_INT, /* Frequency */ + G_TYPE_UINT64, /* Duration */ + G_TYPE_INT, /* standard deviation */ + G_TYPE_STRING, /* Max IRQ handler */ + G_TYPE_STRING, /* Min IRQ handler */ + G_TYPE_INT, /* Average period */ + G_TYPE_INT, /* period standard deviation */ + G_TYPE_INT /* frequency standard deviation */ + + ); + + event_viewer_data->TreeView = gtk_tree_view_new_with_model (GTK_TREE_MODEL (event_viewer_data->ListStore)); + + g_object_unref (G_OBJECT (event_viewer_data->ListStore)); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("CPU ID", + renderer, + "text", CPUID_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_fixed_width (column, 45); + gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column); + + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("IRQ ID", + renderer, + "text", IRQ_ID_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_fixed_width (column, 220); + gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Frequency (Hz)", + renderer, + "text", FREQUENCY_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 1.0); + gtk_tree_view_column_set_fixed_width (column, 220); + gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Total Duration (nsec)", + renderer, + "text", DURATION_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_fixed_width (column, 145); + gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column); + + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Duration standard deviation (nsec)", + renderer, + "text", DURATION_STANDARD_DEV_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_fixed_width (column, 200); + gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Max IRQ handler duration (nsec) [time interval]", + renderer, + "text", MAX_IRQ_HANDLER_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_fixed_width (column, 250); + gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Min IRQ handler duration (nsec) [time interval]", + renderer, + "text", MIN_IRQ_HANDLER_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_fixed_width (column, 250); + gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (" Average period (nsec)", + renderer, + "text", AVERAGE_PERIOD, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_fixed_width (column, 200); + gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Period standard deviation (nsec)", + renderer, + "text", PERIOD_STANDARD_DEV_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_fixed_width (column, 200); + gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Frequency standard deviation (Hz)", + renderer, + "text", FREQUENCY_STANDARD_DEV_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_fixed_width (column, 200); + gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column); + + + event_viewer_data->SelectionTree = gtk_tree_view_get_selection (GTK_TREE_VIEW (event_viewer_data->TreeView)); + gtk_tree_selection_set_mode (event_viewer_data->SelectionTree, GTK_SELECTION_SINGLE); + + gtk_container_add (GTK_CONTAINER (event_viewer_data->ScrollWindow), event_viewer_data->TreeView); + + event_viewer_data->Hbox = gtk_hbox_new(0, 0); + gtk_box_pack_start(GTK_BOX(event_viewer_data->Hbox), event_viewer_data->ScrollWindow, TRUE, TRUE, 0); + + gtk_widget_show(event_viewer_data->Hbox); + gtk_widget_show(event_viewer_data->TreeView); + + interrupt_data_list = g_slist_append(interrupt_data_list, event_viewer_data); + /* Registration for time notification */ + lttvwindow_register_time_window_notify(tab, + interrupt_update_time_window, + event_viewer_data); + + g_object_set_data_full(G_OBJECT(event_viewer_data->Hbox), + "event_data", + event_viewer_data, + (GDestroyNotify) InterruptFree); + + FirstRequest(event_viewer_data ); + return event_viewer_data; +} + + +/** + * + * For each trace in the traceset, this function: + * - registers a callback function to each hook + * - calls lttv_trace_find_hook() registers a hook function to event_by_id_hooks + * - calls lttvwindow_events_request() to request data in a specific + * time interval to the main window + * + */ +static void FirstRequest(InterruptEventData *event_data ) +{ + guint i, k, l, nb_trace; + + LttvTraceHook *hook; + + guint ret; + + LttvTraceState *ts; + + GArray *hooks; + + EventsRequest *events_request; + + LttvTraceHook *th; + + LttvTracesetContext *tsc = lttvwindow_get_traceset_context(event_data->tab); + + + /* Get the traceset */ + LttvTraceset *traceset = tsc->ts; + + nb_trace = lttv_traceset_number(traceset); + + /* There are many traces in a traceset. Iteration for each trace. */ + //for(i = 0; ihooks_trace_before = lttv_hooks_new(); + + /* Registers a hook function */ + lttv_hooks_add(event_data->hooks_trace_before, trace_header, event_data, LTTV_PRIO_DEFAULT); + + event_data->hooks_trace_after = lttv_hooks_new(); + + /* Registers a hook function */ + lttv_hooks_add(event_data->hooks_trace_after, SecondRequest, event_data, LTTV_PRIO_DEFAULT); + /* Get a trace state */ + ts = (LttvTraceState *)tsc->traces[i]; + /* Create event_by_Id hooks */ + event_data->event_by_id_hooks = lttv_hooks_by_id_new(); + + /*Register event_by_id_hooks with a callback function*/ + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_IRQ_ENTRY, + FIELD_ARRAY(LTT_FIELD_IRQ_ID), + FirstRequestIrqEntryCallback, + events_request, + &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_IRQ_EXIT, + NULL, + FirstRequestIrqExitCallback, + events_request, + &hooks); + + /*iterate through the facility list*/ + for(k = 0 ; k < hooks->len; k++) + { + th = &g_array_index(hooks, LttvTraceHook, k); + lttv_hooks_add(lttv_hooks_by_id_find(event_data->event_by_id_hooks, th->id), + th->h, + th, + LTTV_PRIO_DEFAULT); + + } + /* Initalize the EventsRequest structure */ + events_request->owner = event_data; + events_request->viewer_data = event_data; + events_request->servicing = FALSE; + events_request->start_time = event_data->time_window.start_time; + events_request->start_position = NULL; + events_request->stop_flag = FALSE; + events_request->end_time = event_data->time_window.end_time; + events_request->num_events = G_MAXUINT; + events_request->end_position = NULL; + events_request->trace = i; + + events_request->hooks = hooks; + + events_request->before_chunk_traceset = NULL; + events_request->before_chunk_trace = event_data->hooks_trace_before; + events_request->before_chunk_tracefile= NULL; + events_request->event = NULL; + events_request->event_by_id = event_data->event_by_id_hooks; + events_request->after_chunk_tracefile = NULL; + events_request->after_chunk_trace = NULL; + events_request->after_chunk_traceset = NULL; + events_request->before_request = NULL; + events_request->after_request = event_data->hooks_trace_after; + + lttvwindow_events_request(event_data->tab, events_request); + } + +} + +/** + * This function is called whenever an irq_entry event occurs. + * + */ +static gboolean FirstRequestIrqEntryCallback(void *hook_data, void *call_data) +{ + + LttTime event_time; + unsigned cpu_id; + irq_entry entry; + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + LttvTraceHook *th = (LttvTraceHook*) hook_data; + EventsRequest *events_request = (EventsRequest*)th->hook_data; + InterruptEventData *event_data = events_request->viewer_data; + GArray* FirstRequestIrqEntry = event_data->FirstRequestIrqEntry; + LttEvent *e = ltt_tracefile_get_event(tfc->tf); + event_time = ltt_event_time(e); + cpu_id = tfs->cpu; + + + entry.id = ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 0)); + entry.cpu_id = cpu_id; + entry.event_time = event_time; + g_array_append_val (FirstRequestIrqEntry, entry); + + return FALSE; +} + +/** + * This function is called whenever an irq_exit event occurs. + * + */ +gboolean FirstRequestIrqExitCallback(void *hook_data, void *call_data) +{ + LttTime event_time; + unsigned cpu_id; + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + LttvTraceHook *th = (LttvTraceHook*) hook_data; + EventsRequest *events_request = (EventsRequest*)th->hook_data; + InterruptEventData *event_data = events_request->viewer_data; + LttEvent *e = ltt_tracefile_get_event(tfc->tf); + event_time = ltt_event_time(e); + cpu_id = tfs->cpu; + + CalculateData( event_time, cpu_id, event_data); + + return FALSE; +} + +/** + * This function calculates the duration of an interrupt. + * + */ +static void CalculateData(LttTime time_exit, guint cpu_id,InterruptEventData *event_data) +{ + + gint i, irq_id; + irq_entry *element; + LttTime duration; + GArray *FirstRequestIrqExit = event_data->FirstRequestIrqExit; + GArray *FirstRequestIrqEntry = event_data->FirstRequestIrqEntry; + for(i = FirstRequestIrqEntry->len-1; i >=0; i--) + { + element = &g_array_index(FirstRequestIrqEntry,irq_entry,i); + if(element->cpu_id == cpu_id) + { + CalculateTotalDurationAndMaxIrqDurationAndMinIrqDuration(element,time_exit, FirstRequestIrqExit); + g_array_remove_index(FirstRequestIrqEntry, i); + break; + } + } +} + + +/** + * This function calculates the total duration of an interrupt and the longest & shortest Irq handlers. + * + */ +static void CalculateTotalDurationAndMaxIrqDurationAndMinIrqDuration(irq_entry *e, LttTime time_exit, GArray *FirstRequestIrqExit) +{ + Irq irq; + Irq *element; + guint i; + LttTime duration; + gboolean notFound = FALSE; + memset ((void*)&irq, 0,sizeof(Irq)); + + /*first time*/ + if(FirstRequestIrqExit->len == NO_ITEMS) + { + irq.cpu_id = e->cpu_id; + irq.id = e->id; + irq.TotalNumberOfInterrupts++; + irq.total_duration = ltt_time_sub(time_exit, e->event_time); + + irq.max_irq_handler.start_time = e->event_time; + irq.max_irq_handler.end_time = time_exit; + irq.max_irq_handler.duration = ltt_time_sub(time_exit, e->event_time); + + irq.min_irq_handler.start_time = e->event_time; + irq.min_irq_handler.end_time = time_exit; + irq.min_irq_handler.duration = ltt_time_sub(time_exit, e->event_time); + + g_array_append_val (FirstRequestIrqExit, irq); + } + else + { + for(i = 0; i < FirstRequestIrqExit->len; i++) + { + element = &g_array_index(FirstRequestIrqExit,Irq,i); + if(element->id == e->id) + { + notFound = TRUE; + duration = ltt_time_sub(time_exit, e->event_time); + element->total_duration = ltt_time_add(element->total_duration, duration); + element->TotalNumberOfInterrupts++; + // Max irq handler + if(ltt_time_compare(duration,element->max_irq_handler.duration) > 0) + { + element->max_irq_handler.duration = duration; + element->max_irq_handler.start_time = e->event_time; + element->max_irq_handler.end_time = time_exit; + } + // Min irq handler + if(ltt_time_compare(duration,element->min_irq_handler.duration) < 0) + { + element->min_irq_handler.duration = duration; + element->min_irq_handler.start_time = e->event_time; + element->min_irq_handler.end_time = time_exit; + } + } + } + if(!notFound) + { + irq.cpu_id = e->cpu_id; + irq.id = e->id; + irq.TotalNumberOfInterrupts++; + irq.total_duration = ltt_time_sub(time_exit, e->event_time); + // Max irq handler + irq.max_irq_handler.start_time = e->event_time; + irq.max_irq_handler.end_time = time_exit; + irq.max_irq_handler.duration = ltt_time_sub(time_exit, e->event_time); + // Min irq handler + irq.min_irq_handler.start_time = e->event_time; + irq.min_irq_handler.end_time = time_exit; + irq.min_irq_handler.duration = ltt_time_sub(time_exit, e->event_time); + + g_array_append_val (FirstRequestIrqExit, irq); + } + } +} + +/** + * This function passes the second EventsRequest to LTTV + * + */ +static gboolean SecondRequest(void *hook_data, void *call_data) +{ + + guint i, k, l, nb_trace; + + LttvTraceHook *hook; + + guint ret; + + LttvTraceState *ts; + + GArray *hooks; + + EventsRequest *events_request; + + LttvTraceHook *th; + + InterruptEventData *event_data = (InterruptEventData *)hook_data; + + LttvTracesetContext *tsc = lttvwindow_get_traceset_context(event_data->tab); + + CalculateAverageDurationForEachIrqId(event_data); + + /* Get the traceset */ + LttvTraceset *traceset = tsc->ts; + + nb_trace = lttv_traceset_number(traceset); + + /* There are many traces in a traceset. Iteration for each trace. */ + for(i = 0 ; i < nb_trace ; i++) { + // fixed for(i = 0; ihooks_trace_after = lttv_hooks_new(); + + /* Registers a hook function */ + lttv_hooks_add(event_data->hooks_trace_after, DisplayViewer, event_data, LTTV_PRIO_DEFAULT); + + /* Get a trace state */ + ts = (LttvTraceState *)tsc->traces[i]; + /* Create event_by_Id hooks */ + event_data->event_by_id_hooks = lttv_hooks_by_id_new(); + + /*Register event_by_id_hooks with a callback function*/ + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_IRQ_ENTRY, + FIELD_ARRAY(LTT_FIELD_IRQ_ID), + SecondRequestIrqEntryCallback, + events_request, + &hooks); + + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_IRQ_EXIT, + NULL, + SecondRequestIrqExitCallback, + events_request, + &hooks); + + g_assert(!ret); + + /* iterate through the facility list */ + for(k = 0 ; k < hooks->len; k++) + { + th = &g_array_index(hooks, LttvTraceHook, k); + lttv_hooks_add(lttv_hooks_by_id_find(event_data->event_by_id_hooks, th->id), + th->h, + th, + LTTV_PRIO_DEFAULT); + + } + /* Initalize the EventsRequest structure */ + events_request->owner = event_data; + events_request->viewer_data = event_data; + events_request->servicing = FALSE; + events_request->start_time = event_data->time_window.start_time; + events_request->start_position = NULL; + events_request->stop_flag = FALSE; + events_request->end_time = event_data->time_window.end_time; + events_request->num_events = G_MAXUINT; + events_request->end_position = NULL; + events_request->trace = i; + + events_request->hooks = hooks; + + events_request->before_chunk_traceset = NULL; + events_request->before_chunk_trace = NULL; + events_request->before_chunk_tracefile= NULL; + events_request->event = NULL; + events_request->event_by_id = event_data->event_by_id_hooks; + events_request->after_chunk_tracefile = NULL; + events_request->after_chunk_trace = NULL; + events_request->after_chunk_traceset = NULL; + events_request->before_request = NULL; + events_request->after_request = event_data->hooks_trace_after; + + lttvwindow_events_request(event_data->tab, events_request); + } + return FALSE; +} + +/** + * This function calculates the average duration for each Irq Id + * + */ +static void CalculateAverageDurationForEachIrqId(InterruptEventData *event_data) +{ + guint64 real_data; + Irq *element; + gint i; + GArray* FirstRequestIrqExit = event_data->FirstRequestIrqExit; + for(i = 0; i < FirstRequestIrqExit->len; i++) + { + element = &g_array_index(FirstRequestIrqExit,Irq,i); + real_data = element->total_duration.tv_sec; + real_data *= NANOSECONDS_PER_SECOND; + real_data += element->total_duration.tv_nsec; + if(element->TotalNumberOfInterrupts != 0) + element->average_duration = real_data / element->TotalNumberOfInterrupts; + else + element->average_duration = 0; + } + +} + +/** + * This function is called whenever an irq_entry event occurs. Use in the second request + * + */ +static gboolean SecondRequestIrqEntryCallback(void *hook_data, void *call_data) +{ + + LttTime event_time; + unsigned cpu_id; + irq_entry entry; + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + LttvTraceHook *th = (LttvTraceHook *)hook_data; + EventsRequest *events_request = (EventsRequest*)th->hook_data; + InterruptEventData *event_data = events_request->viewer_data; + GArray* SecondRequestIrqEntry = event_data->SecondRequestIrqEntry; + LttEvent *e = ltt_tracefile_get_event(tfc->tf); + event_time = ltt_event_time(e); + cpu_id = tfs->cpu; + + + entry.id = ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 0)); + entry.cpu_id = cpu_id; + entry.event_time = event_time; + g_array_append_val (SecondRequestIrqEntry, entry); + + return FALSE; +} + +/** + * This function is called whenever an irq_exit event occurs in the second request. + * + */ +static gboolean SecondRequestIrqExitCallback(void *hook_data, void *call_data) +{ + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + LttvTraceHook *th = (LttvTraceHook *)hook_data; + EventsRequest *events_request = (EventsRequest*)th->hook_data; + InterruptEventData *event_data = events_request->viewer_data; + + LttEvent *event = ltt_tracefile_get_event(tfc->tf); + + CalculateXi(event, event_data, tfs->cpu); + return FALSE; +} + + +/** + * This function is called whenever an irq_exit event occurs in the second request. + * + */ +static void CalculateXi(LttEvent *event_irq_exit, InterruptEventData *event_data, guint cpu_id) +{ + gint i, irq_id; + irq_entry *element; + LttTime Xi; + LttTime exit_time; + + GArray *SecondRequestIrqExit = event_data->SecondRequestIrqExit; + GArray *SecondRequestIrqEntry = event_data->SecondRequestIrqEntry; + for(i = 0; i < SecondRequestIrqEntry->len; i++) + { + element = &g_array_index(SecondRequestIrqEntry,irq_entry,i); + if(element->cpu_id == cpu_id) + { + + /* time calculation */ + exit_time = ltt_event_time(event_irq_exit); + Xi = ltt_time_sub(exit_time, element->event_time); + irq_id = element->id; + + SumItems(irq_id, Xi,event_data); + g_array_remove_index(SecondRequestIrqEntry, i); + break; + } + } +} + + +/** + * This function computes the Sum ((xi -Xa)^2) and store the result in SumArray + * + */ +static void SumItems(gint irq_id, LttTime Xi, InterruptEventData *event_data) +{ + gint i; + guint Xi_in_ns; + + gint duration_inner_part; + guint64 period_inner_part; + guint64 frequency_inner_part; + + Irq *average; + SumId *sumItem; + SumId sum; + int FrequencyHZ = 0; + gboolean notFound = FALSE; + GArray *FirstRequestIrqExit = event_data->FirstRequestIrqExit; + GArray *SumArray = event_data->SumArray; + Xi_in_ns = Xi.tv_sec; + Xi_in_ns *= NANOSECONDS_PER_SECOND; + Xi_in_ns += Xi.tv_nsec; + + for(i = 0; i < FirstRequestIrqExit->len; i++) + { + average = &g_array_index(FirstRequestIrqExit,Irq,i); + if(irq_id == average->id) + { + duration_inner_part = Xi_in_ns - average->average_duration; + FrequencyHZ = FrequencyInHZ(average->TotalNumberOfInterrupts, event_data->time_window); + sum.irqId = irq_id; + // compute (xi -Xa)^2 of the duration Standard deviation + sum.TotalNumberOfInterrupts = average->TotalNumberOfInterrupts; + sum.sumOfDurations = pow (duration_inner_part , 2); + + // compute (xi -Xa)^2 of the period Standard deviation + period_inner_part = CalculatePeriodInnerPart(Xi_in_ns, FrequencyHZ); + + // compute (xi -Xa)^2 of the frequency Standard deviation + frequency_inner_part = CalculateFrequencyInnerPart(Xi_in_ns, FrequencyHZ); + + sum.sumOfPeriods = period_inner_part; + + sum.sumOfFrequencies = frequency_inner_part; + + if(event_data->SumArray->len == NO_ITEMS) + { + g_array_append_val (SumArray, sum); + } + else + { + for(i = 0; i < SumArray->len; i++) + { + sumItem = &g_array_index(SumArray, SumId, i); + if(sumItem->irqId == irq_id) + { + notFound = TRUE; + sumItem->sumOfDurations += sum.sumOfDurations; + sumItem->sumOfPeriods += sum.sumOfPeriods; + sumItem->sumOfFrequencies += sum.sumOfFrequencies; + } + } + if(!notFound) + { + g_array_append_val (SumArray, sum); + } + + } + + } + } +} + +/** + * This function computes the inner part of the period standard deviation = sqrt(1/N Sum ((xi -Xa)^2)) + * The inner part is: (xi -Xa)^2 + */ +static guint64 CalculatePeriodInnerPart(guint Xi, guint FrequencyHZ) +{ + + double periodInSec; /*period in sec*/ + int periodInNSec; + gint difference; + guint64 result; + periodInSec = (double)1/FrequencyHZ; + periodInSec *= NANOSECONDS_PER_SECOND; + periodInNSec = (int)periodInSec; + + difference = Xi - periodInNSec; + result = pow (difference , 2); + return result; +} + +/** + * This function computes the inner part of the frequency standard deviation = sqrt(1/N Sum ((xi -Xa)^2)) + * The inner part is: (xi -Xa)^2 + */ +static guint64 CalculateFrequencyInnerPart(guint Xi_in_ns, guint FrequencyHZ) +{ + guint64 result; + gint difference; + + difference = Xi_in_ns - FrequencyHZ; + result = pow (difference , 2); + return result; +} +/** + * This function displays the result on the viewer + * + */ +static gboolean DisplayViewer(void *hook_data, void *call_data) +{ + + guint average; + gint i; + Irq element; + LttTime average_duration; + GtkTreeIter iter; + guint64 real_data; + guint maxIRQduration; + guint minIRQduration; + double periodInSec; + int periodInNsec; + char maxIrqHandler[80]; + char minIrqHandler[80]; + InterruptEventData *event_data = (InterruptEventData *)hook_data; + GArray *FirstRequestIrqExit = event_data->FirstRequestIrqExit; + int FrequencyHZ = 0; + periodInSec = 0; + gtk_list_store_clear(event_data->ListStore); + for(i = 0; i < FirstRequestIrqExit->len; i++) + { + element = g_array_index(FirstRequestIrqExit,Irq,i); + real_data = element.total_duration.tv_sec; + real_data *= NANOSECONDS_PER_SECOND; + real_data += element.total_duration.tv_nsec; + + + maxIRQduration = element.max_irq_handler.duration.tv_sec; + maxIRQduration *= NANOSECONDS_PER_SECOND; + maxIRQduration += element.max_irq_handler.duration.tv_nsec; + + sprintf(maxIrqHandler, "%d [%d.%d - %d.%d]",maxIRQduration, element.max_irq_handler.start_time.tv_sec, \ + element.max_irq_handler.start_time.tv_nsec, element.max_irq_handler.end_time.tv_sec, \ + element.max_irq_handler.end_time.tv_nsec) ; + + minIRQduration = element.min_irq_handler.duration.tv_sec; + minIRQduration *= NANOSECONDS_PER_SECOND; + minIRQduration += element.min_irq_handler.duration.tv_nsec; + sprintf(minIrqHandler, "%d [%d.%d - %d.%d]",minIRQduration, element.min_irq_handler.start_time.tv_sec, \ + element.min_irq_handler.start_time.tv_nsec, element.min_irq_handler.end_time.tv_sec, \ + element.min_irq_handler.end_time.tv_nsec) ; + + + FrequencyHZ = FrequencyInHZ(element.TotalNumberOfInterrupts,event_data->time_window); + + if(FrequencyHZ != 0) + { + periodInSec = (double)1/FrequencyHZ; + periodInSec *= NANOSECONDS_PER_SECOND; + periodInNsec = (int)periodInSec; + + } + + gtk_list_store_append (event_data->ListStore, &iter); + gtk_list_store_set (event_data->ListStore, &iter, + CPUID_COLUMN, element.cpu_id, + IRQ_ID_COLUMN, element.id, + FREQUENCY_COLUMN, FrequencyHZ, + DURATION_COLUMN, real_data, + DURATION_STANDARD_DEV_COLUMN, CalculateDurationStandardDeviation(element.id, event_data), + MAX_IRQ_HANDLER_COLUMN, maxIrqHandler, + MIN_IRQ_HANDLER_COLUMN, minIrqHandler, + AVERAGE_PERIOD , periodInNsec, + PERIOD_STANDARD_DEV_COLUMN, CalculatePeriodStandardDeviation(element.id, event_data), + FREQUENCY_STANDARD_DEV_COLUMN, CalculateFrequencyStandardDeviation(element.id, event_data), + -1); + } + + + if(event_data->FirstRequestIrqExit->len) + { + g_array_remove_range (event_data->FirstRequestIrqExit,0,event_data->FirstRequestIrqExit->len); + } + + if(event_data->FirstRequestIrqEntry->len) + { + g_array_remove_range (event_data->FirstRequestIrqEntry,0,event_data->FirstRequestIrqEntry->len); + } + + if(event_data->SecondRequestIrqEntry->len) + { + g_array_remove_range (event_data->SecondRequestIrqEntry,0,event_data->SecondRequestIrqEntry->len); + } + + if(event_data->SecondRequestIrqExit->len) + { + g_array_remove_range (event_data->SecondRequestIrqExit,0, event_data->SecondRequestIrqExit->len); + } + + if(event_data->SumArray->len) + { + g_array_remove_range (event_data->SumArray,0, event_data->SumArray->len); + } + + return FALSE; +} + + +/** + * This function converts the number of interrupts over a time window to + * frequency in HZ + */ +static int FrequencyInHZ(gint NumerofInterruptions, TimeWindow time_window) +{ + guint64 frequencyHz = 0; + double timeSec; // time in second + double result; + result = ltt_time_to_double(time_window.time_width); + timeSec = (result/NANOSECONDS_PER_SECOND); //time in second + frequencyHz = NumerofInterruptions / timeSec; + return frequencyHz; +} + +/** + * This function calculates the duration standard deviation + * Duration standard deviation = sqrt(1/N Sum ((xi -Xa)^2)) + * Where: + * sumId.sumOfDurations -> Sum ((xi -Xa)^2) + * inner_component -> 1/N Sum ((xi -Xa)^2) + * deviation-> sqrt(1/N Sum ((xi -Xa)^2)) + */ +static int CalculateDurationStandardDeviation(gint id, InterruptEventData *event_data) +{ + int i; + SumId sumId; + double inner_component; + int deviation = 0; + for(i = 0; i < event_data->SumArray->len; i++) + { + sumId = g_array_index(event_data->SumArray, SumId, i); + if(id == sumId.irqId) + { + if(sumId.TotalNumberOfInterrupts != 0) + inner_component = sumId.sumOfDurations/ sumId.TotalNumberOfInterrupts; + else + inner_component = 0.0; + deviation = sqrt(inner_component); + return deviation; + } + } + return deviation; +} + + +/** + * This function calculates the period standard deviation + * Period standard deviation = sqrt(1/N Sum ((xi -Xa)^2)) + * Where: + * sumId.sumOfPeriods -> Sum ((xi -Xa)^2) + * inner_component -> 1/N Sum ((xi -Xa)^2) + * period_standard_deviation-> sqrt(1/N Sum ((xi -Xa)^2)) + + * + */ +static int CalculatePeriodStandardDeviation(gint id, InterruptEventData *event_data) +{ + int i; + SumId sumId; + guint64 inner_component; + guint64 period_standard_deviation = 0; + + for(i = 0; i < event_data->SumArray->len; i++) + { + sumId = g_array_index(event_data->SumArray, SumId, i); + if(id == sumId.irqId) + { + if(sumId.TotalNumberOfInterrupts != 0) + inner_component = sumId.sumOfPeriods / sumId.TotalNumberOfInterrupts; + else + inner_component = 0; + + period_standard_deviation = sqrt(inner_component); + } + } + + return period_standard_deviation; +} + +/** + * This function calculates the frequency standard deviation + * Frequency standard deviation = sqrt(1/N Sum ((xi -Xa)^2)) + * Where: + * sumId.sumOfFrequencies -> Sum ((xi -Xa)^2) + * inner_component -> 1/N Sum ((xi -Xa)^2) + * frequency_standard_deviation-> sqrt(1/N Sum ((xi -Xa)^2)) + * + */ +static int CalculateFrequencyStandardDeviation(gint id, InterruptEventData *event_data) +{ + int i; + SumId sumId; + guint64 inner_component; + guint64 frequency_standard_deviation = 0; + for(i = 0; i < event_data->SumArray->len; i++) + { + sumId = g_array_index(event_data->SumArray, SumId, i); + if(id == sumId.irqId) + { + if(sumId.TotalNumberOfInterrupts != 0) + inner_component = sumId.sumOfFrequencies / sumId.TotalNumberOfInterrupts; + else + inner_component = 0; + + frequency_standard_deviation = sqrt(inner_component); + } + } + return frequency_standard_deviation; +} + +/* + * This function is called by the main window + * when the time interval needs to be updated. + **/ +gboolean interrupt_update_time_window(void * hook_data, void * call_data) +{ + InterruptEventData *event_data = (InterruptEventData *) hook_data; + const TimeWindowNotifyData *time_window_nofify_data = ((const TimeWindowNotifyData *)call_data); + event_data->time_window = *time_window_nofify_data->new_time_window; + g_info("interrupts: interrupt_update_time_window()\n"); + Tab *tab = event_data->tab; + lttvwindow_events_request_remove_all(tab, event_data); + FirstRequest(event_data ); + return FALSE; +} + + +gboolean trace_header(void *hook_data, void *call_data) +{ + + InterruptEventData *event_data = (InterruptEventData *)hook_data; + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + LttEvent *e; + LttTime event_time; + return FALSE; +} + +void interrupt_destroy_walk(gpointer data, gpointer user_data) +{ + g_info("interrupt_destroy_walk"); + InterruptEventData *event_data = (InterruptEventData*) data; + interrupt_destructor((InterruptEventData*)data); +} + + +void interrupt_destructor(InterruptEventData *event_viewer_data) +{ + /* May already been done by GTK window closing */ + g_info("enter interrupt_destructor \n"); + if(GTK_IS_WIDGET(event_viewer_data->Hbox)) + { + gtk_widget_destroy(event_viewer_data->Hbox); + } +} + +/** + This function is called when the viewer is destroyed to free hooks and memory +*/ +static void InterruptFree(InterruptEventData *event_viewer_data) +{ + Tab *tab = event_viewer_data->tab; + if(tab != NULL) + { + + g_array_free(event_viewer_data->FirstRequestIrqExit, TRUE); + g_array_free(event_viewer_data->FirstRequestIrqEntry, TRUE); + g_array_free(event_viewer_data->SecondRequestIrqEntry, TRUE); + g_array_free(event_viewer_data->SecondRequestIrqExit, TRUE); + g_array_free(event_viewer_data->SumArray, TRUE); + + lttvwindow_unregister_time_window_notify(tab, interrupt_update_time_window, event_viewer_data); + + lttvwindow_events_request_remove_all(event_viewer_data->tab, + event_viewer_data); + + interrupt_data_list = g_slist_remove(interrupt_data_list, event_viewer_data); + + } + +} + +/** + * plugin's destroy function + * + * This function releases the memory reserved by the module and unregisters + * everything that has been registered in the gtkTraceSet API. + */ +static void destroy() +{ + + g_info("Destroy interrupts"); + g_slist_foreach(interrupt_data_list, interrupt_destroy_walk, NULL ); + g_slist_free(interrupt_data_list); + lttvwindow_unregister_constructor(interrupts); + +} + +LTTV_MODULE("interrupts", "interrupts info view", \ + "Graphical module to display interrupts performance", \ + init, destroy, "lttvwindow") diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/Makefile.am b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/Makefile.am new file mode 100644 index 00000000..9d31aac8 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/Makefile.am @@ -0,0 +1,25 @@ +## Process this file with automake to produce Makefile.in + +SUBDIRS = lttvwindow pixmaps + +install-data-local: + @$(NORMAL_INSTALL) + if test -d $(srcdir)/pixmaps; then \ + $(mkinstalldirs) $(DESTDIR)$(pkgdatadir)/pixmaps; \ + for pixmap in $(srcdir)/pixmaps/*.xpm $(srcdir)/pixmaps/*.png; do \ + if test -f $$pixmap; then \ + $(INSTALL_DATA) $$pixmap $(DESTDIR)$(pkgdatadir)/pixmaps; \ + fi \ + done \ + fi + +dist-hook: + if test -d pixmaps; then \ + mkdir $(distdir)/pixmaps; \ + for pixmap in pixmaps/*.xpm pixmaps/*.png; do \ + if test -f $$pixmap; then \ + cp -p $$pixmap $(distdir)/pixmaps; \ + fi \ + done \ + fi + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/glade/lttvwindow.glade b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/glade/lttvwindow.glade new file mode 100644 index 00000000..0d4cd97d --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/glade/lttvwindow.glade @@ -0,0 +1,760 @@ + + + + + + + 100 + 50 + True + Main window + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + 600 + 400 + True + False + + + + + True + False + 0 + + + + True + False + 0 + + + + True + + + + True + _File + True + + + + + + + True + New + True + + + + + + + True + Empty trace set + True + + + + + + + True + Clone trace set + True + + + + + + + True + + + + + + True + Tab + True + + + + + + + + + + + True + Open + True + + + + + + + True + Close + True + + + + + + + True + Close Tab + True + + + + + + + True + + + + + + True + Add Trace + True + + + + + + + True + Remove Trace + True + + + + + + + True + Save + True + + + + + + + True + Save As + True + + + + + + + True + + + + + + True + Quit + True + + + + + + + + + + + True + _Edit + True + + + + + + + True + gtk-cut + True + + + + + + + True + gtk-copy + True + + + + + + + True + gtk-paste + True + + + + + + + True + gtk-delete + True + + + + + + + + + + + True + _View + True + + + + + + + True + Zoom in + True + + + + + + + True + Zoom out + True + + + + + + + True + Zoom extended + True + + + + + + + True + + + + + + True + Go to time + True + + + + + + + True + Show time frame + True + + + + + + + + + + + True + Tools + True + + + + + + + True + Move viewer up + True + + + + + + + True + Move viewer down + True + + + + + + + True + Remove viewer + True + + + + + + + True + + + + + + True + Insert viewer test + True + + + + + + + + + + + True + Plugins + True + + + + + + + True + Load module + True + + + + + + + True + Unload module + True + + + + + + + True + Add module search path + True + + + + + + + + + + + True + Options + True + + + + + + + True + Color + True + + + + + + + True + + + + + + True + Filter + True + + + + + + + True + Save configuration + True + + + + + + + + + + 0 + False + False + + + + + + True + + + + True + _Help + True + + + + + + + True + Content + True + + + + + + + True + + + + + + True + About... + True + + + + + + + + + + 0 + False + False + GTK_PACK_END + + + + + 0 + False + False + + + + + + True + GTK_ORIENTATION_HORIZONTAL + GTK_TOOLBAR_ICONS + True + + + + 1 + True + New window with empty trace set + + True + filenew.png + + + + + + + 1 + True + open a trace set + + True + fileopen.png + + + + + + + 1 + True + Add a trace + + True + edit_add_22.png + + + + + + + 1 + True + Remove a trace + + True + edit_remove_22.png + + + + + + + 1 + True + save the current trace set + + True + filesave.png + + + + + + + 1 + True + save as + + True + filesaveas.png + + + + + + + 1 + True + Zoom in + + True + stock_zoom_in_24.png + True + + + + True + + + + + + 1 + True + Zoom out + + True + stock_zoom_out_24.png + + + + + + + 1 + True + Zoom extended + + True + stock_zoom_fit_24.png + + + + + + + 1 + True + Go to time + + True + gtk-jump-to.png + + + + + + + 1 + True + Show time frame + + True + mini-display.xpm + + + + + + + 1 + True + Move up current viewer + + True + 1uparrow.png + True + + + + True + + + + + + 1 + True + Move down current viewer + + True + 1downarrow.png + + + + + + + 1 + True + Delete current viewer + + True + remove.png + + + + + + 0 + False + False + + + + + + True + GTK_ORIENTATION_HORIZONTAL + GTK_TOOLBAR_BOTH + True + + + + + + + 0 + False + False + + + + + + True + True + True + True + GTK_POS_TOP + False + False + + + + + + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + tab + + + + + 0 + True + True + + + + + + True + True + + + 0 + False + False + + + + + + + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/glade/lttvwindow.gladep b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/glade/lttvwindow.gladep new file mode 100644 index 00000000..31586846 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/glade/lttvwindow.gladep @@ -0,0 +1,11 @@ + + + + + MainWin + mainwin + FALSE + FALSE + FALSE + FALSE + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/Makefile.am b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/Makefile.am new file mode 100644 index 00000000..243bd4f7 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/Makefile.am @@ -0,0 +1,48 @@ +## Process this file with automake to produce Makefile.in + +AM_CFLAGS = $(GLIB_CFLAGS) +AM_CFLAGS += $(GTK_CFLAGS) +AM_CFLAGS += -fvisibility=hidden +LIBS += $(GLIB_LIBS) +LIBS += $(GTK_LIBS) + +INCLUDES = \ + -DPACKAGE_DATA_DIR=\""$(datadir)"\" \ + -DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \ + @PACKAGE_CFLAGS@ \ + $(DEFAULT_INCLUDES) + +#libdir = ${lttvplugindir} + +lib_LTLIBRARIES = liblttvwindow.la + + +liblttvwindow_la_SOURCES = \ + toolbar.c\ + menu.c\ + lttvwindow.c \ + lttvwindowtraces.c \ + init_module.c \ + support.c \ + interface.c \ + callbacks.c \ + lttv_plugin_tab.c \ + lttv_plugin.c + +noinst_HEADERS = \ + support.h \ + interface.h \ + callbacks.h\ + mainwindow-private.h + +lttvwindowinclude_HEADERS = \ + lttvwindow.h\ + lttvwindowtraces.h\ + mainwindow.h\ + menu.h\ + toolbar.h\ + lttv_plugin_tab.h \ + lttv_plugin.h + +liblttvwindow_la_LIBADD = @PACKAGE_LIBS@ $(INTLLIBS) + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/callbacks.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/callbacks.c new file mode 100644 index 00000000..817044b8 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/callbacks.c @@ -0,0 +1,4919 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 XangXiu Yang, Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include // for PATH_MAX +#include +#include +#include +#include + +#include + +#include "callbacks.h" +#include "interface.h" +#include "support.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static LttTime lttvwindow_default_time_width = { 1, 0 }; +#define CLIP_BUF 256 // size of clipboard buffer + +extern LttvTrace *g_init_trace ; + + +/** Array containing instanced objects. */ +extern GSList * g_main_window_list; + +/** MD : keep old directory. */ +static char remember_plugins_dir[PATH_MAX] = ""; +static char remember_trace_dir[PATH_MAX] = ""; + +void tab_destructor(LttvPluginTab * ptab); + +MainWindow * get_window_data_struct(GtkWidget * widget); +char * get_load_module(MainWindow *mw, + char ** load_module_name, int nb_module); +char * get_unload_module(MainWindow *mw, + char ** loaded_module_name, int nb_module); +char * get_remove_trace(MainWindow *mw, char ** all_trace_name, int nb_trace); +char * get_selection(MainWindow *mw, + char ** all_name, int nb, char *title, char * column_title); +void init_tab(Tab *tab, MainWindow * mw, Tab *copy_tab, + GtkNotebook * notebook, char * label); + +static void insert_viewer(GtkWidget* widget, lttvwindow_viewer_constructor constructor); + +LttvPluginTab *create_new_tab(GtkWidget* widget, gpointer user_data); + +static gboolean lttvwindow_process_pending_requests(Tab *tab); + +enum { + CHECKBOX_COLUMN, + NAME_COLUMN, + TOTAL_COLUMNS +}; + +enum +{ + MODULE_COLUMN, + N_COLUMNS +}; + +/* Pasting routines */ + +static void MEventBox1a_receive(GtkClipboard *clipboard, + const gchar *text, + gpointer data) +{ + if(text == NULL) return; + Tab *tab = (Tab *)data; + gchar buffer[CLIP_BUF]; + gchar *ptr = buffer, *ptr_ssec, *ptr_snsec, *ptr_esec, *ptr_ensec; + + strncpy(buffer, text, CLIP_BUF); + + /* start */ + while(!isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* remove leading junk */ + ptr_ssec = ptr; + while(isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* read all the first number */ + *ptr = '\0'; + ptr++; + + while(!isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* remove leading junk */ + ptr_snsec = ptr; + while(isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* read all the first number */ + *ptr = '\0'; + + /* end */ + while(!isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* remove leading junk */ + ptr_esec = ptr; + while(isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* read all the first number */ + *ptr = '\0'; + ptr++; + + while(!isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* remove leading junk */ + ptr_ensec = ptr; + while(isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* read all the first number */ + *ptr = '\0'; + + gtk_spin_button_set_value(GTK_SPIN_BUTTON(tab->MEntry1), + (double)strtoul(ptr_ssec, NULL, 10)); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(tab->MEntry2), + (double)strtoul(ptr_snsec, NULL, 10)); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(tab->MEntry3), + (double)strtoul(ptr_esec, NULL, 10)); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(tab->MEntry4), + (double)strtoul(ptr_ensec, NULL, 10)); +} + +static gboolean on_MEventBox1a_paste(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + Tab *tab = (Tab*)data; + + GtkClipboard *clip = gtk_clipboard_get_for_display(gdk_display_get_default(), + GDK_SELECTION_PRIMARY); + gtk_clipboard_request_text(clip, + (GtkClipboardTextReceivedFunc)MEventBox1a_receive, + (gpointer)tab); + return 0; +} + + +/* Start */ +static void MEventBox1b_receive(GtkClipboard *clipboard, + const gchar *text, + gpointer data) +{ + if(text == NULL) return; + Tab *tab = (Tab *)data; + gchar buffer[CLIP_BUF]; + gchar *ptr = buffer, *ptr_sec, *ptr_nsec; + + strncpy(buffer, text, CLIP_BUF); + + while(!isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* remove leading junk */ + ptr_sec = ptr; + while(isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* read all the first number */ + *ptr = '\0'; + ptr++; + + while(!isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* remove leading junk */ + ptr_nsec = ptr; + while(isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* read all the first number */ + *ptr = '\0'; + + gtk_spin_button_set_value(GTK_SPIN_BUTTON(tab->MEntry1), + (double)strtoul(ptr_sec, NULL, 10)); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(tab->MEntry2), + (double)strtoul(ptr_nsec, NULL, 10)); +} + +/* Start */ +static gboolean on_MEventBox1b_paste(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + Tab *tab = (Tab*)data; + + GtkClipboard *clip = gtk_clipboard_get_for_display(gdk_display_get_default(), + GDK_SELECTION_PRIMARY); + gtk_clipboard_request_text(clip, + (GtkClipboardTextReceivedFunc)MEventBox1b_receive, + (gpointer)tab); + return 0; +} + +/* End */ +static void MEventBox3b_receive(GtkClipboard *clipboard, + const gchar *text, + gpointer data) +{ + if(text == NULL) return; + Tab *tab = (Tab *)data; + gchar buffer[CLIP_BUF]; + gchar *ptr = buffer, *ptr_sec, *ptr_nsec; + + strncpy(buffer, text, CLIP_BUF); + + while(!isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* remove leading junk */ + ptr_sec = ptr; + while(isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* read all the first number */ + *ptr = '\0'; + ptr++; + + while(!isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* remove leading junk */ + ptr_nsec = ptr; + while(isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* read all the first number */ + *ptr = '\0'; + + gtk_spin_button_set_value(GTK_SPIN_BUTTON(tab->MEntry3), + (double)strtoul(ptr_sec, NULL, 10)); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(tab->MEntry4), + (double)strtoul(ptr_nsec, NULL, 10)); +} + +/* End */ +static gboolean on_MEventBox3b_paste(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + Tab *tab = (Tab*)data; + + GtkClipboard *clip = gtk_clipboard_get_for_display(gdk_display_get_default(), + GDK_SELECTION_PRIMARY); + gtk_clipboard_request_text(clip, + (GtkClipboardTextReceivedFunc)MEventBox3b_receive, + (gpointer)tab); + return 0; +} + +/* Current */ +static void MEventBox5b_receive(GtkClipboard *clipboard, + const gchar *text, + gpointer data) +{ + if(text == NULL) return; + Tab *tab = (Tab *)data; + gchar buffer[CLIP_BUF]; + gchar *ptr = buffer, *ptr_sec, *ptr_nsec; + + strncpy(buffer, text, CLIP_BUF); + + while(!isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* remove leading junk */ + ptr_sec = ptr; + while(isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* read all the first number */ + *ptr = '\0'; + ptr++; + + while(!isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* remove leading junk */ + ptr_nsec = ptr; + while(isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* read all the first number */ + *ptr = '\0'; + + gtk_spin_button_set_value(GTK_SPIN_BUTTON(tab->MEntry5), + (double)strtoul(ptr_sec, NULL, 10)); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(tab->MEntry6), + (double)strtoul(ptr_nsec, NULL, 10)); +} + +/* Current */ +static gboolean on_MEventBox5b_paste(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + Tab *tab = (Tab*)data; + + GtkClipboard *clip = gtk_clipboard_get_for_display(gdk_display_get_default(), + GDK_SELECTION_PRIMARY); + gtk_clipboard_request_text(clip, + (GtkClipboardTextReceivedFunc)MEventBox5b_receive, + (gpointer)tab); + return 0; +} + +/* Interval */ +static void MEventBox8_receive(GtkClipboard *clipboard, + const gchar *text, + gpointer data) +{ + if(text == NULL) return; + Tab *tab = (Tab *)data; + gchar buffer[CLIP_BUF]; + gchar *ptr = buffer, *ptr_sec, *ptr_nsec; + + strncpy(buffer, text, CLIP_BUF); + + while(!isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* remove leading junk */ + ptr_sec = ptr; + while(isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* read all the first number */ + *ptr = '\0'; + ptr++; + + while(!isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* remove leading junk */ + ptr_nsec = ptr; + while(isdigit(*ptr) && ptr < buffer+CLIP_BUF-1) ptr++; + /* read all the first number */ + *ptr = '\0'; + + gtk_spin_button_set_value(GTK_SPIN_BUTTON(tab->MEntry7), + (double)strtoul(ptr_sec, NULL, 10)); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(tab->MEntry8), + (double)strtoul(ptr_nsec, NULL, 10)); +} + +/* Interval */ +static gboolean on_MEventBox8_paste(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + Tab *tab = (Tab*)data; + + GtkClipboard *clip = gtk_clipboard_get_for_display(gdk_display_get_default(), + GDK_SELECTION_PRIMARY); + gtk_clipboard_request_text(clip, + (GtkClipboardTextReceivedFunc)MEventBox8_receive, + (gpointer)tab); + return 0; +} + +#if 0 +static void on_top_notify(GObject *gobject, + GParamSpec *arg1, + gpointer user_data) +{ + Tab *tab = (Tab*)user_data; + g_message("in on_top_notify.\n"); + +} +#endif //0 +static gboolean viewer_grab_focus(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + GtkWidget *viewer = GTK_WIDGET(data); + GtkWidget *viewer_container = gtk_widget_get_parent(viewer); + + g_debug("FOCUS GRABBED"); + g_object_set_data(G_OBJECT(viewer_container), "focused_viewer", viewer); + return 0; +} + + +static void connect_focus_recursive(GtkWidget *widget, + GtkWidget *viewer) +{ + if(GTK_IS_CONTAINER(widget)) { + gtk_container_forall(GTK_CONTAINER(widget), + (GtkCallback)connect_focus_recursive, + viewer); + + } + if(GTK_IS_TREE_VIEW(widget)) { + gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(widget), TRUE); + } + gtk_widget_add_events(widget, GDK_BUTTON_PRESS_MASK); + g_signal_connect (G_OBJECT(widget), + "button-press-event", + G_CALLBACK (viewer_grab_focus), + (gpointer)viewer); +} + +/* Stop all the processings and call gtk_main_quit() */ +static void mainwindow_quit() +{ + lttvwindowtraces_unregister_requests(g_quark_from_string("stats")); + lttvwindowtraces_unregister_requests(g_quark_from_string("state")); + lttvwindowtraces_unregister_computation_hooks(g_quark_from_string("stats")); + lttvwindowtraces_unregister_computation_hooks(g_quark_from_string("state")); + + gtk_main_quit(); +} + + +/* insert_viewer function constructs an instance of a viewer first, + * then inserts the widget of the instance into the container of the + * main window + */ + +void +insert_viewer_wrap(GtkWidget *menuitem, gpointer user_data) +{ + insert_viewer((GtkWidget*)menuitem, (lttvwindow_viewer_constructor)user_data); +} + + +/* internal functions */ +void insert_viewer(GtkWidget* widget, lttvwindow_viewer_constructor constructor) +{ + GtkWidget * viewer_container; + MainWindow * mw_data = get_window_data_struct(widget); + GtkWidget * notebook = lookup_widget(widget, "MNotebook"); + GtkWidget * viewer; + TimeInterval * time_interval; + GtkWidget *page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), + gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook))); + LttvPluginTab *ptab; + Tab *tab; + + if(!page) { + ptab = create_new_tab(widget, NULL); + } else { + ptab = (LttvPluginTab *)g_object_get_data(G_OBJECT(page), "Tab_Plugin"); + } + tab = ptab->tab; + + viewer_container = tab->viewer_container; + + viewer = (GtkWidget*)constructor(ptab); + if(viewer) + { + //gtk_multivpaned_widget_add(GTK_MULTIVPANED(multivpaned), viewer); + + gtk_box_pack_end(GTK_BOX(viewer_container), + viewer, + TRUE, + TRUE, + 0); + + /* We want to connect the viewer_grab_focus to EVERY + * child of this widget. The little trick is to get each child + * of each GTK_CONTAINER, even subchildren. + */ + connect_focus_recursive(viewer, viewer); + } +} + +/** + * Function to set/update traceset for the viewers + * @param tab viewer's tab + * @param traceset traceset of the main window. + * return value : + * 0 : traceset updated + * 1 : no traceset hooks to update; not an error. + */ + +int SetTraceset(Tab * tab, LttvTraceset *traceset) +{ + LttvTracesetContext *tsc = + LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context); + TimeInterval time_span = tsc->time_span; + TimeWindow new_time_window = tab->time_window; + LttTime new_current_time = tab->current_time; + + /* Set the tab's time window and current time if + * out of bounds */ + if(ltt_time_compare(tab->time_window.start_time, time_span.start_time) < 0 + || ltt_time_compare(tab->time_window.end_time, + time_span.end_time) > 0) { + new_time_window.start_time = time_span.start_time; + + new_current_time = time_span.start_time; + + LttTime tmp_time; + + if(ltt_time_compare(lttvwindow_default_time_width, + ltt_time_sub(time_span.end_time, time_span.start_time)) < 0 + || + ltt_time_compare(time_span.end_time, time_span.start_time) == 0) + tmp_time = lttvwindow_default_time_width; + else + tmp_time = time_span.end_time; + + new_time_window.time_width = tmp_time ; + new_time_window.time_width_double = ltt_time_to_double(tmp_time); + new_time_window.end_time = ltt_time_add(new_time_window.start_time, + new_time_window.time_width) ; + } + + + +#if 0 + /* Set scrollbar */ + GtkAdjustment *adjustment = gtk_range_get_adjustment(GTK_RANGE(tab->scrollbar)); + LttTime upper = ltt_time_sub(time_span.end_time, time_span.start_time); + + g_object_set(G_OBJECT(adjustment), + "lower", + 0.0, /* lower */ + "upper", + ltt_time_to_double(upper) + * NANOSECONDS_PER_SECOND, /* upper */ + "step_increment", + ltt_time_to_double(tab->time_window.time_width) + / SCROLL_STEP_PER_PAGE + * NANOSECONDS_PER_SECOND, /* step increment */ + "page_increment", + ltt_time_to_double(tab->time_window.time_width) + * NANOSECONDS_PER_SECOND, /* page increment */ + "page_size", + ltt_time_to_double(tab->time_window.time_width) + * NANOSECONDS_PER_SECOND, /* page size */ + NULL); + gtk_adjustment_changed(adjustment); + + g_object_set(G_OBJECT(adjustment), + "value", + ltt_time_to_double( + ltt_time_sub(tab->time_window.start_time, time_span.start_time)) + * NANOSECONDS_PER_SECOND, /* value */ + NULL); + gtk_adjustment_value_changed(adjustment); + + /* set the time bar. The value callbacks will change their nsec themself */ + /* start seconds */ + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry1), + (double)time_span.start_time.tv_sec, + (double)time_span.end_time.tv_sec); + + /* end seconds */ + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry3), + (double)time_span.start_time.tv_sec, + (double)time_span.end_time.tv_sec); + + /* current seconds */ + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry5), + (double)time_span.start_time.tv_sec, + (double)time_span.end_time.tv_sec); +#endif //0 + + /* Finally, call the update hooks of the viewers */ + LttvHooks * tmp; + LttvAttributeValue value; + gint retval = 0; + + + g_assert( lttv_iattribute_find_by_path(tab->attributes, + "hooks/updatetraceset", LTTV_POINTER, &value)); + + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp == NULL) retval = 1; + else lttv_hooks_call(tmp,traceset); + + time_change_manager(tab, new_time_window); + current_time_change_manager(tab, new_current_time); + + return retval; +} + +/** + * Function to set/update filter for the viewers + * @param tab viewer's tab + * @param filter filter of the main window. + * return value : + * -1 : error + * 0 : filters updated + * 1 : no filter hooks to update; not an error. + */ +#if 0 +int SetFilter(Tab * tab, gpointer filter) +{ + LttvHooks * tmp; + LttvAttributeValue value; + + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/updatefilter", LTTV_POINTER, &value)); + + tmp = (LttvHooks*)*(value.v_pointer); + + if(tmp == NULL) return 1; + lttv_hooks_call(tmp,filter); + + return 0; +} +#endif //0 + + +/** + * Function to redraw each viewer belonging to the current tab + * @param tab viewer's tab + */ + +void update_traceset(Tab *tab) +{ + LttvAttributeValue value; + LttvHooks * tmp; + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/updatetraceset", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp == NULL) return; + lttv_hooks_call(tmp, NULL); +} + + +/* get_label function is used to get user input, it displays an input + * box, which allows user to input a string + */ + +void get_label_string (GtkWidget * text, gchar * label) +{ + GtkEntry * entry = (GtkEntry*)text; + if(strlen(gtk_entry_get_text(entry))!=0) + strcpy(label,gtk_entry_get_text(entry)); +} + +gboolean get_label(MainWindow * mw, gchar * str, gchar* dialogue_title, gchar * label_str) +{ + GtkWidget * dialogue; + GtkWidget * text; + GtkWidget * label; + gint id; + + dialogue = gtk_dialog_new_with_buttons(dialogue_title,NULL, + GTK_DIALOG_MODAL, + GTK_STOCK_OK,GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL,GTK_RESPONSE_REJECT, + NULL); + + label = gtk_label_new(label_str); + gtk_widget_show(label); + + text = gtk_entry_new(); + gtk_widget_show(text); + + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialogue)->vbox), label,TRUE, TRUE,0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialogue)->vbox), text,FALSE, FALSE,0); + + id = gtk_dialog_run(GTK_DIALOG(dialogue)); + switch(id){ + case GTK_RESPONSE_ACCEPT: + get_label_string(text,str); + gtk_widget_destroy(dialogue); + break; + case GTK_RESPONSE_REJECT: + default: + gtk_widget_destroy(dialogue); + return FALSE; + } + return TRUE; +} + + +/* get_window_data_struct function is actually a lookup function, + * given a widget which is in the tree of the main window, it will + * return the MainWindow data structure associated with main window + */ + +MainWindow * get_window_data_struct(GtkWidget * widget) +{ + GtkWidget * mw; + MainWindow * mw_data; + + mw = lookup_widget(widget, "MWindow"); + if(mw == NULL){ + g_info("Main window does not exist\n"); + return NULL; + } + + mw_data = (MainWindow *) g_object_get_data(G_OBJECT(mw),"main_window_data"); + if(mw_data == NULL){ + g_warning("Main window data does not exist\n"); + return NULL; + } + return mw_data; +} + + +/* create_new_window function, just constructs a new main window + */ + +void create_new_window(GtkWidget* widget, gpointer user_data, gboolean clone) +{ + MainWindow * parent = get_window_data_struct(widget); + + if(clone){ + g_info("Clone : use the same traceset\n"); + construct_main_window(parent); + }else{ + g_info("Empty : traceset is set to NULL\n"); + construct_main_window(NULL); + } +} + +/* Get the currently focused viewer. + * If no viewer is focused, use the first one. + * + * If no viewer available, return NULL. + */ +GtkWidget *viewer_container_focus(GtkWidget *container) +{ + GtkWidget *widget; + + widget = (GtkWidget*)g_object_get_data(G_OBJECT(container), + "focused_viewer"); + + if(widget == NULL) { + g_debug("no widget focused"); + GList *children = gtk_container_get_children(GTK_CONTAINER(container)); + + if(children != NULL) + widget = GTK_WIDGET(children->data); + g_object_set_data(G_OBJECT(container), + "focused_viewer", + widget); + } + + return widget; + + +} + +gint viewer_container_position(GtkWidget *container, GtkWidget *child) +{ + + if(child == NULL) return -1; + + gint pos; + GValue value; + memset(&value, 0, sizeof(GValue)); + g_value_init(&value, G_TYPE_INT); + gtk_container_child_get_property(GTK_CONTAINER(container), + child, + "position", + &value); + pos = g_value_get_int(&value); + + return pos; +} + + +/* move_*_viewer functions move the selected view up/down in + * the current tab + */ + +void move_down_viewer(GtkWidget * widget, gpointer user_data) +{ + MainWindow * mw = get_window_data_struct(widget); + GtkWidget * notebook = lookup_widget(widget, "MNotebook"); + + GtkWidget *page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), + gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook))); + + Tab *tab; + if(!page) { + return; + } else { + LttvPluginTab *ptab; + ptab = (Tab *)g_object_get_data(G_OBJECT(page), "Tab_Plugin"); + tab = ptab->tab; + } + + //gtk_multivpaned_widget_move_up(GTK_MULTIVPANED(tab->multivpaned)); + + /* change the position in the vbox */ + GtkWidget *focus_widget; + gint position; + focus_widget = viewer_container_focus(tab->viewer_container); + position = viewer_container_position(tab->viewer_container, focus_widget); + + if(position > 0) { + /* can move up one position */ + gtk_box_reorder_child(GTK_BOX(tab->viewer_container), + focus_widget, + position-1); + } + +} + +void move_up_viewer(GtkWidget * widget, gpointer user_data) +{ + MainWindow * mw = get_window_data_struct(widget); + GtkWidget * notebook = lookup_widget(widget, "MNotebook"); + + GtkWidget *page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), + gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook))); + Tab *tab; + + if(!page) { + return; + } else { + LttvPluginTab *ptab; + ptab = (LttvPluginTab *)g_object_get_data(G_OBJECT(page), "Tab_Plugin"); + tab = ptab->tab; + } + + //gtk_multivpaned_widget_move_down(GTK_MULTIVPANED(tab->multivpaned)); + /* change the position in the vbox */ + GtkWidget *focus_widget; + gint position; + focus_widget = viewer_container_focus(tab->viewer_container); + position = viewer_container_position(tab->viewer_container, focus_widget); + + if(position != -1 && + position < + g_list_length(gtk_container_get_children( + GTK_CONTAINER(tab->viewer_container)))-1 + ) { + /* can move down one position */ + gtk_box_reorder_child(GTK_BOX(tab->viewer_container), + focus_widget, + position+1); + } + +} + + +/* delete_viewer deletes the selected viewer in the current tab + */ + +void delete_viewer(GtkWidget * widget, gpointer user_data) +{ + MainWindow * mw = get_window_data_struct(widget); + GtkWidget * notebook = lookup_widget(widget, "MNotebook"); + + GtkWidget *page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), + gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook))); + Tab *tab; + + if(!page) { + return; + } else { + LttvPluginTab *ptab; + ptab = (Tab *)g_object_get_data(G_OBJECT(page), "Tab_Plugin"); + tab = ptab->tab; + } + + //gtk_multivpaned_widget_delete(GTK_MULTIVPANED(tab->multivpaned)); + + GtkWidget *focus_widget = viewer_container_focus(tab->viewer_container); + + if(focus_widget != NULL) + gtk_widget_destroy(focus_widget); + + g_object_set_data(G_OBJECT(tab->viewer_container), "focused_viewer", NULL); +} + + +/* open_traceset will open a traceset saved in a file + * Right now, it is not finished yet, (not working) + * FIXME + */ + +void open_traceset(GtkWidget * widget, gpointer user_data) +{ + char ** dir; + gint id; + LttvTraceset * traceset; + MainWindow * mw_data = get_window_data_struct(widget); + GtkFileSelection * file_selector = + (GtkFileSelection *)gtk_file_selection_new("Select a traceset"); + + gtk_file_selection_hide_fileop_buttons(file_selector); + + gtk_window_set_transient_for(GTK_WINDOW(file_selector), + GTK_WINDOW(mw_data->mwindow)); + + id = gtk_dialog_run(GTK_DIALOG(file_selector)); + switch(id){ + case GTK_RESPONSE_ACCEPT: + case GTK_RESPONSE_OK: + dir = gtk_file_selection_get_selections (file_selector); + traceset = lttv_traceset_load(dir[0]); + g_info("Open a trace set %s\n", dir[0]); + //Not finished yet + g_strfreev(dir); + case GTK_RESPONSE_REJECT: + case GTK_RESPONSE_CANCEL: + default: + gtk_widget_destroy((GtkWidget*)file_selector); + break; + } + +} + +/* lttvwindow_process_pending_requests + * + * Process requests for parts of the trace from viewers. + * + * These requests are made by lttvwindow_events_request(). + * + * This internal function gets called by g_idle, taking care of the pending + * requests. It is responsible for concatenation of time intervals and position + * requests. It does it with the following algorithm organizing process traceset + * calls. Here is the detailed description of the way it works : + * + * - Events Requests Servicing Algorithm + * + * Data structures necessary : + * + * List of requests added to context : list_in + * List of requests not added to context : list_out + * + * Initial state : + * + * list_in : empty + * list_out : many events requests + * + * FIXME : insert rest of algorithm here + * + */ + +#define list_out tab->events_requests + +gboolean lttvwindow_process_pending_requests(Tab *tab) +{ + GtkWidget* widget; + LttvTracesetContext *tsc; + LttvTracefileContext *tfc; + GSList *list_in = NULL; + LttTime end_time; + guint end_nb_events; + guint count; + LttvTracesetContextPosition *end_position; + + if(lttvwindow_preempt_count > 0) return TRUE; + + if(tab == NULL) { + g_critical("Foreground processing : tab does not exist. Processing removed."); + return FALSE; + } + + /* There is no events requests pending : we should never have been called! */ + g_assert(g_slist_length(list_out) != 0); + + tsc = LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context); + + //set the cursor to be X shape, indicating that the computer is busy in doing its job +#if 0 + new = gdk_cursor_new(GDK_X_CURSOR); + widget = lookup_widget(tab->mw->mwindow, "MToolbar1"); + win = gtk_widget_get_parent_window(widget); + gdk_window_set_cursor(win, new); + gdk_cursor_unref(new); + gdk_window_stick(win); + gdk_window_unstick(win); +#endif //0 + + g_debug("SIZE events req len : %d", g_slist_length(list_out)); + + /* Preliminary check for no trace in traceset */ + /* Unregister the routine if empty, empty list_out too */ + if(lttv_traceset_number(tsc->ts) == 0) { + + /* - For each req in list_out */ + GSList *iter = list_out; + + while(iter != NULL) { + + gboolean remove = FALSE; + gboolean free_data = FALSE; + EventsRequest *events_request = (EventsRequest *)iter->data; + + /* - Call end request for req */ + if(events_request->servicing == TRUE) + lttv_hooks_call(events_request->after_request, (gpointer)tsc); + + /* - remove req from list_out */ + /* Destroy the request */ + remove = TRUE; + free_data = TRUE; + + /* Go to next */ + if(remove) + { + GSList *remove_iter = iter; + + iter = g_slist_next(iter); + if(free_data) events_request_free((EventsRequest*)remove_iter->data); + list_out = g_slist_remove_link(list_out, remove_iter); + } else { // not remove + iter = g_slist_next(iter); + } + } + } + + /* 0.1 Lock Traces */ + { + guint iter_trace=0; + + for(iter_trace=0; + iter_tracets); + iter_trace++) { + LttvTrace *trace_v = lttv_traceset_get(tsc->ts, iter_trace); + + if(lttvwindowtraces_lock(trace_v) != 0) { + g_critical("Foreground processing : Unable to get trace lock"); + return TRUE; /* Cannot get lock, try later */ + } + } + } + + /* 0.2 Seek tracefiles positions to context position */ + //g_assert(lttv_process_traceset_seek_position(tsc, sync_position) == 0); + lttv_process_traceset_synchronize_tracefiles(tsc); + + + /* Events processing algorithm implementation */ + /* Warning : the gtk_events_pending takes a LOT of cpu time. So what we do + * instead is to leave the control to GTK and take it back. + */ + /* A. Servicing loop */ + //while( (g_slist_length(list_in) != 0 || g_slist_length(list_out) != 0)) { + if((g_slist_length(list_in) != 0 || g_slist_length(list_out) != 0)) { + /* Servicing */ + /* 1. If list_in is empty (need a seek) */ + if( g_slist_length(list_in) == 0 ) { + + /* list in is empty, need a seek */ + { + /* 1.1 Add requests to list_in */ + GSList *ltime = NULL; + GSList *lpos = NULL; + GSList *iter = NULL; + + /* 1.1.1 Find all time requests with the lowest start time in list_out + * (ltime) + */ + if(g_slist_length(list_out) > 0) + ltime = g_slist_append(ltime, g_slist_nth_data(list_out, 0)); + for(iter=g_slist_nth(list_out,1);iter!=NULL;iter=g_slist_next(iter)) { + /* Find all time requests with the lowest start time in list_out */ + EventsRequest *event_request_ltime = (EventsRequest*)g_slist_nth_data(ltime, 0); + EventsRequest *event_request_list_out = (EventsRequest*)iter->data; + + int comp; + comp = ltt_time_compare(event_request_ltime->start_time, + event_request_list_out->start_time); + if(comp == 0) + ltime = g_slist_append(ltime, event_request_list_out); + else if(comp > 0) { + /* Remove all elements from ltime, and add current */ + while(ltime != NULL) + ltime = g_slist_delete_link(ltime, g_slist_nth(ltime, 0)); + ltime = g_slist_append(ltime, event_request_list_out); + } + } + + /* 1.1.2 Find all position requests with the lowest position in list_out + * (lpos) + */ + if(g_slist_length(list_out) > 0) + lpos = g_slist_append(lpos, g_slist_nth_data(list_out, 0)); + for(iter=g_slist_nth(list_out,1);iter!=NULL;iter=g_slist_next(iter)) { + /* Find all position requests with the lowest position in list_out */ + EventsRequest *event_request_lpos = (EventsRequest*)g_slist_nth_data(lpos, 0); + EventsRequest *event_request_list_out = (EventsRequest*)iter->data; + + int comp; + if(event_request_lpos->start_position != NULL + && event_request_list_out->start_position != NULL) + { + comp = lttv_traceset_context_pos_pos_compare + (event_request_lpos->start_position, + event_request_list_out->start_position); + } else { + comp = -1; + } + if(comp == 0) + lpos = g_slist_append(lpos, event_request_list_out); + else if(comp > 0) { + /* Remove all elements from lpos, and add current */ + while(lpos != NULL) + lpos = g_slist_delete_link(lpos, g_slist_nth(lpos, 0)); + lpos = g_slist_append(lpos, event_request_list_out); + } + } + + { + EventsRequest *event_request_lpos = (EventsRequest*)g_slist_nth_data(lpos, 0); + EventsRequest *event_request_ltime = (EventsRequest*)g_slist_nth_data(ltime, 0); + LttTime lpos_start_time; + + if(event_request_lpos != NULL + && event_request_lpos->start_position != NULL) { + lpos_start_time = lttv_traceset_context_position_get_time( + event_request_lpos->start_position); + } + + /* 1.1.3 If lpos.start time < ltime */ + if(event_request_lpos != NULL + && event_request_lpos->start_position != NULL + && ltt_time_compare(lpos_start_time, + event_request_ltime->start_time)<0) { + /* Add lpos to list_in, remove them from list_out */ + for(iter=lpos;iter!=NULL;iter=g_slist_next(iter)) { + /* Add to list_in */ + EventsRequest *event_request_lpos = + (EventsRequest*)iter->data; + + list_in = g_slist_append(list_in, event_request_lpos); + /* Remove from list_out */ + list_out = g_slist_remove(list_out, event_request_lpos); + } + } else { + /* 1.1.4 (lpos.start time >= ltime) */ + /* Add ltime to list_in, remove them from list_out */ + + for(iter=ltime;iter!=NULL;iter=g_slist_next(iter)) { + /* Add to list_in */ + EventsRequest *event_request_ltime = + (EventsRequest*)iter->data; + + list_in = g_slist_append(list_in, event_request_ltime); + /* Remove from list_out */ + list_out = g_slist_remove(list_out, event_request_ltime); + } + } + } + g_slist_free(lpos); + g_slist_free(ltime); + } + + /* 1.2 Seek */ + { + tfc = lttv_traceset_context_get_current_tfc(tsc); + g_assert(g_slist_length(list_in)>0); + EventsRequest *events_request = g_slist_nth_data(list_in, 0); + guint seek_count; + + /* 1.2.1 If first request in list_in is a time request */ + if(events_request->start_position == NULL) { + /* - If first req in list_in start time != current time */ + if(tfc == NULL || ltt_time_compare(events_request->start_time, + tfc->timestamp) != 0) + /* - Seek to that time */ + g_debug("SEEK TIME : %lu, %lu", events_request->start_time.tv_sec, + events_request->start_time.tv_nsec); + //lttv_process_traceset_seek_time(tsc, events_request->start_time); + lttv_state_traceset_seek_time_closest(LTTV_TRACESET_STATE(tsc), + events_request->start_time); + + /* Process the traceset with only state hooks */ + seek_count = + lttv_process_traceset_middle(tsc, + events_request->start_time, + G_MAXUINT, NULL); +#ifdef DEBUG + g_assert(seek_count < LTTV_STATE_SAVE_INTERVAL); +#endif //DEBUG + + + } else { + LttTime pos_time; + LttvTracefileContext *tfc = + lttv_traceset_context_get_current_tfc(tsc); + /* Else, the first request in list_in is a position request */ + /* If first req in list_in pos != current pos */ + g_assert(events_request->start_position != NULL); + g_debug("SEEK POS time : %lu, %lu", + lttv_traceset_context_position_get_time( + events_request->start_position).tv_sec, + lttv_traceset_context_position_get_time( + events_request->start_position).tv_nsec); + + if(tfc) { + g_debug("SEEK POS context time : %lu, %lu", + tfc->timestamp.tv_sec, + tfc->timestamp.tv_nsec); + } else { + g_debug("SEEK POS context time : %lu, %lu", + ltt_time_infinite.tv_sec, + ltt_time_infinite.tv_nsec); + } + g_assert(events_request->start_position != NULL); + if(lttv_traceset_context_ctx_pos_compare(tsc, + events_request->start_position) != 0) { + /* 1.2.2.1 Seek to that position */ + g_debug("SEEK POSITION"); + //lttv_process_traceset_seek_position(tsc, events_request->start_position); + pos_time = lttv_traceset_context_position_get_time( + events_request->start_position); + + lttv_state_traceset_seek_time_closest(LTTV_TRACESET_STATE(tsc), + pos_time); + + /* Process the traceset with only state hooks */ + seek_count = + lttv_process_traceset_middle(tsc, + ltt_time_infinite, + G_MAXUINT, + events_request->start_position); + g_assert(lttv_traceset_context_ctx_pos_compare(tsc, + events_request->start_position) == 0); + + + } + } + } + + /* 1.3 Add hooks and call before request for all list_in members */ + { + GSList *iter = NULL; + + for(iter=list_in;iter!=NULL;iter=g_slist_next(iter)) { + EventsRequest *events_request = (EventsRequest*)iter->data; + /* 1.3.1 If !servicing */ + if(events_request->servicing == FALSE) { + /* - begin request hooks called + * - servicing = TRUE + */ + lttv_hooks_call(events_request->before_request, (gpointer)tsc); + events_request->servicing = TRUE; + } + /* 1.3.2 call before chunk + * 1.3.3 events hooks added + */ + if(events_request->trace == -1) + lttv_process_traceset_begin(tsc, + events_request->before_chunk_traceset, + events_request->before_chunk_trace, + events_request->before_chunk_tracefile, + events_request->event, + events_request->event_by_id); + else { + guint nb_trace = lttv_traceset_number(tsc->ts); + g_assert((guint)events_request->trace < nb_trace && + events_request->trace > -1); + LttvTraceContext *tc = tsc->traces[events_request->trace]; + + lttv_hooks_call(events_request->before_chunk_traceset, tsc); + + lttv_trace_context_add_hooks(tc, + events_request->before_chunk_trace, + events_request->before_chunk_tracefile, + events_request->event, + events_request->event_by_id); + } + } + } + } else { + /* 2. Else, list_in is not empty, we continue a read */ + + { + /* 2.0 For each req of list_in */ + GSList *iter = list_in; + + while(iter != NULL) { + + EventsRequest *events_request = (EventsRequest *)iter->data; + + /* - Call before chunk + * - events hooks added + */ + if(events_request->trace == -1) + lttv_process_traceset_begin(tsc, + events_request->before_chunk_traceset, + events_request->before_chunk_trace, + events_request->before_chunk_tracefile, + events_request->event, + events_request->event_by_id); + else { + guint nb_trace = lttv_traceset_number(tsc->ts); + g_assert((guint)events_request->trace < nb_trace && + events_request->trace > -1); + LttvTraceContext *tc = tsc->traces[events_request->trace]; + + lttv_hooks_call(events_request->before_chunk_traceset, tsc); + + lttv_trace_context_add_hooks(tc, + events_request->before_chunk_trace, + events_request->before_chunk_tracefile, + events_request->event, + events_request->event_by_id); + } + + iter = g_slist_next(iter); + } + } + + { + tfc = lttv_traceset_context_get_current_tfc(tsc); + + /* 2.1 For each req of list_out */ + GSList *iter = list_out; + + while(iter != NULL) { + + gboolean remove = FALSE; + gboolean free_data = FALSE; + EventsRequest *events_request = (EventsRequest *)iter->data; + + /* if req.start time == current context time + * or req.start position == current position*/ + if( ltt_time_compare(events_request->start_time, + tfc->timestamp) == 0 + || + (events_request->start_position != NULL + && + lttv_traceset_context_ctx_pos_compare(tsc, + events_request->start_position) == 0) + ) { + /* - Add to list_in, remove from list_out */ + list_in = g_slist_append(list_in, events_request); + remove = TRUE; + free_data = FALSE; + + /* - If !servicing */ + if(events_request->servicing == FALSE) { + /* - begin request hooks called + * - servicing = TRUE + */ + lttv_hooks_call(events_request->before_request, (gpointer)tsc); + events_request->servicing = TRUE; + } + /* call before chunk + * events hooks added + */ + if(events_request->trace == -1) + lttv_process_traceset_begin(tsc, + events_request->before_chunk_traceset, + events_request->before_chunk_trace, + events_request->before_chunk_tracefile, + events_request->event, + events_request->event_by_id); + else { + guint nb_trace = lttv_traceset_number(tsc->ts); + g_assert((guint)events_request->trace < nb_trace && + events_request->trace > -1); + LttvTraceContext *tc = tsc->traces[events_request->trace]; + + lttv_hooks_call(events_request->before_chunk_traceset, tsc); + + lttv_trace_context_add_hooks(tc, + events_request->before_chunk_trace, + events_request->before_chunk_tracefile, + events_request->event, + events_request->event_by_id); + } + + + } + + /* Go to next */ + if(remove) + { + GSList *remove_iter = iter; + + iter = g_slist_next(iter); + if(free_data) events_request_free((EventsRequest*)remove_iter->data); + list_out = g_slist_remove_link(list_out, remove_iter); + } else { // not remove + iter = g_slist_next(iter); + } + } + } + } + + /* 3. Find end criterions */ + { + /* 3.1 End time */ + GSList *iter; + + /* 3.1.1 Find lowest end time in list_in */ + g_assert(g_slist_length(list_in)>0); + end_time = ((EventsRequest*)g_slist_nth_data(list_in,0))->end_time; + + for(iter=g_slist_nth(list_in,1);iter!=NULL;iter=g_slist_next(iter)) { + EventsRequest *events_request = (EventsRequest*)iter->data; + + if(ltt_time_compare(events_request->end_time, + end_time) < 0) + end_time = events_request->end_time; + } + + /* 3.1.2 Find lowest start time in list_out */ + for(iter=list_out;iter!=NULL;iter=g_slist_next(iter)) { + EventsRequest *events_request = (EventsRequest*)iter->data; + + if(ltt_time_compare(events_request->start_time, + end_time) < 0) + end_time = events_request->start_time; + } + } + + { + /* 3.2 Number of events */ + + /* 3.2.1 Find lowest number of events in list_in */ + GSList *iter; + + end_nb_events = ((EventsRequest*)g_slist_nth_data(list_in,0))->num_events; + + for(iter=g_slist_nth(list_in,1);iter!=NULL;iter=g_slist_next(iter)) { + EventsRequest *events_request = (EventsRequest*)iter->data; + + if(events_request->num_events < end_nb_events) + end_nb_events = events_request->num_events; + } + + /* 3.2.2 Use min(CHUNK_NUM_EVENTS, min num events in list_in) as + * num_events */ + + end_nb_events = MIN(CHUNK_NUM_EVENTS, end_nb_events); + } + + { + /* 3.3 End position */ + + /* 3.3.1 Find lowest end position in list_in */ + GSList *iter; + + end_position =((EventsRequest*)g_slist_nth_data(list_in,0))->end_position; + + for(iter=g_slist_nth(list_in,1);iter!=NULL;iter=g_slist_next(iter)) { + EventsRequest *events_request = (EventsRequest*)iter->data; + + if(events_request->end_position != NULL && end_position != NULL && + lttv_traceset_context_pos_pos_compare(events_request->end_position, + end_position) <0) + end_position = events_request->end_position; + } + } + + { + /* 3.3.2 Find lowest start position in list_out */ + GSList *iter; + + for(iter=list_out;iter!=NULL;iter=g_slist_next(iter)) { + EventsRequest *events_request = (EventsRequest*)iter->data; + + if(events_request->end_position != NULL && end_position != NULL && + lttv_traceset_context_pos_pos_compare(events_request->end_position, + end_position) <0) + end_position = events_request->end_position; + } + } + + { + /* 4. Call process traceset middle */ + g_debug("Calling process traceset middle with %p, %lu sec %lu nsec, %u nb ev, %p end pos", tsc, end_time.tv_sec, end_time.tv_nsec, end_nb_events, end_position); + count = lttv_process_traceset_middle(tsc, end_time, end_nb_events, end_position); + + tfc = lttv_traceset_context_get_current_tfc(tsc); + if(tfc != NULL) + g_debug("Context time after middle : %lu, %lu", tfc->timestamp.tv_sec, + tfc->timestamp.tv_nsec); + else + g_debug("End of trace reached after middle."); + + } + { + /* 5. After process traceset middle */ + tfc = lttv_traceset_context_get_current_tfc(tsc); + + /* - if current context time > traceset.end time */ + if(tfc == NULL || ltt_time_compare(tfc->timestamp, + tsc->time_span.end_time) > 0) { + /* - For each req in list_in */ + GSList *iter = list_in; + + while(iter != NULL) { + + gboolean remove = FALSE; + gboolean free_data = FALSE; + EventsRequest *events_request = (EventsRequest *)iter->data; + + /* - Remove events hooks for req + * - Call end chunk for req + */ + + if(events_request->trace == -1) + lttv_process_traceset_end(tsc, + events_request->after_chunk_traceset, + events_request->after_chunk_trace, + events_request->after_chunk_tracefile, + events_request->event, + events_request->event_by_id); + + else { + guint nb_trace = lttv_traceset_number(tsc->ts); + g_assert(events_request->trace < nb_trace && + events_request->trace > -1); + LttvTraceContext *tc = tsc->traces[events_request->trace]; + + lttv_trace_context_remove_hooks(tc, + events_request->after_chunk_trace, + events_request->after_chunk_tracefile, + events_request->event, + events_request->event_by_id); + lttv_hooks_call(events_request->after_chunk_traceset, tsc); + + + } + + /* - Call end request for req */ + lttv_hooks_call(events_request->after_request, (gpointer)tsc); + + /* - remove req from list_in */ + /* Destroy the request */ + remove = TRUE; + free_data = TRUE; + + /* Go to next */ + if(remove) + { + GSList *remove_iter = iter; + + iter = g_slist_next(iter); + if(free_data) events_request_free((EventsRequest*)remove_iter->data); + list_in = g_slist_remove_link(list_in, remove_iter); + } else { // not remove + iter = g_slist_next(iter); + } + } + } + { + /* 5.1 For each req in list_in */ + GSList *iter = list_in; + + while(iter != NULL) { + + gboolean remove = FALSE; + gboolean free_data = FALSE; + EventsRequest *events_request = (EventsRequest *)iter->data; + + /* - Remove events hooks for req + * - Call end chunk for req + */ + if(events_request->trace == -1) + lttv_process_traceset_end(tsc, + events_request->after_chunk_traceset, + events_request->after_chunk_trace, + events_request->after_chunk_tracefile, + events_request->event, + events_request->event_by_id); + + else { + guint nb_trace = lttv_traceset_number(tsc->ts); + g_assert(events_request->trace < nb_trace && + events_request->trace > -1); + LttvTraceContext *tc = tsc->traces[events_request->trace]; + + lttv_trace_context_remove_hooks(tc, + events_request->after_chunk_trace, + events_request->after_chunk_tracefile, + events_request->event, + events_request->event_by_id); + + lttv_hooks_call(events_request->after_chunk_traceset, tsc); + } + + /* - req.num -= count */ + g_assert(events_request->num_events >= count); + events_request->num_events -= count; + + g_assert(tfc != NULL); + /* - if req.num == 0 + * or + * current context time >= req.end time + * or + * req.end pos == current pos + * or + * req.stop_flag == TRUE + */ + if( events_request->num_events == 0 + || + events_request->stop_flag == TRUE + || + ltt_time_compare(tfc->timestamp, + events_request->end_time) >= 0 + || + (events_request->end_position != NULL + && + lttv_traceset_context_ctx_pos_compare(tsc, + events_request->end_position) == 0) + + ) { + g_assert(events_request->servicing == TRUE); + /* - Call end request for req + * - remove req from list_in */ + lttv_hooks_call(events_request->after_request, (gpointer)tsc); + /* - remove req from list_in */ + /* Destroy the request */ + remove = TRUE; + free_data = TRUE; + } + + /* Go to next */ + if(remove) + { + GSList *remove_iter = iter; + + iter = g_slist_next(iter); + if(free_data) events_request_free((EventsRequest*)remove_iter->data); + list_in = g_slist_remove_link(list_in, remove_iter); + } else { // not remove + iter = g_slist_next(iter); + } + } + } + } + } + /* End of removed servicing loop : leave control to GTK instead. */ + // if(gtk_events_pending()) break; + //} + + /* B. When interrupted between chunks */ + + { + GSList *iter = list_in; + + /* 1. for each request in list_in */ + while(iter != NULL) { + + gboolean remove = FALSE; + gboolean free_data = FALSE; + EventsRequest *events_request = (EventsRequest *)iter->data; + + /* 1.1. Use current postition as start position */ + if(events_request->start_position != NULL) + lttv_traceset_context_position_destroy(events_request->start_position); + events_request->start_position = lttv_traceset_context_position_new(tsc); + lttv_traceset_context_position_save(tsc, events_request->start_position); + + /* 1.2. Remove start time */ + events_request->start_time = ltt_time_infinite; + + /* 1.3. Move from list_in to list_out */ + remove = TRUE; + free_data = FALSE; + list_out = g_slist_append(list_out, events_request); + + /* Go to next */ + if(remove) + { + GSList *remove_iter = iter; + + iter = g_slist_next(iter); + if(free_data) events_request_free((EventsRequest*)remove_iter->data); + list_in = g_slist_remove_link(list_in, remove_iter); + } else { // not remove + iter = g_slist_next(iter); + } + } + + + } + /* C Unlock Traces */ + { + lttv_process_traceset_get_sync_data(tsc); + //lttv_traceset_context_position_save(tsc, sync_position); + + guint iter_trace; + + for(iter_trace=0; + iter_tracets); + iter_trace++) { + LttvTrace *trace_v = lttv_traceset_get(tsc->ts, iter_trace); + + lttvwindowtraces_unlock(trace_v); + } + } +#if 0 + //set the cursor back to normal + gdk_window_set_cursor(win, NULL); +#endif //0 + + g_assert(g_slist_length(list_in) == 0); + + if( g_slist_length(list_out) == 0 ) { + /* Put tab's request pending flag back to normal */ + tab->events_request_pending = FALSE; + g_debug("remove the idle fct"); + return FALSE; /* Remove the idle function */ + } + g_debug("leave the idle fct"); + return TRUE; /* Leave the idle function */ + + /* We do not use simili-round-robin, it may require to read 1 meg buffers + * again and again if many tracesets use the same tracefiles. */ + /* Hack for round-robin idle functions */ + /* It will put the idle function at the end of the pool */ + /*g_idle_add_full((G_PRIORITY_HIGH_IDLE + 21), + (GSourceFunc)execute_events_requests, + tab, + NULL); + return FALSE; + */ +} + +#undef list_out + + +static void lttvwindow_add_trace(Tab *tab, LttvTrace *trace_v) +{ + LttvTraceset *traceset = tab->traceset_info->traceset; + guint i; + guint num_traces = lttv_traceset_number(traceset); + + //Verify if trace is already present. + for(i=0; itraceset_info->traceset_context); + + lttv_context_fini(LTTV_TRACESET_CONTEXT( + tab->traceset_info->traceset_context)); + g_object_unref(tab->traceset_info->traceset_context); + + lttv_traceset_add(traceset, trace_v); + lttv_trace_ref(trace_v); /* local ref */ + + /* Create new context */ + tab->traceset_info->traceset_context = + g_object_new(LTTV_TRACESET_STATS_TYPE, NULL); + lttv_context_init( + LTTV_TRACESET_CONTEXT(tab->traceset_info-> + traceset_context), + traceset); + + + //add state update hooks + lttv_state_add_event_hooks( + (LttvTracesetState*)tab->traceset_info->traceset_context); + //Remove local reference to the traces. + for(i=0; imultivpaned), lttv_trace(trace_v)); +} + +/* add_trace adds a trace into the current traceset. It first displays a + * directory selection dialogue to let user choose a trace, then recreates + * tracset_context, and redraws all the viewer of the current tab + */ + +void add_trace(GtkWidget * widget, gpointer user_data) +{ + LttTrace *trace; + LttvTrace * trace_v; + LttvTraceset * traceset; + const char * dir; + char abs_path[PATH_MAX]; + gint id; + MainWindow * mw_data = get_window_data_struct(widget); + GtkWidget * notebook = lookup_widget(widget, "MNotebook"); + + GtkWidget *page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), + gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook))); + LttvPluginTab *ptab; + Tab *tab; + + if(!page) { + ptab = create_new_tab(widget, NULL); + tab = ptab->tab; + } else { + ptab = (LttvPluginTab *)g_object_get_data(G_OBJECT(page), "Tab_Plugin"); + tab = ptab->tab; + } + + //GtkDirSelection * file_selector = (GtkDirSelection *)gtk_dir_selection_new("Select a trace"); + GtkFileSelection * file_selector = (GtkFileSelection *)gtk_file_selection_new("Select a trace"); + gtk_widget_hide( (file_selector)->file_list->parent) ; + gtk_file_selection_hide_fileop_buttons(file_selector); + gtk_window_set_transient_for(GTK_WINDOW(file_selector), + GTK_WINDOW(mw_data->mwindow)); + + if(remember_trace_dir[0] != '\0') + gtk_file_selection_set_filename(file_selector, remember_trace_dir); + + id = gtk_dialog_run(GTK_DIALOG(file_selector)); + switch(id){ + case GTK_RESPONSE_ACCEPT: + case GTK_RESPONSE_OK: + dir = gtk_file_selection_get_filename (file_selector); + strncpy(remember_trace_dir, dir, PATH_MAX); + strncat(remember_trace_dir, "/", PATH_MAX); + if(!dir || strlen(dir) == 0){ + gtk_widget_destroy((GtkWidget*)file_selector); + break; + } + get_absolute_pathname(dir, abs_path); + trace_v = lttvwindowtraces_get_trace_by_name(abs_path); + if(trace_v == NULL) { + trace = ltt_trace_open(abs_path); + if(trace == NULL) { + g_warning("cannot open trace %s", abs_path); + + GtkWidget *dialogue = + gtk_message_dialog_new( + GTK_WINDOW(gtk_widget_get_toplevel(widget)), + GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "Cannot open trace : maybe you should enter in the trace " + "directory to select it ?"); + gtk_dialog_run(GTK_DIALOG(dialogue)); + gtk_widget_destroy(dialogue); + + } else { + trace_v = lttv_trace_new(trace); + lttvwindowtraces_add_trace(trace_v); + lttvwindow_add_trace(tab, trace_v); + } + } else { + lttvwindow_add_trace(tab, trace_v); + } + + gtk_widget_destroy((GtkWidget*)file_selector); + + //update current tab + //update_traceset(mw_data); + + /* Call the updatetraceset hooks */ + + traceset = tab->traceset_info->traceset; + SetTraceset(tab, traceset); + // in expose now call_pending_read_hooks(mw_data); + + //lttvwindow_report_current_time(mw_data,&(tab->current_time)); + break; + case GTK_RESPONSE_REJECT: + case GTK_RESPONSE_CANCEL: + default: + gtk_widget_destroy((GtkWidget*)file_selector); + break; + } +} + +/* remove_trace removes a trace from the current traceset if all viewers in + * the current tab are not interested in the trace. It first displays a + * dialogue, which shows all traces in the current traceset, to let user choose + * a trace, then it checks if all viewers unselect the trace, if it is true, + * it will remove the trace, recreate the traceset_contex, + * and redraws all the viewer of the current tab. If there is on trace in the + * current traceset, it will delete all viewers of the current tab + * + * It destroys the filter tree. FIXME... we should request for an update + * instead. + */ + +void remove_trace(GtkWidget *widget, gpointer user_data) +{ + LttTrace *trace; + LttvTrace * trace_v; + LttvTraceset * traceset; + gint i, j, nb_trace, index=-1; + char ** name, *remove_trace_name; + MainWindow * mw_data = get_window_data_struct(widget); + GtkWidget * notebook = lookup_widget(widget, "MNotebook"); + + GtkWidget *page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), + gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook))); + Tab *tab; + + if(!page) { + return; + } else { + LttvPluginTab *ptab; + ptab = (LttvPluginTab *)g_object_get_data(G_OBJECT(page), "Tab_Plugin"); + tab = ptab->tab; + } + + nb_trace =lttv_traceset_number(tab->traceset_info->traceset); + name = g_new(char*,nb_trace); + for(i = 0; i < nb_trace; i++){ + trace_v = lttv_traceset_get(tab->traceset_info->traceset, i); + trace = lttv_trace(trace_v); + name[i] = g_quark_to_string(ltt_trace_name(trace)); + } + + remove_trace_name = get_remove_trace(mw_data, name, nb_trace); + + + if(remove_trace_name){ + + /* yuk, cut n paste from old code.. should be better (MD)*/ + for(i = 0; itraceset_info->traceset; + //Keep a reference to the traces so they are not freed. + for(j=0; jtraceset_info->traceset_context); + lttv_context_fini(LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context)); + g_object_unref(tab->traceset_info->traceset_context); + + trace_v = lttv_traceset_get(traceset, index); + + lttv_traceset_remove(traceset, index); + lttv_trace_unref(trace_v); // Remove local reference + + if(lttv_trace_get_ref_number(trace_v) <= 1) { + /* ref 1 : lttvwindowtraces only*/ + ltt_trace_close(lttv_trace(trace_v)); + /* lttvwindowtraces_remove_trace takes care of destroying + * the traceset linked with the trace_v and also of destroying + * the trace_v at the same time. + */ + lttvwindowtraces_remove_trace(trace_v); + } + + tab->traceset_info->traceset_context = + g_object_new(LTTV_TRACESET_STATS_TYPE, NULL); + lttv_context_init( + LTTV_TRACESET_CONTEXT(tab-> + traceset_info->traceset_context),traceset); + //add state update hooks + lttv_state_add_event_hooks( + (LttvTracesetState*)tab->traceset_info->traceset_context); + + //Remove local reference to the traces. + for(j=0; jtraceset_info->traceset); + name = g_new(char*,nb_trace); + for(i = 0; i < nb_trace; i++){ + trace_v = lttv_traceset_get(tab->traceset_info->traceset, i); + trace = lttv_trace(trace_v); + name[i] = ltt_trace_name(trace); + } + + remove_trace_name = get_remove_trace(name, nb_trace); + + if(remove_trace_name){ + for(i=0; imultivpaned)); + if(w){ + s = g_object_get_data(G_OBJECT(w), "Traceset_Selector"); + if(s){ + t = lttv_traceset_selector_trace_get(s,i); + lttv_trace_selector_set_selected(t, FALSE); + } + + //check if other viewers select the trace + w = gtk_multivpaned_get_first_widget(GTK_MULTIVPANED(tab->multivpaned)); + while(w){ + s = g_object_get_data(G_OBJECT(w), "Traceset_Selector"); + if(s){ + t = lttv_traceset_selector_trace_get(s,i); + selected = lttv_trace_selector_get_selected(t); + if(selected)break; + } + w = gtk_multivpaned_get_next_widget(GTK_MULTIVPANED(tab->multivpaned)); + } + }else selected = FALSE; + + //if no viewer selects the trace, remove it + if(!selected){ + remove_trace_from_traceset_selector(GTK_MULTIVPANED(tab->multivpaned), i); + + traceset = tab->traceset_info->traceset; + //Keep a reference to the traces so they are not freed. + for(j=0; jtraceset_info->traceset_context); + lttv_context_fini(LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context)); + g_object_unref(tab->traceset_info->traceset_context); + + + trace_v = lttv_traceset_get(traceset, i); + + if(lttv_trace_get_ref_number(trace_v) <= 2) { + /* ref 2 : traceset, local */ + lttvwindowtraces_remove_trace(trace_v); + ltt_trace_close(lttv_trace(trace_v)); + } + + lttv_traceset_remove(traceset, i); + lttv_trace_unref(trace_v); // Remove local reference + + if(!lttv_trace_get_ref_number(trace_v)) + lttv_trace_destroy(trace_v); + + tab->traceset_info->traceset_context = + g_object_new(LTTV_TRACESET_STATS_TYPE, NULL); + lttv_context_init( + LTTV_TRACESET_CONTEXT(tab-> + traceset_info->traceset_context),traceset); + //add state update hooks + lttv_state_add_event_hooks( + (LttvTracesetState*)tab->traceset_info->traceset_context); + + //Remove local reference to the traces. + for(j=0; j 1){ + + SetTraceset(tab, (gpointer)traceset); + // in expose now call_pending_read_hooks(mw_data); + + //lttvwindow_report_current_time(mw_data,&(tab->current_time)); + //}else{ + // if(tab){ + // while(tab->multi_vpaned->num_children){ + // gtk_multi_vpaned_widget_delete(tab->multi_vpaned); + // } + // } + //} + } + break; + } + } + } + + g_free(name); +} +#endif //0 + +/* Redraw all the viewers in the current tab */ +void redraw(GtkWidget *widget, gpointer user_data) +{ + GtkWidget * notebook = lookup_widget(widget, "MNotebook"); + GtkWidget *page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), + gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook))); + Tab *tab; + if(!page) { + return; + } else { + LttvPluginTab *ptab; + ptab = (LttvPluginTab *)g_object_get_data(G_OBJECT(page), "Tab_Plugin"); + tab = ptab->tab; + } + + LttvHooks * tmp; + LttvAttributeValue value; + + g_assert(lttv_iattribute_find_by_path(tab->attributes, "hooks/redraw", LTTV_POINTER, &value)); + + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp != NULL) + lttv_hooks_call(tmp,NULL); +} + + +void continue_processing(GtkWidget *widget, gpointer user_data) +{ + GtkWidget * notebook = lookup_widget(widget, "MNotebook"); + GtkWidget *page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), + gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook))); + Tab *tab; + if(!page) { + return; + } else { + LttvPluginTab *ptab; + ptab = (LttvPluginTab *)g_object_get_data(G_OBJECT(page), "Tab_Plugin"); + tab = ptab->tab; + } + + LttvHooks * tmp; + LttvAttributeValue value; + + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/continue", LTTV_POINTER, &value)); + + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp != NULL) + lttv_hooks_call(tmp,NULL); +} + +/* Stop the processing for the calling main window's current tab. + * It removes every processing requests that are in its list. It does not call + * the end request hooks, because the request is not finished. + */ + +void stop_processing(GtkWidget *widget, gpointer user_data) +{ + GtkWidget * notebook = lookup_widget(widget, "MNotebook"); + GtkWidget *page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), + gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook))); + Tab *tab; + if(!page) { + return; + } else { + LttvPluginTab *ptab; + ptab = (LttvPluginTab *)g_object_get_data(G_OBJECT(page), "Tab_Plugin"); + tab = ptab->tab; + } + GSList *iter = tab->events_requests; + + while(iter != NULL) { + GSList *remove_iter = iter; + iter = g_slist_next(iter); + + g_free(remove_iter->data); + tab->events_requests = + g_slist_remove_link(tab->events_requests, remove_iter); + } + tab->events_request_pending = FALSE; + tab->stop_foreground = TRUE; + g_idle_remove_by_data(tab); + g_assert(g_slist_length(tab->events_requests) == 0); +} + + +/* save will save the traceset to a file + * Not implemented yet FIXME + */ + +void save(GtkWidget * widget, gpointer user_data) +{ + g_info("Save\n"); +} + +void save_as(GtkWidget * widget, gpointer user_data) +{ + g_info("Save as\n"); +} + + +/* zoom will change the time_window of all the viewers of the + * current tab, and redisplay them. The main functionality is to + * determine the new time_window of the current tab + */ + +void zoom(GtkWidget * widget, double size) +{ + TimeInterval time_span; + TimeWindow new_time_window; + LttTime current_time, time_delta; + MainWindow * mw_data = get_window_data_struct(widget); + LttvTracesetContext *tsc; + GtkWidget * notebook = lookup_widget(widget, "MNotebook"); + + GtkWidget *page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), + gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook))); + Tab *tab; + + if(!page) { + return; + } else { + LttvPluginTab *ptab; + ptab = (LttvPluginTab *)g_object_get_data(G_OBJECT(page), "Tab_Plugin"); + tab = ptab->tab; + } + + if(size == 1) return; + + tsc = LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context); + time_span = tsc->time_span; + new_time_window = tab->time_window; + current_time = tab->current_time; + + time_delta = ltt_time_sub(time_span.end_time,time_span.start_time); + if(size == 0){ + new_time_window.start_time = time_span.start_time; + new_time_window.time_width = time_delta; + new_time_window.time_width_double = ltt_time_to_double(time_delta); + new_time_window.end_time = ltt_time_add(new_time_window.start_time, + new_time_window.time_width) ; + }else{ + new_time_window.time_width = ltt_time_div(new_time_window.time_width, size); + new_time_window.time_width_double = + ltt_time_to_double(new_time_window.time_width); + if(ltt_time_compare(new_time_window.time_width,time_delta) > 0) + { /* Case where zoom out is bigger than trace length */ + new_time_window.start_time = time_span.start_time; + new_time_window.time_width = time_delta; + new_time_window.time_width_double = ltt_time_to_double(time_delta); + new_time_window.end_time = ltt_time_add(new_time_window.start_time, + new_time_window.time_width) ; + } + else + { + /* Center the image on the current time */ + new_time_window.start_time = + ltt_time_sub(current_time, + ltt_time_from_double(new_time_window.time_width_double/2.0)); + new_time_window.end_time = ltt_time_add(new_time_window.start_time, + new_time_window.time_width) ; + /* If on borders, don't fall off */ + if(ltt_time_compare(new_time_window.start_time, time_span.start_time) <0 + || ltt_time_compare(new_time_window.start_time, time_span.end_time) >0) + { + new_time_window.start_time = time_span.start_time; + new_time_window.end_time = ltt_time_add(new_time_window.start_time, + new_time_window.time_width) ; + } + else + { + if(ltt_time_compare(new_time_window.end_time, + time_span.end_time) > 0 + || ltt_time_compare(new_time_window.end_time, + time_span.start_time) < 0) + { + new_time_window.start_time = + ltt_time_sub(time_span.end_time, new_time_window.time_width); + + new_time_window.end_time = ltt_time_add(new_time_window.start_time, + new_time_window.time_width) ; + } + } + + } + } + + if(ltt_time_compare(new_time_window.time_width, ltt_time_zero) == 0) { + g_warning("Zoom more than 1 ns impossible"); + } else { + time_change_manager(tab, new_time_window); + } +} + +void zoom_in(GtkWidget * widget, gpointer user_data) +{ + zoom(widget, 2); +} + +void zoom_out(GtkWidget * widget, gpointer user_data) +{ + zoom(widget, 0.5); +} + +void zoom_extended(GtkWidget * widget, gpointer user_data) +{ + zoom(widget, 0); +} + +void go_to_time(GtkWidget * widget, gpointer user_data) +{ + g_info("Go to time\n"); +} + +void show_time_frame(GtkWidget * widget, gpointer user_data) +{ + g_info("Show time frame\n"); +} + + +/* callback function */ + +void +on_empty_traceset_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + create_new_window((GtkWidget*)menuitem, user_data, FALSE); +} + + +void +on_clone_traceset_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + create_new_window((GtkWidget*)menuitem, user_data, TRUE); +} + + +/* create_new_tab calls create_tab to construct a new tab in the main window + */ + +LttvPluginTab *create_new_tab(GtkWidget* widget, gpointer user_data) +{ + gchar label[PATH_MAX]; + MainWindow * mw_data = get_window_data_struct(widget); + + GtkNotebook * notebook = (GtkNotebook *)lookup_widget(widget, "MNotebook"); + if(notebook == NULL){ + g_info("Notebook does not exist\n"); + return NULL; + } + GtkWidget *page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), + gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook))); + Tab *copy_tab; + + if(!page) { + copy_tab = NULL; + } else { + LttvPluginTab *ptab; + ptab = (LttvPluginTab *)g_object_get_data(G_OBJECT(page), "Tab_Plugin"); + copy_tab = ptab->tab; + } + + strcpy(label,"Page"); + if(get_label(mw_data, label,"Get the name of the tab","Please input tab's name")) { + LttvPluginTab *ptab; + + ptab = g_object_new(LTTV_TYPE_PLUGIN_TAB, NULL); + init_tab (ptab->tab, mw_data, copy_tab, notebook, label); + ptab->parent.top_widget = ptab->tab->top_widget; + g_object_set_data_full( + G_OBJECT(ptab->tab->vbox), + "Tab_Plugin", + ptab, + (GDestroyNotify)tab_destructor); + return ptab; + } + else return NULL; +} + +void +on_tab_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + create_new_tab((GtkWidget*)menuitem, user_data); +} + + +void +on_open_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + open_traceset((GtkWidget*)menuitem, user_data); +} + + +void +on_close_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + MainWindow * mw_data = get_window_data_struct((GtkWidget*)menuitem); + main_window_destructor(mw_data); +} + + +/* remove the current tab from the main window + */ + +void +on_close_tab_activate (GtkWidget *widget, + gpointer user_data) +{ + gint page_num; + GtkWidget * notebook; + GtkWidget * page; + MainWindow * mw_data = get_window_data_struct(widget); + notebook = lookup_widget(widget, "MNotebook"); + if(notebook == NULL){ + g_info("Notebook does not exist\n"); + return; + } + + page_num = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)); + + gtk_notebook_remove_page(GTK_NOTEBOOK(notebook), page_num); + +} + +void +on_close_tab_X_clicked (GtkWidget *widget, + gpointer user_data) +{ + gint page_num; + GtkWidget *notebook = lookup_widget(widget, "MNotebook"); + if(notebook == NULL){ + g_info("Notebook does not exist\n"); + return; + } + + if((page_num = gtk_notebook_page_num(GTK_NOTEBOOK(notebook), widget)) != -1) + gtk_notebook_remove_page(GTK_NOTEBOOK(notebook), page_num); + +} + + +void +on_add_trace_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + add_trace((GtkWidget*)menuitem, user_data); +} + + +void +on_remove_trace_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + remove_trace((GtkWidget*)menuitem, user_data); +} + + +void +on_save_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + save((GtkWidget*)menuitem, user_data); +} + + +void +on_save_as_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + save_as((GtkWidget*)menuitem, user_data); +} + + +void +on_quit_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + mainwindow_quit(); +} + + +void +on_cut_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + g_info("Cut\n"); +} + + +void +on_copy_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + g_info("Copye\n"); +} + + +void +on_paste_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + g_info("Paste\n"); +} + + +void +on_delete_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + g_info("Delete\n"); +} + + +void +on_zoom_in_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + zoom_in((GtkWidget*)menuitem, user_data); +} + + +void +on_zoom_out_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + zoom_out((GtkWidget*)menuitem, user_data); +} + + +void +on_zoom_extended_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + zoom_extended((GtkWidget*)menuitem, user_data); +} + + +void +on_go_to_time_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + go_to_time((GtkWidget*)menuitem, user_data); +} + + +void +on_show_time_frame_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + show_time_frame((GtkWidget*)menuitem, user_data); +} + + +void +on_move_viewer_up_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + move_up_viewer((GtkWidget*)menuitem, user_data); +} + + +void +on_move_viewer_down_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + move_down_viewer((GtkWidget*)menuitem, user_data); +} + + +void +on_remove_viewer_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + delete_viewer((GtkWidget*)menuitem, user_data); +} + +void +on_trace_facility_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + g_info("Trace facility selector: %s\n"); +} + + +/* Dispaly a file selection dialogue to let user select a library, then call + * lttv_library_load(). + */ + +void +on_load_library_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + GError *error = NULL; + MainWindow * mw_data = get_window_data_struct((GtkWidget*)menuitem); + + gchar load_module_path_alter[PATH_MAX]; + { + GPtrArray *name; + guint nb,i; + gchar *load_module_path; + name = g_ptr_array_new(); + nb = lttv_library_path_number(); + /* ask for the library path */ + + for(i=0;ipdata), name->len, + "Select a library path", "Library paths"); + if(load_module_path != NULL) + strncpy(load_module_path_alter, load_module_path, PATH_MAX-1); // -1 for / + + g_ptr_array_free(name, TRUE); + + if(load_module_path == NULL) return; + } + + { + /* Make sure the module path ends with a / */ + gchar *ptr = load_module_path_alter; + + ptr = strchr(ptr, '\0'); + + if(*(ptr-1) != '/') { + *ptr = '/'; + *(ptr+1) = '\0'; + } + } + + { + /* Ask for the library to load : list files in the previously selected + * directory */ + gchar str[PATH_MAX]; + gchar ** dir; + gint id; + GtkFileSelection * file_selector = + (GtkFileSelection *)gtk_file_selection_new("Select a module"); + gtk_file_selection_set_filename(file_selector, load_module_path_alter); + gtk_file_selection_hide_fileop_buttons(file_selector); + + gtk_window_set_transient_for(GTK_WINDOW(file_selector), + GTK_WINDOW(mw_data->mwindow)); + + str[0] = '\0'; + id = gtk_dialog_run(GTK_DIALOG(file_selector)); + switch(id){ + case GTK_RESPONSE_ACCEPT: + case GTK_RESPONSE_OK: + dir = gtk_file_selection_get_selections (file_selector); + strncpy(str,dir[0],PATH_MAX); + strncpy(remember_plugins_dir,dir[0],PATH_MAX); + /* only keep file name */ + gchar *str1; + str1 = strrchr(str,'/'); + if(str1)str1++; + else{ + str1 = strrchr(str,'\\'); + str1++; + } +#if 0 + /* remove "lib" */ + if(*str1 == 'l' && *(str1+1)== 'i' && *(str1+2)=='b') + str1=str1+3; + remove info after . */ + { + gchar *str2 = str1; + + str2 = strrchr(str2, '.'); + if(str2 != NULL) *str2 = '\0'; + } + lttv_module_require(str1, &error); +#endif //0 + lttv_library_load(str1, &error); + if(error != NULL) g_warning("%s", error->message); + else g_info("Load library: %s\n", str); + g_strfreev(dir); + case GTK_RESPONSE_REJECT: + case GTK_RESPONSE_CANCEL: + default: + gtk_widget_destroy((GtkWidget*)file_selector); + break; + } + + } + + + +} + + +/* Display all loaded modules, let user to select a module to unload + * by calling lttv_module_unload + */ + +void +on_unload_library_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + MainWindow * mw_data = get_window_data_struct((GtkWidget*)menuitem); + + LttvLibrary *library = NULL; + + GPtrArray *name; + guint nb,i; + gchar *lib_name; + name = g_ptr_array_new(); + nb = lttv_library_number(); + LttvLibraryInfo *lib_info = g_new(LttvLibraryInfo,nb); + /* ask for the library name */ + + for(i=0;ipdata), name->len, + "Select a library", "Libraries"); + if(lib_name != NULL) { + for(i=0;ipdata), name->len, + "Select a library", "Libraries"); + if(lib_name != NULL) { + for(i=0;ipdata), name->len, + "Select a module", "Modules"); + if(module_name != NULL) { + for(i=0;imessage); + else g_info("Load module: %s", module_name_out); + + +#if 0 + { + + + gchar str[PATH_MAX]; + gchar ** dir; + gint id; + GtkFileSelection * file_selector = + (GtkFileSelection *)gtk_file_selection_new("Select a module"); + gtk_file_selection_set_filename(file_selector, load_module_path_alter); + gtk_file_selection_hide_fileop_buttons(file_selector); + + str[0] = '\0'; + id = gtk_dialog_run(GTK_DIALOG(file_selector)); + switch(id){ + case GTK_RESPONSE_ACCEPT: + case GTK_RESPONSE_OK: + dir = gtk_file_selection_get_selections (file_selector); + strncpy(str,dir[0],PATH_MAX); + strncpy(remember_plugins_dir,dir[0],PATH_MAX); + { + /* only keep file name */ + gchar *str1; + str1 = strrchr(str,'/'); + if(str1)str1++; + else{ + str1 = strrchr(str,'\\'); + str1++; + } +#if 0 + /* remove "lib" */ + if(*str1 == 'l' && *(str1+1)== 'i' && *(str1+2)=='b') + str1=str1+3; + remove info after . */ + { + gchar *str2 = str1; + + str2 = strrchr(str2, '.'); + if(str2 != NULL) *str2 = '\0'; + } + lttv_module_require(str1, &error); +#endif //0 + lttv_library_load(str1, &error); + if(error != NULL) g_warning(error->message); + else g_info("Load library: %s\n", str); + g_strfreev(dir); + case GTK_RESPONSE_REJECT: + case GTK_RESPONSE_CANCEL: + default: + gtk_widget_destroy((GtkWidget*)file_selector); + break; + } + + } +#endif //0 + + +} + + + +/* Display all loaded modules, let user to select a module to unload + * by calling lttv_module_unload + */ + +void +on_unload_module_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + GError *error = NULL; + MainWindow * mw_data = get_window_data_struct((GtkWidget*)menuitem); + + LttvLibrary *library; + { + GPtrArray *name; + guint nb,i; + gchar *lib_name; + name = g_ptr_array_new(); + nb = lttv_library_number(); + LttvLibraryInfo *lib_info = g_new(LttvLibraryInfo,nb); + /* ask for the library name */ + + for(i=0;ipdata), name->len, + "Select a library", "Libraries"); + if(lib_name != NULL) { + for(i=0;i 0) g_ptr_array_add(name, path); + } + module_name = get_selection(mw_data, (char **)(name->pdata), name->len, + "Select a module", "Modules"); + if(module_name != NULL) { + for(i=0;ifile_list->parent) ; + + gtk_window_set_transient_for(GTK_WINDOW(file_selector), + GTK_WINDOW(mw_data->mwindow)); + + const char * dir; + gint id; + + if(remember_plugins_dir[0] != '\0') + gtk_file_selection_set_filename(file_selector, remember_plugins_dir); + + id = gtk_dialog_run(GTK_DIALOG(file_selector)); + switch(id){ + case GTK_RESPONSE_ACCEPT: + case GTK_RESPONSE_OK: + dir = gtk_file_selection_get_filename (file_selector); + strncpy(remember_plugins_dir,dir,PATH_MAX); + strncat(remember_plugins_dir,"/",PATH_MAX); + lttv_library_path_add(dir); + case GTK_RESPONSE_REJECT: + case GTK_RESPONSE_CANCEL: + default: + gtk_widget_destroy((GtkWidget*)file_selector); + break; + } +} + + +/* Display a directory dialogue to let user select a path for library searching + */ + +void +on_remove_library_search_path_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + MainWindow * mw_data = get_window_data_struct((GtkWidget*)menuitem); + + const char *lib_path; + { + GPtrArray *name; + guint nb,i; + gchar *lib_name; + name = g_ptr_array_new(); + nb = lttv_library_path_number(); + /* ask for the library name */ + + for(i=0;ipdata), name->len, + "Select a library path", "Library paths"); + + g_ptr_array_free(name, TRUE); + + if(lib_path == NULL) return; + } + + lttv_library_path_remove(lib_path); +} + +void +on_color_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + g_info("Color\n"); +} + + +void +on_save_configuration_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + g_info("Save configuration\n"); +} + + +void +on_content_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + g_info("Content\n"); +} + + +static void +on_about_close_activate (GtkButton *button, + gpointer user_data) +{ + GtkWidget *about_widget = GTK_WIDGET(user_data); + + gtk_widget_destroy(about_widget); +} + +void +on_about_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + MainWindow *main_window = get_window_data_struct(GTK_WIDGET(menuitem)); + GtkWidget *window_widget = main_window->mwindow; + GtkWidget *about_widget = gtk_window_new(GTK_WINDOW_TOPLEVEL); + GtkWindow *about_window = GTK_WINDOW(about_widget); + gint window_width, window_height; + + gtk_window_set_title(about_window, "About Linux Trace Toolkit"); + + gtk_window_set_resizable(about_window, FALSE); + gtk_window_set_transient_for(about_window, GTK_WINDOW(window_widget)); + gtk_window_set_destroy_with_parent(about_window, TRUE); + gtk_window_set_modal(about_window, FALSE); + + /* Put the about window at the center of the screen */ + gtk_window_get_size(about_window, &window_width, &window_height); + gtk_window_move (about_window, + (gdk_screen_width() - window_width)/2, + (gdk_screen_height() - window_height)/2); + + GtkWidget *vbox = gtk_vbox_new(FALSE, 1); + + gtk_container_add(GTK_CONTAINER(about_widget), vbox); + + + /* Text to show */ + GtkWidget *label1 = gtk_label_new(""); + gtk_misc_set_padding(GTK_MISC(label1), 10, 20); + gtk_label_set_markup(GTK_LABEL(label1), "\ +Linux Trace Toolkit " VERSION ""); + gtk_label_set_justify(GTK_LABEL(label1), GTK_JUSTIFY_CENTER); + + GtkWidget *label2 = gtk_label_new(""); + gtk_misc_set_padding(GTK_MISC(label2), 10, 20); + gtk_label_set_markup(GTK_LABEL(label2), "\ +Contributors :\n\ +\n\ +Michel Dagenais (New trace format, lttv main)\n\ +Mathieu Desnoyers (Kernel Tracer, Directory structure, build with automake/conf,\n\ + lttv gui, control flow view, gui cooperative trace reading\n\ + scheduler with interruptible foreground and background\n\ + computation, detailed event list (rewrite), trace reading\n\ + library (rewrite))\n\ +Benoit Des Ligneris, Eric Clement (Cluster adaptation, work in progress)\n\ +Xang-Xiu Yang (new trace reading library and converter, lttv gui, \n\ + detailed event list and statistics view)\n\ +Tom Zanussi (RelayFS)\n\ +\n\ +Inspired from the original Linux Trace Toolkit Visualizer made by\n\ +Karim Yaghmour"); + + GtkWidget *label3 = gtk_label_new(""); + gtk_label_set_markup(GTK_LABEL(label3), "\ +Linux Trace Toolkit Viewer, Copyright (C) 2004, 2005, 2006\n\ + Michel Dagenais\n\ + Mathieu Desnoyers\n\ + Xang-Xiu Yang\n\ +Linux Trace Toolkit comes with ABSOLUTELY NO WARRANTY.\n\ +This is free software, and you are welcome to redistribute it\n\ +under certain conditions. See COPYING for details."); + gtk_misc_set_padding(GTK_MISC(label3), 10, 20); + + gtk_box_pack_start_defaults(GTK_BOX(vbox), label1); + gtk_box_pack_start_defaults(GTK_BOX(vbox), label2); + gtk_box_pack_start_defaults(GTK_BOX(vbox), label3); + + GtkWidget *hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + GtkWidget *close_button = gtk_button_new_with_mnemonic("_Close"); + gtk_box_pack_end(GTK_BOX(hbox), close_button, FALSE, FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(close_button), 20); + + g_signal_connect(G_OBJECT(close_button), "clicked", + G_CALLBACK(on_about_close_activate), + (gpointer)about_widget); + + gtk_widget_show_all(about_widget); +} + + +void +on_button_new_clicked (GtkButton *button, + gpointer user_data) +{ + create_new_window((GtkWidget*)button, user_data, TRUE); +} + +void +on_button_new_tab_clicked (GtkButton *button, + gpointer user_data) +{ + create_new_tab((GtkWidget*)button, user_data); +} + +void +on_button_open_clicked (GtkButton *button, + gpointer user_data) +{ + open_traceset((GtkWidget*)button, user_data); +} + + +void +on_button_add_trace_clicked (GtkButton *button, + gpointer user_data) +{ + add_trace((GtkWidget*)button, user_data); +} + + +void +on_button_remove_trace_clicked (GtkButton *button, + gpointer user_data) +{ + remove_trace((GtkWidget*)button, user_data); +} + +void +on_button_redraw_clicked (GtkButton *button, + gpointer user_data) +{ + redraw((GtkWidget*)button, user_data); +} + +void +on_button_continue_processing_clicked (GtkButton *button, + gpointer user_data) +{ + continue_processing((GtkWidget*)button, user_data); +} + +void +on_button_stop_processing_clicked (GtkButton *button, + gpointer user_data) +{ + stop_processing((GtkWidget*)button, user_data); +} + + + +void +on_button_save_clicked (GtkButton *button, + gpointer user_data) +{ + save((GtkWidget*)button, user_data); +} + + +void +on_button_save_as_clicked (GtkButton *button, + gpointer user_data) +{ + save_as((GtkWidget*)button, user_data); +} + + +void +on_button_zoom_in_clicked (GtkButton *button, + gpointer user_data) +{ + zoom_in((GtkWidget*)button, user_data); +} + + +void +on_button_zoom_out_clicked (GtkButton *button, + gpointer user_data) +{ + zoom_out((GtkWidget*)button, user_data); +} + + +void +on_button_zoom_extended_clicked (GtkButton *button, + gpointer user_data) +{ + zoom_extended((GtkWidget*)button, user_data); +} + + +void +on_button_go_to_time_clicked (GtkButton *button, + gpointer user_data) +{ + go_to_time((GtkWidget*)button, user_data); +} + + +void +on_button_show_time_frame_clicked (GtkButton *button, + gpointer user_data) +{ + show_time_frame((GtkWidget*)button, user_data); +} + + +void +on_button_move_up_clicked (GtkButton *button, + gpointer user_data) +{ + move_up_viewer((GtkWidget*)button, user_data); +} + + +void +on_button_move_down_clicked (GtkButton *button, + gpointer user_data) +{ + move_down_viewer((GtkWidget*)button, user_data); +} + + +void +on_button_delete_viewer_clicked (GtkButton *button, + gpointer user_data) +{ + delete_viewer((GtkWidget*)button, user_data); +} + +void +on_MWindow_destroy (GtkWidget *widget, + gpointer user_data) +{ + MainWindow *main_window = get_window_data_struct(widget); + LttvIAttribute *attributes = main_window->attributes; + LttvAttributeValue value; + + //This is unnecessary, since widgets will be destroyed + //by the main window widget anyway. + //remove_all_menu_toolbar_constructors(main_window, NULL); + + g_assert(lttv_iattribute_find_by_path(attributes, + "viewers/menu", LTTV_POINTER, &value)); + lttv_menus_destroy((LttvMenus*)*(value.v_pointer)); + + g_assert(lttv_iattribute_find_by_path(attributes, + "viewers/toolbar", LTTV_POINTER, &value)); + lttv_toolbars_destroy((LttvToolbars*)*(value.v_pointer)); + + g_object_unref(main_window->attributes); + g_main_window_list = g_slist_remove(g_main_window_list, main_window); + + g_info("There are now : %d windows\n",g_slist_length(g_main_window_list)); + if(g_slist_length(g_main_window_list) == 0) + mainwindow_quit(); +} + +gboolean +on_MWindow_configure (GtkWidget *widget, + GdkEventConfigure *event, + gpointer user_data) +{ + MainWindow * mw_data = get_window_data_struct((GtkWidget*)widget); + + // MD : removed time width modification upon resizing of the main window. + // The viewers will redraw themselves completely, without time interval + // modification. +/* while(tab){ + if(mw_data->window_width){ + time_span = LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context)->Time_Span ; + time_win = tab->time_window; + ratio = width / mw_data->window_width; + tab->time_window.time_width = ltt_time_mul(time_win.time_width,ratio); + time = ltt_time_sub(time_span->endTime, time_win.start_time); + if(ltt_time_compare(time, tab->time_window.time_width) < 0){ + tab->time_window.time_width = time; + } + } + tab = tab->next; + } + + mw_data->window_width = (int)width; + */ + return FALSE; +} + +/* Set current tab + */ + +void +on_MNotebook_switch_page (GtkNotebook *notebook, + GtkNotebookPage *page, + guint page_num, + gpointer user_data) +{ + +} + + +void time_change_manager (Tab *tab, + TimeWindow new_time_window) +{ + /* Only one source of time change */ + if(tab->time_manager_lock == TRUE) return; + + tab->time_manager_lock = TRUE; + + LttvTracesetContext *tsc = LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context); + TimeInterval time_span = tsc->time_span; + LttTime start_time = new_time_window.start_time; + LttTime end_time = new_time_window.end_time; + LttTime time_width = new_time_window.time_width; + + g_assert(ltt_time_compare(start_time, end_time) < 0); + + /* Set scrollbar */ + GtkAdjustment *adjustment = gtk_range_get_adjustment(GTK_RANGE(tab->scrollbar)); + LttTime upper = ltt_time_sub(time_span.end_time, time_span.start_time); +#if 0 + gtk_range_set_increments(GTK_RANGE(tab->scrollbar), + ltt_time_to_double(new_time_window.time_width) + / SCROLL_STEP_PER_PAGE + * NANOSECONDS_PER_SECOND, /* step increment */ + ltt_time_to_double(new_time_window.time_width) + * NANOSECONDS_PER_SECOND); /* page increment */ + gtk_range_set_range(GTK_RANGE(tab->scrollbar), + 0.0, /* lower */ + ltt_time_to_double(upper) + * NANOSECONDS_PER_SECOND); /* upper */ +#endif //0 + g_object_set(G_OBJECT(adjustment), + "lower", + 0.0, /* lower */ + "upper", + ltt_time_to_double(upper), /* upper */ + "step_increment", + new_time_window.time_width_double + / SCROLL_STEP_PER_PAGE, /* step increment */ + "page_increment", + new_time_window.time_width_double, + /* page increment */ + "page_size", + new_time_window.time_width_double, /* page size */ + NULL); + gtk_adjustment_changed(adjustment); + + // g_object_set(G_OBJECT(adjustment), + // "value", + // ltt_time_to_double( + // ltt_time_sub(start_time, time_span.start_time)) + // , /* value */ + // NULL); + //gtk_adjustment_value_changed(adjustment); + gtk_range_set_value(GTK_RANGE(tab->scrollbar), + ltt_time_to_double( + ltt_time_sub(start_time, time_span.start_time)) /* value */); + + /* set the time bar. */ + /* start seconds */ + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry1), + (double)time_span.start_time.tv_sec, + (double)time_span.end_time.tv_sec); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(tab->MEntry1), + (double)start_time.tv_sec); + + /* start nanoseconds */ + if(start_time.tv_sec == time_span.start_time.tv_sec) { + /* can be both beginning and end at the same time. */ + if(start_time.tv_sec == time_span.end_time.tv_sec) { + /* If we are at the end, max nsec to end.. -1 (not zero length) */ + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry2), + (double)time_span.start_time.tv_nsec, + (double)time_span.end_time.tv_nsec-1); + } else { + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry2), + (double)time_span.start_time.tv_nsec, + (double)NANOSECONDS_PER_SECOND-1); + } + } else if(start_time.tv_sec == time_span.end_time.tv_sec) { + /* If we are at the end, max nsec to end.. -1 (not zero length) */ + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry2), + 0.0, + (double)time_span.end_time.tv_nsec-1); + } else /* anywhere else */ + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry2), + 0.0, + (double)NANOSECONDS_PER_SECOND-1); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(tab->MEntry2), + (double)start_time.tv_nsec); + + /* end seconds */ + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry3), + (double)time_span.start_time.tv_sec, + (double)time_span.end_time.tv_sec); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(tab->MEntry3), + (double)end_time.tv_sec); + + /* end nanoseconds */ + if(end_time.tv_sec == time_span.start_time.tv_sec) { + /* can be both beginning and end at the same time. */ + if(end_time.tv_sec == time_span.end_time.tv_sec) { + /* If we are at the end, max nsec to end.. */ + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry4), + (double)time_span.start_time.tv_nsec+1, + (double)time_span.end_time.tv_nsec); + } else { + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry4), + (double)time_span.start_time.tv_nsec+1, + (double)NANOSECONDS_PER_SECOND-1); + } + } + else if(end_time.tv_sec == time_span.end_time.tv_sec) { + /* If we are at the end, max nsec to end.. */ + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry4), + 0.0, + (double)time_span.end_time.tv_nsec); + } + else /* anywhere else */ + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry4), + 0.0, + (double)NANOSECONDS_PER_SECOND-1); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(tab->MEntry4), + (double)end_time.tv_nsec); + + /* width seconds */ + if(time_width.tv_nsec == 0) { + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry7), + (double)1, + (double)upper.tv_sec); + } else { + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry7), + (double)0, + (double)upper.tv_sec); + } + gtk_spin_button_set_value(GTK_SPIN_BUTTON(tab->MEntry7), + (double)time_width.tv_sec); + + /* width nanoseconds */ + if(time_width.tv_sec == upper.tv_sec) { + if(time_width.tv_sec == 0) { + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry8), + (double)1, + (double)upper.tv_nsec); + } else { + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry8), + (double)0, + (double)upper.tv_nsec); + } + } + else if(time_width.tv_sec == 0) { + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry8), + 1.0, + (double)upper.tv_nsec); + } + else /* anywhere else */ + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry8), + 0.0, + (double)NANOSECONDS_PER_SECOND-1); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(tab->MEntry8), + (double)time_width.tv_nsec); + + /* call viewer hooks for new time window */ + set_time_window(tab, &new_time_window); + + tab->time_manager_lock = FALSE; +} + + +/* value changed for frame start s + * + * Check time span : if ns is out of range, clip it the nearest good value. + */ +void +on_MEntry1_value_changed (GtkSpinButton *spinbutton, + gpointer user_data) +{ + Tab *tab =(Tab *)user_data; + LttvTracesetContext * tsc = + LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context); + TimeInterval time_span = tsc->time_span; + gint value = gtk_spin_button_get_value_as_int(spinbutton); + + TimeWindow new_time_window = tab->time_window; + + LttTime end_time = new_time_window.end_time; + + new_time_window.start_time.tv_sec = value; + + /* start nanoseconds */ + if(new_time_window.start_time.tv_sec == time_span.start_time.tv_sec) { + if(new_time_window.start_time.tv_sec == time_span.end_time.tv_sec) { + if(new_time_window.start_time.tv_nsec > time_span.end_time.tv_nsec) + new_time_window.start_time.tv_nsec = time_span.end_time.tv_nsec-1; + if(new_time_window.start_time.tv_nsec < time_span.start_time.tv_nsec) + new_time_window.start_time.tv_nsec = time_span.start_time.tv_nsec; + } else { + if(new_time_window.start_time.tv_nsec < time_span.start_time.tv_nsec) + new_time_window.start_time.tv_nsec = time_span.start_time.tv_nsec; + } + } + else if(new_time_window.start_time.tv_sec == time_span.end_time.tv_sec) { + if(new_time_window.start_time.tv_nsec > time_span.end_time.tv_nsec) + new_time_window.start_time.tv_nsec = time_span.end_time.tv_nsec-1; + } + + if(ltt_time_compare(new_time_window.start_time, end_time) >= 0) { + /* Then, we must push back end time : keep the same time width + * if possible, else end traceset time */ + end_time = LTT_TIME_MIN(ltt_time_add(new_time_window.start_time, + new_time_window.time_width), + time_span.end_time); + } + + /* Fix the time width to fit start time and end time */ + new_time_window.time_width = ltt_time_sub(end_time, + new_time_window.start_time); + new_time_window.time_width_double = + ltt_time_to_double(new_time_window.time_width); + + new_time_window.end_time = end_time; + + time_change_manager(tab, new_time_window); + +} + +void +on_MEntry2_value_changed (GtkSpinButton *spinbutton, + gpointer user_data) +{ + Tab *tab =(Tab *)user_data; + LttvTracesetContext * tsc = + LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context); + TimeInterval time_span = tsc->time_span; + gint value = gtk_spin_button_get_value_as_int(spinbutton); + + TimeWindow new_time_window = tab->time_window; + + LttTime end_time = new_time_window.end_time; + + new_time_window.start_time.tv_nsec = value; + + if(ltt_time_compare(new_time_window.start_time, end_time) >= 0) { + /* Then, we must push back end time : keep the same time width + * if possible, else end traceset time */ + end_time = LTT_TIME_MIN(ltt_time_add(new_time_window.start_time, + new_time_window.time_width), + time_span.end_time); + } + + /* Fix the time width to fit start time and end time */ + new_time_window.time_width = ltt_time_sub(end_time, + new_time_window.start_time); + new_time_window.time_width_double = + ltt_time_to_double(new_time_window.time_width); + + new_time_window.end_time = end_time; + + time_change_manager(tab, new_time_window); + +} + +void +on_MEntry3_value_changed (GtkSpinButton *spinbutton, + gpointer user_data) +{ + Tab *tab =(Tab *)user_data; + LttvTracesetContext * tsc = + LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context); + TimeInterval time_span = tsc->time_span; + gint value = gtk_spin_button_get_value_as_int(spinbutton); + + TimeWindow new_time_window = tab->time_window; + + LttTime end_time = new_time_window.end_time; + + end_time.tv_sec = value; + + /* end nanoseconds */ + if(end_time.tv_sec == time_span.start_time.tv_sec) { + if(end_time.tv_sec == time_span.end_time.tv_sec) { + if(end_time.tv_nsec > time_span.end_time.tv_nsec) + end_time.tv_nsec = time_span.end_time.tv_nsec; + if(end_time.tv_nsec < time_span.start_time.tv_nsec) + end_time.tv_nsec = time_span.start_time.tv_nsec+1; + } else { + if(end_time.tv_nsec < time_span.start_time.tv_nsec) + end_time.tv_nsec = time_span.start_time.tv_nsec+1; + } + } + else if(end_time.tv_sec == time_span.end_time.tv_sec) { + if(end_time.tv_nsec > time_span.end_time.tv_nsec) + end_time.tv_nsec = time_span.end_time.tv_nsec; + } + + if(ltt_time_compare(new_time_window.start_time, end_time) >= 0) { + /* Then, we must push front start time : keep the same time width + * if possible, else end traceset time */ + new_time_window.start_time = LTT_TIME_MAX( + ltt_time_sub(end_time, + new_time_window.time_width), + time_span.start_time); + } + + /* Fix the time width to fit start time and end time */ + new_time_window.time_width = ltt_time_sub(end_time, + new_time_window.start_time); + new_time_window.time_width_double = + ltt_time_to_double(new_time_window.time_width); + + new_time_window.end_time = end_time; + + time_change_manager(tab, new_time_window); + +} + +void +on_MEntry4_value_changed (GtkSpinButton *spinbutton, + gpointer user_data) +{ + Tab *tab =(Tab *)user_data; + LttvTracesetContext * tsc = + LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context); + TimeInterval time_span = tsc->time_span; + gint value = gtk_spin_button_get_value_as_int(spinbutton); + + TimeWindow new_time_window = tab->time_window; + + LttTime end_time = new_time_window.end_time; + + end_time.tv_nsec = value; + + if(ltt_time_compare(new_time_window.start_time, end_time) >= 0) { + /* Then, we must push front start time : keep the same time width + * if possible, else end traceset time */ + new_time_window.start_time = LTT_TIME_MAX( + ltt_time_sub(end_time, + new_time_window.time_width), + time_span.start_time); + } + + /* Fix the time width to fit start time and end time */ + new_time_window.time_width = ltt_time_sub(end_time, + new_time_window.start_time); + new_time_window.time_width_double = + ltt_time_to_double(new_time_window.time_width); + new_time_window.end_time = end_time; + + time_change_manager(tab, new_time_window); + +} + +/* value changed for time frame interval s + * + * Check time span : if ns is out of range, clip it the nearest good value. + */ +void +on_MEntry7_value_changed (GtkSpinButton *spinbutton, + gpointer user_data) +{ + Tab *tab =(Tab *)user_data; + LttvTracesetContext * tsc = + LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context); + TimeInterval time_span = tsc->time_span; + gint value = gtk_spin_button_get_value_as_int(spinbutton); + LttTime current_time, time_delta; + TimeWindow new_time_window = tab->time_window; + current_time = tab->current_time; + + time_delta = ltt_time_sub(time_span.end_time,time_span.start_time); + new_time_window.time_width.tv_sec = value; + new_time_window.time_width_double = + ltt_time_to_double(new_time_window.time_width); + if(ltt_time_compare(new_time_window.time_width,time_delta) > 0) + { /* Case where zoom out is bigger than trace length */ + new_time_window.start_time = time_span.start_time; + new_time_window.time_width = time_delta; + new_time_window.time_width_double = ltt_time_to_double(time_delta); + new_time_window.end_time = ltt_time_add(new_time_window.start_time, + new_time_window.time_width) ; + } + else + { + /* Center the image on the current time */ + new_time_window.start_time = + ltt_time_sub(current_time, + ltt_time_from_double(new_time_window.time_width_double/2.0)); + new_time_window.end_time = ltt_time_add(new_time_window.start_time, + new_time_window.time_width) ; + /* If on borders, don't fall off */ + if(ltt_time_compare(new_time_window.start_time, time_span.start_time) <0 + || ltt_time_compare(new_time_window.start_time, time_span.end_time) >0) + { + new_time_window.start_time = time_span.start_time; + new_time_window.end_time = ltt_time_add(new_time_window.start_time, + new_time_window.time_width) ; + } + else + { + if(ltt_time_compare(new_time_window.end_time, + time_span.end_time) > 0 + || ltt_time_compare(new_time_window.end_time, + time_span.start_time) < 0) + { + new_time_window.start_time = + ltt_time_sub(time_span.end_time, new_time_window.time_width); + + new_time_window.end_time = ltt_time_add(new_time_window.start_time, + new_time_window.time_width) ; + } + } + + } + + if(ltt_time_compare(new_time_window.time_width, ltt_time_zero) == 0) { + g_warning("Zoom more than 1 ns impossible"); + } else { + time_change_manager(tab, new_time_window); + } +} + +void +on_MEntry8_value_changed (GtkSpinButton *spinbutton, + gpointer user_data) +{ + Tab *tab =(Tab *)user_data; + LttvTracesetContext * tsc = + LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context); + TimeInterval time_span = tsc->time_span; + gint value = gtk_spin_button_get_value_as_int(spinbutton); + LttTime current_time, time_delta; + TimeWindow new_time_window = tab->time_window; + current_time = tab->current_time; + + time_delta = ltt_time_sub(time_span.end_time,time_span.start_time); + new_time_window.time_width.tv_nsec = value; + new_time_window.time_width_double = + ltt_time_to_double(new_time_window.time_width); + if(ltt_time_compare(new_time_window.time_width,time_delta) > 0) + { /* Case where zoom out is bigger than trace length */ + new_time_window.start_time = time_span.start_time; + new_time_window.time_width = time_delta; + new_time_window.time_width_double = ltt_time_to_double(time_delta); + new_time_window.end_time = ltt_time_add(new_time_window.start_time, + new_time_window.time_width) ; + } + else + { + /* Center the image on the current time */ + new_time_window.start_time = + ltt_time_sub(current_time, + ltt_time_from_double(new_time_window.time_width_double/2.0)); + new_time_window.end_time = ltt_time_add(new_time_window.start_time, + new_time_window.time_width) ; + /* If on borders, don't fall off */ + if(ltt_time_compare(new_time_window.start_time, time_span.start_time) <0 + || ltt_time_compare(new_time_window.start_time, time_span.end_time) >0) + { + new_time_window.start_time = time_span.start_time; + new_time_window.end_time = ltt_time_add(new_time_window.start_time, + new_time_window.time_width) ; + } + else + { + if(ltt_time_compare(new_time_window.end_time, + time_span.end_time) > 0 + || ltt_time_compare(new_time_window.end_time, + time_span.start_time) < 0) + { + new_time_window.start_time = + ltt_time_sub(time_span.end_time, new_time_window.time_width); + + new_time_window.end_time = ltt_time_add(new_time_window.start_time, + new_time_window.time_width) ; + } + } + + } + + if(ltt_time_compare(new_time_window.time_width, ltt_time_zero) == 0) { + g_warning("Zoom more than 1 ns impossible"); + } else { + time_change_manager(tab, new_time_window); + } +} + + + +void current_time_change_manager (Tab *tab, + LttTime new_current_time) +{ + /* Only one source of time change */ + if(tab->current_time_manager_lock == TRUE) return; + + tab->current_time_manager_lock = TRUE; + + LttvTracesetContext *tsc = LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context); + TimeInterval time_span = tsc->time_span; + + /* current seconds */ + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry5), + (double)time_span.start_time.tv_sec, + (double)time_span.end_time.tv_sec); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(tab->MEntry5), + (double)new_current_time.tv_sec); + + + /* start nanoseconds */ + if(new_current_time.tv_sec == time_span.start_time.tv_sec) { + /* can be both beginning and end at the same time. */ + if(new_current_time.tv_sec == time_span.end_time.tv_sec) { + /* If we are at the end, max nsec to end.. */ + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry6), + (double)time_span.start_time.tv_nsec, + (double)time_span.end_time.tv_nsec); + } else { + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry6), + (double)time_span.start_time.tv_nsec, + (double)NANOSECONDS_PER_SECOND-1); + } + } else if(new_current_time.tv_sec == time_span.end_time.tv_sec) { + /* If we are at the end, max nsec to end.. */ + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry6), + 0.0, + (double)time_span.end_time.tv_nsec); + } else /* anywhere else */ + gtk_spin_button_set_range(GTK_SPIN_BUTTON(tab->MEntry6), + 0.0, + (double)NANOSECONDS_PER_SECOND-1); + + gtk_spin_button_set_value(GTK_SPIN_BUTTON(tab->MEntry6), + (double)new_current_time.tv_nsec); + + set_current_time(tab, &new_current_time); + + tab->current_time_manager_lock = FALSE; +} + +void current_position_change_manager(Tab *tab, + LttvTracesetContextPosition *pos) +{ + LttvTracesetContext *tsc = + LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context); + TimeInterval time_span = tsc->time_span; + + g_assert(lttv_process_traceset_seek_position(tsc, pos) == 0); + LttTime new_time = lttv_traceset_context_position_get_time(pos); + /* Put the context in a state coherent position */ + lttv_state_traceset_seek_time_closest((LttvTracesetState*)tsc, ltt_time_zero); + + current_time_change_manager(tab, new_time); + + set_current_position(tab, pos); +} + + +void +on_MEntry5_value_changed (GtkSpinButton *spinbutton, + gpointer user_data) +{ + Tab *tab = (Tab*)user_data; + LttvTracesetContext * tsc = + LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context); + TimeInterval time_span = tsc->time_span; + gint value = gtk_spin_button_get_value_as_int(spinbutton); + LttTime new_current_time = tab->current_time; + new_current_time.tv_sec = value; + + /* current nanoseconds */ + if(new_current_time.tv_sec == time_span.start_time.tv_sec) { + if(new_current_time.tv_sec == time_span.end_time.tv_sec) { + if(new_current_time.tv_nsec > time_span.end_time.tv_nsec) + new_current_time.tv_nsec = time_span.end_time.tv_nsec; + if(new_current_time.tv_nsec < time_span.start_time.tv_nsec) + new_current_time.tv_nsec = time_span.start_time.tv_nsec; + } else { + if(new_current_time.tv_nsec < time_span.start_time.tv_nsec) + new_current_time.tv_nsec = time_span.start_time.tv_nsec; + } + } + else if(new_current_time.tv_sec == time_span.end_time.tv_sec) { + if(new_current_time.tv_nsec > time_span.end_time.tv_nsec) + new_current_time.tv_nsec = time_span.end_time.tv_nsec; + } + + current_time_change_manager(tab, new_current_time); +} + +void +on_MEntry6_value_changed (GtkSpinButton *spinbutton, + gpointer user_data) +{ + Tab *tab = (Tab*)user_data; + gint value = gtk_spin_button_get_value_as_int(spinbutton); + LttTime new_current_time = tab->current_time; + new_current_time.tv_nsec = value; + + current_time_change_manager(tab, new_current_time); +} + + +void scroll_value_changed_cb(GtkWidget *scrollbar, + gpointer user_data) +{ + Tab *tab = (Tab *)user_data; + TimeWindow new_time_window; + LttTime time; + GtkAdjustment *adjust = gtk_range_get_adjustment(GTK_RANGE(scrollbar)); + gdouble value = gtk_adjustment_get_value(adjust); + // gdouble upper, lower, ratio, page_size; + gdouble page_size; + LttvTracesetContext * tsc = + LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context); + TimeInterval time_span = tsc->time_span; + + time = ltt_time_add(ltt_time_from_double(value), + time_span.start_time); + + new_time_window.start_time = time; + + page_size = adjust->page_size; + + new_time_window.time_width = + ltt_time_from_double(page_size); + + new_time_window.time_width_double = + page_size; + + new_time_window.end_time = ltt_time_add(new_time_window.start_time, + new_time_window.time_width); + + + time_change_manager(tab, new_time_window); +#if 0 + //time_window = tab->time_window; + + lower = adjust->lower; + upper = adjust->upper; + ratio = (value - lower) / (upper - lower); + g_info("lower %lu, upper %lu, value %lu, ratio %lu", lower, upper, value, ratio); + + //time = ltt_time_sub(time_span->end_time, time_span->start_time); + //time = ltt_time_mul(time, (float)ratio); + //time = ltt_time_add(time_span->start_time, time); + time = ltt_time_add(ltt_time_from_double(value), + time_span.start_time); + + time_window.start_time = time; + + page_size = adjust->page_size; + + time_window.time_width = + ltt_time_from_double(page_size); + //time = ltt_time_sub(time_span.end_time, time); + //if(ltt_time_compare(time,time_window.time_width) < 0){ + // time_window.time_width = time; + //} + + /* call viewer hooks for new time window */ + set_time_window(tab, &time_window); +#endif //0 +} + + +/* Display a dialogue showing all eventtypes and traces, let user to select the interested + * eventtypes, tracefiles and traces (filter) + */ + +/* Select a trace which will be removed from traceset + */ + +char * get_remove_trace(MainWindow *mw_data, + char ** all_trace_name, int nb_trace) +{ + return get_selection(mw_data, all_trace_name, nb_trace, + "Select a trace", "Trace pathname"); +} + + +/* Select a module which will be loaded + */ + +char * get_load_module(MainWindow *mw_data, + char ** load_module_name, int nb_module) +{ + return get_selection(mw_data, load_module_name, nb_module, + "Select a module to load", "Module name"); +} + + + + +/* Select a module which will be unloaded + */ + +char * get_unload_module(MainWindow *mw_data, + char ** loaded_module_name, int nb_module) +{ + return get_selection(mw_data, loaded_module_name, nb_module, + "Select a module to unload", "Module name"); +} + + +/* Display a dialogue which shows all selectable items, let user to + * select one of them + */ + +char * get_selection(MainWindow *mw_data, + char ** loaded_module_name, int nb_module, + char *title, char * column_title) +{ + GtkWidget * dialogue; + GtkWidget * scroll_win; + GtkWidget * tree; + GtkListStore * store; + GtkTreeViewColumn * column; + GtkCellRenderer * renderer; + GtkTreeSelection * select; + GtkTreeIter iter; + gint id, i; + char * unload_module_name = NULL; + + dialogue = gtk_dialog_new_with_buttons(title, + NULL, + GTK_DIALOG_MODAL, + GTK_STOCK_OK,GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL,GTK_RESPONSE_REJECT, + NULL); + gtk_window_set_default_size((GtkWindow*)dialogue, 500, 200); + gtk_window_set_transient_for(GTK_WINDOW(dialogue), + GTK_WINDOW(mw_data->mwindow)); + + scroll_win = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show ( scroll_win); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_win), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + + store = gtk_list_store_new (N_COLUMNS,G_TYPE_STRING); + tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL (store)); + gtk_widget_show ( tree); + g_object_unref (G_OBJECT (store)); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (column_title, + renderer, + "text", MODULE_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.5); + gtk_tree_view_column_set_fixed_width (column, 150); + gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); + + select = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree)); + gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE); + + gtk_container_add (GTK_CONTAINER (scroll_win), tree); + + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialogue)->vbox), scroll_win,TRUE, TRUE,0); + + for(i=0;iattributes; + GtkWidget * tool_menu_title_menu, *new_widget, *pixmap; + + g_assert(lttv_iattribute_find_by_path(global_attributes, + "viewers/menu", LTTV_POINTER, &value)); + if(*(value.v_pointer) == NULL) + *(value.v_pointer) = lttv_menus_new(); + global_menu = (LttvMenus*)*(value.v_pointer); + + g_assert(lttv_iattribute_find_by_path(attributes, + "viewers/menu", LTTV_POINTER, &value)); + if(*(value.v_pointer) == NULL) + *(value.v_pointer) = lttv_menus_new(); + instance_menu = (LttvMenus*)*(value.v_pointer); + + + + g_assert(lttv_iattribute_find_by_path(global_attributes, + "viewers/toolbar", LTTV_POINTER, &value)); + if(*(value.v_pointer) == NULL) + *(value.v_pointer) = lttv_toolbars_new(); + global_toolbar = (LttvToolbars*)*(value.v_pointer); + + g_assert(lttv_iattribute_find_by_path(attributes, + "viewers/toolbar", LTTV_POINTER, &value)); + if(*(value.v_pointer) == NULL) + *(value.v_pointer) = lttv_toolbars_new(); + instance_toolbar = (LttvToolbars*)*(value.v_pointer); + + /* Add missing menu entries to window instance */ + for(i=0;ilen;i++) { + menu_item = &g_array_index(global_menu, LttvMenuClosure, i); + + //add menu_item to window instance; + constructor = menu_item->con; + tool_menu_title_menu = lookup_widget(mw->mwindow,"ToolMenuTitle_menu"); + new_widget = + gtk_menu_item_new_with_mnemonic (menu_item->menu_text); + gtk_container_add (GTK_CONTAINER (tool_menu_title_menu), + new_widget); + g_signal_connect ((gpointer) new_widget, "activate", + G_CALLBACK (insert_viewer_wrap), + constructor); + gtk_widget_show (new_widget); + lttv_menus_add(instance_menu, menu_item->con, + menu_item->menu_path, + menu_item->menu_text, + new_widget); + + } + + /* Add missing toolbar entries to window instance */ + for(i=0;ilen;i++) { + toolbar_item = &g_array_index(global_toolbar, LttvToolbarClosure, i); + + //add toolbar_item to window instance; + constructor = toolbar_item->con; + tool_menu_title_menu = lookup_widget(mw->mwindow,"MToolbar1"); + pixbuf = gdk_pixbuf_new_from_xpm_data((const char**)toolbar_item->pixmap); + pixmap = gtk_image_new_from_pixbuf(pixbuf); + new_widget = + gtk_toolbar_append_element (GTK_TOOLBAR (tool_menu_title_menu), + GTK_TOOLBAR_CHILD_BUTTON, + NULL, + "", + toolbar_item->tooltip, NULL, + pixmap, NULL, NULL); + gtk_label_set_use_underline( + GTK_LABEL (((GtkToolbarChild*) ( + g_list_last (GTK_TOOLBAR + (tool_menu_title_menu)->children)->data))->label), + TRUE); + gtk_container_set_border_width (GTK_CONTAINER (new_widget), 1); + g_signal_connect ((gpointer) new_widget, + "clicked", + G_CALLBACK (insert_viewer_wrap), + constructor); + gtk_widget_show (new_widget); + + lttv_toolbars_add(instance_toolbar, toolbar_item->con, + toolbar_item->tooltip, + toolbar_item->pixmap, + new_widget); + + } + +} + + +/* Create a main window + */ + +MainWindow *construct_main_window(MainWindow * parent) +{ + g_debug("construct_main_window()"); + GtkWidget * new_window; /* New generated main window */ + MainWindow * new_m_window;/* New main window structure */ + GtkNotebook * notebook; + LttvIAttribute *attributes = + LTTV_IATTRIBUTE(g_object_new(LTTV_ATTRIBUTE_TYPE, NULL)); + LttvAttributeValue value; + Tab *new_tab; + + new_m_window = g_new(MainWindow, 1); + + // Add the object's information to the module's array + g_main_window_list = g_slist_append(g_main_window_list, new_m_window); + + new_window = create_MWindow(); + gtk_widget_show (new_window); + + new_m_window->mwindow = new_window; + new_m_window->attributes = attributes; + + g_assert(lttv_iattribute_find_by_path(attributes, + "viewers/menu", LTTV_POINTER, &value)); + *(value.v_pointer) = lttv_menus_new(); + + g_assert(lttv_iattribute_find_by_path(attributes, + "viewers/toolbar", LTTV_POINTER, &value)); + *(value.v_pointer) = lttv_toolbars_new(); + + add_all_menu_toolbar_constructors(new_m_window, NULL); + + g_object_set_data_full(G_OBJECT(new_window), + "main_window_data", + (gpointer)new_m_window, + (GDestroyNotify)g_free); + //create a default tab + notebook = (GtkNotebook *)lookup_widget(new_m_window->mwindow, "MNotebook"); + if(notebook == NULL){ + g_info("Notebook does not exist\n"); + /* FIXME : destroy partially created widgets */ + g_free(new_m_window); + return NULL; + } + //gtk_notebook_popup_enable (GTK_NOTEBOOK(notebook)); + //for now there is no name field in LttvTraceset structure + //Use "Traceset" as the label for the default tab + if(parent) { + GtkWidget * parent_notebook = lookup_widget(parent->mwindow, "MNotebook"); + GtkWidget *page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(parent_notebook), + gtk_notebook_get_current_page(GTK_NOTEBOOK(parent_notebook))); + Tab *parent_tab; + + if(!page) { + parent_tab = NULL; + } else { + LttvPluginTab *ptab; + ptab = (LttvPluginTab *)g_object_get_data(G_OBJECT(page), "Tab_Plugin"); + parent_tab = ptab->tab; + } + LttvPluginTab *ptab = g_object_new(LTTV_TYPE_PLUGIN_TAB, NULL); + init_tab(ptab->tab, + new_m_window, parent_tab, notebook, "Traceset"); + ptab->parent.top_widget = ptab->tab->top_widget; + g_object_set_data_full( + G_OBJECT(ptab->tab->vbox), + "Tab_Plugin", + ptab, + (GDestroyNotify)tab_destructor); + new_tab = ptab->tab; + } else { + LttvPluginTab *ptab = g_object_new(LTTV_TYPE_PLUGIN_TAB, NULL); + init_tab(ptab->tab, new_m_window, NULL, notebook, "Traceset"); + ptab->parent.top_widget = ptab->tab->top_widget; + g_object_set_data_full( + G_OBJECT(ptab->tab->vbox), + "Tab_Plugin", + ptab, + (GDestroyNotify)tab_destructor); + new_tab = ptab->tab; + } + + /* Insert default viewers */ + { + LttvAttributeType type; + LttvAttributeName name; + LttvAttributeValue value; + LttvAttribute *attribute; + + LttvIAttribute *attributes_global = + LTTV_IATTRIBUTE(lttv_global_attributes()); + + g_assert(attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir( + LTTV_IATTRIBUTE(attributes_global), + LTTV_VIEWER_CONSTRUCTORS))); + + name = g_quark_from_string("guievents"); + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(attribute), + name, &value); + if(type == LTTV_POINTER) { + lttvwindow_viewer_constructor viewer_constructor = + (lttvwindow_viewer_constructor)*value.v_pointer; + insert_viewer(new_window, viewer_constructor); + } + + name = g_quark_from_string("guicontrolflow"); + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(attribute), + name, &value); + if(type == LTTV_POINTER) { + lttvwindow_viewer_constructor viewer_constructor = + (lttvwindow_viewer_constructor)*value.v_pointer; + insert_viewer(new_window, viewer_constructor); + } + + name = g_quark_from_string("guistatistics"); + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(attribute), + name, &value); + if(type == LTTV_POINTER) { + lttvwindow_viewer_constructor viewer_constructor = + (lttvwindow_viewer_constructor)*value.v_pointer; + insert_viewer(new_window, viewer_constructor); + } + } + + g_info("There are now : %d windows\n",g_slist_length(g_main_window_list)); + + return new_m_window; +} + + +/* Free the memory occupied by a tab structure + * destroy the tab + */ + +void tab_destructor(LttvPluginTab * ptab) +{ + int i, nb, ref_count; + LttvTrace * trace; + Tab *tab = ptab->tab; + + gtk_object_destroy(GTK_OBJECT(tab->tooltips)); + + if(tab->attributes) + g_object_unref(tab->attributes); + + if(tab->interrupted_state) + g_object_unref(tab->interrupted_state); + + + if(tab->traceset_info->traceset_context != NULL){ + //remove state update hooks + lttv_state_remove_event_hooks( + (LttvTracesetState*)tab->traceset_info-> + traceset_context); + lttv_context_fini(LTTV_TRACESET_CONTEXT(tab->traceset_info-> + traceset_context)); + g_object_unref(tab->traceset_info->traceset_context); + } + if(tab->traceset_info->traceset != NULL) { + nb = lttv_traceset_number(tab->traceset_info->traceset); + for(i = 0 ; i < nb ; i++) { + trace = lttv_traceset_get(tab->traceset_info->traceset, i); + ref_count = lttv_trace_get_ref_number(trace); + if(ref_count <= 1){ + ltt_trace_close(lttv_trace(trace)); + } + } + } + lttv_traceset_destroy(tab->traceset_info->traceset); + /* Remove the idle events requests processing function of the tab */ + g_idle_remove_by_data(tab); + + g_slist_free(tab->events_requests); + g_free(tab->traceset_info); + //g_free(tab); + g_object_unref(ptab); +} + + +/* Create a tab and insert it into the current main window + */ + +void init_tab(Tab *tab, MainWindow * mw, Tab *copy_tab, + GtkNotebook * notebook, char * label) +{ + GList * list; + //Tab * tab; + //LttvFilter *filter = NULL; + + //create a new tab data structure + //tab = g_new(Tab,1); + + //construct and initialize the traceset_info + tab->traceset_info = g_new(TracesetInfo,1); + + if(copy_tab) { + tab->traceset_info->traceset = + lttv_traceset_copy(copy_tab->traceset_info->traceset); + + /* Copy the previous tab's filter */ + /* We can clone the filter, as we copy the trace set also */ + /* The filter must always be in sync with the trace set */ + tab->filter = lttv_filter_clone(copy_tab->filter); + } else { + tab->traceset_info->traceset = lttv_traceset_new(); + tab->filter = NULL; + } +#ifdef DEBUG + lttv_attribute_write_xml( + lttv_traceset_attribute(tab->traceset_info->traceset), + stdout, + 0, 4); + fflush(stdout); +#endif //DEBUG + + tab->time_manager_lock = FALSE; + tab->current_time_manager_lock = FALSE; + + //FIXME copy not implemented in lower level + tab->traceset_info->traceset_context = + g_object_new(LTTV_TRACESET_STATS_TYPE, NULL); + g_assert(tab->traceset_info->traceset_context != NULL); + lttv_context_init( + LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context), + tab->traceset_info->traceset); + //add state update hooks + lttv_state_add_event_hooks( + (LttvTracesetState*)tab->traceset_info->traceset_context); + + //determine the current_time and time_window of the tab +#if 0 + if(copy_tab != NULL){ + tab->time_window = copy_tab->time_window; + tab->current_time = copy_tab->current_time; + }else{ + tab->time_window.start_time = + LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context)-> + time_span.start_time; + if(DEFAULT_TIME_WIDTH_S < + LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context)-> + time_span.end_time.tv_sec) + tmp_time.tv_sec = DEFAULT_TIME_WIDTH_S; + else + tmp_time.tv_sec = + LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context)-> + time_span.end_time.tv_sec; + tmp_time.tv_nsec = 0; + tab->time_window.time_width = tmp_time ; + tab->current_time.tv_sec = + LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context)-> + time_span.start_time.tv_sec; + tab->current_time.tv_nsec = + LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context)-> + time_span.start_time.tv_nsec; + } +#endif //0 + tab->attributes = LTTV_IATTRIBUTE(g_object_new(LTTV_ATTRIBUTE_TYPE, NULL)); + tab->interrupted_state = g_object_new(LTTV_ATTRIBUTE_TYPE, NULL); + + tab->vbox = gtk_vbox_new(FALSE, 2); + tab->top_widget = tab->vbox; + //g_object_set_data_full(G_OBJECT(tab->top_widget), "filter", +// filter, (GDestroyNotify)lttv_filter_destroy); + +// g_signal_connect (G_OBJECT(tab->top_widget), +// "notify", +// G_CALLBACK (on_top_notify), +// (gpointer)tab); + + tab->viewer_container = gtk_vbox_new(TRUE, 2); + tab->scrollbar = gtk_hscrollbar_new(NULL); + //tab->multivpaned = gtk_multi_vpaned_new(); + + gtk_box_pack_start(GTK_BOX(tab->vbox), + tab->viewer_container, + TRUE, /* expand */ + TRUE, /* Give the extra space to the child */ + 0); /* No padding */ + +// if(copy_tab) { +// tab->time_window = copy_tab->time_window; +// tab->current_time = copy_tab->current_time; +// } + + /* Create the timebar */ + { + tab->MTimebar = gtk_hbox_new(FALSE, 2); + gtk_widget_show(tab->MTimebar); + tab->tooltips = gtk_tooltips_new(); + + tab->MEventBox1a = gtk_event_box_new(); + gtk_widget_show(tab->MEventBox1a); + gtk_tooltips_set_tip(tab->tooltips, tab->MEventBox1a, + "Paste Start and End Times Here", ""); + tab->MText1a = gtk_label_new("Time Frame "); + gtk_widget_show(tab->MText1a); + gtk_container_add(GTK_CONTAINER(tab->MEventBox1a), tab->MText1a); + tab->MEventBox1b = gtk_event_box_new(); + gtk_widget_show(tab->MEventBox1b); + gtk_tooltips_set_tip(tab->tooltips, tab->MEventBox1b, + "Paste Start Time Here", ""); + tab->MText1b = gtk_label_new("start: "); + gtk_widget_show(tab->MText1b); + gtk_container_add(GTK_CONTAINER(tab->MEventBox1b), tab->MText1b); + tab->MText2 = gtk_label_new("s"); + gtk_widget_show(tab->MText2); + tab->MText3a = gtk_label_new("ns"); + gtk_widget_show(tab->MText3a); + + tab->MEventBox3b = gtk_event_box_new(); + gtk_widget_show(tab->MEventBox3b); + gtk_tooltips_set_tip(tab->tooltips, tab->MEventBox3b, + "Paste End Time Here", ""); + tab->MText3b = gtk_label_new("end:"); + gtk_widget_show(tab->MText3b); + gtk_container_add(GTK_CONTAINER(tab->MEventBox3b), tab->MText3b); + tab->MText4 = gtk_label_new("s"); + gtk_widget_show(tab->MText4); + tab->MText5a = gtk_label_new("ns"); + gtk_widget_show(tab->MText5a); + + tab->MEventBox8 = gtk_event_box_new(); + gtk_widget_show(tab->MEventBox8); + gtk_tooltips_set_tip(tab->tooltips, tab->MEventBox8, + "Paste Time Interval here", ""); + tab->MText8 = gtk_label_new("Time Interval:"); + gtk_widget_show(tab->MText8); + gtk_container_add(GTK_CONTAINER(tab->MEventBox8), tab->MText8); + tab->MText9 = gtk_label_new("s"); + gtk_widget_show(tab->MText9); + tab->MText10 = gtk_label_new("ns"); + gtk_widget_show(tab->MText10); + + tab->MEventBox5b = gtk_event_box_new(); + gtk_widget_show(tab->MEventBox5b); + gtk_tooltips_set_tip(tab->tooltips, tab->MEventBox5b, + "Paste Current Time Here", ""); + tab->MText5b = gtk_label_new("Current Time:"); + gtk_widget_show(tab->MText5b); + gtk_container_add(GTK_CONTAINER(tab->MEventBox5b), tab->MText5b); + tab->MText6 = gtk_label_new("s"); + gtk_widget_show(tab->MText6); + tab->MText7 = gtk_label_new("ns"); + gtk_widget_show(tab->MText7); + + tab->MEntry1 = gtk_spin_button_new_with_range(0.0, 1.0, 1.0); + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(tab->MEntry1),0); + gtk_spin_button_set_snap_to_ticks(GTK_SPIN_BUTTON(tab->MEntry1),TRUE); + gtk_widget_show(tab->MEntry1); + tab->MEntry2 = gtk_spin_button_new_with_range(0.0, 1.0, 1.0); + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(tab->MEntry2),0); + gtk_spin_button_set_snap_to_ticks(GTK_SPIN_BUTTON(tab->MEntry2),TRUE); + gtk_widget_show(tab->MEntry2); + tab->MEntry3 = gtk_spin_button_new_with_range(0.0, 1.0, 1.0); + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(tab->MEntry3),0); + gtk_spin_button_set_snap_to_ticks(GTK_SPIN_BUTTON(tab->MEntry3),TRUE); + gtk_widget_show(tab->MEntry3); + tab->MEntry4 = gtk_spin_button_new_with_range(0.0, 1.0, 1.0); + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(tab->MEntry4),0); + gtk_spin_button_set_snap_to_ticks(GTK_SPIN_BUTTON(tab->MEntry4),TRUE); + gtk_widget_show(tab->MEntry4); + tab->MEntry5 = gtk_spin_button_new_with_range(0.0, 1.0, 1.0); + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(tab->MEntry5),0); + gtk_spin_button_set_snap_to_ticks(GTK_SPIN_BUTTON(tab->MEntry5),TRUE); + gtk_widget_show(tab->MEntry5); + tab->MEntry6 = gtk_spin_button_new_with_range(0.0, 1.0, 1.0); + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(tab->MEntry6),0); + gtk_spin_button_set_snap_to_ticks(GTK_SPIN_BUTTON(tab->MEntry6),TRUE); + gtk_widget_show(tab->MEntry6); + tab->MEntry7 = gtk_spin_button_new_with_range(0.0, 1.0, 1.0); + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(tab->MEntry7),0); + gtk_spin_button_set_snap_to_ticks(GTK_SPIN_BUTTON(tab->MEntry7),TRUE); + gtk_widget_show(tab->MEntry7); + tab->MEntry8 = gtk_spin_button_new_with_range(0.0, 1.0, 1.0); + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(tab->MEntry8),0); + gtk_spin_button_set_snap_to_ticks(GTK_SPIN_BUTTON(tab->MEntry8),TRUE); + gtk_widget_show(tab->MEntry8); + + GtkWidget *temp_widget; + + gtk_box_pack_start (GTK_BOX (tab->MTimebar), tab->MEventBox1a, FALSE, + FALSE, 0); + gtk_box_pack_start (GTK_BOX (tab->MTimebar), tab->MEventBox1b, FALSE, + FALSE, 0); + gtk_box_pack_start (GTK_BOX (tab->MTimebar), tab->MEntry1, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (tab->MTimebar), tab->MText2, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (tab->MTimebar), tab->MEntry2, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (tab->MTimebar), tab->MText3a, FALSE, FALSE, 0); + temp_widget = gtk_vseparator_new(); + gtk_widget_show(temp_widget); + gtk_box_pack_start (GTK_BOX (tab->MTimebar), temp_widget, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (tab->MTimebar), tab->MEventBox3b, FALSE, + FALSE, 0); + gtk_box_pack_start (GTK_BOX (tab->MTimebar), tab->MEntry3, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (tab->MTimebar), tab->MText4, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (tab->MTimebar), tab->MEntry4, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (tab->MTimebar), tab->MText5a, FALSE, FALSE, 0); + temp_widget = gtk_vseparator_new(); + gtk_widget_show(temp_widget); + gtk_box_pack_start (GTK_BOX (tab->MTimebar), temp_widget, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (tab->MTimebar), tab->MEventBox8, FALSE, + FALSE, 0); + gtk_box_pack_start (GTK_BOX (tab->MTimebar), tab->MEntry7, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (tab->MTimebar), tab->MText9, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (tab->MTimebar), tab->MEntry8, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (tab->MTimebar), tab->MText10, FALSE, FALSE, 0); + + temp_widget = gtk_vseparator_new(); + gtk_widget_show(temp_widget); + gtk_box_pack_end (GTK_BOX (tab->MTimebar), tab->MText7, FALSE, FALSE, 0); + gtk_box_pack_end (GTK_BOX (tab->MTimebar), tab->MEntry6, FALSE, FALSE, 0); + gtk_box_pack_end (GTK_BOX (tab->MTimebar), tab->MText6, FALSE, FALSE, 0); + gtk_box_pack_end (GTK_BOX (tab->MTimebar), tab->MEntry5, FALSE, FALSE, 0); + gtk_box_pack_end (GTK_BOX (tab->MTimebar), tab->MEventBox5b, FALSE, + FALSE, 0); + gtk_box_pack_end (GTK_BOX (tab->MTimebar), temp_widget, FALSE, FALSE, 0); + + + //GtkWidget *test = gtk_button_new_with_label("drop"); + //gtk_button_set_relief(GTK_BUTTON(test), GTK_RELIEF_NONE); + //gtk_widget_show(test); + //gtk_box_pack_end(GTK_BOX (tab->MTimebar), test, FALSE, FALSE, 0); + //gtk_widget_add_events(tab->MText1, GDK_ALL_EVENTS_MASK);//GDK_BUTTON_PRESS_MASK); + /*GtkWidget *event_box = gtk_event_box_new(); + gtk_widget_show(event_box); + gtk_tooltips_set_tip(tooltips, event_box, + "Paste Current Time Here", ""); + gtk_box_pack_end(GTK_BOX (tab->MTimebar), event_box, FALSE, FALSE, 0); + GtkWidget *test = gtk_label_new("drop"); + gtk_container_add(GTK_CONTAINER(event_box), test); + gtk_widget_show(test); + g_signal_connect (G_OBJECT(event_box), + "button-press-event", + G_CALLBACK (on_MText1_paste), + (gpointer)tab); +*/ + + g_signal_connect (G_OBJECT(tab->MEventBox1a), + "button-press-event", + G_CALLBACK (on_MEventBox1a_paste), + (gpointer)tab); + + g_signal_connect (G_OBJECT(tab->MEventBox1b), + "button-press-event", + G_CALLBACK (on_MEventBox1b_paste), + (gpointer)tab); + g_signal_connect (G_OBJECT(tab->MEventBox3b), + "button-press-event", + G_CALLBACK (on_MEventBox3b_paste), + (gpointer)tab); + g_signal_connect (G_OBJECT(tab->MEventBox5b), + "button-press-event", + G_CALLBACK (on_MEventBox5b_paste), + (gpointer)tab); + g_signal_connect (G_OBJECT(tab->MEventBox8), + "button-press-event", + G_CALLBACK (on_MEventBox8_paste), + (gpointer)tab); + } + + gtk_box_pack_end(GTK_BOX(tab->vbox), + tab->scrollbar, + FALSE, /* Do not expand */ + FALSE, /* Fill has no effect here (expand false) */ + 0); /* No padding */ + + gtk_box_pack_end(GTK_BOX(tab->vbox), + tab->MTimebar, + FALSE, /* Do not expand */ + FALSE, /* Fill has no effect here (expand false) */ + 0); /* No padding */ + + g_object_set_data(G_OBJECT(tab->viewer_container), "focused_viewer", NULL); + + + tab->mw = mw; + + /*{ + // Display a label with a X + GtkWidget *w_hbox = gtk_hbox_new(FALSE, 4); + GtkWidget *w_label = gtk_label_new (label); + GtkWidget *pixmap = create_pixmap(GTK_WIDGET(notebook), "close.png"); + GtkWidget *w_button = gtk_button_new (); + gtk_container_add(GTK_CONTAINER(w_button), pixmap); + //GtkWidget *w_button = gtk_button_new_with_label("x"); + + gtk_button_set_relief(GTK_BUTTON(w_button), GTK_RELIEF_NONE); + + gtk_box_pack_start(GTK_BOX(w_hbox), w_label, TRUE, TRUE, 0); + gtk_box_pack_end(GTK_BOX(w_hbox), w_button, FALSE, + FALSE, 0); + + g_signal_connect_swapped (w_button, "clicked", + G_CALLBACK (on_close_tab_X_clicked), + tab->multi_vpaned); + + gtk_widget_set_state(w_button, GTK_STATE_ACTIVE); + + gtk_widget_show (w_label); + gtk_widget_show (pixmap); + gtk_widget_show (w_button); + gtk_widget_show (w_hbox); + + tab->label = w_hbox; + }*/ + + + tab->label = gtk_label_new (label); + + gtk_widget_show(tab->label); + gtk_widget_show(tab->scrollbar); + gtk_widget_show(tab->viewer_container); + gtk_widget_show(tab->vbox); + //gtk_widget_show(tab->multivpaned); + + + /* Start with empty events requests list */ + tab->events_requests = NULL; + tab->events_request_pending = FALSE; + tab->stop_foreground = FALSE; + + + + g_signal_connect(G_OBJECT(tab->scrollbar), "value-changed", + G_CALLBACK(scroll_value_changed_cb), tab); + + g_signal_connect ((gpointer) tab->MEntry1, "value-changed", + G_CALLBACK (on_MEntry1_value_changed), + tab); + g_signal_connect ((gpointer) tab->MEntry2, "value-changed", + G_CALLBACK (on_MEntry2_value_changed), + tab); + g_signal_connect ((gpointer) tab->MEntry3, "value-changed", + G_CALLBACK (on_MEntry3_value_changed), + tab); + g_signal_connect ((gpointer) tab->MEntry4, "value-changed", + G_CALLBACK (on_MEntry4_value_changed), + tab); + g_signal_connect ((gpointer) tab->MEntry5, "value-changed", + G_CALLBACK (on_MEntry5_value_changed), + tab); + g_signal_connect ((gpointer) tab->MEntry6, "value-changed", + G_CALLBACK (on_MEntry6_value_changed), + tab); + g_signal_connect ((gpointer) tab->MEntry7, "value-changed", + G_CALLBACK (on_MEntry7_value_changed), + tab); + g_signal_connect ((gpointer) tab->MEntry8, "value-changed", + G_CALLBACK (on_MEntry8_value_changed), + tab); + + //g_signal_connect(G_OBJECT(tab->scrollbar), "changed", + // G_CALLBACK(scroll_value_changed_cb), tab); + + + //insert tab into notebook + gtk_notebook_append_page(notebook, + tab->vbox, + tab->label); + list = gtk_container_get_children(GTK_CONTAINER(notebook)); + gtk_notebook_set_current_page(notebook,g_list_length(list)-1); + // always show : not if(g_list_length(list)>1) + gtk_notebook_set_show_tabs(notebook, TRUE); + + if(copy_tab) { + lttvwindow_report_time_window(tab, copy_tab->time_window); + lttvwindow_report_current_time(tab, copy_tab->current_time); + } else { + TimeWindow time_window; + + time_window.start_time = ltt_time_zero; + time_window.end_time = ltt_time_add(time_window.start_time, + lttvwindow_default_time_width); + time_window.time_width = lttvwindow_default_time_width; + time_window.time_width_double = ltt_time_to_double(time_window.time_width); + + lttvwindow_report_time_window(tab, time_window); + lttvwindow_report_current_time(tab, ltt_time_zero); + } + + LttvTraceset *traceset = tab->traceset_info->traceset; + SetTraceset(tab, traceset); +} + +/* + * execute_events_requests + * + * Idle function that executes the pending requests for a tab. + * + * @return return value : TRUE : keep the idle function, FALSE : remove it. + */ +gboolean execute_events_requests(Tab *tab) +{ + return ( lttvwindow_process_pending_requests(tab) ); +} + + +__EXPORT void create_main_window_with_trace_list(GSList *traces) +{ + GSList *iter = NULL; + + /* Create window */ + MainWindow *mw = construct_main_window(NULL); + GtkWidget *widget = mw->mwindow; + + GtkWidget * notebook = lookup_widget(widget, "MNotebook"); + GtkWidget *page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), + gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook))); + LttvPluginTab *ptab; + Tab *tab; + + if(!page) { + ptab = create_new_tab(widget, NULL); + tab = ptab->tab; + } else { + ptab = (LttvPluginTab *)g_object_get_data(G_OBJECT(page), "Tab_Plugin"); + tab = ptab->tab; + } + + for(iter=traces; iter!=NULL; iter=g_slist_next(iter)) { + gchar *path = (gchar*)iter->data; + /* Add trace */ + gchar abs_path[PATH_MAX]; + LttvTrace *trace_v; + LttTrace *trace; + + get_absolute_pathname(path, abs_path); + trace_v = lttvwindowtraces_get_trace_by_name(abs_path); + if(trace_v == NULL) { + trace = ltt_trace_open(abs_path); + if(trace == NULL) { + g_warning("cannot open trace %s", abs_path); + + GtkWidget *dialogue = + gtk_message_dialog_new( + GTK_WINDOW(gtk_widget_get_toplevel(widget)), + GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "Cannot open trace : maybe you should enter in the directory " + "to select it ?"); + gtk_dialog_run(GTK_DIALOG(dialogue)); + gtk_widget_destroy(dialogue); + } else { + trace_v = lttv_trace_new(trace); + lttvwindowtraces_add_trace(trace_v); + lttvwindow_add_trace(tab, trace_v); + } + } else { + lttvwindow_add_trace(tab, trace_v); + } + } + + LttvTraceset *traceset; + + traceset = tab->traceset_info->traceset; + SetTraceset(tab, traceset); +} + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/callbacks.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/callbacks.h new file mode 100644 index 00000000..1bbd4fbf --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/callbacks.h @@ -0,0 +1,315 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 XangXiu Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 +#include + +/* internal functions */ + +void create_new_window(GtkWidget* widget, gpointer user_data, gboolean clone); +void insert_menu_toolbar_item(MainWindow * mw, gpointer user_data); +MainWindow *construct_main_window(MainWindow * parent); +void main_window_free(MainWindow * mw); +void main_window_destructor(MainWindow * mw); + +void create_main_window_with_trace_list(GSList *traces); + +void insert_viewer_wrap(GtkWidget *menuitem, gpointer user_data); +gboolean execute_events_requests(Tab *tab); + + +/* callback functions*/ + +void +on_empty_traceset_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_clone_traceset_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_tab_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_open_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_close_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_close_tab_X_clicked (GtkWidget *widget, + gpointer user_data); + +void +on_close_tab_activate (GtkWidget *widget, + gpointer user_data); + +void +on_add_trace_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_remove_trace_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_save_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_save_as_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_quit_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_cut_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_copy_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_paste_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_delete_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_zoom_in_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_zoom_out_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_zoom_extended_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_go_to_time_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_show_time_frame_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_move_viewer_up_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_move_viewer_down_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_remove_viewer_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_trace_filter_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_trace_facility_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_load_library_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_unload_library_activate (GtkMenuItem *menuitem, + gpointer user_data); + + +void +on_load_module_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_unload_module_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_add_library_search_path_activate (GtkMenuItem *menuitem, + gpointer user_data); +void +on_remove_library_search_path_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_color_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_filter_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_save_configuration_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_content_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_about_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_button_new_clicked (GtkButton *button, + gpointer user_data); +void +on_button_new_tab_clicked (GtkButton *button, + gpointer user_data); +void +on_button_open_clicked (GtkButton *button, + gpointer user_data); + +void +on_button_add_trace_clicked (GtkButton *button, + gpointer user_data); + +void +on_button_remove_trace_clicked (GtkButton *button, + gpointer user_data); +void +on_button_redraw_clicked (GtkButton *button, + gpointer user_data); + +void +on_button_continue_processing_clicked (GtkButton *button, + gpointer user_data); + +void +on_button_stop_processing_clicked (GtkButton *button, + gpointer user_data); + +void +on_button_save_clicked (GtkButton *button, + gpointer user_data); + +void +on_button_save_as_clicked (GtkButton *button, + gpointer user_data); + +void +on_button_zoom_in_clicked (GtkButton *button, + gpointer user_data); + +void +on_button_zoom_out_clicked (GtkButton *button, + gpointer user_data); + +void +on_button_zoom_extended_clicked (GtkButton *button, + gpointer user_data); + +void +on_button_go_to_time_clicked (GtkButton *button, + gpointer user_data); + +void +on_button_show_time_frame_clicked (GtkButton *button, + gpointer user_data); + +void +on_button_move_up_clicked (GtkButton *button, + gpointer user_data); + +void +on_button_move_down_clicked (GtkButton *button, + gpointer user_data); + +void +on_button_delete_viewer_clicked (GtkButton *button, + gpointer user_data); + +void +on_MWindow_destroy (GtkWidget *widget, + gpointer user_data); + +gboolean +on_MWindow_configure (GtkWidget *widget, + GdkEventConfigure *event, + gpointer user_data); + +gboolean +on_MWindow_expose (GtkWidget *widget, + GdkEventExpose *event, + gpointer user_data); +gboolean +on_MWindow_after (GtkWidget *widget, + GdkEvent *event, + gpointer user_data); + + + +void +on_insert_viewer_test_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +insertViewTest(GtkMenuItem *menuitem, gpointer user_data); + +void +on_MNotebook_switch_page (GtkNotebook *notebook, + GtkNotebookPage *page, + guint page_num, + gpointer user_data); + + +void +on_MEntry1_value_changed (GtkSpinButton *spinbutton, + gpointer user_data); +void +on_MEntry2_value_changed (GtkSpinButton *spinbutton, + gpointer user_data); +void +on_MEntry3_value_changed (GtkSpinButton *spinbutton, + gpointer user_data); +void +on_MEntry4_value_changed (GtkSpinButton *spinbutton, + gpointer user_data); +void +on_MEntry5_value_changed (GtkSpinButton *spinbutton, + gpointer user_data); +void +on_MEntry6_value_changed (GtkSpinButton *spinbutton, + gpointer user_data); + + +void time_change_manager (Tab *tab, + TimeWindow new_time_window); + +void current_time_change_manager (Tab *tab, + LttTime new_current_time); + +gboolean execute_time_requests(MainWindow * mw); diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/computetrace.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/computetrace.c new file mode 100644 index 00000000..29f52448 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/computetrace.c @@ -0,0 +1,94 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* This file does not even compile yet. It is a starting point to compute + some values in the background. This is why process_trace was split in + three. However, process_trace_middle as it is currently is would not work. + It needs to reinitialize its trace event positions each time since, + in between background calls to process middle, other foreground calls to + process_middle can happen. */ + +#include + +/* The calling function has checked that the needed information has not + been or is not being computed yet, has prepared the trace, and now all + that is needed is to queue it for processing. + + CHECK remove the work_queue global variable, have an automatic adjustment + of the number of events to process by iteration. */ + +static gboolean inserted = false; + +static GList *work_queue = NULL; + +typedef struct _WorkPiece WorkPiece; + +struct _WorkPiece { + LttvTracesetContext *self; + LttTime end; + unsigned nb_events; + LttvHook f; + void *hook_data; + unsigned nb_done; +} + +guint lttv_process_traceset_piece(gpointer data) +{ + GList *first = g_list_first(work_queue); + + guint nb_done, nb_asked; + + if(first == NULL) { + inserted = false; + return false; + } + + WorkPiece *work_piece = (WorkPiece *)first->data; + nb_asked = work_piece->nb_events - work_piece->nb_done; + nb_asked = min(nb_asked, 10000); + nb_done = lttv_process_trace_middle(work_piece->self,work_piece->end, + nb_asked); + work_piece->nb_done += nb_done; + if(nb_done < nb_asked) { + lttv_process_trace_end(work_piece->self); + work_queue = g_list_delete(work_queue, first); + } +} + + +void lttv_process_traceset_when_idle(LttvTracesetContext *self, LttTime end, + unsigned nb_events, LttvHook f, void *hook_data) +{ + WorkPiece *work_piece = g_new(WorkPiece); + work_piece->self = self; + work_piece->end = end; + work_piece->nb_events = nb_events; + work_piece->f = f; + work_piece->hook_data = hook_data; + eork_piece->nb_done = 0; + + lttv_process_traceset_begin(self); + work_queue = g_list_append(work_queue, work_piece); + if(!inserted) g_idle_add(lttv_process_traceset_piece, work_queue); +} + + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/gtkmultivpaned.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/gtkmultivpaned.c new file mode 100644 index 00000000..ec8b3755 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/gtkmultivpaned.c @@ -0,0 +1,580 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 XangXiu Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include +//#include "gtkintl.h" + +static void gtk_multi_vpaned_class_init (GtkMultiVPanedClass *klass); +static void gtk_multi_vpaned_init (GtkMultiVPaned *multi_vpaned); + + +static void gtk_multi_vpaned_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void gtk_multi_vpaned_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); + +void gtk_multi_vpaned_scroll_value_changed (GtkAdjustment *adjust, gpointer multi_vpaned); + +gboolean gtk_multi_vpaned_destroy(GtkObject *object, + gpointer user_data) +{ + GtkMultiVPaned * multi_vpaned = (GtkMultiVPaned * )object; + while(multi_vpaned->num_children){ + gtk_multi_vpaned_widget_delete(multi_vpaned); + } + return FALSE; +} + +GType +gtk_multi_vpaned_get_type (void) +{ + static GType multi_vpaned_type = 0; + + if (!multi_vpaned_type) + { + static const GTypeInfo multi_vpaned_info = + { + sizeof (GtkMultiVPanedClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gtk_multi_vpaned_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkMultiVPaned), + 0, /* n_preallocs */ + (GInstanceInitFunc) gtk_multi_vpaned_init, + NULL, /* value_table */ + }; + + multi_vpaned_type = g_type_register_static (GTK_TYPE_PANED, "GtkMultiVPaned", + &multi_vpaned_info, 0); + } + + return multi_vpaned_type; +} + +static void +gtk_multi_vpaned_class_init (GtkMultiVPanedClass *class) +{ + GtkWidgetClass *widget_class; + + widget_class = (GtkWidgetClass *) class; + + widget_class->size_request = gtk_multi_vpaned_size_request; + widget_class->size_allocate = gtk_multi_vpaned_size_allocate; +} + +static void +gtk_multi_vpaned_init (GtkMultiVPaned * multi_vpaned) +{ + GtkWidget * button; + + GTK_WIDGET_SET_FLAGS (multi_vpaned, GTK_NO_WINDOW); + gtk_widget_set_redraw_on_allocate (GTK_WIDGET (multi_vpaned), FALSE); + + multi_vpaned->first_pane = NULL; + multi_vpaned->last_pane = NULL; + multi_vpaned->focused_pane = NULL; + multi_vpaned->iter = NULL; + multi_vpaned->num_children = 0; + + multi_vpaned->vbox = NULL; + // multi_vpaned->scrollWindow = NULL; + // multi_vpaned->viewport = NULL; + multi_vpaned->hscrollbar = NULL; +} + + +GtkWidget* gtk_multi_vpaned_new () +{ + GtkWidget * widget = GTK_WIDGET (g_object_new (gtk_multi_vpaned_get_type (), NULL)); + g_signal_connect(G_OBJECT(widget), "destroy", + G_CALLBACK(gtk_multi_vpaned_destroy),NULL); + + return widget; +} + +GtkWidget * gtk_multi_vpaned_get_widget(GtkMultiVPaned * multi_vpaned) +{ + if(multi_vpaned->focused_pane == NULL)return NULL; + return (GtkWidget*)multi_vpaned->focused_pane->child2; +} + +GtkWidget * gtk_multi_vpaned_get_first_widget(GtkMultiVPaned * multi_vpaned) +{ + if(multi_vpaned->first_pane == NULL)return NULL; + multi_vpaned->iter = multi_vpaned->first_pane; + return multi_vpaned->first_pane->child2; +} + +GtkWidget * gtk_multi_vpaned_get_next_widget(GtkMultiVPaned * multi_vpaned) +{ + if(multi_vpaned->iter != multi_vpaned->last_pane){ + multi_vpaned->iter = (GtkPaned *)multi_vpaned->iter->child1; + return multi_vpaned->iter->child2; + }else { + return NULL; + } +} + +void gtk_multi_vpaned_set_data(GtkMultiVPaned * multi_vpaned,char * key, gpointer value) +{ + g_object_set_data(G_OBJECT(multi_vpaned->focused_pane), key, value); +} + +gpointer gtk_multi_vpaned_get_data(GtkMultiVPaned * multi_vpaned,char * key) +{ + if(multi_vpaned->focused_pane == NULL)return NULL; + return g_object_get_data(G_OBJECT(multi_vpaned->focused_pane), key); +} + +void gtk_multi_vpaned_set_focus (GtkWidget * widget, GtkPaned* paned) +{ + GtkMultiVPaned * multi_vpaned = GTK_MULTI_VPANED(widget); + GtkPaned * pane; + if(!multi_vpaned->first_pane) return; + + + pane = multi_vpaned->first_pane; + while(1){ + if((GtkWidget*)pane == GTK_WIDGET(paned)){ + multi_vpaned->focused_pane = pane; + break; + } + if(pane == multi_vpaned->last_pane){ + multi_vpaned->focused_pane = NULL; + break; + } + pane = (GtkPaned*)pane->child1; + } +} + +void gtk_multi_vpaned_set_adjust(GtkMultiVPaned * multi_vpaned, const TimeWindow *time_window, gboolean first_time) +{ + TimeInterval *time_span; + double len, start; + Tab *tab = (Tab *)g_object_get_data(G_OBJECT(multi_vpaned), "Tab_Info"); + LttvTracesetContext *tsc = + LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context); + + + if(first_time){ + time_span = &tsc->time_span ; + + multi_vpaned->hadjust->lower = ltt_time_to_double(time_span->start_time) * + NANOSECONDS_PER_SECOND; + multi_vpaned->hadjust->value = multi_vpaned->hadjust->lower; + multi_vpaned->hadjust->upper = ltt_time_to_double(time_span->end_time) * + NANOSECONDS_PER_SECOND; + } + + /* Page increment of whole visible area */ + if(multi_vpaned->hadjust == NULL){ + g_warning("Insert a viewer first"); + return; + } + + start = ltt_time_to_double(time_window->start_time) * NANOSECONDS_PER_SECOND; + len = multi_vpaned->hadjust->upper - multi_vpaned->hadjust->lower; + + multi_vpaned->hadjust->page_increment = ltt_time_to_double( + time_window->time_width) * NANOSECONDS_PER_SECOND; + + //if(multi_vpaned->hadjust->page_increment >= len ) + // multi_vpaned->hadjust->value = multi_vpaned->hadjust->lower; + //if(start + multi_vpaned->hadjust->page_increment >= multi_vpaned->hadjust->upper ) + // multi_vpaned->hadjust->value = start; + multi_vpaned->hadjust->value = start; + + /* page_size to the whole visible area will take care that the + * scroll value + the shown area will never be more than what is + * in the trace. */ + multi_vpaned->hadjust->page_size = multi_vpaned->hadjust->page_increment; + multi_vpaned->hadjust->step_increment = multi_vpaned->hadjust->page_increment / 10; + + gtk_adjustment_changed (multi_vpaned->hadjust); + +} + +void gtk_multi_vpaned_widget_add(GtkMultiVPaned * multi_vpaned, GtkWidget * widget1) +{ + GtkPaned * tmpPane; + GtkWidget * w; + Tab *tab = (Tab *)g_object_get_data(G_OBJECT(multi_vpaned), "Tab_Info"); + + g_return_if_fail(GTK_IS_MULTI_VPANED(multi_vpaned)); + g_object_ref(G_OBJECT(widget1)); + + + if(!multi_vpaned->first_pane){ + multi_vpaned->first_pane = (GtkPaned *)gtk_vpaned_new(); + multi_vpaned->last_pane = multi_vpaned->first_pane; + + multi_vpaned->hscrollbar = gtk_hscrollbar_new (NULL); + gtk_widget_show(multi_vpaned->hscrollbar); + + multi_vpaned->hadjust = gtk_range_get_adjustment(GTK_RANGE(multi_vpaned->hscrollbar)); + gtk_multi_vpaned_set_adjust(multi_vpaned, &tab->time_window, TRUE); + + gtk_range_set_update_policy (GTK_RANGE(multi_vpaned->hscrollbar), + GTK_UPDATE_CONTINUOUS); + //changed by Mathieu Desnoyers, was : + // GTK_UPDATE_DISCONTINUOUS); + g_signal_connect(G_OBJECT(multi_vpaned->hadjust), "value-changed", + G_CALLBACK(gtk_multi_vpaned_scroll_value_changed), multi_vpaned); + g_signal_connect(G_OBJECT(multi_vpaned->hadjust), "changed", + G_CALLBACK(gtk_multi_vpaned_scroll_value_changed), multi_vpaned); + + + multi_vpaned->vbox = gtk_vbox_new(FALSE,0); + gtk_widget_show(multi_vpaned->vbox); + + // multi_vpaned->viewport = gtk_viewport_new (NULL,NULL); + // gtk_widget_show(multi_vpaned->viewport); + + // gtk_container_add(GTK_CONTAINER(multi_vpaned->viewport), (GtkWidget*)multi_vpaned->vbox); + gtk_box_pack_end(GTK_BOX(multi_vpaned->vbox),(GtkWidget*)multi_vpaned->hscrollbar,FALSE,FALSE,0); + gtk_box_pack_end(GTK_BOX(multi_vpaned->vbox),(GtkWidget*)multi_vpaned->first_pane,TRUE,TRUE,0); + + // multi_vpaned->scrollWindow = gtk_scrolled_window_new (NULL, NULL); + // gtk_widget_show(multi_vpaned->scrollWindow); + // gtk_container_add (GTK_CONTAINER (multi_vpaned->scrollWindow), (GtkWidget*)multi_vpaned->viewport); + // gtk_paned_pack1(GTK_PANED(multi_vpaned), (GtkWidget*)multi_vpaned->scrollWindow,FALSE, TRUE); + + gtk_paned_pack1(GTK_PANED(multi_vpaned), (GtkWidget*)multi_vpaned->vbox,FALSE, TRUE); + }else{ + tmpPane = multi_vpaned->last_pane; + multi_vpaned->last_pane = (GtkPaned *)gtk_vpaned_new(); + gtk_paned_pack1 (tmpPane,(GtkWidget*)multi_vpaned->last_pane, FALSE,TRUE); + } + gtk_widget_show((GtkWidget *)multi_vpaned->last_pane); + + gtk_paned_pack2 (multi_vpaned->last_pane,widget1, TRUE, TRUE); + multi_vpaned->focused_pane = multi_vpaned->last_pane; + multi_vpaned->num_children++; + +} + +void gtk_multi_vpaned_widget_delete(GtkMultiVPaned * multi_vpaned) +{ + GtkPaned * tmp, *prev, *next; + GtkWidget *widget; + + if(!multi_vpaned->focused_pane) return; + + widget = GTK_WIDGET(multi_vpaned->focused_pane->child2); //widget in vpaned + g_object_unref(G_OBJECT(widget)); + + if(multi_vpaned->focused_pane == multi_vpaned->first_pane && + multi_vpaned->focused_pane == multi_vpaned->last_pane){ + // gtk_container_remove(GTK_CONTAINER(multi_vpaned),(GtkWidget*)multi_vpaned->scrollWindow); + gtk_container_remove(GTK_CONTAINER(multi_vpaned),(GtkWidget*)multi_vpaned->vbox); + multi_vpaned->first_pane = NULL; + multi_vpaned->last_pane = NULL; + multi_vpaned->focused_pane = NULL; + }else if(multi_vpaned->focused_pane == multi_vpaned->first_pane && + multi_vpaned->focused_pane != multi_vpaned->last_pane){ + next = (GtkPaned*)multi_vpaned->first_pane->child1; + g_object_ref(G_OBJECT(next)); + gtk_container_remove(GTK_CONTAINER(multi_vpaned->first_pane),(GtkWidget*)next); + gtk_container_remove(GTK_CONTAINER(multi_vpaned->vbox),(GtkWidget*)multi_vpaned->first_pane); + multi_vpaned->first_pane = next; + gtk_box_pack_end(GTK_BOX(multi_vpaned->vbox),(GtkWidget*)multi_vpaned->first_pane,TRUE,TRUE,0); + multi_vpaned->focused_pane = multi_vpaned->first_pane; + g_object_unref(G_OBJECT(next)); + }else if(multi_vpaned->focused_pane != multi_vpaned->first_pane && + multi_vpaned->focused_pane == multi_vpaned->last_pane){ + tmp = multi_vpaned->last_pane; + multi_vpaned->last_pane = (GtkPaned*)gtk_widget_get_parent((GtkWidget*)multi_vpaned->last_pane); + multi_vpaned->focused_pane = multi_vpaned->last_pane; + gtk_container_remove(GTK_CONTAINER(multi_vpaned->last_pane),(GtkWidget*)tmp); + }else{ + tmp = multi_vpaned->focused_pane; + prev = (GtkPaned*)gtk_widget_get_parent((GtkWidget*)tmp); + next = (GtkPaned*)tmp->child1; + g_object_ref(G_OBJECT(next)); + gtk_container_remove(GTK_CONTAINER(multi_vpaned->focused_pane),(GtkWidget*)next); + gtk_container_remove(GTK_CONTAINER(prev),(GtkWidget*)multi_vpaned->focused_pane); + gtk_paned_pack1(prev, (GtkWidget*)next, FALSE, TRUE); + multi_vpaned->focused_pane = next; + g_object_unref(G_OBJECT(next)); + } + + multi_vpaned->num_children--; +} + + +void gtk_multi_vpaned_widget_move_up(GtkMultiVPaned * multi_vpaned) +{ + GtkWidget* upWidget, *downWidget; + GtkPaned * prev,*next, *prevPrev; + + if(multi_vpaned->last_pane == multi_vpaned->focused_pane) return; + + // move VPane + prev = (GtkPaned*)multi_vpaned->focused_pane->child1; + g_object_ref(G_OBJECT(prev)); + gtk_container_remove(GTK_CONTAINER(multi_vpaned->focused_pane),(GtkWidget*)prev); + + if(prev == multi_vpaned->last_pane){ + prevPrev = NULL; + multi_vpaned->last_pane = multi_vpaned->focused_pane; + }else{ + prevPrev = (GtkPaned*)prev->child1; + g_object_ref(G_OBJECT(prevPrev)); + gtk_container_remove(GTK_CONTAINER(prev),(GtkWidget*)prevPrev); + } + + g_object_ref(G_OBJECT(multi_vpaned->focused_pane)); + if(multi_vpaned->first_pane == multi_vpaned->focused_pane){ + gtk_container_remove(GTK_CONTAINER(multi_vpaned->vbox),(GtkWidget*)multi_vpaned->focused_pane); + gtk_box_pack_end(GTK_BOX(multi_vpaned->vbox),(GtkWidget*)prev,TRUE,TRUE,0); + multi_vpaned->first_pane = prev; + }else{ + next = (GtkPaned*)gtk_widget_get_parent((GtkWidget*)multi_vpaned->focused_pane); + gtk_container_remove(GTK_CONTAINER(next),(GtkWidget*)multi_vpaned->focused_pane); + gtk_paned_pack1(GTK_PANED(next), (GtkWidget*)prev, FALSE,TRUE); + } + gtk_paned_pack1(GTK_PANED(prev),(GtkWidget*)multi_vpaned->focused_pane, FALSE,TRUE); + if(prevPrev) + gtk_paned_pack1(GTK_PANED(multi_vpaned->focused_pane),(GtkWidget*)prevPrev, FALSE,TRUE); + + g_object_unref(G_OBJECT(prev)); + if(prevPrev) g_object_unref(G_OBJECT(prevPrev)); + g_object_unref(G_OBJECT(multi_vpaned->focused_pane)); +} + + +void gtk_multi_vpaned_widget_move_down(GtkMultiVPaned * multi_vpaned) +{ + GtkWidget* upWidget, *downWidget; + GtkPaned * prev,*next, *nextNext; + + if(multi_vpaned->first_pane == multi_vpaned->focused_pane) return; + + //move VPane + next = (GtkPaned*)gtk_widget_get_parent((GtkWidget*)multi_vpaned->focused_pane); + g_object_ref(G_OBJECT(next)); + + if(multi_vpaned->last_pane == multi_vpaned->focused_pane){ + prev = NULL; + multi_vpaned->last_pane = next; + }else{ + prev = (GtkPaned*)multi_vpaned->focused_pane->child1; + g_object_ref(G_OBJECT(prev)); + gtk_container_remove(GTK_CONTAINER(multi_vpaned->focused_pane),(GtkWidget*)prev); + } + + g_object_ref(G_OBJECT(multi_vpaned->focused_pane)); + gtk_container_remove(GTK_CONTAINER(next),(GtkWidget*)multi_vpaned->focused_pane); + + if(next == multi_vpaned->first_pane){ + multi_vpaned->first_pane = multi_vpaned->focused_pane; + gtk_container_remove(GTK_CONTAINER(multi_vpaned->vbox),(GtkWidget*)next); + gtk_box_pack_end(GTK_BOX(multi_vpaned->vbox),(GtkWidget*)multi_vpaned->focused_pane,TRUE,TRUE,0); + }else{ + nextNext = (GtkPaned*)gtk_widget_get_parent((GtkWidget*)next); + gtk_container_remove(GTK_CONTAINER(nextNext),(GtkWidget*)next); + gtk_paned_pack1(nextNext, (GtkWidget*)multi_vpaned->focused_pane, FALSE, TRUE); + } + gtk_paned_pack1(multi_vpaned->focused_pane,(GtkWidget*)next, FALSE,TRUE); + if(prev) + gtk_paned_pack1(next,(GtkWidget*)prev, FALSE,TRUE); + + if(prev)g_object_unref(G_OBJECT(prev)); + g_object_unref(G_OBJECT(next)); + g_object_unref(G_OBJECT(multi_vpaned->focused_pane)); +} + +void gtk_multi_vpaned_set_scroll_value(GtkMultiVPaned * multi_vpaned, double value) +{ + gtk_adjustment_set_value(multi_vpaned->hadjust, value); + //g_signal_stop_emission_by_name(G_OBJECT(multi_vpaned->hscrollbar), "value-changed"); +} + +void gtk_multi_vpaned_scroll_value_changed(GtkAdjustment *adjust, gpointer multi_vpaned_arg) +{ + TimeWindow time_window; + TimeInterval *time_span; + LttTime time; + GtkMultiVPaned * multi_vpaned = (GtkMultiVPaned*)multi_vpaned_arg; + gdouble value = gtk_adjustment_get_value(adjust); + gdouble upper, lower, ratio; + Tab *tab = (Tab *)g_object_get_data(G_OBJECT(multi_vpaned), "Tab_Info"); + LttvTracesetContext * tsc = + LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context); + + time_window = tab->time_window; + + time_span = &tsc->time_span ; + lower = multi_vpaned->hadjust->lower; + upper = multi_vpaned->hadjust->upper; + ratio = (value - lower) / (upper - lower); + + time = ltt_time_sub(time_span->end_time, time_span->start_time); + time = ltt_time_mul(time, (float)ratio); + time = ltt_time_add(time_span->start_time, time); + + time_window.start_time = time; + + time = ltt_time_sub(time_span->end_time, time); + if(ltt_time_compare(time,time_window.time_width) < 0){ + time_window.time_width = time; + } + set_time_window(tab, &time_window); +} + + +static void +gtk_multi_vpaned_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + GtkPaned *paned = GTK_PANED (widget); + GtkRequisition child_requisition; + + requisition->width = 0; + requisition->height = 0; + + if (paned->child1 && GTK_WIDGET_VISIBLE (paned->child1)) + { + gtk_widget_size_request (paned->child1, &child_requisition); + + requisition->height = child_requisition.height; + requisition->width = child_requisition.width; + } + + if (paned->child2 && GTK_WIDGET_VISIBLE (paned->child2)) + { + gtk_widget_size_request (paned->child2, &child_requisition); + + requisition->width = MAX (requisition->width, child_requisition.width); + requisition->height += child_requisition.height; + } + + requisition->height += GTK_CONTAINER (paned)->border_width * 2; + requisition->width += GTK_CONTAINER (paned)->border_width * 2; + + if (paned->child1 && GTK_WIDGET_VISIBLE (paned->child1) && + paned->child2 && GTK_WIDGET_VISIBLE (paned->child2)) + { + gint handle_size; + + gtk_widget_style_get (widget, "handle_size", &handle_size, NULL); + requisition->height += handle_size; + } + +} + +static void +gtk_multi_vpaned_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkPaned *paned = GTK_PANED (widget); + gint border_width = GTK_CONTAINER (paned)->border_width; + + widget->allocation = *allocation; + + if (paned->child1 && GTK_WIDGET_VISIBLE (paned->child1) && + paned->child2 && GTK_WIDGET_VISIBLE (paned->child2)) + { + GtkRequisition child1_requisition; + GtkRequisition child2_requisition; + GtkAllocation child1_allocation; + GtkAllocation child2_allocation; + gint handle_size; + + gtk_widget_style_get (widget, "handle_size", &handle_size, NULL); + + gtk_widget_get_child_requisition (paned->child1, &child1_requisition); + gtk_widget_get_child_requisition (paned->child2, &child2_requisition); + + gtk_paned_compute_position (paned, + MAX (1, widget->allocation.height + - handle_size + - 2 * border_width), + child1_requisition.height, + child2_requisition.height); + + paned->handle_pos.x = widget->allocation.x + border_width; + paned->handle_pos.y = widget->allocation.y + paned->child1_size + border_width; + paned->handle_pos.width = MAX (1, (gint) widget->allocation.width - 2 * border_width); + paned->handle_pos.height = handle_size; + + if (GTK_WIDGET_REALIZED (widget)) + { + if (GTK_WIDGET_MAPPED (widget)) + gdk_window_show (paned->handle); + gdk_window_move_resize (paned->handle, + paned->handle_pos.x, + paned->handle_pos.y, + paned->handle_pos.width, + handle_size); + } + + child1_allocation.width = child2_allocation.width = MAX (1, (gint) allocation->width - border_width * 2); + child1_allocation.height = MAX (1, paned->child1_size); + child1_allocation.x = child2_allocation.x = widget->allocation.x + border_width; + child1_allocation.y = widget->allocation.y + border_width; + + child2_allocation.y = child1_allocation.y + paned->child1_size + paned->handle_pos.height; + child2_allocation.height = MAX (1, widget->allocation.y + widget->allocation.height - child2_allocation.y - border_width); + + if (GTK_WIDGET_MAPPED (widget) && + paned->child1->allocation.height < child1_allocation.height) + { + gtk_widget_size_allocate (paned->child2, &child2_allocation); + gtk_widget_size_allocate (paned->child1, &child1_allocation); + } + else + { + gtk_widget_size_allocate (paned->child1, &child1_allocation); + gtk_widget_size_allocate (paned->child2, &child2_allocation); + } + } + else + { + GtkAllocation child_allocation; + + if (GTK_WIDGET_REALIZED (widget)) + gdk_window_hide (paned->handle); + + if (paned->child1) + gtk_widget_set_child_visible (paned->child1, TRUE); + if (paned->child2) + gtk_widget_set_child_visible (paned->child2, TRUE); + + child_allocation.x = widget->allocation.x + border_width; + child_allocation.y = widget->allocation.y + border_width; + child_allocation.width = MAX (1, allocation->width - 2 * border_width); + child_allocation.height = MAX (1, allocation->height - 2 * border_width); + + if (paned->child1 && GTK_WIDGET_VISIBLE (paned->child1)) + gtk_widget_size_allocate (paned->child1, &child_allocation); + else if (paned->child2 && GTK_WIDGET_VISIBLE (paned->child2)) + gtk_widget_size_allocate (paned->child2, &child_allocation); + } +} + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/gtkmultivpaned.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/gtkmultivpaned.h new file mode 100644 index 00000000..9d6a49fb --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/gtkmultivpaned.h @@ -0,0 +1,90 @@ + +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Xiangxiu Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 __GTK_MULTI_VPANED_H__ +#define __GTK_MULTI_VPANED_H__ + + +#include +#include +#include +#include +#include // for TimeWindow + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GTK_TYPE_MULTI_VPANED (gtk_multi_vpaned_get_type ()) +#define GTK_MULTI_VPANED(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_MULTI_VPANED, GtkMultiVPaned)) +#define GTK_MULTI_VPANED_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_MULTI_VPANED, GtkMultiVPanedClass)) +#define GTK_IS_MULTI_VPANED(obj ) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_MULTI_VPANED)) +#define GTK_IS_MULTI_VPANED_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_MULTI_VPANED)) +#define GTK_MULTI_VPANED_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_MULTI_VPANED, GtkMultiVPanedClass)) + +typedef struct _GtkMultiVPaned GtkMultiVPaned; +typedef struct _GtkMultiVPanedClass GtkMultiVPanedClass; + +struct _GtkMultiVPaned +{ + GtkPaned container; + + /*< public >*/ + GtkPaned * first_pane; + GtkPaned * last_pane; + GtkPaned * focused_pane; + GtkPaned * iter; + guint num_children; + + GtkWidget * vbox; + // GtkWidget * scrollWindow; + // GtkWidget * viewport; + GtkWidget * hscrollbar; + GtkAdjustment *hadjust; +}; + +struct _GtkMultiVPanedClass +{ + GtkPanedClass parent_class; +}; + + +GType gtk_multi_vpaned_get_type (void) G_GNUC_CONST; +GtkWidget* gtk_multi_vpaned_new (void); + +void gtk_multi_vpaned_set_focus (GtkWidget * widget, GtkPaned *Paned); +void gtk_multi_vpaned_widget_add(GtkMultiVPaned * multi_vpaned, GtkWidget * widget1); +void gtk_multi_vpaned_widget_delete(GtkMultiVPaned * multi_vpaned); +void gtk_multi_vpaned_widget_move_up(GtkMultiVPaned * multi_vpaned); +void gtk_multi_vpaned_widget_move_down(GtkMultiVPaned * multi_vpaned); +void gtk_multi_vpaned_set_adjust(GtkMultiVPaned * multi_vpaned, const TimeWindow * time_window, gboolean first_time); +void gtk_multi_vpaned_set_data(GtkMultiVPaned * multi_vpaned, char * key, gpointer value); +gpointer gtk_multi_vpaned_get_data(GtkMultiVPaned * multi_vpaned, char * key); +GtkWidget * gtk_multi_vpaned_get_widget(GtkMultiVPaned * multi_vpaned); +GtkWidget * gtk_multi_vpaned_get_first_widget(GtkMultiVPaned * multi_vpaned); +GtkWidget * gtk_multi_vpaned_get_next_widget(GtkMultiVPaned * multi_vpaned); +void gtk_multi_vpaned_set_scroll_value(GtkMultiVPaned * multi_vpaned, double value); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GTK_MULTI_VPANED_H__ */ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/init_module.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/init_module.c new file mode 100644 index 00000000..c964481d --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/init_module.c @@ -0,0 +1,264 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers and XangXiu Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +/* + * Initial main.c file generated by Glade. Edit as required. + * Glade will not overwrite this file. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "interface.h" +#include "support.h" +#include +#include +#include "callbacks.h" +#include + + +__EXPORT LttvTraceInfo + LTTV_TRACES, + LTTV_COMPUTATION, + LTTV_VIEWER_CONSTRUCTORS, + LTTV_REQUESTS_QUEUE, + LTTV_REQUESTS_CURRENT, + LTTV_NOTIFY_QUEUE, + LTTV_NOTIFY_CURRENT, + LTTV_COMPUTATION_TRACESET, + LTTV_COMPUTATION_TRACESET_CONTEXT, + LTTV_COMPUTATION_SYNC_POSITION, + LTTV_BEFORE_CHUNK_TRACESET, + LTTV_BEFORE_CHUNK_TRACE, + LTTV_BEFORE_CHUNK_TRACEFILE, + LTTV_AFTER_CHUNK_TRACESET, + LTTV_AFTER_CHUNK_TRACE, + LTTV_AFTER_CHUNK_TRACEFILE, + LTTV_BEFORE_REQUEST, + LTTV_AFTER_REQUEST, + LTTV_EVENT_HOOK, + LTTV_EVENT_HOOK_BY_ID, + LTTV_HOOK_ADDER, + LTTV_HOOK_REMOVER, + LTTV_IN_PROGRESS, + LTTV_READY, + LTTV_LOCK; + + +/** Array containing instanced objects. */ +GSList * g_main_window_list = NULL ; + +LttvHooks + *main_hooks; + +/* Initial trace from command line */ +static GSList *g_init_trace = NULL; + +static char *a_trace; +//static char g_init_trace[PATH_MAX] = ""; + + +void lttv_trace_option(void *hook_data) +{ + //LttTrace *trace; + + //get_absolute_pathname(a_trace, g_init_trace); + g_init_trace = g_slist_append(g_init_trace, a_trace); +} + +/***************************************************************************** + * Functions for module loading/unloading * + *****************************************************************************/ +/** + * plugin's init function + * + * This function initializes the GUI. + */ + +static gboolean window_creation_hook(void *hook_data, void *call_data) +{ + g_debug("GUI window_creation_hook()"); +#ifdef ENABLE_NLS + bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); +#endif + + gtk_set_locale (); + gtk_init (<tv_argc, <tv_argv); + + add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps"); + add_pixmap_directory ("pixmaps"); + add_pixmap_directory ("../modules/gui/main/pixmaps"); + + /* First window, use command line trace */ + create_main_window_with_trace_list(g_init_trace); + + gtk_main (); + + return FALSE; +} + +static void init() { + + LttvAttributeValue value; + + // Global attributes only used for interaction with main() here. + LttvIAttribute *attributes = LTTV_IATTRIBUTE(lttv_global_attributes()); + + LTTV_TRACES = g_quark_from_string("traces"); + LTTV_COMPUTATION = g_quark_from_string("computation"); + LTTV_VIEWER_CONSTRUCTORS = g_quark_from_string("viewer_constructors"); + LTTV_REQUESTS_QUEUE = g_quark_from_string("requests_queue"); + LTTV_REQUESTS_CURRENT = g_quark_from_string("requests_current"); + LTTV_NOTIFY_QUEUE = g_quark_from_string("notify_queue"); + LTTV_NOTIFY_CURRENT = g_quark_from_string("notify_current"); + LTTV_COMPUTATION_TRACESET = g_quark_from_string("computation_traceset"); + LTTV_COMPUTATION_TRACESET_CONTEXT = + g_quark_from_string("computation_traceset_context"); + LTTV_COMPUTATION_SYNC_POSITION = + g_quark_from_string("computation_sync_position"); + LTTV_BEFORE_CHUNK_TRACESET = g_quark_from_string("before_chunk_traceset"); + LTTV_BEFORE_CHUNK_TRACE = g_quark_from_string("before_chunk_trace"); + LTTV_BEFORE_CHUNK_TRACEFILE = g_quark_from_string("before_chunk_tracefile"); + LTTV_AFTER_CHUNK_TRACESET = g_quark_from_string("after_chunk_traceset"); + LTTV_AFTER_CHUNK_TRACE = g_quark_from_string("after_chunk_trace"); + LTTV_AFTER_CHUNK_TRACEFILE = g_quark_from_string("after_chunk_tracefile"); + LTTV_BEFORE_REQUEST = g_quark_from_string("before_request"); + LTTV_AFTER_REQUEST = g_quark_from_string("after_request"); + LTTV_EVENT_HOOK = g_quark_from_string("event_hook"); + LTTV_EVENT_HOOK_BY_ID = g_quark_from_string("event_hook_by_id"); + LTTV_HOOK_ADDER = g_quark_from_string("hook_adder"); + LTTV_HOOK_REMOVER = g_quark_from_string("hook_remover"); + LTTV_IN_PROGRESS = g_quark_from_string("in_progress"); + LTTV_READY = g_quark_from_string("ready"); + LTTV_LOCK = g_quark_from_string("lock"); + + g_debug("GUI init()"); + + lttv_option_add("trace", 't', + "add a trace to the trace set to analyse", + "pathname of the directory containing the trace", + LTTV_OPT_STRING, &a_trace, lttv_trace_option, NULL); + + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/main/before", + LTTV_POINTER, &value)); + g_assert((main_hooks = *(value.v_pointer)) != NULL); + + lttv_hooks_add(main_hooks, window_creation_hook, NULL, LTTV_PRIO_DEFAULT); + + { + /* Register state calculator */ + LttvHooks *hook_adder = lttv_hooks_new(); + lttv_hooks_add(hook_adder, lttv_state_save_hook_add_event_hooks, NULL, + LTTV_PRIO_DEFAULT); + lttv_hooks_add(hook_adder, lttv_state_hook_add_event_hooks, NULL, + LTTV_PRIO_DEFAULT); + LttvHooks *hook_remover = lttv_hooks_new(); + lttv_hooks_add(hook_remover, lttv_state_save_hook_remove_event_hooks, + NULL, LTTV_PRIO_DEFAULT); + lttv_hooks_add(hook_remover, lttv_state_hook_remove_event_hooks, + NULL, LTTV_PRIO_DEFAULT); + /* Add state computation background hook adder to attributes */ + lttvwindowtraces_register_computation_hooks(g_quark_from_string("state"), + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + hook_adder, hook_remover); + } + + { + /* Register statistics calculator */ + LttvHooks *hook_adder = lttv_hooks_new(); + lttv_hooks_add(hook_adder, lttv_stats_hook_add_event_hooks, NULL, + LTTV_PRIO_DEFAULT); + lttv_hooks_add(hook_adder, lttv_state_hook_add_event_hooks, NULL, + LTTV_PRIO_DEFAULT); + LttvHooks *hook_remover = lttv_hooks_new(); + lttv_hooks_add(hook_remover, lttv_stats_hook_remove_event_hooks, + NULL, LTTV_PRIO_DEFAULT); + lttv_hooks_add(hook_remover, lttv_state_hook_remove_event_hooks, + NULL, LTTV_PRIO_DEFAULT); + LttvHooks *after_request = lttv_hooks_new(); + lttv_hooks_add(after_request, lttv_stats_sum_traceset_hook, NULL, + LTTV_PRIO_DEFAULT); + /* Add state computation background hook adder to attributes */ + lttvwindowtraces_register_computation_hooks(g_quark_from_string("stats"), + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + after_request, NULL, NULL, + hook_adder, hook_remover); + } +} + +void +main_window_destructor(MainWindow * mw) +{ + g_assert(GTK_IS_WIDGET(mw->mwindow)); + gtk_widget_destroy(mw->mwindow); +} + +static void destroy_walk(gpointer data, gpointer user_data) +{ + main_window_destructor((MainWindow*)data); +} + +/** + * plugin's destroy function + * + * This function releases the memory reserved by the module and unregisters + * everything that has been registered in the gtkTraceSet API. + */ +static void destroy() { + + LttvAttributeValue value; + LttvTrace *trace; + GSList *iter = NULL; + + lttv_option_remove("trace"); + + lttv_hooks_remove_data(main_hooks, window_creation_hook, NULL); + + g_debug("GUI destroy()"); + + g_slist_foreach(g_main_window_list, destroy_walk, NULL); + + g_slist_free(g_main_window_list); + + g_slist_free(g_init_trace); + +} + + +LTTV_MODULE("lttvwindow", "Viewer main window", \ + "Viewer with multiple windows, tabs and panes for graphical modules", \ + init, destroy, "stats", "option") diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/interface.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/interface.c new file mode 100644 index 00000000..007b33e6 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/interface.c @@ -0,0 +1,938 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 XangXiu Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#include "callbacks.h" +#include "interface.h" +#include "support.h" + +#define GLADE_HOOKUP_OBJECT(component,widget,name) \ + g_object_set_data_full (G_OBJECT (component), name, \ + gtk_widget_ref (widget), (GDestroyNotify) gtk_widget_unref) + +#define GLADE_HOOKUP_OBJECT_NO_REF(component,widget,name) \ + g_object_set_data (G_OBJECT (component), name, widget) + +GtkWidget* +create_MWindow (void) +{ + GtkWidget *MWindow; + GtkWidget *MVbox; + GtkWidget *MMenuBox; + GtkWidget *MenuMain; + GtkWidget *FileMenuTitle; + GtkWidget *FileMenuTitle_menu; + GtkWidget *FileMenuNewTitle; + GtkWidget *FileMenuNewTitle_menu; + // GtkWidget *EmptyTraceset; + GtkWidget *CloneTraceset; + GtkWidget *FileMenuNewSep; + GtkWidget *Tab; + // GtkWidget *OpenTraceset; + GtkWidget *Close; + GtkWidget *CloseTab; + GtkWidget *FileMenuSeparator1; + GtkWidget *AddTrace; + GtkWidget *RemoveTrace; + // GtkWidget *Save; + // GtkWidget *SaveAs; + GtkWidget *FileMenuSeparator2; + GtkWidget *Quit; + // GtkWidget *EditMenuTitle; + // GtkWidget *EditMenuTitle_menu; + // GtkWidget *Cut; + // GtkWidget *Copy; + // GtkWidget *Paste; + // GtkWidget *Delete; + GtkWidget *ViewMenuTitle; + GtkWidget *ViewMenuTitle_menu; + GtkWidget *ZoomIn; + GtkWidget *ZoomOut; + GtkWidget *ZoomExtended; + GtkWidget *ViewMenuSeparator; + // GtkWidget *GoToTime; + // GtkWidget *ShowTimeFrame; + GtkWidget *ToolMenuTitle; + GtkWidget *ToolMenuTitle_menu; + GtkWidget *MoveViewerUp; + GtkWidget *MoveViewerDown; + GtkWidget *RemoveViewer; + GtkWidget *ToolMenuSeparator; + GtkWidget *Filter; + // GtkWidget *Facility; + GtkWidget *ToolMenuSeparator1; + // GtkWidget *insert_viewer_test; + GtkWidget *PluginMenuTitle; + GtkWidget *PluginMenuTitle_menu; + GtkWidget *LoadLibrary; + GtkWidget *UnloadLibrary; + GtkWidget *LoadModule; + GtkWidget *UnloadModule; + GtkWidget *AddLibrarySearchPath; + GtkWidget *RemoveLibrarySearchPath; + // GtkWidget *OptionMenuTitle; + // GtkWidget *OptionMenuTitle_menu; + // GtkWidget *Color; + // GtkWidget *OptMenuSeparator; + // GtkWidget *OpenFilter; + // GtkWidget *SaveConfiguration; + GtkWidget *MenuHelp; + GtkWidget *HelpMenuTitle; + GtkWidget *HelpMenu; + GtkWidget *Content; + GtkWidget *HelpmenuSeparator; + GtkWidget *About; + GtkWidget *MToolbar1; + GtkWidget *tmp_toolbar_icon; + GtkWidget *tlbEmptyTraceset; + GtkWidget *tlbTab; + // GtkWidget *tlbOpenTraceset; + GtkWidget *tlbAddTrace; + GtkWidget *tlbRemoveTrace; + GtkWidget *tlbRedraw; + GtkWidget *tlbContinueProcessing; + GtkWidget *tlbStopProcessing; + // GtkWidget *tlbSave; + // GtkWidget *tlbSaveAs; + GtkWidget *tlbZoomIn; + GtkWidget *tlbZoomOut; + GtkWidget *tlbZoomExtended; + //GtkWidget *tlbGoToTime; + //GtkWidget *tlbShowTimeFrame; + GtkWidget *tlbMoveViewerUp; + GtkWidget *tlbMoveViewerDown; + GtkWidget *tlbRemoveViewer; + GtkWidget *MToolbar2; + GtkWidget *MNotebook; + // GtkWidget *empty_notebook_page; + // GtkWidget *label1; + GtkWidget *MStatusbar; + GtkAccelGroup *accel_group; + + accel_group = gtk_accel_group_new (); + + MWindow = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_widget_set_size_request (MWindow, 100, 50); + gtk_window_set_title (GTK_WINDOW (MWindow), "Linux Trace Toolkit Viewer"); + gtk_window_set_default_size (GTK_WINDOW (MWindow), + gdk_screen_width()*0.9, gdk_screen_height()*0.9); + + MVbox = gtk_vbox_new (FALSE, 0); + gtk_widget_show (MVbox); + gtk_container_add (GTK_CONTAINER (MWindow), MVbox); + + MMenuBox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (MMenuBox); + gtk_box_pack_start (GTK_BOX (MVbox), MMenuBox, FALSE, FALSE, 0); + + MenuMain = gtk_menu_bar_new (); + gtk_widget_show (MenuMain); + gtk_box_pack_start (GTK_BOX (MMenuBox), MenuMain, FALSE, FALSE, 0); + + FileMenuTitle = gtk_menu_item_new_with_mnemonic ("_File"); + gtk_widget_show (FileMenuTitle); + gtk_container_add (GTK_CONTAINER (MenuMain), FileMenuTitle); + + FileMenuTitle_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (FileMenuTitle), FileMenuTitle_menu); + + FileMenuNewTitle = gtk_menu_item_new_with_mnemonic ("New"); + gtk_widget_show (FileMenuNewTitle); + gtk_container_add (GTK_CONTAINER (FileMenuTitle_menu), FileMenuNewTitle); + + FileMenuNewTitle_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (FileMenuNewTitle), FileMenuNewTitle_menu); + + // EmptyTraceset = gtk_menu_item_new_with_mnemonic ("Empty trace set"); + // gtk_widget_show (EmptyTraceset); + // gtk_container_add (GTK_CONTAINER (FileMenuNewTitle_menu), EmptyTraceset); + + // CloneTraceset = gtk_menu_item_new_with_mnemonic ("Clone trace set"); + CloneTraceset = gtk_menu_item_new_with_mnemonic ("New window"); + gtk_widget_show (CloneTraceset); + gtk_container_add (GTK_CONTAINER (FileMenuNewTitle_menu), CloneTraceset); + + FileMenuNewSep = gtk_menu_item_new (); + gtk_widget_show (FileMenuNewSep); + gtk_container_add (GTK_CONTAINER (FileMenuNewTitle_menu), FileMenuNewSep); + gtk_widget_set_sensitive (FileMenuNewSep, FALSE); + + Tab = gtk_menu_item_new_with_mnemonic ("Tab"); + gtk_widget_show (Tab); + gtk_container_add (GTK_CONTAINER (FileMenuNewTitle_menu), Tab); +/* + OpenTraceset = gtk_menu_item_new_with_mnemonic ("Open"); + gtk_widget_show (OpenTraceset); + gtk_container_add (GTK_CONTAINER (FileMenuTitle_menu), OpenTraceset); +*/ + Close = gtk_menu_item_new_with_mnemonic ("Close"); + gtk_widget_show (Close); + gtk_container_add (GTK_CONTAINER (FileMenuTitle_menu), Close); + + CloseTab = gtk_menu_item_new_with_mnemonic ("Close Tab"); + gtk_widget_show (CloseTab); + gtk_container_add (GTK_CONTAINER (FileMenuTitle_menu), CloseTab); + + FileMenuSeparator1 = gtk_menu_item_new (); + gtk_widget_show (FileMenuSeparator1); + gtk_container_add (GTK_CONTAINER (FileMenuTitle_menu), FileMenuSeparator1); + gtk_widget_set_sensitive (FileMenuSeparator1, FALSE); + + AddTrace = gtk_menu_item_new_with_mnemonic ("Add Trace"); + gtk_widget_show (AddTrace); + gtk_container_add (GTK_CONTAINER (FileMenuTitle_menu), AddTrace); + + RemoveTrace = gtk_menu_item_new_with_mnemonic ("Remove Trace"); + gtk_widget_show (RemoveTrace); + gtk_container_add (GTK_CONTAINER (FileMenuTitle_menu), RemoveTrace); +/* + Save = gtk_menu_item_new_with_mnemonic ("Save"); + gtk_widget_show (Save); + gtk_container_add (GTK_CONTAINER (FileMenuTitle_menu), Save); + + SaveAs = gtk_menu_item_new_with_mnemonic ("Save As"); + gtk_widget_show (SaveAs); + gtk_container_add (GTK_CONTAINER (FileMenuTitle_menu), SaveAs); +*/ + FileMenuSeparator2 = gtk_menu_item_new (); + gtk_widget_show (FileMenuSeparator2); + gtk_container_add (GTK_CONTAINER (FileMenuTitle_menu), FileMenuSeparator2); + gtk_widget_set_sensitive (FileMenuSeparator2, FALSE); + + Quit = gtk_menu_item_new_with_mnemonic ("Quit"); + gtk_widget_show (Quit); + gtk_container_add (GTK_CONTAINER (FileMenuTitle_menu), Quit); +/* + EditMenuTitle = gtk_menu_item_new_with_mnemonic ("_Edit"); + gtk_widget_show (EditMenuTitle); + gtk_container_add (GTK_CONTAINER (MenuMain), EditMenuTitle); + + EditMenuTitle_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (EditMenuTitle), EditMenuTitle_menu); + + Cut = gtk_image_menu_item_new_from_stock ("gtk-cut", accel_group); + gtk_widget_show (Cut); + gtk_container_add (GTK_CONTAINER (EditMenuTitle_menu), Cut); + + Copy = gtk_image_menu_item_new_from_stock ("gtk-copy", accel_group); + gtk_widget_show (Copy); + gtk_container_add (GTK_CONTAINER (EditMenuTitle_menu), Copy); + + Paste = gtk_image_menu_item_new_from_stock ("gtk-paste", accel_group); + gtk_widget_show (Paste); + gtk_container_add (GTK_CONTAINER (EditMenuTitle_menu), Paste); + + Delete = gtk_image_menu_item_new_from_stock ("gtk-delete", accel_group); + gtk_widget_show (Delete); + gtk_container_add (GTK_CONTAINER (EditMenuTitle_menu), Delete); +*/ + ViewMenuTitle = gtk_menu_item_new_with_mnemonic ("_View"); + gtk_widget_show (ViewMenuTitle); + gtk_container_add (GTK_CONTAINER (MenuMain), ViewMenuTitle); + + ViewMenuTitle_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (ViewMenuTitle), ViewMenuTitle_menu); + + ZoomIn = gtk_menu_item_new_with_mnemonic ("Zoom in"); + gtk_widget_show (ZoomIn); + gtk_container_add (GTK_CONTAINER (ViewMenuTitle_menu), ZoomIn); + + ZoomOut = gtk_menu_item_new_with_mnemonic ("Zoom out"); + gtk_widget_show (ZoomOut); + gtk_container_add (GTK_CONTAINER (ViewMenuTitle_menu), ZoomOut); + + ZoomExtended = gtk_menu_item_new_with_mnemonic ("Zoom extended"); + gtk_widget_show (ZoomExtended); + gtk_container_add (GTK_CONTAINER (ViewMenuTitle_menu), ZoomExtended); + + ViewMenuSeparator = gtk_menu_item_new (); + gtk_widget_show (ViewMenuSeparator); + gtk_container_add (GTK_CONTAINER (ViewMenuTitle_menu), ViewMenuSeparator); + gtk_widget_set_sensitive (ViewMenuSeparator, FALSE); +/* + GoToTime = gtk_menu_item_new_with_mnemonic ("Go to time"); + gtk_widget_show (GoToTime); + gtk_container_add (GTK_CONTAINER (ViewMenuTitle_menu), GoToTime); + + ShowTimeFrame = gtk_menu_item_new_with_mnemonic ("Show time frame"); + gtk_widget_show (ShowTimeFrame); + gtk_container_add (GTK_CONTAINER (ViewMenuTitle_menu), ShowTimeFrame); +*/ + ToolMenuTitle = gtk_menu_item_new_with_mnemonic ("Tools"); + gtk_widget_show (ToolMenuTitle); + gtk_container_add (GTK_CONTAINER (MenuMain), ToolMenuTitle); + + ToolMenuTitle_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (ToolMenuTitle), ToolMenuTitle_menu); + + MoveViewerUp = gtk_menu_item_new_with_mnemonic ("Move viewer up"); + gtk_widget_show (MoveViewerUp); + gtk_container_add (GTK_CONTAINER (ToolMenuTitle_menu), MoveViewerUp); + + MoveViewerDown = gtk_menu_item_new_with_mnemonic ("Move viewer down"); + gtk_widget_show (MoveViewerDown); + gtk_container_add (GTK_CONTAINER (ToolMenuTitle_menu), MoveViewerDown); + + RemoveViewer = gtk_menu_item_new_with_mnemonic ("Remove viewer"); + gtk_widget_show (RemoveViewer); + gtk_container_add (GTK_CONTAINER (ToolMenuTitle_menu), RemoveViewer); + + ToolMenuSeparator = gtk_menu_item_new (); + gtk_widget_show (ToolMenuSeparator); + gtk_container_add (GTK_CONTAINER (ToolMenuTitle_menu), ToolMenuSeparator); + gtk_widget_set_sensitive (ToolMenuSeparator, FALSE); + + Filter = gtk_menu_item_new_with_mnemonic ("Trace Filter Selector"); + gtk_widget_show (Filter); + gtk_container_add (GTK_CONTAINER (ToolMenuTitle_menu), Filter); + + // Facility = gtk_menu_item_new_with_mnemonic ("Facility Selector"); + // gtk_widget_show (Facility); + // gtk_container_add (GTK_CONTAINER (ToolMenuTitle_menu), Facility); + + ToolMenuSeparator1 = gtk_menu_item_new (); + gtk_widget_show (ToolMenuSeparator1); + gtk_container_add (GTK_CONTAINER (ToolMenuTitle_menu), ToolMenuSeparator1); + gtk_widget_set_sensitive (ToolMenuSeparator1, FALSE); + + // insert_viewer_test = gtk_menu_item_new_with_mnemonic ("Insert viewer test"); + // gtk_widget_show (insert_viewer_test); + // gtk_container_add (GTK_CONTAINER (ToolMenuTitle_menu), insert_viewer_test); + + PluginMenuTitle = gtk_menu_item_new_with_mnemonic ("Plugins"); + gtk_widget_show (PluginMenuTitle); + gtk_container_add (GTK_CONTAINER (MenuMain), PluginMenuTitle); + + PluginMenuTitle_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (PluginMenuTitle), PluginMenuTitle_menu); + + LoadLibrary = gtk_menu_item_new_with_mnemonic ("Load library"); + gtk_widget_show (LoadLibrary); + gtk_container_add (GTK_CONTAINER (PluginMenuTitle_menu), LoadLibrary); + + UnloadLibrary = gtk_menu_item_new_with_mnemonic ("Unload library"); + gtk_widget_show (UnloadLibrary); + gtk_container_add (GTK_CONTAINER (PluginMenuTitle_menu), UnloadLibrary); + + LoadModule = gtk_menu_item_new_with_mnemonic ("Load module"); + gtk_widget_show (LoadModule); + gtk_container_add (GTK_CONTAINER (PluginMenuTitle_menu), LoadModule); + + UnloadModule = gtk_menu_item_new_with_mnemonic ("Unload module"); + gtk_widget_show (UnloadModule); + gtk_container_add (GTK_CONTAINER (PluginMenuTitle_menu), UnloadModule); + + AddLibrarySearchPath = gtk_menu_item_new_with_mnemonic ("Add library search path"); + gtk_widget_show (AddLibrarySearchPath); + gtk_container_add (GTK_CONTAINER (PluginMenuTitle_menu), AddLibrarySearchPath); + + RemoveLibrarySearchPath = gtk_menu_item_new_with_mnemonic ("Remove library search path"); + gtk_widget_show (RemoveLibrarySearchPath); + gtk_container_add (GTK_CONTAINER (PluginMenuTitle_menu), RemoveLibrarySearchPath); +/* + OptionMenuTitle = gtk_menu_item_new_with_mnemonic ("Options"); + gtk_widget_show (OptionMenuTitle); + gtk_container_add (GTK_CONTAINER (MenuMain), OptionMenuTitle); + + OptionMenuTitle_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (OptionMenuTitle), OptionMenuTitle_menu); + + Color = gtk_menu_item_new_with_mnemonic ("Color"); + gtk_widget_show (Color); + gtk_container_add (GTK_CONTAINER (OptionMenuTitle_menu), Color); + + OptMenuSeparator = gtk_menu_item_new (); + gtk_widget_show (OptMenuSeparator); + gtk_container_add (GTK_CONTAINER (OptionMenuTitle_menu), OptMenuSeparator); + gtk_widget_set_sensitive (OptMenuSeparator, FALSE); + + OpenFilter = gtk_menu_item_new_with_mnemonic ("Filter"); + gtk_widget_show (OpenFilter); + gtk_container_add (GTK_CONTAINER (OptionMenuTitle_menu), OpenFilter); + + SaveConfiguration = gtk_menu_item_new_with_mnemonic ("Save configuration"); + gtk_widget_show (SaveConfiguration); + gtk_container_add (GTK_CONTAINER (OptionMenuTitle_menu), SaveConfiguration); +*/ + MenuHelp = gtk_menu_bar_new (); + gtk_widget_show (MenuHelp); + gtk_box_pack_end (GTK_BOX (MMenuBox), MenuHelp, FALSE, FALSE, 0); + + HelpMenuTitle = gtk_menu_item_new_with_mnemonic ("_Help"); + gtk_widget_show (HelpMenuTitle); + gtk_container_add (GTK_CONTAINER (MenuHelp), HelpMenuTitle); + + HelpMenu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (HelpMenuTitle), HelpMenu); + + Content = gtk_menu_item_new_with_mnemonic ("Content"); + gtk_widget_show (Content); + gtk_container_add (GTK_CONTAINER (HelpMenu), Content); + + HelpmenuSeparator = gtk_menu_item_new (); + gtk_widget_show (HelpmenuSeparator); + gtk_container_add (GTK_CONTAINER (HelpMenu), HelpmenuSeparator); + gtk_widget_set_sensitive (HelpmenuSeparator, FALSE); + + About = gtk_menu_item_new_with_mnemonic ("About..."); + gtk_widget_show (About); + gtk_container_add (GTK_CONTAINER (HelpMenu), About); + + MToolbar1 = gtk_toolbar_new (); + gtk_widget_show (MToolbar1); + gtk_box_pack_start (GTK_BOX (MVbox), MToolbar1, FALSE, FALSE, 0); + gtk_toolbar_set_style (GTK_TOOLBAR (MToolbar1), GTK_TOOLBAR_ICONS); + + tmp_toolbar_icon = create_pixmap (MWindow, "filenew.png"); + tlbEmptyTraceset = gtk_toolbar_append_element (GTK_TOOLBAR (MToolbar1), + GTK_TOOLBAR_CHILD_BUTTON, + NULL, + "", + "New window", NULL, + //"New window with empty trace set", NULL, + tmp_toolbar_icon, NULL, NULL); + gtk_label_set_use_underline (GTK_LABEL (((GtkToolbarChild*) (g_list_last (GTK_TOOLBAR (MToolbar1)->children)->data))->label), TRUE); + gtk_widget_show (tlbEmptyTraceset); + gtk_container_set_border_width (GTK_CONTAINER (tlbEmptyTraceset), 1); + + tmp_toolbar_icon = create_pixmap (MWindow, "filenew.png"); + tlbTab = gtk_toolbar_append_element (GTK_TOOLBAR (MToolbar1), + GTK_TOOLBAR_CHILD_BUTTON, + NULL, + "", + "New tab", NULL, + tmp_toolbar_icon, NULL, NULL); + gtk_label_set_use_underline (GTK_LABEL (((GtkToolbarChild*) (g_list_last (GTK_TOOLBAR (MToolbar1)->children)->data))->label), TRUE); + gtk_widget_show (tlbTab); + gtk_container_set_border_width (GTK_CONTAINER (tlbTab), 1); + +/* + tmp_toolbar_icon = create_pixmap (MWindow, "fileopen.png"); + tlbOpenTraceset = gtk_toolbar_append_element (GTK_TOOLBAR (MToolbar1), + GTK_TOOLBAR_CHILD_BUTTON, + NULL, + "", + "open a trace set", NULL, + tmp_toolbar_icon, NULL, NULL); + gtk_label_set_use_underline (GTK_LABEL (((GtkToolbarChild*) (g_list_last (GTK_TOOLBAR (MToolbar1)->children)->data))->label), TRUE); + gtk_widget_show (tlbOpenTraceset); + gtk_container_set_border_width (GTK_CONTAINER (tlbOpenTraceset), 1); +*/ + tmp_toolbar_icon = create_pixmap (MWindow, "edit_add_22.png"); + tlbAddTrace = gtk_toolbar_append_element (GTK_TOOLBAR (MToolbar1), + GTK_TOOLBAR_CHILD_BUTTON, + NULL, + "", + "Add a trace ", NULL, + tmp_toolbar_icon, NULL, NULL); + gtk_label_set_use_underline (GTK_LABEL (((GtkToolbarChild*) (g_list_last (GTK_TOOLBAR (MToolbar1)->children)->data))->label), TRUE); + gtk_widget_show (tlbAddTrace); + gtk_container_set_border_width (GTK_CONTAINER (tlbAddTrace), 1); + + tmp_toolbar_icon = create_pixmap (MWindow, "edit_remove_22.png"); + tlbRemoveTrace = gtk_toolbar_append_element (GTK_TOOLBAR (MToolbar1), + GTK_TOOLBAR_CHILD_BUTTON, + NULL, + "", + "Remove a trace", NULL, + tmp_toolbar_icon, NULL, NULL); + gtk_label_set_use_underline (GTK_LABEL (((GtkToolbarChild*) (g_list_last (GTK_TOOLBAR (MToolbar1)->children)->data))->label), TRUE); + gtk_widget_show (tlbRemoveTrace); + gtk_container_set_border_width (GTK_CONTAINER (tlbRemoveTrace), 1); +/* + tmp_toolbar_icon = create_pixmap (MWindow, "filesave.png"); + tlbSave = gtk_toolbar_append_element (GTK_TOOLBAR (MToolbar1), + GTK_TOOLBAR_CHILD_BUTTON, + NULL, + "", + "save the current trace set", NULL, + tmp_toolbar_icon, NULL, NULL); + gtk_label_set_use_underline (GTK_LABEL (((GtkToolbarChild*) (g_list_last (GTK_TOOLBAR (MToolbar1)->children)->data))->label), TRUE); + gtk_widget_show (tlbSave); + gtk_container_set_border_width (GTK_CONTAINER (tlbSave), 1); + + tmp_toolbar_icon = create_pixmap (MWindow, "filesaveas.png"); + tlbSaveAs = gtk_toolbar_append_element (GTK_TOOLBAR (MToolbar1), + GTK_TOOLBAR_CHILD_BUTTON, + NULL, + "", + "save as ", NULL, + tmp_toolbar_icon, NULL, NULL); + gtk_label_set_use_underline (GTK_LABEL (((GtkToolbarChild*) (g_list_last (GTK_TOOLBAR (MToolbar1)->children)->data))->label), TRUE); + gtk_widget_show (tlbSaveAs); + gtk_container_set_border_width (GTK_CONTAINER (tlbSaveAs), 1); +*/ + gtk_toolbar_append_space (GTK_TOOLBAR (MToolbar1)); + + /* Manually added by Mathieu Desnoyers */ + + tmp_toolbar_icon = create_pixmap (MWindow, "stock_refresh_24.png"); + tlbRedraw = gtk_toolbar_append_element (GTK_TOOLBAR (MToolbar1), + GTK_TOOLBAR_CHILD_BUTTON, + NULL, + "", + "Redraw", NULL, + tmp_toolbar_icon, NULL, NULL); + gtk_label_set_use_underline (GTK_LABEL (((GtkToolbarChild*) (g_list_last (GTK_TOOLBAR (MToolbar1)->children)->data))->label), TRUE); + gtk_widget_show (tlbRedraw); + gtk_container_set_border_width (GTK_CONTAINER (tlbRedraw), 1); + + tmp_toolbar_icon = create_pixmap (MWindow, "stock_redo_24.png"); + tlbContinueProcessing = gtk_toolbar_append_element (GTK_TOOLBAR (MToolbar1), + GTK_TOOLBAR_CHILD_BUTTON, + NULL, + "", + "Continue Processing", NULL, + tmp_toolbar_icon, NULL, NULL); + gtk_label_set_use_underline (GTK_LABEL (((GtkToolbarChild*) (g_list_last (GTK_TOOLBAR (MToolbar1)->children)->data))->label), TRUE); + gtk_widget_show (tlbContinueProcessing); + gtk_container_set_border_width (GTK_CONTAINER (tlbContinueProcessing), 1); + + tmp_toolbar_icon = create_pixmap (MWindow, "stock_stop_24.png"); + tlbStopProcessing = gtk_toolbar_append_element (GTK_TOOLBAR (MToolbar1), + GTK_TOOLBAR_CHILD_BUTTON, + NULL, + "", + "Stop Processing", NULL, + tmp_toolbar_icon, NULL, NULL); + gtk_label_set_use_underline (GTK_LABEL (((GtkToolbarChild*) (g_list_last (GTK_TOOLBAR (MToolbar1)->children)->data))->label), TRUE); + gtk_widget_show (tlbStopProcessing); + gtk_container_set_border_width (GTK_CONTAINER (tlbStopProcessing), 1); + GLADE_HOOKUP_OBJECT (MWindow, tlbStopProcessing, "StopProcessingButton"); + + + gtk_toolbar_append_space (GTK_TOOLBAR (MToolbar1)); + + tmp_toolbar_icon = create_pixmap (MWindow, "stock_zoom_in_24.png"); + tlbZoomIn = gtk_toolbar_append_element (GTK_TOOLBAR (MToolbar1), + GTK_TOOLBAR_CHILD_BUTTON, + NULL, + "", + "Zoom in", NULL, + tmp_toolbar_icon, NULL, NULL); + gtk_label_set_use_underline (GTK_LABEL (((GtkToolbarChild*) (g_list_last (GTK_TOOLBAR (MToolbar1)->children)->data))->label), TRUE); + gtk_widget_show (tlbZoomIn); + gtk_container_set_border_width (GTK_CONTAINER (tlbZoomIn), 1); + + tmp_toolbar_icon = create_pixmap (MWindow, "stock_zoom_out_24.png"); + tlbZoomOut = gtk_toolbar_append_element (GTK_TOOLBAR (MToolbar1), + GTK_TOOLBAR_CHILD_BUTTON, + NULL, + "", + "Zoom out", NULL, + tmp_toolbar_icon, NULL, NULL); + gtk_label_set_use_underline (GTK_LABEL (((GtkToolbarChild*) (g_list_last (GTK_TOOLBAR (MToolbar1)->children)->data))->label), TRUE); + gtk_widget_show (tlbZoomOut); + gtk_container_set_border_width (GTK_CONTAINER (tlbZoomOut), 1); + + tmp_toolbar_icon = create_pixmap (MWindow, "stock_zoom_fit_24.png"); + tlbZoomExtended = gtk_toolbar_append_element (GTK_TOOLBAR (MToolbar1), + GTK_TOOLBAR_CHILD_BUTTON, + NULL, + "", + "Zoom extended", NULL, + tmp_toolbar_icon, NULL, NULL); + gtk_label_set_use_underline (GTK_LABEL (((GtkToolbarChild*) (g_list_last (GTK_TOOLBAR (MToolbar1)->children)->data))->label), TRUE); + gtk_widget_show (tlbZoomExtended); + gtk_container_set_border_width (GTK_CONTAINER (tlbZoomExtended), 1); + + /* + tmp_toolbar_icon = create_pixmap (MWindow, "gtk-jump-to.png"); + tlbGoToTime = gtk_toolbar_append_element (GTK_TOOLBAR (MToolbar1), + GTK_TOOLBAR_CHILD_BUTTON, + NULL, + "", + "Go to time", NULL, + tmp_toolbar_icon, NULL, NULL); + gtk_label_set_use_underline (GTK_LABEL (((GtkToolbarChild*) (g_list_last (GTK_TOOLBAR (MToolbar1)->children)->data))->label), TRUE); + gtk_widget_show (tlbGoToTime); + gtk_container_set_border_width (GTK_CONTAINER (tlbGoToTime), 1); + + tmp_toolbar_icon = create_pixmap (MWindow, "mini-display.xpm"); + tlbShowTimeFrame = gtk_toolbar_append_element (GTK_TOOLBAR (MToolbar1), + GTK_TOOLBAR_CHILD_BUTTON, + NULL, + "", + "Show time frame", NULL, + tmp_toolbar_icon, NULL, NULL); + gtk_label_set_use_underline (GTK_LABEL (((GtkToolbarChild*) (g_list_last (GTK_TOOLBAR (MToolbar1)->children)->data))->label), TRUE); + gtk_widget_show (tlbShowTimeFrame); + gtk_container_set_border_width (GTK_CONTAINER (tlbShowTimeFrame), 1); + */ + gtk_toolbar_append_space (GTK_TOOLBAR (MToolbar1)); + + tmp_toolbar_icon = create_pixmap (MWindow, "1uparrow.png"); + tlbMoveViewerUp = gtk_toolbar_append_element (GTK_TOOLBAR (MToolbar1), + GTK_TOOLBAR_CHILD_BUTTON, + NULL, + "", + "Move up current viewer", NULL, + tmp_toolbar_icon, NULL, NULL); + gtk_label_set_use_underline (GTK_LABEL (((GtkToolbarChild*) (g_list_last (GTK_TOOLBAR (MToolbar1)->children)->data))->label), TRUE); + gtk_widget_show (tlbMoveViewerUp); + gtk_container_set_border_width (GTK_CONTAINER (tlbMoveViewerUp), 1); + + tmp_toolbar_icon = create_pixmap (MWindow, "1downarrow.png"); + tlbMoveViewerDown = gtk_toolbar_append_element (GTK_TOOLBAR (MToolbar1), + GTK_TOOLBAR_CHILD_BUTTON, + NULL, + "", + "Move down current viewer", NULL, + tmp_toolbar_icon, NULL, NULL); + gtk_label_set_use_underline (GTK_LABEL (((GtkToolbarChild*) (g_list_last (GTK_TOOLBAR (MToolbar1)->children)->data))->label), TRUE); + gtk_widget_show (tlbMoveViewerDown); + gtk_container_set_border_width (GTK_CONTAINER (tlbMoveViewerDown), 1); + + tmp_toolbar_icon = create_pixmap (MWindow, "remove.png"); + tlbRemoveViewer = gtk_toolbar_append_element (GTK_TOOLBAR (MToolbar1), + GTK_TOOLBAR_CHILD_BUTTON, + NULL, + "", + "Delete current viewer", NULL, + tmp_toolbar_icon, NULL, NULL); + gtk_label_set_use_underline (GTK_LABEL (((GtkToolbarChild*) (g_list_last (GTK_TOOLBAR (MToolbar1)->children)->data))->label), TRUE); + gtk_widget_show (tlbRemoveViewer); + gtk_container_set_border_width (GTK_CONTAINER (tlbRemoveViewer), 1); + + gtk_toolbar_append_space (GTK_TOOLBAR (MToolbar1)); + + // MToolbar2 = gtk_toolbar_new (); + // gtk_widget_show (MToolbar2); + // gtk_box_pack_start (GTK_BOX (MVbox), MToolbar2, FALSE, FALSE, 0); + // gtk_toolbar_set_style (GTK_TOOLBAR (MToolbar2), GTK_TOOLBAR_ICONS); + + MNotebook = gtk_notebook_new (); + gtk_widget_show (MNotebook); + gtk_notebook_set_show_tabs((GtkNotebook*)MNotebook, FALSE); + gtk_box_pack_start (GTK_BOX (MVbox), MNotebook, TRUE, TRUE, 0); + +/* + empty_notebook_page = gtk_vbox_new (FALSE, 0); + gtk_widget_show (empty_notebook_page); + gtk_container_add (GTK_CONTAINER (MNotebook), empty_notebook_page); + + label1 = gtk_label_new (""); + gtk_widget_show (label1); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (MNotebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (MNotebook), 0), label1); + gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT); +*/ + MStatusbar = gtk_statusbar_new (); + gtk_widget_show (MStatusbar); + gtk_box_pack_start (GTK_BOX (MVbox), MStatusbar, FALSE, FALSE, 0); + + g_signal_connect ((gpointer) MWindow, "destroy", + G_CALLBACK (on_MWindow_destroy), + NULL); + g_signal_connect ((gpointer) MWindow, "configure-event", + G_CALLBACK (on_MWindow_configure), + NULL); + + // g_signal_connect ((gpointer) EmptyTraceset, "activate", + // G_CALLBACK (on_empty_traceset_activate), + // NULL); + g_signal_connect ((gpointer) CloneTraceset, "activate", + G_CALLBACK (on_clone_traceset_activate), + NULL); + g_signal_connect ((gpointer) Tab, "activate", + G_CALLBACK (on_tab_activate), + NULL); +/* + g_signal_connect ((gpointer) OpenTraceset, "activate", + G_CALLBACK (on_open_activate), + NULL); +*/ + g_signal_connect ((gpointer) Close, "activate", + G_CALLBACK (on_close_activate), + NULL); + g_signal_connect ((gpointer) CloseTab, "activate", + G_CALLBACK (on_close_tab_activate), + NULL); + g_signal_connect ((gpointer) AddTrace, "activate", + G_CALLBACK (on_add_trace_activate), + NULL); + g_signal_connect ((gpointer) RemoveTrace, "activate", + G_CALLBACK (on_remove_trace_activate), + NULL); +/* + g_signal_connect ((gpointer) Save, "activate", + G_CALLBACK (on_save_activate), + NULL); + g_signal_connect ((gpointer) SaveAs, "activate", + G_CALLBACK (on_save_as_activate), + NULL); +*/ + g_signal_connect ((gpointer) Quit, "activate", + G_CALLBACK (on_quit_activate), + NULL); +/* + g_signal_connect ((gpointer) Cut, "activate", + G_CALLBACK (on_cut_activate), + NULL); + g_signal_connect ((gpointer) Copy, "activate", + G_CALLBACK (on_copy_activate), + NULL); + g_signal_connect ((gpointer) Paste, "activate", + G_CALLBACK (on_paste_activate), + NULL); + g_signal_connect ((gpointer) Delete, "activate", + G_CALLBACK (on_delete_activate), + NULL); +*/ + g_signal_connect ((gpointer) ZoomIn, "activate", + G_CALLBACK (on_zoom_in_activate), + NULL); + g_signal_connect ((gpointer) ZoomOut, "activate", + G_CALLBACK (on_zoom_out_activate), + NULL); + g_signal_connect ((gpointer) ZoomExtended, "activate", + G_CALLBACK (on_zoom_extended_activate), + NULL); +/* + g_signal_connect ((gpointer) GoToTime, "activate", + G_CALLBACK (on_go_to_time_activate), + NULL); + g_signal_connect ((gpointer) ShowTimeFrame, "activate", + G_CALLBACK (on_show_time_frame_activate), + NULL); +*/ + g_signal_connect ((gpointer) MoveViewerUp, "activate", + G_CALLBACK (on_move_viewer_up_activate), + NULL); + g_signal_connect ((gpointer) MoveViewerDown, "activate", + G_CALLBACK (on_move_viewer_down_activate), + NULL); + g_signal_connect ((gpointer) RemoveViewer, "activate", + G_CALLBACK (on_remove_viewer_activate), + NULL); + //g_signal_connect ((gpointer) Filter, "activate", + // G_CALLBACK (on_trace_filter_activate), + // NULL); + // g_signal_connect ((gpointer) Facility, "activate", + // G_CALLBACK (on_trace_facility_activate), + // NULL); + // g_signal_connect ((gpointer) insert_viewer_test, "activate", + // G_CALLBACK (on_insert_viewer_test_activate), + // NULL); + g_signal_connect ((gpointer) LoadLibrary, "activate", + G_CALLBACK (on_load_library_activate), + NULL); + g_signal_connect ((gpointer) UnloadLibrary, "activate", + G_CALLBACK (on_unload_library_activate), + NULL); + g_signal_connect ((gpointer) LoadModule, "activate", + G_CALLBACK (on_load_module_activate), + NULL); + g_signal_connect ((gpointer) UnloadModule, "activate", + G_CALLBACK (on_unload_module_activate), + NULL); + g_signal_connect ((gpointer) AddLibrarySearchPath, "activate", + G_CALLBACK (on_add_library_search_path_activate), + NULL); + g_signal_connect ((gpointer) RemoveLibrarySearchPath, "activate", + G_CALLBACK (on_remove_library_search_path_activate), + NULL); +/* + g_signal_connect ((gpointer) Color, "activate", + G_CALLBACK (on_color_activate), + NULL); + g_signal_connect ((gpointer) OpenFilter, "activate", + G_CALLBACK (on_filter_activate), + NULL); + g_signal_connect ((gpointer) SaveConfiguration, "activate", + G_CALLBACK (on_save_configuration_activate), + NULL); +*/ + g_signal_connect ((gpointer) Content, "activate", + G_CALLBACK (on_content_activate), + NULL); + g_signal_connect ((gpointer) About, "activate", + G_CALLBACK (on_about_activate), + NULL); + g_signal_connect ((gpointer) tlbEmptyTraceset, "clicked", + G_CALLBACK (on_button_new_clicked), + NULL); + g_signal_connect ((gpointer) tlbTab, "clicked", + G_CALLBACK (on_button_new_tab_clicked), + NULL); +/* + g_signal_connect ((gpointer) tlbOpenTraceset, "clicked", + G_CALLBACK (on_button_open_clicked), + NULL); +*/ + g_signal_connect ((gpointer) tlbAddTrace, "clicked", + G_CALLBACK (on_button_add_trace_clicked), + NULL); + g_signal_connect ((gpointer) tlbRemoveTrace, "clicked", + G_CALLBACK (on_button_remove_trace_clicked), + NULL); + g_signal_connect ((gpointer) tlbRedraw, "clicked", + G_CALLBACK (on_button_redraw_clicked), + NULL); + g_signal_connect ((gpointer) tlbContinueProcessing, "clicked", + G_CALLBACK (on_button_continue_processing_clicked), + NULL); + g_signal_connect ((gpointer) tlbStopProcessing, "clicked", + G_CALLBACK (on_button_stop_processing_clicked), + NULL); +/* + g_signal_connect ((gpointer) tlbSave, "clicked", + G_CALLBACK (on_button_save_clicked), + NULL); + g_signal_connect ((gpointer) tlbSaveAs, "clicked", + G_CALLBACK (on_button_save_as_clicked), + NULL); +*/ + g_signal_connect ((gpointer) tlbZoomIn, "clicked", + G_CALLBACK (on_button_zoom_in_clicked), + NULL); + g_signal_connect ((gpointer) tlbZoomOut, "clicked", + G_CALLBACK (on_button_zoom_out_clicked), + NULL); + g_signal_connect ((gpointer) tlbZoomExtended, "clicked", + G_CALLBACK (on_button_zoom_extended_clicked), + NULL); + /* + g_signal_connect ((gpointer) tlbGoToTime, "clicked", + G_CALLBACK (on_button_go_to_time_clicked), + NULL); + g_signal_connect ((gpointer) tlbShowTimeFrame, "clicked", + G_CALLBACK (on_button_show_time_frame_clicked), + NULL); + */ + g_signal_connect ((gpointer) tlbMoveViewerUp, "clicked", + G_CALLBACK (on_button_move_up_clicked), + NULL); + g_signal_connect ((gpointer) tlbMoveViewerDown, "clicked", + G_CALLBACK (on_button_move_down_clicked), + NULL); + g_signal_connect ((gpointer) tlbRemoveViewer, "clicked", + G_CALLBACK (on_button_delete_viewer_clicked), + NULL); + g_signal_connect ((gpointer) MNotebook, "switch_page", + G_CALLBACK (on_MNotebook_switch_page), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (MWindow, MWindow, "MWindow"); + GLADE_HOOKUP_OBJECT (MWindow, MVbox, "MVbox"); + GLADE_HOOKUP_OBJECT (MWindow, MMenuBox, "MMenuBox"); + GLADE_HOOKUP_OBJECT (MWindow, MenuMain, "MenuMain"); + GLADE_HOOKUP_OBJECT (MWindow, FileMenuTitle, "FileMenuTitle"); + GLADE_HOOKUP_OBJECT (MWindow, FileMenuTitle_menu, "FileMenuTitle_menu"); + GLADE_HOOKUP_OBJECT (MWindow, FileMenuNewTitle, "FileMenuNewTitle"); + GLADE_HOOKUP_OBJECT (MWindow, FileMenuNewTitle_menu, "FileMenuNewTitle_menu"); + // GLADE_HOOKUP_OBJECT (MWindow, EmptyTraceset, "EmptyTraceset"); + GLADE_HOOKUP_OBJECT (MWindow, CloneTraceset, "CloneTraceset"); + GLADE_HOOKUP_OBJECT (MWindow, FileMenuNewSep, "FileMenuNewSep"); + GLADE_HOOKUP_OBJECT (MWindow, Tab, "Tab"); + // GLADE_HOOKUP_OBJECT (MWindow, OpenTraceset, "OpenTraceset"); + GLADE_HOOKUP_OBJECT (MWindow, Close, "Close"); + GLADE_HOOKUP_OBJECT (MWindow, CloseTab, "CloseTab"); + GLADE_HOOKUP_OBJECT (MWindow, FileMenuSeparator1, "FileMenuSeparator1"); + GLADE_HOOKUP_OBJECT (MWindow, AddTrace, "AddTrace"); + GLADE_HOOKUP_OBJECT (MWindow, RemoveTrace, "RemoveTrace"); + // GLADE_HOOKUP_OBJECT (MWindow, Save, "Save"); + // GLADE_HOOKUP_OBJECT (MWindow, SaveAs, "SaveAs"); + GLADE_HOOKUP_OBJECT (MWindow, FileMenuSeparator2, "FileMenuSeparator2"); + GLADE_HOOKUP_OBJECT (MWindow, Quit, "Quit"); + // GLADE_HOOKUP_OBJECT (MWindow, EditMenuTitle, "EditMenuTitle"); + // GLADE_HOOKUP_OBJECT (MWindow, EditMenuTitle_menu, "EditMenuTitle_menu"); + // GLADE_HOOKUP_OBJECT (MWindow, Cut, "Cut"); + // GLADE_HOOKUP_OBJECT (MWindow, Copy, "Copy"); + // GLADE_HOOKUP_OBJECT (MWindow, Paste, "Paste"); + // GLADE_HOOKUP_OBJECT (MWindow, Delete, "Delete"); + GLADE_HOOKUP_OBJECT (MWindow, ViewMenuTitle, "ViewMenuTitle"); + GLADE_HOOKUP_OBJECT (MWindow, ViewMenuTitle_menu, "ViewMenuTitle_menu"); + GLADE_HOOKUP_OBJECT (MWindow, ZoomIn, "ZoomIn"); + GLADE_HOOKUP_OBJECT (MWindow, ZoomOut, "ZoomOut"); + GLADE_HOOKUP_OBJECT (MWindow, ZoomExtended, "ZoomExtended"); + GLADE_HOOKUP_OBJECT (MWindow, ViewMenuSeparator, "ViewMenuSeparator"); + // GLADE_HOOKUP_OBJECT (MWindow, GoToTime, "GoToTime"); + // GLADE_HOOKUP_OBJECT (MWindow, ShowTimeFrame, "ShowTimeFrame"); + GLADE_HOOKUP_OBJECT (MWindow, ToolMenuTitle, "ToolMenuTitle"); + GLADE_HOOKUP_OBJECT (MWindow, ToolMenuTitle_menu, "ToolMenuTitle_menu"); + GLADE_HOOKUP_OBJECT (MWindow, MoveViewerUp, "MoveViewerUp"); + GLADE_HOOKUP_OBJECT (MWindow, MoveViewerDown, "MoveViewerDown"); + GLADE_HOOKUP_OBJECT (MWindow, RemoveViewer, "RemoveViewer"); + GLADE_HOOKUP_OBJECT (MWindow, ToolMenuSeparator, "ToolMenuSeparator"); + GLADE_HOOKUP_OBJECT (MWindow, Filter, "Filter"); + // GLADE_HOOKUP_OBJECT (MWindow, Facility, "Facility"); + GLADE_HOOKUP_OBJECT (MWindow, ToolMenuSeparator1, "ToolMenuSeparator1"); + // GLADE_HOOKUP_OBJECT (MWindow, insert_viewer_test, "insert_viewer_test"); + GLADE_HOOKUP_OBJECT (MWindow, PluginMenuTitle, "PluginMenuTitle"); + GLADE_HOOKUP_OBJECT (MWindow, PluginMenuTitle_menu, "PluginMenuTitle_menu"); + GLADE_HOOKUP_OBJECT (MWindow, LoadLibrary, "LoadLibrary"); + GLADE_HOOKUP_OBJECT (MWindow, UnloadLibrary, "UnloadLibrary"); + GLADE_HOOKUP_OBJECT (MWindow, LoadModule, "LoadModule"); + GLADE_HOOKUP_OBJECT (MWindow, UnloadModule, "UnloadModule"); + GLADE_HOOKUP_OBJECT (MWindow, AddLibrarySearchPath, "AddLibrarySearchPath"); + GLADE_HOOKUP_OBJECT (MWindow, RemoveLibrarySearchPath, "RemoveLibrarySearchPath"); + // GLADE_HOOKUP_OBJECT (MWindow, OptionMenuTitle, "OptionMenuTitle"); + // GLADE_HOOKUP_OBJECT (MWindow, OptionMenuTitle_menu, "OptionMenuTitle_menu"); + // GLADE_HOOKUP_OBJECT (MWindow, Color, "Color"); + // GLADE_HOOKUP_OBJECT (MWindow, OptMenuSeparator, "OptMenuSeparator"); + // GLADE_HOOKUP_OBJECT (MWindow, OpenFilter, "OpenFilter"); + // GLADE_HOOKUP_OBJECT (MWindow, SaveConfiguration, "SaveConfiguration"); + GLADE_HOOKUP_OBJECT (MWindow, MenuHelp, "MenuHelp"); + GLADE_HOOKUP_OBJECT (MWindow, HelpMenuTitle, "HelpMenuTitle"); + GLADE_HOOKUP_OBJECT (MWindow, HelpMenu, "HelpMenu"); + GLADE_HOOKUP_OBJECT (MWindow, Content, "Content"); + GLADE_HOOKUP_OBJECT (MWindow, HelpmenuSeparator, "HelpmenuSeparator"); + GLADE_HOOKUP_OBJECT (MWindow, About, "About"); + GLADE_HOOKUP_OBJECT (MWindow, MToolbar1, "MToolbar1"); + GLADE_HOOKUP_OBJECT (MWindow, tlbEmptyTraceset, "tlbEmptyTraceset"); + GLADE_HOOKUP_OBJECT (MWindow, tlbTab, "tlbTab"); + // GLADE_HOOKUP_OBJECT (MWindow, tlbOpenTraceset, "tlbOpenTraceset"); + GLADE_HOOKUP_OBJECT (MWindow, tlbAddTrace, "tlbAddTrace"); + GLADE_HOOKUP_OBJECT (MWindow, tlbRemoveTrace, "tlbRemoveTrace"); + // GLADE_HOOKUP_OBJECT (MWindow, tlbSave, "tlbSave"); + // GLADE_HOOKUP_OBJECT (MWindow, tlbSaveAs, "tlbSaveAs"); + GLADE_HOOKUP_OBJECT (MWindow, tlbZoomIn, "tlbZoomIn"); + GLADE_HOOKUP_OBJECT (MWindow, tlbZoomOut, "tlbZoomOut"); + GLADE_HOOKUP_OBJECT (MWindow, tlbZoomExtended, "tlbZoomExtended"); + // GLADE_HOOKUP_OBJECT (MWindow, tlbGoToTime, "tlbGoToTime"); + // GLADE_HOOKUP_OBJECT (MWindow, tlbShowTimeFrame, "tlbShowTimeFrame"); + GLADE_HOOKUP_OBJECT (MWindow, tlbMoveViewerUp, "tlbMoveViewerUp"); + GLADE_HOOKUP_OBJECT (MWindow, tlbMoveViewerDown, "tlbMoveViewerDown"); + GLADE_HOOKUP_OBJECT (MWindow, tlbRemoveViewer, "tlbRemoveViewer"); + // GLADE_HOOKUP_OBJECT (MWindow, MToolbar2, "MToolbar2"); + GLADE_HOOKUP_OBJECT (MWindow, MNotebook, "MNotebook"); + // GLADE_HOOKUP_OBJECT (MWindow, label1, "label1"); + GLADE_HOOKUP_OBJECT (MWindow, MStatusbar, "MStatusbar"); + + gtk_window_add_accel_group (GTK_WINDOW (MWindow), accel_group); + + return MWindow; +} + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/interface.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/interface.h new file mode 100644 index 00000000..05d88466 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/interface.h @@ -0,0 +1,23 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 XangXiu Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +GtkWidget* create_MWindow (void); diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttv_plugin.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttv_plugin.c new file mode 100644 index 00000000..f7b4ded4 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttv_plugin.c @@ -0,0 +1,73 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2006 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 "lttv_plugin.h" + +/* + * forward definitions + */ + + + +static void +lttv_plugin_class_init (LttvPluginClass *klass) +{ + klass->update_filter = NULL; /* Pure Virtual */ +} + +static void +lttv_plugin_instance_init (GTypeInstance *instance, gpointer g_class) +{ + LttvPlugin *self; + self->top_widget = NULL; +} + +GType +lttv_plugin_get_type (void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (LttvPluginClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + lttv_plugin_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (LttvPlugin), + 0, /* n_preallocs */ + NULL /* instance_init */ + }; + type = g_type_register_static (G_TYPE_OBJECT, + "LttvPluginType", + &info, 0); + } + return type; +} + + +/* implementation in the source file */ +__EXPORT void lttv_plugin_update_filter (LttvPlugin *self, LttvFilter *filter) +{ + LTTV_PLUGIN_GET_CLASS (self)->update_filter (self, filter); +} + + + + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttv_plugin.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttv_plugin.h new file mode 100644 index 00000000..4db2d04c --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttv_plugin.h @@ -0,0 +1,72 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2006 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +/* The LttvPlugin class is a pure virtual class. It only contains the functions + * available for interaction with a plugin (tab or viewer). + */ + +#ifndef LTTV_PLUGIN_H +#define LTTV_PLUGIN_H + +#include +#include + +/* + * Type macros. + */ + +#define LTTV_TYPE_PLUGIN (lttv_plugin_get_type ()) +#define LTTV_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LTTV_TYPE_PLUGIN, LttvPlugin)) +#define LTTV_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), LTTV_TYPE_PLUGIN, LttvPluginClass)) +#define LTTV_IS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LTTV_TYPE_PLUGIN)) +#define LTTV_IS_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), LTTV_TYPE_PLUGIN)) +#define LTTV_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), LTTV_TYPE_PLUGIN, LttvPluginClass)) + +typedef struct _LttvPlugin LttvPlugin; +typedef struct _LttvPluginClass LttvPluginClass; + +struct _LttvPlugin { + GObject parent; + /* instance members */ + GtkWidget *top_widget; + + /* private */ +}; + +struct _LttvPluginClass { + GObjectClass parent; + + void (*update_filter) (LttvPlugin *self, LttvFilter *filter); + + /* class members */ +}; + +/* used by LTTV_PLUGIN_TYPE */ +GType lttv_plugin_get_type (void); + +/* + * Method definitions. + */ + +/* declaration in the header. */ +void lttv_plugin_update_filter (LttvPlugin *self, LttvFilter *filter); + + + + +#endif diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttv_plugin_tab.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttv_plugin_tab.c new file mode 100644 index 00000000..2e641387 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttv_plugin_tab.c @@ -0,0 +1,83 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2006 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 "lttv_plugin_tab.h" +#include + +/* + * forward definitions + */ + +/* + * Implementation + */ + +static void tab_update_filter(LttvPlugin *parent, LttvFilter *filter) +{ + LttvPluginTab *self = LTTV_PLUGIN_TAB(parent); + g_message("In tab update filter."); + lttv_filter_destroy(self->tab->filter); + self->tab->filter = filter; + lttvwindow_report_filter(self->tab, filter); +} + + +static void +lttv_plugin_tab_class_init (LttvPluginTabClass *klass) +{ + LttvPluginClass *parent_klass; + parent_klass = &klass->parent; + parent_klass->update_filter = tab_update_filter; + g_type_class_add_private (klass, sizeof (Tab)); +} + + +static void +lttv_plugin_tab_init (GTypeInstance *instance, gpointer g_class) +{ + LttvPluginTab *self = LTTV_PLUGIN_TAB (instance); + self->tab = G_TYPE_INSTANCE_GET_PRIVATE (self, + LTTV_TYPE_PLUGIN_TAB, Tab); +} + + +__EXPORT GType +lttv_plugin_tab_get_type (void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (LttvPluginTabClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + lttv_plugin_tab_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (LttvPluginTab), + 0, /* n_preallocs */ + lttv_plugin_tab_init /* instance_init */ + }; + type = g_type_register_static (G_TYPE_OBJECT, + "LttvPluginTabType", + &info, 0); + } + return type; +} + + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttv_plugin_tab.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttv_plugin_tab.h new file mode 100644 index 00000000..6acbec43 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttv_plugin_tab.h @@ -0,0 +1,62 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2006 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 LTTV_PLUGIN_TAB_H +#define LTTV_PLUGIN_TAB_H + +#include +#include + +/* + * Type macros. + */ + +#define LTTV_TYPE_PLUGIN_TAB (lttv_plugin_tab_get_type ()) +#define LTTV_PLUGIN_TAB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LTTV_TYPE_PLUGIN_TAB, LttvPluginTab)) +#define LTTV_PLUGIN_TAB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), LTTV_TYPE_PLUGIN_TAB, LttvPluginTabClass)) +#define LTTV_IS_PLUGIN_TAB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LTTV_TYPE_PLUGIN_TAB)) +#define LTTV_IS_PLUGIN_TAB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), LTTV_TYPE_PLUGIN_TAB)) +#define LTTV_PLUGIN_TAB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), LTTV_TYPE_PLUGIN_TAB, LttvPluginTabClass)) + +typedef struct _LttvPluginTab LttvPluginTab; +typedef struct _LttvPluginTabClass LttvPluginTabClass; + +struct _LttvPluginTab { + LttvPlugin parent; + + /* instance members */ + Tab *tab; + + /* private */ +}; + +struct _LttvPluginTabClass { + LttvPluginClass parent; + + /* class members */ +}; + +/* used by LTTV_PLUGIN_TAB_TYPE */ +GType lttv_plugin_tab_get_type (void); + +/* + * Method definitions. + */ + + +#endif diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttvwindow.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttvwindow.c new file mode 100644 index 00000000..3dea5a92 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttvwindow.c @@ -0,0 +1,1151 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 XangXiu Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +/*! \file lttvwindow.c + * \brief API used by the graphical viewers to interact with their tab. + * + * Main window (gui module) is the place to contain and display viewers. + * Viewers (lttv plugins) interact with tab and main window through this API + * and events sent by gtk. + * This header file should be included in each graphic module. + * This library is used by graphical modules to interact with their tab and + * main window. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // for execute_events_requests +#include + +/** + * Internal function parts + */ + +extern GSList * g_main_window_list; + +__EXPORT gint lttvwindow_preempt_count = 0; + +/* set_time_window + * + * It updates the time window of the tab, then calls the updatetimewindow + * hooks of each viewer. + * + * This is called whenever the scrollbar value changes. + */ + +void set_time_window(Tab *tab, const TimeWindow *time_window) +{ + LttvAttributeValue value; + LttvHooks * tmp; + + TimeWindowNotifyData time_window_notify_data; + TimeWindow old_time_window = tab->time_window; + time_window_notify_data.old_time_window = &old_time_window; + tab->time_window = *time_window; + time_window_notify_data.new_time_window = + &(tab->time_window); + + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/updatetimewindow", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp != NULL) lttv_hooks_call(tmp, &time_window_notify_data); + + //gtk_multi_vpaned_set_adjust(tab->multi_vpaned, new_time_window, FALSE); + +} + +/* set_current_time + * + * It updates the current time of the tab, then calls the updatetimewindow + * hooks of each viewer. + * + * This is called whenever the current time value changes. + */ + +void set_current_time(Tab *tab, const LttTime *current_time) +{ + LttvAttributeValue value; + LttvHooks * tmp; + + tab->current_time = *current_time; + + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/updatecurrenttime", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp != NULL) lttv_hooks_call(tmp, &tab->current_time); +} + +/* set_current_position + * + * It updates the current time of the tab, then calls the updatetimewindow + * hooks of each viewer. + * + * This is called whenever the current time value changes. + */ + +void set_current_position(Tab *tab, const LttvTracesetContextPosition *pos) +{ + LttvAttributeValue value; + LttvHooks * tmp; + + tab->current_time = lttv_traceset_context_position_get_time(pos); + + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/updatecurrentposition", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp != NULL) lttv_hooks_call(tmp, pos); +} + +void add_toolbar_constructor(MainWindow *mw, LttvToolbarClosure *toolbar_c) +{ + LttvIAttribute *attributes = mw->attributes; + LttvAttributeValue value; + LttvToolbars * instance_toolbar; + lttvwindow_viewer_constructor constructor; + GtkWidget * tool_menu_title_menu, *new_widget, *pixmap; + GdkPixbuf *pixbuf; + + g_assert(lttv_iattribute_find_by_path(attributes, + "viewers/toolbar", LTTV_POINTER, &value)); + if(*(value.v_pointer) == NULL) + *(value.v_pointer) = lttv_toolbars_new(); + instance_toolbar = (LttvToolbars*)*(value.v_pointer); + + constructor = toolbar_c->con; + tool_menu_title_menu = lookup_widget(mw->mwindow,"MToolbar1"); + pixbuf = gdk_pixbuf_new_from_xpm_data((const char**)toolbar_c->pixmap); + pixmap = gtk_image_new_from_pixbuf(pixbuf); + new_widget = + gtk_toolbar_append_element (GTK_TOOLBAR (tool_menu_title_menu), + GTK_TOOLBAR_CHILD_BUTTON, + NULL, + "", + toolbar_c->tooltip, NULL, + pixmap, NULL, NULL); + gtk_label_set_use_underline( + GTK_LABEL (((GtkToolbarChild*) ( + g_list_last (GTK_TOOLBAR + (tool_menu_title_menu)->children)->data))->label), + TRUE); + gtk_container_set_border_width (GTK_CONTAINER (new_widget), 1); + g_signal_connect ((gpointer) new_widget, + "clicked", + G_CALLBACK (insert_viewer_wrap), + constructor); + gtk_widget_show (new_widget); + + lttv_toolbars_add(instance_toolbar, toolbar_c->con, + toolbar_c->tooltip, + toolbar_c->pixmap, + new_widget); + +} + +void add_menu_constructor(MainWindow *mw, LttvMenuClosure *menu_c) +{ + LttvIAttribute *attributes = mw->attributes; + LttvAttributeValue value; + LttvToolbars * instance_menu; + lttvwindow_viewer_constructor constructor; + GtkWidget * tool_menu_title_menu, *new_widget; + + g_assert(lttv_iattribute_find_by_path(attributes, + "viewers/menu", LTTV_POINTER, &value)); + if(*(value.v_pointer) == NULL) + *(value.v_pointer) = lttv_menus_new(); + instance_menu = (LttvMenus*)*(value.v_pointer); + + + constructor = menu_c->con; + tool_menu_title_menu = lookup_widget(mw->mwindow,"ToolMenuTitle_menu"); + new_widget = + gtk_menu_item_new_with_mnemonic (menu_c->menu_text); + gtk_container_add (GTK_CONTAINER (tool_menu_title_menu), + new_widget); + g_signal_connect ((gpointer) new_widget, "activate", + G_CALLBACK (insert_viewer_wrap), + constructor); + gtk_widget_show (new_widget); + lttv_menus_add(instance_menu, menu_c->con, + menu_c->menu_path, + menu_c->menu_text, + new_widget); +} + +void remove_toolbar_constructor(MainWindow *mw, lttvwindow_viewer_constructor viewer_constructor) +{ + LttvIAttribute *attributes = mw->attributes; + LttvAttributeValue value; + LttvToolbars * instance_toolbar; + GtkWidget * tool_menu_title_menu, *widget; + + g_assert(lttv_iattribute_find_by_path(attributes, + "viewers/toolbar", LTTV_POINTER, &value)); + if(*(value.v_pointer) == NULL) + *(value.v_pointer) = lttv_toolbars_new(); + instance_toolbar = (LttvToolbars*)*(value.v_pointer); + + tool_menu_title_menu = lookup_widget(mw->mwindow,"MToolbar1"); + widget = lttv_menus_remove(instance_toolbar, viewer_constructor); + gtk_container_remove (GTK_CONTAINER (tool_menu_title_menu), + widget); +} + + +void remove_menu_constructor(MainWindow *mw, lttvwindow_viewer_constructor viewer_constructor) +{ + LttvIAttribute *attributes = mw->attributes; + LttvAttributeValue value; + LttvMenus * instance_menu; + GtkWidget * tool_menu_title_menu, *widget; + + g_assert(lttv_iattribute_find_by_path(attributes, + "viewers/menu", LTTV_POINTER, &value)); + if(*(value.v_pointer) == NULL) + *(value.v_pointer) = lttv_menus_new(); + instance_menu = (LttvMenus*)*(value.v_pointer); + + widget = lttv_menus_remove(instance_menu, viewer_constructor); + tool_menu_title_menu = lookup_widget(mw->mwindow,"ToolMenuTitle_menu"); + gtk_container_remove (GTK_CONTAINER (tool_menu_title_menu), widget); +} + + +/** + * API parts + */ + + +/** + * Function to register a view constructor so that main window can generate + * a menu item and a toolbar item for the viewer in order to generate a new + * instance easily. A menu entry and toolbar item will be added to each main + * window. + * + * It should be called by init function of the module. + * + * @param name name of the viewer + * @param menu_path path of the menu item. + * @param menu_text text of the menu item. + * @param pixmap Image shown on the toolbar item. + * @param tooltip tooltip of the toolbar item. + * @param view_constructor constructor of the viewer. + */ + +__EXPORT void lttvwindow_register_constructor + (char * name, + char * menu_path, + char * menu_text, + char ** pixmap, + char * tooltip, + lttvwindow_viewer_constructor view_constructor) +{ + LttvIAttribute *attributes_global = LTTV_IATTRIBUTE(lttv_global_attributes()); + LttvToolbars * toolbar; + LttvMenus * menu; + LttvToolbarClosure toolbar_c; + LttvMenuClosure menu_c; + LttvAttributeValue value; + + if(view_constructor == NULL) return; + + if(pixmap != NULL) { + g_assert(lttv_iattribute_find_by_path(attributes_global, + "viewers/toolbar", LTTV_POINTER, &value)); + toolbar = (LttvToolbars*)*(value.v_pointer); + + if(toolbar == NULL) { + toolbar = lttv_toolbars_new(); + *(value.v_pointer) = toolbar; + } + toolbar_c = lttv_toolbars_add(toolbar, view_constructor, tooltip, pixmap, + NULL); + + g_slist_foreach(g_main_window_list, + (gpointer)add_toolbar_constructor, + &toolbar_c); + } + + if(menu_path != NULL) { + g_assert(lttv_iattribute_find_by_path(attributes_global, + "viewers/menu", LTTV_POINTER, &value)); + menu = (LttvMenus*)*(value.v_pointer); + + if(menu == NULL) { + menu = lttv_menus_new(); + *(value.v_pointer) = menu; + } + menu_c = lttv_menus_add(menu, view_constructor, menu_path, menu_text,NULL); + + g_slist_foreach(g_main_window_list, + (gpointer)add_menu_constructor, + &menu_c); + } + { + LttvAttribute *attribute; + gboolean result; + + attribute = LTTV_ATTRIBUTE(lttv_iattribute_find_subdir( + LTTV_IATTRIBUTE(attributes_global), + LTTV_VIEWER_CONSTRUCTORS)); + g_assert(attribute); + + result = lttv_iattribute_find_by_path(LTTV_IATTRIBUTE(attribute), + name, LTTV_POINTER, &value); + g_assert(result); + + *(value.v_pointer) = view_constructor; + + } +} + + +/** + * Function to unregister the viewer's constructor, release the space + * occupied by menu_path, menu_text, pixmap, tooltip and constructor of the + * viewer. + * + * It will be called when a module is unloaded. + * + * @param view_constructor constructor of the viewer. + */ + + +__EXPORT void lttvwindow_unregister_constructor + (lttvwindow_viewer_constructor view_constructor) +{ + LttvIAttribute *attributes_global = LTTV_IATTRIBUTE(lttv_global_attributes()); + LttvToolbars * toolbar; + LttvMenus * menu; + LttvAttributeValue value; + gboolean is_named; + + g_assert(lttv_iattribute_find_by_path(attributes_global, + "viewers/toolbar", LTTV_POINTER, &value)); + toolbar = (LttvToolbars*)*(value.v_pointer); + + if(toolbar != NULL) { + g_slist_foreach(g_main_window_list, + (gpointer)remove_toolbar_constructor, + view_constructor); + lttv_toolbars_remove(toolbar, view_constructor); + } + + g_assert(lttv_iattribute_find_by_path(attributes_global, + "viewers/menu", LTTV_POINTER, &value)); + menu = (LttvMenus*)*(value.v_pointer); + + if(menu != NULL) { + g_slist_foreach(g_main_window_list, + (gpointer)remove_menu_constructor, + view_constructor); + lttv_menus_remove(menu, view_constructor); + } + + { + LttvAttribute *attribute; + attribute = LTTV_ATTRIBUTE(lttv_iattribute_find_subdir( + LTTV_IATTRIBUTE(attributes_global), + LTTV_VIEWER_CONSTRUCTORS)); + g_assert(attribute); + + guint num = lttv_iattribute_get_number(LTTV_IATTRIBUTE(attribute)); + guint i; + LttvAttributeName name; + LttvAttributeValue value; + LttvAttributeType type; + + for(i=0;iattributes, + "hooks/updatetimewindow", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp == NULL){ + tmp = lttv_hooks_new(); + *(value.v_pointer) = tmp; + } + lttv_hooks_add(tmp, hook,hook_data, LTTV_PRIO_DEFAULT); +} + + +/** + * Function to unregister a viewer's hook function which is used to + * set/update the time interval of the viewer. + * @param tab viewer's tab + * @param hook hook function of the viewer. + * @param hook_data hook data associated with the hook function. + */ + +__EXPORT void lttvwindow_unregister_time_window_notify(Tab *tab, + LttvHook hook, gpointer hook_data) +{ + LttvAttributeValue value; + LttvHooks * tmp; + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/updatetimewindow", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp == NULL) return; + lttv_hooks_remove_data(tmp, hook, hook_data); +} + +/** + * Function to register a hook function for a viewer to set/update its + * traceset. + * @param tab viewer's tab + * @param hook hook function of the viewer. + * @param hook_data hook data associated with the hook function. + */ + +__EXPORT void lttvwindow_register_traceset_notify(Tab *tab, + LttvHook hook, gpointer hook_data) +{ + LttvAttributeValue value; + LttvHooks * tmp; + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/updatetraceset", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp == NULL){ + tmp = lttv_hooks_new(); + *(value.v_pointer) = tmp; + } + lttv_hooks_add(tmp, hook, hook_data, LTTV_PRIO_DEFAULT); +} + + +/** + * Function to unregister a viewer's hook function which is used to + * set/update the traceset of the viewer. + * @param tab viewer's tab + * @param hook hook function of the viewer. + * @param hook_data hook data associated with the hook function. + */ + +__EXPORT void lttvwindow_unregister_traceset_notify(Tab *tab, + LttvHook hook, gpointer hook_data) +{ + LttvAttributeValue value; + LttvHooks * tmp; + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/updatetraceset", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp == NULL) return; + lttv_hooks_remove_data(tmp, hook, hook_data); +} + +/** + * Function to register a hook function for a viewer be completely redrawn. + * + * @param tab viewer's tab + * @param hook hook function of the viewer. + * @param hook_data hook data associated with the hook function. + */ + +__EXPORT void lttvwindow_register_redraw_notify(Tab *tab, + LttvHook hook, gpointer hook_data) +{ + LttvAttributeValue value; + LttvHooks * tmp; + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/redraw", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp == NULL){ + tmp = lttv_hooks_new(); + *(value.v_pointer) = tmp; + } + lttv_hooks_add(tmp, hook, hook_data, LTTV_PRIO_DEFAULT); +} + + +/** + * Function to unregister a hook function for a viewer be completely redrawn. + * + * @param tab viewer's tab + * @param hook hook function of the viewer. + * @param hook_data hook data associated with the hook function. + */ + +__EXPORT void lttvwindow_unregister_redraw_notify(Tab *tab, + LttvHook hook, gpointer hook_data) +{ + LttvAttributeValue value; + LttvHooks * tmp; + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/redraw", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp == NULL) return; + lttv_hooks_remove_data(tmp, hook, hook_data); +} + +/** + * Function to register a hook function for a viewer to re-do the events + * requests for the needed interval. + * + * This action is typically done after a "stop". + * + * The typical hook will remove all current requests for the viewer + * and make requests for missing information. + * + * @param tab viewer's tab + * @param hook hook function of the viewer. + * @param hook_data hook data associated with the hook function. + */ + +__EXPORT void lttvwindow_register_continue_notify(Tab *tab, + LttvHook hook, gpointer hook_data) +{ + LttvAttributeValue value; + LttvHooks * tmp; + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/continue", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp == NULL){ + tmp = lttv_hooks_new(); + *(value.v_pointer) = tmp; + } + lttv_hooks_add(tmp, hook, hook_data, LTTV_PRIO_DEFAULT); +} + + +/** + * Function to unregister a hook function for a viewer to re-do the events + * requests for the needed interval. + * + * @param tab viewer's tab + * @param hook hook function of the viewer. + * @param hook_data hook data associated with the hook function. + */ + +__EXPORT void lttvwindow_unregister_continue_notify(Tab *tab, + LttvHook hook, gpointer hook_data) +{ + LttvAttributeValue value; + LttvHooks * tmp; + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/continue", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp == NULL) return; + lttv_hooks_remove_data(tmp, hook, hook_data); +} + + +/** + * Function to register a hook function for a viewer to set/update its + * filter. + * @param tab viewer's tab + * @param hook hook function of the viewer. + * @param hook_data hook data associated with the hook function. + */ + +__EXPORT void lttvwindow_register_filter_notify(Tab *tab, + LttvHook hook, gpointer hook_data) +{ + LttvAttributeValue value; + LttvHooks * tmp; + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/updatefilter", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp == NULL){ + tmp = lttv_hooks_new(); + *(value.v_pointer) = tmp; + } + lttv_hooks_add(tmp, hook, hook_data, LTTV_PRIO_DEFAULT); +} + + +/** + * Function to unregister a viewer's hook function which is used to + * set/update the filter of the viewer. + * @param tab viewer's tab + * @param hook hook function of the viewer. + * @param hook_data hook data associated with the hook function. + */ + +__EXPORT void lttvwindow_unregister_filter_notify(Tab *tab, + LttvHook hook, + gpointer hook_data) +{ + LttvAttributeValue value; + LttvHooks * tmp; + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/updatefilter", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp == NULL) return; + lttv_hooks_remove_data(tmp, hook, hook_data); +} + +/** + * function to register a hook function for a viewer to set/update its + * current time. + * @param tab viewer's tab + * @param hook hook function of the viewer. + * @param hook_data hook data associated with the hook function. + */ + +__EXPORT void lttvwindow_register_current_time_notify(Tab *tab, + LttvHook hook, gpointer hook_data) +{ + LttvAttributeValue value; + LttvHooks * tmp; + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/updatecurrenttime", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp == NULL){ + tmp = lttv_hooks_new(); + *(value.v_pointer) = tmp; + } + lttv_hooks_add(tmp, hook, hook_data, LTTV_PRIO_DEFAULT); +} + + +/** + * function to unregister a viewer's hook function which is used to + * set/update the current time of the viewer. + * @param tab viewer's tab + * @param hook hook function of the viewer. + * @param hook_data hook data associated with the hook function. + */ + +__EXPORT void lttvwindow_unregister_current_time_notify(Tab *tab, + LttvHook hook, gpointer hook_data) +{ + LttvAttributeValue value; + LttvHooks * tmp; + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/updatecurrenttime", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp == NULL) return; + lttv_hooks_remove_data(tmp, hook, hook_data); +} + +/** + * function to register a hook function for a viewer to set/update its + * current position. + * @param tab viewer's tab + * @param hook hook function of the viewer. + * @param hook_data hook data associated with the hook function. + */ + +__EXPORT void lttvwindow_register_current_position_notify(Tab *tab, + LttvHook hook, gpointer hook_data) +{ + LttvAttributeValue value; + LttvHooks * tmp; + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/updatecurrentposition", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp == NULL){ + tmp = lttv_hooks_new(); + *(value.v_pointer) = tmp; + } + lttv_hooks_add(tmp, hook, hook_data, LTTV_PRIO_DEFAULT); +} + + +/** + * function to unregister a viewer's hook function which is used to + * set/update the current position of the viewer. + * @param tab viewer's tab + * @param hook hook function of the viewer. + * @param hook_data hook data associated with the hook function. + */ + +__EXPORT void lttvwindow_unregister_current_position_notify(Tab *tab, + LttvHook hook, gpointer hook_data) +{ + LttvAttributeValue value; + LttvHooks * tmp; + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/updatecurrentposition", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp == NULL) return; + lttv_hooks_remove_data(tmp, hook, hook_data); +} + + +/** + * Function to register a hook function for a viewer to show + * the content of the viewer. + * @param tab viewer's tab + * @param hook hook function of the viewer. + * @param hook_data hook data associated with the hook function. + */ + +void lttvwindow_register_show_notify(Tab *tab, + LttvHook hook, gpointer hook_data) +{ + LttvAttributeValue value; + LttvHooks * tmp; + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/showviewer", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp == NULL){ + tmp = lttv_hooks_new(); + *(value.v_pointer) = tmp; + } + lttv_hooks_add(tmp, hook, hook_data, LTTV_PRIO_DEFAULT); +} + + +/** + * Function to unregister a viewer's hook function which is used to + * show the content of the viewer.. + * @param tab viewer's tab + * @param hook hook function of the viewer. + * @param hook_data hook data associated with the hook function. + */ + +void lttvwindow_unregister_show_notify(Tab *tab, + LttvHook hook, gpointer hook_data) +{ + LttvAttributeValue value; + LttvHooks * tmp; + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/showviewer", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp == NULL) return; + lttv_hooks_remove_data(tmp, hook, hook_data); +} + +/** + * Function to register a hook function for a viewer to set/update the + * dividor of the hpane. + * @param tab viewer's tab + * @param hook hook function of the viewer. + * @param hook_data hook data associated with the hook function. + */ + +void lttvwindow_register_dividor(Tab *tab, + LttvHook hook, gpointer hook_data) +{ + LttvAttributeValue value; + LttvHooks * tmp; + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/hpanedividor", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp == NULL){ + tmp = lttv_hooks_new(); + *(value.v_pointer) = tmp; + } + lttv_hooks_add(tmp, hook, hook_data, LTTV_PRIO_DEFAULT); +} + + +/** + * Function to unregister a viewer's hook function which is used to + * set/update hpane's dividor of the viewer. + * It will be called by the destructor of the viewer. + * @param tab viewer's tab + * @param hook hook function of the viewer. + * @param hook_data hook data associated with the hook function. + */ + +void lttvwindow_unregister_dividor(Tab *tab, + LttvHook hook, gpointer hook_data) +{ + LttvAttributeValue value; + LttvHooks * tmp; + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/hpanedividor", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp == NULL) return; + lttv_hooks_remove_data(tmp, hook, hook_data); +} + + +/** + * Function to set the time interval of the current tab. + * It will be called by a viewer's signal handle associated with + * the move_slider signal + * @param tab viewer's tab + * @param time_interval a pointer where time interval is stored. + */ + +__EXPORT void lttvwindow_report_time_window(Tab *tab, + TimeWindow time_window) +{ + //set_time_window(tab, time_window); + //set_time_window_adjustment(tab, time_window); + + time_change_manager(tab, time_window); + + +#if 0 + /* Set scrollbar */ + LttvTracesetContext *tsc = + LTTV_TRACESET_CONTEXT(tab->traceset_info->traceset_context); + TimeInterval time_span = tsc->time_span; + GtkAdjustment *adjustment = gtk_range_get_adjustment(GTK_RANGE(tab->scrollbar)); + g_object_set(G_OBJECT(adjustment), + "lower", + 0.0, /* lower */ + "upper", + ltt_time_to_double( + ltt_time_sub(time_span.end_time, time_span.start_time)) + , /* upper */ + "step_increment", + ltt_time_to_double(time_window->time_width) + / SCROLL_STEP_PER_PAGE + , /* step increment */ + "page_increment", + ltt_time_to_double(time_window->time_width) + , /* page increment */ + "page_size", + ltt_time_to_double(time_window->time_width) + , /* page size */ + NULL); + gtk_adjustment_changed(adjustment); + + //g_object_set(G_OBJECT(adjustment), + // "value", + // ltt_time_to_double(time_window->start_time) + // , /* value */ + // NULL); + /* Note : the set value will call set_time_window if scrollbar value changed + */ + gtk_adjustment_set_value(adjustment, + ltt_time_to_double( + ltt_time_sub(time_window->start_time, + time_span.start_time)) + ); +#endif //0 +} + + +/** + * Function to set the current time of the current tab. + * It will be called by a viewer's signal handle associated with + * the button-release-event signal + * @param tab viewer's tab + * @param time a pointer where time is stored. + */ + +__EXPORT void lttvwindow_report_current_time(Tab *tab, + LttTime time) +{ + current_time_change_manager(tab, time); +} + +/** + * Function to set the current event of the current tab. + * It will be called by a viewer's signal handle associated with + * the button-release-event signal + * @param tab viewer's tab + * @param time a pointer where time is stored. + */ + +__EXPORT void lttvwindow_report_current_position(Tab *tab, + LttvTracesetContextPosition *pos) +{ + current_position_change_manager(tab, pos); +} + + +/** + * Function to set the position of the hpane's dividor (viewer). + * It will be called by a viewer's signal handle associated with + * the motion_notify_event event/signal + * @param tab viewer's tab + * @param position position of the hpane's dividor. + */ + +void lttvwindow_report_dividor(Tab *tab, gint position) +{ + LttvAttributeValue value; + LttvHooks * tmp; + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/hpanedividor", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp == NULL) return; + lttv_hooks_call(tmp, &position); +} + +/** + * Function to request data in a specific time interval to the main window. The + * event request servicing is differed until the glib idle functions are + * called. + * + * The viewer has to provide hooks that should be associated with the event + * request. + * + * Either start time or start position must be defined in a EventRequest + * structure for it to be valid. + * + * end_time, end_position and num_events can all be defined. The first one + * to occur will be used as end criterion. + * + * @param tab viewer's tab + * @param events_requested the structure of request from. + */ + +__EXPORT void lttvwindow_events_request(Tab *tab, + EventsRequest *events_request) +{ + tab->events_requests = g_slist_append(tab->events_requests, events_request); + + if(!tab->events_request_pending) + { + /* Redraw has +20 priority. We want to let the redraw be done while we do + * our job. Mathieu : test with high prio higher than events for better + * scrolling. */ + /* Mathieu, 2008 : ok, finally, the control flow view needs the cell updates + * to come soon enough so we can have one active cell to get the pixmap + * buffer height from. Therefore, let the gdk events run before the events + * requests. + */ + g_idle_add_full((G_PRIORITY_HIGH_IDLE + 21), + //g_idle_add_full((G_PRIORITY_DEFAULT + 2), + (GSourceFunc)execute_events_requests, + tab, + NULL); + tab->events_request_pending = TRUE; + } +} + + +/** + * Function to remove data requests related to a viewer. + * + * The existing requests's viewer gpointer is compared to the pointer + * given in argument to establish which data request should be removed. + * + * @param tab the tab the viewer belongs to. + * @param viewer a pointer to the viewer data structure + */ + +gint find_viewer (const EventsRequest *a, gconstpointer b) +{ + return (a->owner != b); +} + + +__EXPORT void lttvwindow_events_request_remove_all(Tab *tab, + gconstpointer viewer) +{ + GSList *element = tab->events_requests; + + while((element = + g_slist_find_custom(element, viewer, + (GCompareFunc)find_viewer)) + != NULL) { + EventsRequest *events_request = (EventsRequest *)element->data; + // Modified so a viewer being destroyed won't have its after_request + // called. Not so important anyway. Note that a viewer that call this + // remove_all function will not get its after_request called. + //if(events_request->servicing == TRUE) { + // lttv_hooks_call(events_request->after_request, NULL); + //} + events_request_free(events_request); + //g_free(events_request); + tab->events_requests = g_slist_remove_link(tab->events_requests, element); + element = g_slist_next(element); + if(element == NULL) break; /* end of list */ + } + if(g_slist_length(tab->events_requests) == 0) { + tab->events_request_pending = FALSE; + g_idle_remove_by_data(tab); + } + +} + + +/** + * Function to see if there are events request pending. + * + * It tells if events requests are pending. Useful for checks in some events, + * i.e. detailed event list scrolling. + * + * @param tab the tab the viewer belongs to. + * @param viewer a pointer to the viewer data structure + * @return : TRUE is events requests are pending, else FALSE. + */ + +__EXPORT gboolean lttvwindow_events_request_pending(Tab *tab) +{ + GSList *element = tab->events_requests; + + if(element == NULL) return FALSE; + else return TRUE; +} + + +/** + * Function to get the current time interval shown on the current tab. + * It will be called by a viewer's hook function to update the + * shown time interval of the viewer and also be called by the constructor + * of the viewer. + * @param tab viewer's tab + * @return time window. + */ + +__EXPORT TimeWindow lttvwindow_get_time_window(Tab *tab) +{ + return tab->time_window; +} + + +/** + * Function to get the current time/event of the current tab. + * It will be called by a viewer's hook function to update the + * current time/event of the viewer. + * @param tab viewer's tab + * @return time + */ + +__EXPORT LttTime lttvwindow_get_current_time(Tab *tab) +{ + return tab->current_time; +} + + +/** + * Function to get the filter of the current tab. + * @param filter, a pointer to a filter. + * + * returns the current filter + */ +__EXPORT LttvFilter *lttvwindow_get_filter(Tab *tab) +{ + return g_object_get_data(G_OBJECT(tab->vbox), "filter"); +} + +/** + * Function to set the filter of the current tab. + * It should be called by the filter GUI to tell the + * main window to update the filter tab's lttv_filter. + * + * This function does change the current filter, removing the + * old one when necessary, and call the updatefilter hooks + * of the registered viewers. + * + * @param main_win, the main window the viewer belongs to. + * @param filter, a pointer to a filter. + */ +void lttvwindow_report_filter(Tab *tab, LttvFilter *filter) +{ + LttvAttributeValue value; + LttvHooks * tmp; + + //lttv_filter_destroy(tab->filter); + //tab->filter = filter; + + g_assert(lttv_iattribute_find_by_path(tab->attributes, + "hooks/updatefilter", LTTV_POINTER, &value)); + tmp = (LttvHooks*)*(value.v_pointer); + if(tmp == NULL) return; + lttv_hooks_call(tmp, filter); +} + + + +/** + * Function to get the stats of the traceset + * @param tab viewer's tab + */ + +__EXPORT LttvTracesetStats* lttvwindow_get_traceset_stats(Tab *tab) +{ + return tab->traceset_info->traceset_context; +} + +__EXPORT LttvTracesetContext* lttvwindow_get_traceset_context(Tab *tab) +{ + return (LttvTracesetContext*)tab->traceset_info->traceset_context; +} + + +void events_request_free(EventsRequest *events_request) +{ + if(events_request == NULL) return; + + if(events_request->start_position != NULL) + lttv_traceset_context_position_destroy(events_request->start_position); + if(events_request->end_position != NULL) + lttv_traceset_context_position_destroy(events_request->end_position); + if(events_request->hooks != NULL) { + guint i; + GArray *hooks = events_request->hooks; + lttv_trace_hook_remove_all(&hooks); + g_array_free(events_request->hooks, TRUE); + } + if(events_request->before_chunk_traceset != NULL) + lttv_hooks_destroy(events_request->before_chunk_traceset); + if(events_request->before_chunk_trace != NULL) + lttv_hooks_destroy(events_request->before_chunk_trace); + if(events_request->before_chunk_tracefile != NULL) + lttv_hooks_destroy(events_request->before_chunk_tracefile); + if(events_request->event != NULL) + lttv_hooks_destroy(events_request->event); + if(events_request->event_by_id != NULL) + lttv_hooks_by_id_destroy(events_request->event_by_id); + if(events_request->after_chunk_tracefile != NULL) + lttv_hooks_destroy(events_request->after_chunk_tracefile); + if(events_request->after_chunk_trace != NULL) + lttv_hooks_destroy(events_request->after_chunk_trace); + if(events_request->after_chunk_traceset != NULL) + lttv_hooks_destroy(events_request->after_chunk_traceset); + if(events_request->before_request != NULL) + lttv_hooks_destroy(events_request->before_request); + if(events_request->after_request != NULL) + lttv_hooks_destroy(events_request->after_request); + + g_free(events_request); +} + + + +__EXPORT GtkWidget *main_window_get_widget(Tab *tab) +{ + return tab->mw->mwindow; +} + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttvwindow.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttvwindow.h new file mode 100644 index 00000000..3078ed15 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttvwindow.h @@ -0,0 +1,859 @@ +/* This file is part of the Linux Trace Toolkit Graphic User Interface + * Copyright (C) 2003-2004 Xiangxiu Yang, Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +/* +This file is what every viewer plugin writer should refer to. + + +Module Related API + +A viewer plugin is, before anything, a plugin. As a dynamically loadable +module, it thus has an init and a destroy function called whenever it is +loaded/initialized and unloaded/destroyed. A graphical module depends on +lttvwindow for construction of its viewer instances. In order to achieve +this, it must register its constructor function to the main window along +with button description or text menu entry description. A module keeps +a list of every viewer that currently sits in memory so it can destroy +them before the module gets unloaded/destroyed. + +The contructor registration to the main windows adds button and menu +entry to each main window, thus allowing instanciation of viewers. + + +Main Window + +The main window is a container that offers menus, buttons and a +notebook. Some of those menus and buttons are part of the core of the +main window, others are dynamically added and removed when modules are +loaded/unloaded. + +The notebook contains as much tabs as wanted. Each tab is linked with +a set of traces (traceset). Each trace contains many tracefiles (one +per cpu). A trace corresponds to a kernel being traced. A traceset +corresponds to many traces read together. The time span of a traceset +goes from the earliest start of all the traces to the latest end of all +the traces. + +Inside each tab are added the viewers. When they interact with the main +window through the lttvwindow API, they affect the other viewers located +in the same tab as they are. + +The insertion of many viewers in a tab permits a quick look at all the +information wanted in a glance. The main window does merge the read +requests from all the viewers in the same tab in a way that every viewer +will get exactly the events it asked for, while the event reading loop +and state update are shared. It improves performance of events delivery +to the viewers. + + + +Viewer Instance Related API + +The lifetime of a viewer is as follows. The viewer constructor function +is called each time an instance view is created (one subwindow of this +viewer type is created by the user either by clicking on the menu item +or the button corresponding to the viewer). Thereafter, the viewer gets +hooks called for different purposes by the window containing it. These +hooks are detailed below. It also has to deal with GTK Events. Finally, +it can be destructed by having its top level widget unreferenced by the +main window or by any GTK Event causing a "destroy-event" signal on the +its top widget. Another possible way for it do be destroyed is if the +module gets unloaded. The module unload function will have to emit a +"destroy" signal on each top level widget of all instances of its viewers. + + +Notices from Main Window + +time_window : This is the time interval visible on the viewer's tab. Every + viewer that cares about being synchronised by respect to the + time with other viewers should register to this notification. + They should redraw all or part of their display when this occurs. + +traceset : This notification is called whenever a trace is added/removed + from the traceset. As it affects all the data displayed by the + viewer, it sould redraw itself totally. + +filter : FIXME : describe.. + +current_time: Being able to zoom nearer a specific time or highlight a specific + time on every viewer in synchronicity implies that the viewer + has to shown a visual sign over the drawing or select an event + when it receives this notice. It should also inform the main + window with the appropriate report API function when a user + selects a specific time as being the current time. + +dividor : This notice links the positions of the horizontal dividors + between the graphic display zone of every viewer and their Y axis, + typically showing processes, cpus, ... + + +Reporting Changes to the Main Window + +In most cases, the enclosing window knows about updates such as described +in the Notification section higher. There are a few cases, however, where +updates are caused by actions known by a view instance. For example, +clicking in a view may update the current time; all viewers within +the same window must be told about the new current time to change the +currently highlighted time point. A viewer reports such events by calling +lttvwindow_report_current_time on its lttvwindow. The lttvwindow will +consequently call current_time_notify for each of its contained viewers. + + +Available report methods are : + +lttvwindow_report_time_window : reports the new time window. +lttvwindow_report_current_time : reports the new current time. +lttvwindow_report_dividor : reports the new horizontal dividor's position. +lttvwindow_report_filter : reports the new filter object + + + +Requesting Events to Main Window + +Events can be requested by passing a EventsRequest structure to the main +window. They will be delivered later when the next g_idle functions +will be called. Event delivery is done by calling the event hook for +this event ID, or the main event hooks. A pointer to the EventsRequest +structure is passed as hook_data to the event hooks of the viewers. + +EventsRequest consists in +- a pointer to the viewer specific data structure +- a start timestamp or position +- a stop_flag, ending the read process when set to TRUE +- a end timestamp and/or position and/or number of events to read +- hook lists to call for traceset/trace/tracefile begin and end, and for each + event (event hooks and event_by_id hooks). + +The main window will deliver events for every EventRequests it has +pending through an algorithm that guarantee that all events requested, +and only them, will be delivered to the viewer between the call of the +tracefile_begin hooks and the call of the tracefile_end hooks. + +If a viewer wants to stop the event request at a certain point inside the +event hooks, it has to set the stop_flag to TRUE and return TRUE from the +hook function. Then return value will stop the process traceset. Then, +the main window will look for the stop_flag and remove the EventRequests +from its lists, calling the process_traceset_end for this request (it +removes hooks from the context and calls the after hooks). + +It no stop_flag is risen, the end timestamp, end position or number +of events to read has to be reached to determine the end of the +request. Otherwise, the end of traceset does determine it. + + +GTK Events + +Events and Signals + +GTK is quite different from the other graphical toolkits around +there. The main difference resides in that there are many X Windows +inside one GtkWindow, instead of just one. That means that X events are +delivered by the glib main loop directly to the widget corresponding to +the GdkWindow affected by the X event. + +Event delivery to a widget emits a signal on that widget. Then, if a +handler is connected to this widget's signal, it will be executed. There +are default handlers for signals, connected at class instantiation +time. There is also the possibility to connect other handlers to these +signals, which is what should be done in most cases when a viewer needs +to interact with X in any way. + + + +Signal emission and propagation is described there : + +http://www.gtk.org/tutorial/sec-signalemissionandpropagation.html + +For further information on the GTK main loop (now a wrapper over glib main loop) +see : + +http://developer.gnome.org/doc/API/2.0/gtk/gtk-General.html +http://developer.gnome.org/doc/API/2.0/glib/glib-The-Main-Event-Loop.html + + +For documentation on event handling in GTK/GDK, see : + +http://developer.gnome.org/doc/API/2.0/gdk/gdk-Events.html +http://developer.gnome.org/doc/API/2.0/gdk/gdk-Event-Structures.html + + +Signals can be connected to handlers, emitted, propagated, blocked, +stopped. See : + +http://developer.gnome.org/doc/API/2.0/gobject/gobject-Signals.html + + + + +The "expose_event" + +Provides the exposed region in the GdkEventExpose structure. + +There are two ways of dealing with exposures. The first one is to directly +draw on the screen and the second one is to draw in a pixmap buffer, +and then to update the screen when necessary. + +In the first case, the expose event will be responsible for registering +hooks to process_traceset and require time intervals to the main +window. So, in this scenario, if a part of the screen is damaged, the +trace has to be read to redraw the screen. + +In the second case, with a pixmap buffer, the expose handler is only +responsible of showing the pixmap buffer on the screen. If the pixmap +buffer has never been filled with a drawing, the expose handler may ask +for it to be filled. + +The interest of using events request to the main window instead of reading +the events directly from the trace comes from the fact that the main +window does merge requests from the different viewers in the same tab so +that the read loop and the state update is shared. As viewers will, in +the common scenario, request the same events, only one pass through the +trace that will call the right hooks for the right intervals will be done. + +When the traceset read is over for a events request, the traceset_end +hook is called. It has the responsibility of finishing the drawing if +some parts still need to be drawn and to show it on the screen (if the +viewer uses a pixmap buffer). + +It can add dotted lines and such visual effects to enhance the user's +experience. + + +FIXME : explain other important events + +*/ + + +#ifndef LTTVWINDOW_H +#define LTTVWINDOW_H + +/*! \file lttvwindow.h + * \brief API used by the graphical viewers to interact with their top window. + * + * Main window (lttvwindow module) is the place to contain and display viewers. + * Viewers (lttv plugins) interact with main window through this API. + * This header file should be included in each graphic module. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Module Related API */ + +/* GQuark containing constructors of viewers in global attributes */ +extern GQuark LTTV_VIEWER_CONSTRUCTORS; + +/* constructor a the viewer */ +typedef GtkWidget* (*lttvwindow_viewer_constructor)(LttvPlugin *plugin); + +extern gint lttvwindow_preempt_count; + +#define CHECK_GDK_INTERVAL 50000 + +/** + * Function to register a view constructor so that main window can generate + * a menu item and a toolbar item for the viewer in order to generate a new + * instance easily. A menu entry and toolbar item will be added to each main + * window. + * + * It should be called by init function of the module. + * + * @param name name of the viewer : mainly used as tag for constructor + * @param menu_path path of the menu item. NULL : no menu entry. + * @param menu_text text of the menu item. + * @param pixmap Image shown on the toolbar item. NULL : no button. + * @param tooltip tooltip of the toolbar item. + * @param view_constructor constructor of the viewer. + */ + +void lttvwindow_register_constructor + (char * name, + char * menu_path, + char * menu_text, + char ** pixmap, + char * tooltip, + lttvwindow_viewer_constructor view_constructor); + + +/** + * Function to unregister the viewer's constructor, release the space + * occupied by menu_path, menu_text, pixmap, tooltip and constructor of the + * viewer. + * + * It will be called when a module is unloaded. + * + * @param view_constructor constructor of the viewer. + */ + +void lttvwindow_unregister_constructor + (lttvwindow_viewer_constructor view_constructor); + + + + +/* Viewer Instance Related API */ + +/** + * Structure used as hook_data for the time_window_notify hook. + */ +typedef struct _TimeWindowNotifyData { + TimeWindow *new_time_window; + TimeWindow *old_time_window; +} TimeWindowNotifyData; + + +/** + * Function to register a hook function that will be called by the main window + * when the time interval needs to be updated. + * + * This register function is typically called by the constructor of the viewer. + * + * @param tab the tab the viewer belongs to. + * @param hook hook that sould be called by the main window when the time + * interval changes. This hook function takes a + * TimeWindowNotifyData* as call_data. + * @param hook_data hook data associated with the hook function. It will + * be typically a pointer to the viewer's data structure. + */ + +void lttvwindow_register_time_window_notify(Tab *tab, + LttvHook hook, + gpointer hook_data); + + +/** + * Function to unregister the time_window notification hook. + * + * This unregister function is typically called by the destructor of the viewer. + * + * @param tab the tab the viewer belongs to. + * @param hook hook that sould be called by the main window when the time + * interval changes. This hook function takes a + * TimeWindowNotifyData* as call_data. + * @param hook_data hook data associated with the hook function. It will + * be typically a pointer to the viewer's data structure. + */ + +void lttvwindow_unregister_time_window_notify(Tab *tab, + LttvHook hook, + gpointer hook_data); + + +/** + * Function to register a hook function that will be called by the main window + * when the traceset is changed. That means that the viewer must redraw + * itself completely or check if it's affected by the particular change to the + * traceset. + * + * This register function is typically called by the constructor of the viewer. + * + * @param tab the tab the viewer belongs to. + * @param hook hook that should be called whenever a change to the traceset + * occurs. The call_data of this hook is a NULL pointer. + * @param hook_data hook data associated with the hook function. It will + * be typically a pointer to the viewer's data structure. + */ + +void lttvwindow_register_traceset_notify(Tab *tab, + LttvHook hook, + gpointer hook_data); + + +/** + * Function to unregister the traceset_notify hook. + * + * @param tab the tab the viewer belongs to. + * @param hook hook that should be called whenever a change to the traceset + * occurs. The call_data of this hook is a NULL pointer. + * @param hook_data hook data associated with the hook function. It will + * be typically a pointer to the viewer's data structure. + */ + +void lttvwindow_unregister_traceset_notify(Tab *tab, + LttvHook hook, + gpointer hook_data); + + +/** + * Function to register a hook function for a viewer be completely redrawn. + * + * @param tab viewer's tab + * @param hook hook function of the viewer. + * @param hook_data hook data associated with the hook function. + */ + +void lttvwindow_register_redraw_notify(Tab *tab, + LttvHook hook, gpointer hook_data); + +/** + * Function to unregister a hook function for a viewer be completely redrawn. + * + * @param tab viewer's tab + * @param hook hook function of the viewer. + * @param hook_data hook data associated with the hook function. + */ + +void lttvwindow_unregister_redraw_notify(Tab *tab, + LttvHook hook, gpointer hook_data); + + +/** + * Function to register a hook function for a viewer to re-do the events + * requests for the needed interval. + * + * This action is typically done after a "stop". + * + * The typical hook will remove all current requests for the viewer + * and make requests for missing information. + * + * @param tab viewer's tab + * @param hook hook function of the viewer. + * @param hook_data hook data associated with the hook function. + */ + +void lttvwindow_register_continue_notify(Tab *tab, + LttvHook hook, gpointer hook_data); + + +/** + * Function to unregister a hook function for a viewer to re-do the events + * requests for the needed interval. + * + * @param tab viewer's tab + * @param hook hook function of the viewer. + * @param hook_data hook data associated with the hook function. + */ + +void lttvwindow_unregister_continue_notify(Tab *tab, + LttvHook hook, gpointer hook_data); + + +/** + * Function to register a hook function for a viewer to set/update its + * filter. + * + * FIXME : Add information about what a filter is as seen from a viewer and how + * to use it. + * + * This register function is typically called by the constructor of the viewer. + * + * @param tab the tab the viewer belongs to. + * @param hook hook function called by the main window when a filter change + * occurs. + * @param hook_data hook data associated with the hook function. It will + * be typically a pointer to the viewer's data structure. + */ + +void lttvwindow_register_filter_notify(Tab *tab, + LttvHook hook, + gpointer hook_data); + + +/** + * Function to unregister a viewer's hook function which is used to + * set/update the filter of the viewer. + * + * This unregistration is called by the destructor of the viewer. + * + * @param tab the tab the viewer belongs to. + * @param hook hook function called by the main window when a filter change + * occurs. + * @param hook_data hook data associated with the hook function. It will + * be typically a pointer to the viewer's data structure. + */ + +void lttvwindow_unregister_filter_notify(Tab *tab, + LttvHook hook, + gpointer hook_data); + + +/** + * Function to get the current filter of the main window : useful at viewer + * instanciation. + * + * @param tab the tab the viewer belongs to. + * + * returns : the current filter. + */ + + +LttvFilter *lttvwindow_get_filter(Tab *tab); + +/** + * Function to register a hook function for a viewer to set/update its + * current time. + * + * @param tab the tab the viewer belongs to. + * @param hook hook function of the viewer that updates the current time. The + * call_data is a LttTime* representing the new current time. + * @param hook_data hook data associated with the hook function. It will + * be typically a pointer to the viewer's data structure. + */ + +void lttvwindow_register_current_time_notify(Tab *tab, + LttvHook hook, + gpointer hook_data); + + +/** + * Function to unregister a viewer's hook function which is used to + * set/update the current time of the viewer. + * @param tab the tab the viewer belongs to. + * @param hook hook function of the viewer that updates the current time. The + * call_data is a LttTime* representing the new current time. + * @param hook_data hook data associated with the hook function. It will + * be typically a pointer to the viewer's data structure. + */ + +void lttvwindow_unregister_current_time_notify(Tab *tab, + LttvHook hook, + gpointer hook_data); + +/** + * Function to register a hook function for a viewer to set/update its + * current position. + * + * @param tab the tab the viewer belongs to. + * @param hook hook function of the viewer that updates the current time. The + * call_data is a LttTime* representing the new current time. + * @param hook_data hook data associated with the hook function. It will + * be typically a pointer to the viewer's data structure. + */ + +void lttvwindow_register_current_position_notify(Tab *tab, + LttvHook hook, + gpointer hook_data); + + +/** + * Function to unregister a viewer's hook function which is used to + * set/update the current position of the viewer. + * @param tab the tab the viewer belongs to. + * @param hook hook function of the viewer that updates the current time. The + * call_data is a LttTime* representing the new current time. + * @param hook_data hook data associated with the hook function. It will + * be typically a pointer to the viewer's data structure. + */ + +void lttvwindow_unregister_current_position_notify(Tab *tab, + LttvHook hook, + gpointer hook_data); + + + +/** + * Function to register a hook function for a viewer to set/update the + * dividor of the hpane. It provides a way to make the horizontal + * dividors of all the viewers linked together. + * + * @param tab the tab the viewer belongs to. + * @param hook hook function of the viewer that will be called whenever a + * dividor changes in another viewer. The call_data of this hook + * is a gint*. The value of the integer is the new position of the + * hpane dividor. + * @param hook_data hook data associated with the hook function. It will + * be typically a pointer to the viewer's data structure. + */ + +void lttvwindow_register_dividor(Tab *tab, + LttvHook hook, + gpointer hook_data); + + +/** + * Function to unregister a viewer's hook function which is used to + * set/update hpane's dividor of the viewer. + * + * @param tab the tab the viewer belongs to. + * @param hook hook function of the viewer that will be called whenever a + * dividor changes in another viewer. The call_data of this hook + * is a gint*. The value of the integer is the new position of the + * hpane dividor. + * @param hook_data hook data associated with the hook function. It will + * be typically a pointer to the viewer's data structure. + */ + +void lttvwindow_unregister_dividor(Tab *tab, + LttvHook hook, + gpointer hook_data); + + + +/** + * Function to set the time interval of the current tab.a + * + * @param tab the tab the viewer belongs to. + * @param time_interval new time window. + */ + +void lttvwindow_report_time_window(Tab *tab, + TimeWindow time_window); + +/** + * Function to set the current time of the current tab. + * It will be called by a viewer's signal handle associated with + * the button-release-event signal + * @param tab the tab the viewer belongs to. + * @param time current time. + */ + +void lttvwindow_report_current_time(Tab *tab, + LttTime time); + + +/** + * Function to set the current event of the current tab. + * It will be called by a viewer's signal handle associated with + * the button-release-event signal + * @param tab the tab the viewer belongs to. + * @param pos the current position. + */ + +void lttvwindow_report_current_position(Tab *tab, + LttvTracesetContextPosition *pos); + +/** + * Function to set the position of the hpane's dividor (viewer). + * It will typically be called by a viewer's signal handle associated + * with the motion_notify_event event/signal. + * + * @param tab the tab the viewer belongs to. + * @param position position of the hpane's dividor. + */ + +void lttvwindow_report_dividor(Tab *tab, gint position); + + +/* Structure sent to the events request hook */ + /* Value considered as empty*/ +typedef struct _EventsRequest { + gpointer owner; /* Owner of the request */ + gpointer viewer_data; /* Unset : NULL */ + gboolean servicing; /* service in progress: TRUE*/ + LttTime start_time; /* Unset : ltt_time_infinite*/ + LttvTracesetContextPosition *start_position; /* Unset : NULL */ + gboolean stop_flag; /* Continue:TRUE Stop:FALSE */ + LttTime end_time; /* Unset : ltt_time_infinite*/ + guint num_events; /* Unset : G_MAXUINT */ + LttvTracesetContextPosition *end_position; /* Unset : NULL */ + gint trace; /* unset : -1 */ + GArray *hooks; /* Unset : NULL */ + LttvHooks *before_chunk_traceset; /* Unset : NULL */ + LttvHooks *before_chunk_trace; /* Unset : NULL */ + LttvHooks *before_chunk_tracefile;/* Unset : NULL */ + LttvHooks *event; /* Unset : NULL */ + LttvHooksById *event_by_id; /* Unset : NULL */ + LttvHooks *after_chunk_tracefile; /* Unset : NULL */ + LttvHooks *after_chunk_trace; /* Unset : NULL */ + LttvHooks *after_chunk_traceset; /* Unset : NULL */ + LttvHooks *before_request; /* Unset : NULL */ + LttvHooks *after_request; /* Unset : NULL */ +} EventsRequest; + +/* Maximum number of events to proceed at once in a chunk */ +#define CHUNK_NUM_EVENTS 6000 + + +/** + * Function to request data in a specific time interval to the main window. The + * event request servicing is differed until the glib idle functions are + * called. + * + * The viewer has to provide hooks that should be associated with the event + * request. + * + * Either start time or start position must be defined in a EventRequest + * structure for it to be valid. + * + * end_time, end_position and num_events can all be defined. The first one + * to occur will be used as end criterion. + * + * The events_request memory will be managed by the main window once its + * pointer is passed by this function. + * + * @param tab the tab the viewer belongs to. + * @param events_requested Details about the event request. + */ + +void lttvwindow_events_request(Tab *tab, + EventsRequest *events_request); + +/** + * Function to remove data requests related to a viewer. + * + * The existing requests's viewer gpointer is compared to the pointer + * given in argument to establish which data request should be removed. + * + * @param tab the tab the viewer belongs to. + * @param viewer a pointer to the viewer data structure + */ + +void lttvwindow_events_request_remove_all(Tab *tab, + gconstpointer viewer); + + +/** + * Function to see if there are events request pending. + * + * It tells if events requests are pending. Useful for checks in some events, + * i.e. detailed event list scrolling. + * + * @param tab the tab the viewer belongs to. + * @param viewer a pointer to the viewer data structure + * @return : TRUE is events requests are pending, else FALSE. + */ + +gboolean lttvwindow_events_request_pending(Tab *tab); + + + + +/** + * Function to get the current time interval shown on the current tab. + * It will be called by a viewer's hook function to update the + * shown time interval of the viewer and also be called by the constructor + * of the viewer. + * @param tab viewer's tab + * @return time window. + */ + +TimeWindow lttvwindow_get_time_window(Tab *tab); + + +/** + * Function to get the current time of the current tab. + * + * @param tab the tab the viewer belongs to. + * @return the current tab's current time. + */ + +LttTime lttvwindow_get_current_time(Tab *tab); + + +/** + * Function to get the filter of the current tab. + * @param main_win, the main window the viewer belongs to. + * @param filter, a pointer to a filter. + */ + +//LttvFilter *lttvwindow_get_filter(Tab *tab); + +/** + * Function to set the filter of the current tab. + * It should be called by the filter GUI to tell the + * main window to update the filter tab's lttv_filter. + * + * Notice : the lttv_filter object will be owned by the + * main window after the return of this function. + * Do NOT desallocate it. + * + * @param main_win, the main window the viewer belongs to. + * @param filter, a pointer to a filter. + */ + +void lttvwindow_report_filter(Tab *tab, LttvFilter *filter); + + + +/** + * Function to get the stats of the traceset + * It must be non const so the viewer can modify it. + * FIXME : a set/get pair of functions would be more appropriate here. + * @param tab the tab the viewer belongs to. + * @return A pointer to Traceset statistics. + */ + +LttvTracesetStats* lttvwindow_get_traceset_stats(Tab *tab); + +/** + * Function to get the context of the traceset + * It must be non const so the viewer can add and remove hooks from it. + * @param tab the tab the viewer belongs to. + * @return Context of the current tab. + */ + + +LttvTracesetContext* lttvwindow_get_traceset_context(Tab *tab); + + +/* set_time_window + * + * It updates the time window of the tab, then calls the updatetimewindow + * hooks of each viewer. + * + * This is called whenever the scrollbar value changes. + * + * This is mostly an internal function. + */ + +void set_time_window(Tab *tab, const TimeWindow *time_window); + + +/* set_current_time + * + * It updates the current time of the tab, then calls the updatetimewindow + * hooks of each viewer. + * + * This is called whenever the current time value changes. + * + * This is mostly an internal function. + */ + +void set_current_time(Tab *tab, const LttTime *current_time); + +void events_request_free(EventsRequest *events_request); + +GtkWidget *main_window_get_widget(Tab *tab); + +void set_current_position(Tab *tab, const LttvTracesetContextPosition *pos); + + +/** + * Function to disable the EventsRequests scheduler, nestable. + * + */ +static inline void lttvwindow_events_request_disable(void) +{ + lttvwindow_preempt_count++; +} + +/** + * Function to restore the EventsRequests scheduler, nestable. + * + */ +static inline void lttvwindow_events_request_enable(void) +{ + lttvwindow_preempt_count--; +} + + + + + + + + +#endif //LTTVWINDOW_H diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttvwindowtraces.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttvwindowtraces.c new file mode 100644 index 00000000..caa84cee --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttvwindowtraces.c @@ -0,0 +1,1976 @@ +/* This file is part of the Linux Trace Toolkit Graphic User Interface + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +/* This file is the API used to launch any background computation on a trace */ + +/* Here is the implementation of the API */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include // for CHUNK_NUM_EVENTS +#include /* for main window structure */ + +extern GSList * g_main_window_list; + +typedef struct _BackgroundRequest { + LttvAttributeName module_name; /* Hook path in global attributes, + where all standard hooks under computation/. + i.e. modulename */ + LttvTrace *trace; /* trace concerned */ + GtkWidget *dialog; /* Dialog linked with the request, may be NULL */ + GtkWidget *parent_window; /* Parent window the dialog must be transient for */ +} BackgroundRequest; + +typedef struct _BackgroundNotify { + gpointer owner; + LttvTrace *trace; /* trace */ + LttTime notify_time; + LttvTracesetContextPosition *notify_position; + LttvHooks *notify; /* Hook to call when the notify is + passed, or at the end of trace */ +} BackgroundNotify; + + + +/* Prototypes */ +gboolean lttvwindowtraces_process_pending_requests(LttvTrace *trace); + +/* Get a trace by its path name. + * + * @param path path of the trace on the virtual file system. + * @return Pointer to trace if found + * NULL is returned if the trace is not present + */ + +__EXPORT LttvTrace *lttvwindowtraces_get_trace_by_name(gchar *path) +{ + guint i; + + for(i=0;i= 0); + + attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir(LTTV_IATTRIBUTE(g_attribute), + LTTV_TRACES)); + g_assert(attribute); + + value = lttv_attribute_add(attribute, + g_quark_from_string(attribute_path), + LTTV_POINTER); + + *(value.v_pointer) = (gpointer)trace; + + /* create new traceset and tracesetcontext */ + LttvTraceset *ts; + LttvTracesetStats *tss; + //LttvTracesetContextPosition *sync_position; + + attribute = lttv_trace_attribute(trace); + result_b = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_COMPUTATION_TRACESET, + LTTV_POINTER, + &value); + g_assert(result_b); + + ts = lttv_traceset_new(); + *(value.v_pointer) = ts; + + lttv_traceset_add(ts,trace); + + result_b = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_COMPUTATION_TRACESET_CONTEXT, + LTTV_POINTER, + &value); + g_assert(result_b); + + tss = g_object_new(LTTV_TRACESET_STATS_TYPE, NULL); + *(value.v_pointer) = tss; + + lttv_context_init(LTTV_TRACESET_CONTEXT(tss), ts); +#if 0 + result_b = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_COMPUTATION_SYNC_POSITION, + LTTV_POINTER, + &value); + g_assert(result_b); + + sync_position = lttv_traceset_context_position_new(); + *(value.v_pointer) = sync_position; +#endif //0 + value = lttv_attribute_add(attribute, + LTTV_REQUESTS_QUEUE, + LTTV_POINTER); + + value = lttv_attribute_add(attribute, + LTTV_REQUESTS_CURRENT, + LTTV_POINTER); + + value = lttv_attribute_add(attribute, + LTTV_NOTIFY_QUEUE, + LTTV_POINTER); + + value = lttv_attribute_add(attribute, + LTTV_NOTIFY_CURRENT, + LTTV_POINTER); +} + +/* Remove a trace from the global attributes */ + +void lttvwindowtraces_remove_trace(LttvTrace *trace) +{ + LttvAttribute *g_attribute = lttv_global_attributes(); + LttvAttribute *attribute; + LttvAttributeValue value; + guint i; + gboolean result; + + attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir(LTTV_IATTRIBUTE(g_attribute), + LTTV_TRACES)); + g_assert(attribute); + + for(i=0;idialog); + bg_req->dialog = NULL; +} + + +/** + * Function to request data from a specific trace + * + * The memory allocated for the request will be managed by the API. + * + * @param widget the current Window + * @param trace the trace to compute + * @param module_name the name of the module which registered global computation + * hooks. + */ + +__EXPORT void lttvwindowtraces_background_request_queue + (GtkWidget *widget, LttvTrace *trace, gchar *module_name) +{ + BackgroundRequest *bg_req; + LttvAttribute *attribute = lttv_trace_attribute(trace); + LttvAttribute *g_attribute = lttv_global_attributes(); + LttvAttribute *module_attribute; + LttvAttributeValue value; + LttvAttributeType type; + GSList **slist; + gboolean result; + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_REQUESTS_QUEUE, + LTTV_POINTER, + &value); + g_assert(result); + + slist = (GSList**)(value.v_pointer); + + /* Verify that the calculator is loaded */ + module_attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir(LTTV_IATTRIBUTE(g_attribute), + LTTV_COMPUTATION)); + g_assert(module_attribute); + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(module_attribute), + g_quark_from_string(module_name), + &value); + if(type == LTTV_NONE) { + g_critical("Missing background calculator %s", module_name); + return; + } + + bg_req = g_new(BackgroundRequest,1); + bg_req->module_name = g_quark_from_string(module_name); + bg_req->trace = trace; + + *slist = g_slist_append(*slist, bg_req); + + /* Priority lower than live servicing */ + g_idle_remove_by_data(trace); + g_idle_add_full((G_PRIORITY_HIGH_IDLE + 23), + (GSourceFunc)lttvwindowtraces_process_pending_requests, + trace, + NULL); + /* FIXME : show message in status bar, need context and message id */ + g_info("Background computation for %s started for trace %p", module_name, + trace); + GtkWidget *dialog = + gtk_message_dialog_new( + GTK_WINDOW(widget), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, GTK_BUTTONS_OK, + "Background computation for %s started for trace %s", + module_name, + g_quark_to_string(ltt_trace_name(lttv_trace(trace)))); + gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(widget)); + g_signal_connect_swapped (dialog, "response", + G_CALLBACK (destroy_dialog), + bg_req); + bg_req->dialog = dialog; + /* the parent window might vanish : only use this pointer for a + * comparison with existing windows */ + bg_req->parent_window = gtk_widget_get_toplevel(widget); + gtk_widget_show(dialog); +} + +/** + * Remove a background request from a trace. + * + * This should ONLY be used by the modules which registered the global hooks + * (module_name). If this is called by the viewers, it may lead to incomplete + * and incoherent background processing information. + * + * Even if the module which deals with the hooks removes the background + * requests, it may cause a problem if the module gets loaded again in the + * session : the data will be partially calculated. The calculation function + * must deal with this case correctly. + * + * @param trace the trace to compute + * @param module_name the name of the module which registered global computation + * hooks. + */ + +void lttvwindowtraces_background_request_remove + (LttvTrace *trace, gchar *module_name) +{ + LttvAttribute *attribute = lttv_trace_attribute(trace); + LttvAttributeValue value; + GSList *iter = NULL; + GSList **slist; + gboolean result; + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_REQUESTS_QUEUE, + LTTV_POINTER, + &value); + g_assert(result); + + slist = (GSList**)(value.v_pointer); + + for(iter=*slist;iter!=NULL;) { + BackgroundRequest *bg_req = + (BackgroundRequest *)iter->data; + + if(bg_req->module_name == g_quark_from_string(module_name)) { + GSList *rem_iter = iter; + iter=g_slist_next(iter); + g_free(bg_req); + *slist = g_slist_delete_link(*slist, rem_iter); + } else { + iter=g_slist_next(iter); + } + } +} + +/** + * Find a background request in a trace + * + */ + +__EXPORT gboolean lttvwindowtraces_background_request_find + (LttvTrace *trace, gchar *module_name) +{ + LttvAttribute *attribute = lttv_trace_attribute(trace); + LttvAttributeValue value; + GSList *iter = NULL; + GSList **slist; + gboolean result; + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_REQUESTS_QUEUE, + LTTV_POINTER, + &value); + g_assert(result); + + slist = (GSList**)(value.v_pointer); + + for(iter=*slist;iter!=NULL;) { + BackgroundRequest *bg_req = + (BackgroundRequest *)iter->data; + + if(bg_req->module_name == g_quark_from_string(module_name)) { + return TRUE; + } else { + iter=g_slist_next(iter); + } + } + return FALSE; +} + +/** + * Register a callback to be called when requested data is passed in the next + * queued background processing. + * + * @param owner owner of the background notification + * @param trace the trace computed + * @param notify_time time when notification hooks must be called + * @param notify_position position when notification hooks must be called + * @param notify Hook to call when the notify position is passed + */ + +__EXPORT void lttvwindowtraces_background_notify_queue + (gpointer owner, + LttvTrace *trace, + LttTime notify_time, + const LttvTracesetContextPosition *notify_position, + const LttvHooks *notify) +{ + BackgroundNotify *bg_notify; + LttvAttribute *attribute = lttv_trace_attribute(trace); + LttvAttributeValue value; + GSList **slist; + gboolean result; + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_NOTIFY_QUEUE, + LTTV_POINTER, + &value); + g_assert(result); + + slist = (GSList**)(value.v_pointer); + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_COMPUTATION_TRACESET_CONTEXT, + LTTV_POINTER, + &value); + g_assert(result); + + LttvTracesetContext *tsc = (LttvTracesetContext*)(value.v_pointer); + + bg_notify = g_new(BackgroundNotify,1); + + bg_notify->owner = owner; + bg_notify->trace = trace; + bg_notify->notify_time = notify_time; + if(notify_position != NULL) { + bg_notify->notify_position = lttv_traceset_context_position_new(tsc); + lttv_traceset_context_position_copy(bg_notify->notify_position, + notify_position); + } else { + bg_notify->notify_position = NULL; + } + + bg_notify->notify = lttv_hooks_new(); + lttv_hooks_add_list(bg_notify->notify, notify); + + *slist = g_slist_append(*slist, bg_notify); +} + +/** + * Register a callback to be called when requested data is passed in the current + * background processing. + * + * @param owner owner of the background notification + * @param trace the trace computed + * @param notify_time time when notification hooks must be called + * @param notify_position position when notification hooks must be called + * @param notify Hook to call when the notify position is passed + */ + +__EXPORT void lttvwindowtraces_background_notify_current + (gpointer owner, + LttvTrace *trace, + LttTime notify_time, + const LttvTracesetContextPosition *notify_position, + const LttvHooks *notify) +{ + BackgroundNotify *bg_notify; + LttvAttribute *attribute = lttv_trace_attribute(trace); + LttvAttributeValue value; + GSList **slist; + gboolean result; + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_NOTIFY_CURRENT, + LTTV_POINTER, + &value); + g_assert(result); + + slist = (GSList**)(value.v_pointer); + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_COMPUTATION_TRACESET_CONTEXT, + LTTV_POINTER, + &value); + g_assert(result); + + LttvTracesetContext *tsc = (LttvTracesetContext*)(value.v_pointer); + + + bg_notify = g_new(BackgroundNotify,1); + + bg_notify->owner = owner; + bg_notify->trace = trace; + bg_notify->notify_time = notify_time; + if(notify_position!= NULL) { + bg_notify->notify_position = lttv_traceset_context_position_new(tsc); + lttv_traceset_context_position_copy(bg_notify->notify_position, + notify_position); + } else { + bg_notify->notify_position = NULL; + } + bg_notify->notify = lttv_hooks_new(); + lttv_hooks_add_list(bg_notify->notify, notify); + + *slist = g_slist_append(*slist, bg_notify); +} + + +static void notify_request_free(BackgroundNotify *notify_req) +{ + if(notify_req == NULL) return; + + if(notify_req->notify_position != NULL) + lttv_traceset_context_position_destroy(notify_req->notify_position); + if(notify_req->notify != NULL) + lttv_hooks_destroy(notify_req->notify); + g_free(notify_req); +} + +/** + * Removes all the notifications requests from a specific viewer. + * + * @param owner owner of the background notification + */ + +__EXPORT void lttvwindowtraces_background_notify_remove(gpointer owner) +{ + guint i; + + for(i=0;idata; + + if(bg_notify->owner == owner) { + GSList *rem_iter = iter; + iter=g_slist_next(iter); + notify_request_free(bg_notify); + *slist = g_slist_remove_link(*slist, rem_iter); + } else { + iter=g_slist_next(iter); + } + } + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_NOTIFY_CURRENT, + LTTV_POINTER, + &value); + g_assert(result); + + slist = (GSList**)(value.v_pointer); + + for(iter=*slist;iter!=NULL;) { + + BackgroundNotify *bg_notify = (BackgroundNotify*)iter->data; + + if(bg_notify->owner == owner) { + GSList *rem_iter = iter; + iter=g_slist_next(iter); + notify_request_free(bg_notify); + *slist = g_slist_remove_link(*slist, rem_iter); + } else { + iter=g_slist_next(iter); + } + } + } +} + + +/* Background processing helper functions */ + +void lttvwindowtraces_add_computation_hooks(LttvAttributeName module_name, + LttvTracesetContext *tsc, + LttvHooks *hook_adder) +{ + LttvAttribute *g_attribute = lttv_global_attributes(); + LttvAttribute *module_attribute; + LttvAttributeType type; + LttvAttributeValue value; + + + module_attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir(LTTV_IATTRIBUTE(g_attribute), + LTTV_COMPUTATION)); + g_assert(module_attribute); + + module_attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir( + LTTV_IATTRIBUTE(module_attribute), + module_name)); + g_assert(module_attribute); + + /* Call the module's hook adder */ + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(module_attribute), + LTTV_HOOK_ADDER, + &value); + if(type == LTTV_POINTER) { + //lttv_hooks_call((LttvHooks*)*(value.v_pointer), (gpointer)tss); + if(hook_adder != NULL) + lttv_hooks_add_list(hook_adder, (LttvHooks*)*(value.v_pointer)); + } +} + +void lttvwindowtraces_remove_computation_hooks(LttvAttributeName module_name, + LttvTracesetContext *tsc, + LttvHooks *hook_remover) +{ + LttvAttribute *g_attribute = lttv_global_attributes(); + LttvAttribute *module_attribute; + LttvAttributeType type; + LttvAttributeValue value; + + module_attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir(LTTV_IATTRIBUTE(g_attribute), + LTTV_COMPUTATION)); + g_assert(module_attribute); + + module_attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir( + LTTV_IATTRIBUTE(module_attribute), + module_name)); + g_assert(module_attribute); + + /* Call the module's hook remover */ + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(module_attribute), + LTTV_HOOK_REMOVER, + &value); + if(type == LTTV_POINTER) { + //lttv_hooks_call((LttvHooks*)*(value.v_pointer), (gpointer)tss); + if(hook_remover != NULL) + lttv_hooks_add_list(hook_remover, (LttvHooks*)*(value.v_pointer)); + } +} + +void lttvwindowtraces_call_before_chunk(LttvAttributeName module_name, + LttvTracesetContext *tsc) +{ + LttvAttribute *g_attribute = lttv_global_attributes(); + LttvAttribute *module_attribute; + LttvAttributeType type; + LttvAttributeValue value; + LttvHooks *before_chunk_traceset=NULL; + LttvHooks *before_chunk_trace=NULL; + LttvHooks *before_chunk_tracefile=NULL; + LttvHooks *event_hook=NULL; + LttvHooksById *event_hook_by_id=NULL; + + + module_attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir(LTTV_IATTRIBUTE(g_attribute), + LTTV_COMPUTATION)); + g_assert(module_attribute); + + module_attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir( + LTTV_IATTRIBUTE(module_attribute), + module_name)); + g_assert(module_attribute); + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(module_attribute), + LTTV_BEFORE_CHUNK_TRACESET, + &value); + if(type == LTTV_POINTER) { + before_chunk_traceset = (LttvHooks*)*(value.v_pointer); + } + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(module_attribute), + LTTV_BEFORE_CHUNK_TRACE, + &value); + if(type == LTTV_POINTER) { + before_chunk_trace = (LttvHooks*)*(value.v_pointer); + } + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(module_attribute), + LTTV_BEFORE_CHUNK_TRACEFILE, + &value); + if(type == LTTV_POINTER) { + before_chunk_tracefile = (LttvHooks*)*(value.v_pointer); + } + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(module_attribute), + LTTV_EVENT_HOOK, + &value); + if(type == LTTV_POINTER) { + event_hook = (LttvHooks*)*(value.v_pointer); + } + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(module_attribute), + LTTV_EVENT_HOOK_BY_ID, + &value); + if(type == LTTV_POINTER) { + event_hook_by_id = (LttvHooksById*)*(value.v_pointer); + } + + lttv_process_traceset_begin(tsc, + before_chunk_traceset, + before_chunk_trace, + before_chunk_tracefile, + event_hook, + event_hook_by_id); +} + + + +void lttvwindowtraces_call_after_chunk(LttvAttributeName module_name, + LttvTracesetContext *tsc) +{ + LttvAttribute *g_attribute = lttv_global_attributes(); + LttvAttribute *module_attribute; + LttvAttributeType type; + LttvAttributeValue value; + LttvHooks *after_chunk_traceset=NULL; + LttvHooks *after_chunk_trace=NULL; + LttvHooks *after_chunk_tracefile=NULL; + LttvHooks *event_hook=NULL; + LttvHooksById *event_hook_by_id=NULL; + + module_attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir(LTTV_IATTRIBUTE(g_attribute), + LTTV_COMPUTATION)); + g_assert(module_attribute); + + module_attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir( + LTTV_IATTRIBUTE(module_attribute), + module_name)); + g_assert(module_attribute); + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(module_attribute), + LTTV_AFTER_CHUNK_TRACESET, + &value); + if(type == LTTV_POINTER) { + after_chunk_traceset = (LttvHooks*)*(value.v_pointer); + } + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(module_attribute), + LTTV_AFTER_CHUNK_TRACE, + &value); + if(type == LTTV_POINTER) { + after_chunk_trace = (LttvHooks*)*(value.v_pointer); + } + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(module_attribute), + LTTV_AFTER_CHUNK_TRACEFILE, + &value); + if(type == LTTV_POINTER) { + after_chunk_tracefile = (LttvHooks*)*(value.v_pointer); + } + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(module_attribute), + LTTV_EVENT_HOOK, + &value); + if(type == LTTV_POINTER) { + event_hook = (LttvHooks*)*(value.v_pointer); + } + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(module_attribute), + LTTV_EVENT_HOOK_BY_ID, + &value); + if(type == LTTV_POINTER) { + event_hook_by_id = (LttvHooksById*)*(value.v_pointer); + } + + lttv_process_traceset_end(tsc, + after_chunk_traceset, + after_chunk_trace, + after_chunk_tracefile, + event_hook, + event_hook_by_id); + +} + + +void lttvwindowtraces_set_in_progress(LttvAttributeName module_name, + LttvTrace *trace) +{ + LttvAttribute *attribute = lttv_trace_attribute(trace); + LttvAttributeValue value; + + attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir(LTTV_IATTRIBUTE(attribute), + module_name)); + g_assert(attribute); + + value = lttv_iattribute_add(LTTV_IATTRIBUTE(attribute), + LTTV_IN_PROGRESS, + LTTV_INT); + /* the value is left unset. The only presence of the attribute is necessary. + */ +} + +void lttvwindowtraces_unset_in_progress(LttvAttributeName module_name, + LttvTrace *trace) +{ + LttvAttribute *attribute = lttv_trace_attribute(trace); + + attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir(LTTV_IATTRIBUTE(attribute), + module_name)); + g_assert(attribute); + + lttv_iattribute_remove_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_IN_PROGRESS); +} + +__EXPORT gboolean lttvwindowtraces_get_in_progress(LttvAttributeName module_name, + LttvTrace *trace) +{ + LttvAttribute *attribute = lttv_trace_attribute(trace); + LttvAttributeType type; + LttvAttributeValue value; + + attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir(LTTV_IATTRIBUTE(attribute), + module_name)); + g_assert(attribute); + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_IN_PROGRESS, + &value); + /* The only presence of the attribute is necessary. */ + if(type == LTTV_NONE) + return FALSE; + else + return TRUE; +} + +void lttvwindowtraces_set_ready(LttvAttributeName module_name, + LttvTrace *trace) +{ + LttvAttribute *attribute = lttv_trace_attribute(trace); + LttvAttributeValue value; + + attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir(LTTV_IATTRIBUTE(attribute), + module_name)); + g_assert(attribute); + + value = lttv_iattribute_add(LTTV_IATTRIBUTE(attribute), + LTTV_READY, + LTTV_INT); + /* the value is left unset. The only presence of the attribute is necessary. + */ +} + +void lttvwindowtraces_unset_ready(LttvAttributeName module_name, + LttvTrace *trace) +{ + LttvAttribute *attribute = lttv_trace_attribute(trace); + + attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir(LTTV_IATTRIBUTE(attribute), + module_name)); + g_assert(attribute); + + lttv_iattribute_remove_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_READY); +} + +__EXPORT gboolean lttvwindowtraces_get_ready(LttvAttributeName module_name, + LttvTrace *trace) +{ + LttvAttribute *attribute = lttv_trace_attribute(trace); + LttvAttributeType type; + LttvAttributeValue value; + + attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir(LTTV_IATTRIBUTE(attribute), + module_name)); + g_assert(attribute); + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_READY, + &value); + /* The only presence of the attribute is necessary. */ + if(type == LTTV_NONE) + return FALSE; + else + return TRUE; +} + +static gint find_window_widget(MainWindow *a, GtkWidget *b) +{ + if(a->mwindow == b) return 0; + else return -1; +} + + +/* lttvwindowtraces_process_pending_requests + * + * Process the pending background computation requests + * + * This internal function gets called by g_idle, taking care of the pending + * requests. + * + */ + + +gboolean lttvwindowtraces_process_pending_requests(LttvTrace *trace) +{ + LttvTracesetContext *tsc; + LttvTracesetStats *tss; + LttvTraceset *ts; + //LttvTracesetContextPosition *sync_position; + LttvAttribute *attribute; + LttvAttribute *g_attribute = lttv_global_attributes(); + GSList **list_out, **list_in, **notify_in, **notify_out; + LttvAttributeValue value; + LttvAttributeType type; + gboolean ret_val; + + if(trace == NULL) + return FALSE; + + if(lttvwindow_preempt_count > 0) return TRUE; + + attribute = lttv_trace_attribute(trace); + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_REQUESTS_QUEUE, + &value); + g_assert(type == LTTV_POINTER); + list_out = (GSList**)(value.v_pointer); + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_REQUESTS_CURRENT, + &value); + g_assert(type == LTTV_POINTER); + list_in = (GSList**)(value.v_pointer); + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_NOTIFY_QUEUE, + &value); + g_assert(type == LTTV_POINTER); + notify_out = (GSList**)(value.v_pointer); + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_NOTIFY_CURRENT, + &value); + g_assert(type == LTTV_POINTER); + notify_in = (GSList**)(value.v_pointer); + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_COMPUTATION_TRACESET, + &value); + g_assert(type == LTTV_POINTER); + ts = (LttvTraceset*)*(value.v_pointer); + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_COMPUTATION_TRACESET_CONTEXT, + &value); + g_assert(type == LTTV_POINTER); + tsc = (LttvTracesetContext*)*(value.v_pointer); + tss = (LttvTracesetStats*)*(value.v_pointer); + g_assert(LTTV_IS_TRACESET_CONTEXT(tsc)); + g_assert(LTTV_IS_TRACESET_STATS(tss)); +#if 0 + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_COMPUTATION_SYNC_POSITION, + &value); + g_assert(type == LTTV_POINTER); + sync_position = (LttvTracesetContextPosition*)*(value.v_pointer); +#endif //0 + /* There is no events requests pending : we should never have been called! */ + g_assert(g_slist_length(*list_out) != 0 || g_slist_length(*list_in) != 0); + /* 0.1 Lock traces */ + { + guint iter_trace=0; + + for(iter_trace=0; + iter_tracets); + iter_trace++) { + LttvTrace *trace_v = lttv_traceset_get(tsc->ts,iter_trace); + + if(lttvwindowtraces_lock(trace_v) != 0) + return TRUE; /* Cannot get trace lock, try later */ + + } + } + /* 0.2 Sync tracefiles */ + //g_assert(lttv_process_traceset_seek_position(tsc, sync_position) == 0); + lttv_process_traceset_synchronize_tracefiles(tsc); + /* 1. Before processing */ + { + /* if list_in is empty */ + if(g_slist_length(*list_in) == 0) { + + { + /* - Add all requests in list_out to list_in, empty list_out */ + GSList *iter = *list_out; + + while(iter != NULL) { + gboolean remove = FALSE; + gboolean free_data = FALSE; + + BackgroundRequest *bg_req = (BackgroundRequest*)iter->data; + + remove = TRUE; + free_data = FALSE; + *list_in = g_slist_append(*list_in, bg_req); + + /* Go to next */ + if(remove) + { + GSList *remove_iter = iter; + + iter = g_slist_next(iter); + if(free_data) g_free(remove_iter->data); + *list_out = g_slist_remove_link(*list_out, remove_iter); + } else { // not remove + iter = g_slist_next(iter); + } + } + } + + { + GSList *iter = *list_in; + /* - for each request in list_in */ + while(iter != NULL) { + + BackgroundRequest *bg_req = (BackgroundRequest*)iter->data; + /* - set hooks'in_progress flag to TRUE */ + lttvwindowtraces_set_in_progress(bg_req->module_name, + bg_req->trace); + + /* - call before request hook */ + /* Get before request hook */ + LttvAttribute *module_attribute; + + module_attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir( + LTTV_IATTRIBUTE(g_attribute), + LTTV_COMPUTATION)); + g_assert(module_attribute); + + module_attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir( + LTTV_IATTRIBUTE(module_attribute), + bg_req->module_name)); + g_assert(module_attribute); + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(module_attribute), + LTTV_BEFORE_REQUEST, + &value); + g_assert(type == LTTV_POINTER); + LttvHooks *before_request = (LttvHooks*)*(value.v_pointer); + + if(before_request != NULL) lttv_hooks_call(before_request, tsc); + + iter = g_slist_next(iter); + } + } + + /* - seek trace to start */ + { + LttTime start = { 0, 0}; + lttv_process_traceset_seek_time(tsc, start); + } + + /* - Move all notifications from notify_out to notify_in. */ + { + GSList *iter = *notify_out; + g_assert(g_slist_length(*notify_in) == 0); + + while(iter != NULL) { + gboolean remove = FALSE; + gboolean free_data = FALSE; + + BackgroundNotify *notify_req = (BackgroundNotify*)iter->data; + + remove = TRUE; + free_data = FALSE; + *notify_in = g_slist_append(*notify_in, notify_req); + + /* Go to next */ + if(remove) + { + GSList *remove_iter = iter; + + iter = g_slist_next(iter); + if(free_data) + notify_request_free((BackgroundNotify*)remove_iter->data); + *notify_out = g_slist_remove_link(*notify_out, remove_iter); + } else { // not remove + iter = g_slist_next(iter); + } + } + } + { + GSList *iter = *list_in; + LttvHooks *hook_adder = lttv_hooks_new(); + /* - for each request in list_in */ + while(iter != NULL) { + + BackgroundRequest *bg_req = (BackgroundRequest*)iter->data; + /*- add hooks to context*/ + lttvwindowtraces_add_computation_hooks(bg_req->module_name, + tsc, + hook_adder); + iter = g_slist_next(iter); + } + lttv_hooks_call(hook_adder,tsc); + lttv_hooks_destroy(hook_adder); + } + + + } + + { + GSList *iter = *list_in; + /* - for each request in list_in */ + while(iter != NULL) { + + BackgroundRequest *bg_req = (BackgroundRequest*)iter->data; + /*- Call before chunk hooks for list_in*/ + lttvwindowtraces_call_before_chunk(bg_req->module_name, + tsc); + iter = g_slist_next(iter); + } + } + + } + /* 2. call process traceset middle for a chunk */ + { + /*(assert list_in is not empty! : should not even be called in that case)*/ + LttTime end = ltt_time_infinite; + g_assert(g_slist_length(*list_in) != 0); + + lttv_process_traceset_middle(tsc, end, CHUNK_NUM_EVENTS, NULL); + } + + /* 3. After the chunk */ + { + /* 3.1 call after_chunk hooks for list_in */ + { + GSList *iter = *list_in; + /* - for each request in list_in */ + while(iter != NULL) { + + BackgroundRequest *bg_req = (BackgroundRequest*)iter->data; + /* - Call after chunk hooks for list_in */ + lttvwindowtraces_call_after_chunk(bg_req->module_name, + tsc); + iter = g_slist_next(iter); + } + } + + /* 3.2 for each notify_in */ + { + GSList *iter = *notify_in; + LttvTracefileContext *tfc = lttv_traceset_context_get_current_tfc(tsc); + + while(iter != NULL) { + gboolean remove = FALSE; + gboolean free_data = FALSE; + + BackgroundNotify *notify_req = (BackgroundNotify*)iter->data; + + /* - if current time >= notify time, call notify and remove from + * notify_in. + * - if current position >= notify position, call notify and remove + * from notify_in. + */ + if( (tfc != NULL && + ltt_time_compare(notify_req->notify_time, tfc->timestamp) <= 0) + || + (notify_req->notify_position != NULL && + lttv_traceset_context_ctx_pos_compare(tsc, + notify_req->notify_position) >= 0) + ) { + + lttv_hooks_call(notify_req->notify, notify_req); + + remove = TRUE; + free_data = TRUE; + } + + /* Go to next */ + if(remove) + { + GSList *remove_iter = iter; + + iter = g_slist_next(iter); + if(free_data) + notify_request_free((BackgroundNotify*)remove_iter->data); + *notify_in = g_slist_remove_link(*notify_in, remove_iter); + } else { // not remove + iter = g_slist_next(iter); + } + } + } + + { + LttvTracefileContext *tfc = lttv_traceset_context_get_current_tfc(tsc); + /* 3.3 if end of trace reached */ + if(tfc != NULL) + g_debug("Current time : %lu sec, %lu nsec", + tfc->timestamp.tv_sec, tfc->timestamp.tv_nsec); + if(tfc == NULL || ltt_time_compare(tfc->timestamp, + tsc->time_span.end_time) > 0) { + + { + GSList *iter = *list_in; + LttvHooks *hook_remover = lttv_hooks_new(); + /* - for each request in list_in */ + while(iter != NULL) { + + BackgroundRequest *bg_req = (BackgroundRequest*)iter->data; + /* - remove hooks from context */ + lttvwindowtraces_remove_computation_hooks(bg_req->module_name, + tsc, + hook_remover); + iter = g_slist_next(iter); + } + lttv_hooks_call(hook_remover,tsc); + lttv_hooks_destroy(hook_remover); + } + + /* - for each request in list_in */ + { + GSList *iter = *list_in; + + while(iter != NULL) { + gboolean remove = FALSE; + gboolean free_data = FALSE; + + BackgroundRequest *bg_req = (BackgroundRequest*)iter->data; + + /* - set hooks'in_progress flag to FALSE */ + lttvwindowtraces_unset_in_progress(bg_req->module_name, + bg_req->trace); + /* - set hooks'ready flag to TRUE */ + lttvwindowtraces_set_ready(bg_req->module_name, + bg_req->trace); + /* - call after request hook */ + /* Get after request hook */ + LttvAttribute *module_attribute; + + module_attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir( + LTTV_IATTRIBUTE(g_attribute), + LTTV_COMPUTATION)); + g_assert(module_attribute); + + module_attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir( + LTTV_IATTRIBUTE(module_attribute), + bg_req->module_name)); + g_assert(module_attribute); + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(module_attribute), + LTTV_AFTER_REQUEST, + &value); + g_assert(type == LTTV_POINTER); + LttvHooks *after_request = (LttvHooks*)*(value.v_pointer); + { + struct sum_traceset_closure t_closure; + t_closure.tss = (LttvTracesetStats*)tsc; + t_closure.current_time = ltt_time_infinite; + if(after_request != NULL) lttv_hooks_call(after_request, + &t_closure); + } + + if(bg_req->dialog != NULL) + gtk_widget_destroy(bg_req->dialog); + GtkWidget *parent_window; + if(g_slist_find_custom(g_main_window_list, + bg_req->parent_window, + (GCompareFunc)find_window_widget)) + parent_window = GTK_WIDGET(bg_req->parent_window); + else + parent_window = NULL; + + GtkWidget *dialog = + gtk_message_dialog_new(GTK_WINDOW(parent_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, GTK_BUTTONS_OK, + "Background computation %s finished for trace %s", + g_quark_to_string(bg_req->module_name), + g_quark_to_string(ltt_trace_name(lttv_trace(bg_req->trace)))); + if(parent_window != NULL) + gtk_window_set_transient_for(GTK_WINDOW(dialog), + GTK_WINDOW(parent_window)); + g_signal_connect_swapped (dialog, "response", + G_CALLBACK (gtk_widget_destroy), + dialog); + gtk_widget_show(dialog); + + /* - remove request */ + remove = TRUE; + free_data = TRUE; + + /* Go to next */ + if(remove) + { + GSList *remove_iter = iter; + + iter = g_slist_next(iter); + if(free_data) g_free(remove_iter->data); + *list_in = g_slist_remove_link(*list_in, remove_iter); + } else { // not remove + iter = g_slist_next(iter); + } + } + } + + /* - for each notifications in notify_in */ + { + GSList *iter = *notify_in; + + while(iter != NULL) { + gboolean remove = FALSE; + gboolean free_data = FALSE; + + BackgroundNotify *notify_req = (BackgroundNotify*)iter->data; + + /* - call notify and remove from notify_in */ + lttv_hooks_call(notify_req->notify, notify_req); + remove = TRUE; + free_data = TRUE; + + /* Go to next */ + if(remove) + { + GSList *remove_iter = iter; + + iter = g_slist_next(iter); + if(free_data) + notify_request_free((BackgroundNotify*)remove_iter->data); + *notify_in = g_slist_remove_link(*notify_in, remove_iter); + } else { // not remove + iter = g_slist_next(iter); + } + } + } + { + /* - reset the context */ + LTTV_TRACESET_CONTEXT_GET_CLASS(tsc)->fini(tsc); + LTTV_TRACESET_CONTEXT_GET_CLASS(tsc)->init(tsc,ts); + } + /* - if list_out is empty */ + if(g_slist_length(*list_out) == 0) { + /* - return FALSE (scheduler stopped) */ + g_debug("Background computation scheduler stopped"); + g_info("Background computation finished for trace %p", trace); + /* FIXME : remove status bar info, need context id and message id */ + + ret_val = FALSE; + } else { + ret_val = TRUE; + } + } else { + /* 3.4 else, end of trace not reached */ + /* - return TRUE (scheduler still registered) */ + g_debug("Background computation left"); + ret_val = TRUE; + } + } + } + /* 4. Unlock traces */ + { + lttv_process_traceset_get_sync_data(tsc); + //lttv_traceset_context_position_save(tsc, sync_position); + guint iter_trace; + + for(iter_trace=0; + iter_tracets); + iter_trace++) { + LttvTrace *trace_v = lttv_traceset_get(tsc->ts, iter_trace); + + lttvwindowtraces_unlock(trace_v); + } + } + return ret_val; +} + + + +/** + * Register the background computation hooks for a specific module. It adds the + * computation hooks to the global attrubutes, under "computation/module name". + * + * @param module_name A GQuark : the name of the module which computes the + * information. + */ +void lttvwindowtraces_register_computation_hooks(LttvAttributeName module_name, + LttvHooks *before_chunk_traceset, + LttvHooks *before_chunk_trace, + LttvHooks *before_chunk_tracefile, + LttvHooks *after_chunk_traceset, + LttvHooks *after_chunk_trace, + LttvHooks *after_chunk_tracefile, + LttvHooks *before_request, + LttvHooks *after_request, + LttvHooks *event_hook, + LttvHooksById *event_hook_by_id, + LttvHooks *hook_adder, + LttvHooks *hook_remover) +{ + LttvAttribute *g_attribute = lttv_global_attributes(); + LttvAttribute *attribute; + LttvAttributeValue value; + gboolean result; + + attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir(LTTV_IATTRIBUTE(g_attribute), + LTTV_COMPUTATION)); + g_assert(attribute); + + attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir(LTTV_IATTRIBUTE(attribute), + module_name)); + g_assert(attribute); + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_BEFORE_CHUNK_TRACESET, + LTTV_POINTER, + &value); + g_assert(result); + + *(value.v_pointer) = before_chunk_traceset; + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_BEFORE_CHUNK_TRACE, + LTTV_POINTER, + &value); + g_assert(result); + *(value.v_pointer) = before_chunk_trace; + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_BEFORE_CHUNK_TRACEFILE, + LTTV_POINTER, + &value); + g_assert(result); + *(value.v_pointer) = before_chunk_tracefile; + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_AFTER_CHUNK_TRACESET, + LTTV_POINTER, + &value); + g_assert(result); + *(value.v_pointer) = after_chunk_traceset; + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_AFTER_CHUNK_TRACE, + LTTV_POINTER, + &value); + g_assert(result); + *(value.v_pointer) = after_chunk_trace; + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_AFTER_CHUNK_TRACEFILE, + LTTV_POINTER, + &value); + g_assert(result); + *(value.v_pointer) = after_chunk_tracefile; + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_BEFORE_REQUEST, + LTTV_POINTER, + &value); + g_assert(result); + *(value.v_pointer) = before_request; + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_AFTER_REQUEST, + LTTV_POINTER, + &value); + g_assert(result); + *(value.v_pointer) = after_request; + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_EVENT_HOOK, + LTTV_POINTER, + &value); + g_assert(result); + *(value.v_pointer) = event_hook; + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_EVENT_HOOK_BY_ID, + LTTV_POINTER, + &value); + g_assert(result); + *(value.v_pointer) = event_hook_by_id; + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_HOOK_ADDER, + LTTV_POINTER, + &value); + g_assert(result); + *(value.v_pointer) = hook_adder; + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_HOOK_REMOVER, + LTTV_POINTER, + &value); + g_assert(result); + *(value.v_pointer) = hook_remover; + +} + + +/** + * It removes all the requests that can be currently processed by the + * background computation algorithm for all the traces (list_in and list_out). + * + * Leaves the flag to in_progress or none.. depending if current or queue + * + * @param module_name A GQuark : the name of the module which computes the + * information. + */ +void lttvwindowtraces_unregister_requests(LttvAttributeName module_name) +{ + guint i; + gboolean result; + + for(i=0;idata; + + if(bg_req->module_name == module_name) { + remove = TRUE; + free_data = TRUE; + } + + /* Go to next */ + if(remove) + { + GSList *remove_iter = iter; + + iter = g_slist_next(iter); + if(free_data) g_free(remove_iter->data); + *queue = g_slist_remove_link(*queue, remove_iter); + } else { // not remove + iter = g_slist_next(iter); + } + } + + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_REQUESTS_CURRENT, + LTTV_POINTER, + &value); + g_assert(result); + + current = (GSList**)(value.v_pointer); + + iter = *current; + while(iter != NULL) { + gboolean remove = FALSE; + gboolean free_data = FALSE; + + BackgroundRequest *bg_req = (BackgroundRequest*)iter->data; + + if(bg_req->module_name == module_name) { + remove = TRUE; + free_data = TRUE; + } + + /* Go to next */ + if(remove) + { + GSList *remove_iter = iter; + + iter = g_slist_next(iter); + if(free_data) g_free(remove_iter->data); + *current = g_slist_remove_link(*current, remove_iter); + } else { // not remove + iter = g_slist_next(iter); + } + } + } +} + + +/** + * Unregister the background computation hooks for a specific module. + * + * It also removes all the requests that can be currently processed by the + * background computation algorithm for all the traces (list_in and list_out). + * + * @param module_name A GQuark : the name of the module which computes the + * information. + */ + +void lttvwindowtraces_unregister_computation_hooks + (LttvAttributeName module_name) +{ + LttvAttribute *g_attribute = lttv_global_attributes(); + LttvAttribute *attribute; + LttvAttributeValue value; + gboolean result; + + attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir(LTTV_IATTRIBUTE(g_attribute), + LTTV_COMPUTATION)); + g_assert(attribute); + + attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir(LTTV_IATTRIBUTE(attribute), + module_name)); + g_assert(attribute); + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_BEFORE_CHUNK_TRACESET, + LTTV_POINTER, + &value); + g_assert(result); + + LttvHooks *before_chunk_traceset = (LttvHooks*)*(value.v_pointer); + if(before_chunk_traceset != NULL) + lttv_hooks_destroy(before_chunk_traceset); + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_BEFORE_CHUNK_TRACE, + LTTV_POINTER, + &value); + g_assert(result); + + LttvHooks *before_chunk_trace = (LttvHooks*)*(value.v_pointer); + if(before_chunk_trace != NULL) + lttv_hooks_destroy(before_chunk_trace); + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_BEFORE_CHUNK_TRACEFILE, + LTTV_POINTER, + &value); + g_assert(result); + + LttvHooks *before_chunk_tracefile = (LttvHooks*)*(value.v_pointer); + if(before_chunk_tracefile != NULL) + lttv_hooks_destroy(before_chunk_tracefile); + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_AFTER_CHUNK_TRACESET, + LTTV_POINTER, + &value); + g_assert(result); + + LttvHooks *after_chunk_traceset = (LttvHooks*)*(value.v_pointer); + if(after_chunk_traceset != NULL) + lttv_hooks_destroy(after_chunk_traceset); + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_AFTER_CHUNK_TRACE, + LTTV_POINTER, + &value); + g_assert(result); + + LttvHooks *after_chunk_trace = (LttvHooks*)*(value.v_pointer); + if(after_chunk_trace != NULL) + lttv_hooks_destroy(after_chunk_trace); + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_AFTER_CHUNK_TRACEFILE, + LTTV_POINTER, + &value); + g_assert(result); + + LttvHooks *after_chunk_tracefile = (LttvHooks*)*(value.v_pointer); + if(after_chunk_tracefile != NULL) + lttv_hooks_destroy(after_chunk_tracefile); + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_BEFORE_REQUEST, + LTTV_POINTER, + &value); + g_assert(result); + + LttvHooks *before_request = (LttvHooks*)*(value.v_pointer); + if(before_request != NULL) + lttv_hooks_destroy(before_request); + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_AFTER_REQUEST, + LTTV_POINTER, + &value); + g_assert(result); + + LttvHooks *after_request = (LttvHooks*)*(value.v_pointer); + if(after_request != NULL) + lttv_hooks_destroy(after_request); + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_EVENT_HOOK, + LTTV_POINTER, + &value); + g_assert(result); + + LttvHooks *event_hook = (LttvHooks*)*(value.v_pointer); + if(event_hook != NULL) + lttv_hooks_destroy(event_hook); + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_EVENT_HOOK_BY_ID, + LTTV_POINTER, + &value); + g_assert(result); + + LttvHooksById *event_hook_by_id = (LttvHooksById*)*(value.v_pointer); + if(event_hook_by_id != NULL) + lttv_hooks_by_id_destroy(event_hook_by_id); + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_HOOK_ADDER, + LTTV_POINTER, + &value); + g_assert(result); + + LttvHooks *hook_adder = (LttvHooks*)*(value.v_pointer); + if(hook_adder != NULL) + lttv_hooks_destroy(hook_adder); + + result = lttv_iattribute_find(LTTV_IATTRIBUTE(attribute), + LTTV_HOOK_REMOVER, + LTTV_POINTER, + &value); + g_assert(result); + + LttvHooks *hook_remover = (LttvHooks*)*(value.v_pointer); + if(hook_remover != NULL) + lttv_hooks_destroy(hook_remover); + + + lttv_iattribute_remove_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_EVENT_HOOK_BY_ID); + lttv_iattribute_remove_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_EVENT_HOOK); + + lttv_iattribute_remove_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_AFTER_REQUEST); + lttv_iattribute_remove_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_BEFORE_REQUEST); + + lttv_iattribute_remove_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_AFTER_CHUNK_TRACEFILE); + lttv_iattribute_remove_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_AFTER_CHUNK_TRACE); + lttv_iattribute_remove_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_AFTER_CHUNK_TRACESET); + + lttv_iattribute_remove_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_BEFORE_CHUNK_TRACEFILE); + lttv_iattribute_remove_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_BEFORE_CHUNK_TRACE); + lttv_iattribute_remove_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_BEFORE_CHUNK_TRACESET); + lttv_iattribute_remove_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_HOOK_ADDER); + lttv_iattribute_remove_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_HOOK_REMOVER); + + /* finally, remove module name */ + attribute = + LTTV_ATTRIBUTE(lttv_iattribute_find_subdir(LTTV_IATTRIBUTE(g_attribute), + LTTV_COMPUTATION)); + g_assert(attribute); + lttv_iattribute_remove_by_name(LTTV_IATTRIBUTE(attribute), + module_name); + +} + +/** + * Lock a trace so no other instance can use it. + * + * @param trace The trace to lock. + * @return 0 on success, -1 if cannot get lock. + */ +gint lttvwindowtraces_lock(LttvTrace *trace) +{ + LttvAttribute *attribute = lttv_trace_attribute(trace); + LttvAttributeValue value; + LttvAttributeType type; + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_LOCK, + &value); + /* Verify the absence of the lock. */ + if(type != LTTV_NONE) { + g_critical("Cannot take trace lock"); + return -1; + } + + value = lttv_iattribute_add(LTTV_IATTRIBUTE(attribute), + LTTV_LOCK, + LTTV_INT); + /* the value is left unset. The only presence of the attribute is necessary. + */ + + return 0; +} + +/** + * Unlock a trace. + * + * @param trace The trace to unlock. + * @return 0 on success, -1 if cannot unlock (not locked ?). + */ +gint lttvwindowtraces_unlock(LttvTrace *trace) +{ + LttvAttribute *attribute = lttv_trace_attribute(trace); + LttvAttributeType type; + LttvAttributeValue value; + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_LOCK, + &value); + /* Verify the presence of the lock. */ + if(type == LTTV_NONE) { + g_critical("Cannot release trace lock"); + return -1; + } + + lttv_iattribute_remove_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_LOCK); + + return 0; +} + +/** + * Verify if a trace is locked. + * + * @param trace The trace to verify. + * @return TRUE if locked, FALSE is unlocked. + */ +gint lttvwindowtraces_get_lock_state(LttvTrace *trace) +{ + LttvAttribute *attribute = lttv_trace_attribute(trace); + LttvAttributeType type; + LttvAttributeValue value; + + type = lttv_iattribute_get_by_name(LTTV_IATTRIBUTE(attribute), + LTTV_LOCK, + &value); + /* The only presence of the attribute is necessary. */ + if(type == LTTV_NONE) + return FALSE; + else + return TRUE; +} + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttvwindowtraces.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttvwindowtraces.h new file mode 100644 index 00000000..23450308 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/lttvwindowtraces.h @@ -0,0 +1,321 @@ +/* This file is part of the Linux Trace Toolkit Graphic User Interface + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +/* This file is the API used to launch any background computation on a trace */ + +/* lttvwindowtraces + * + * This API consists in two main parts. The first one is for the background + * computation provider and the second is for the viewer which needs this + * information. + * + * A computation provider, i.e. a statistics computation module or a state + * computation module, have two things in common : they append data to a trace + * in an extensible container (LttvAttributes). This extended information, once + * computed, can be kept all along with the trace and does not need to be + * recomputed : a computation done on a trace must result in a identical result + * each time it is done. + * + * This API provides functions for computation provider to register their + * computation functions (or computation functions insertion and removal + * functions). Once the computation provider is registered with its module name, + * extended computation for a trace can be requested by any viewer by specifying + * the module name, as we will describe in a moment. + * + * A viewer which needs extended information about a trace must ask for it to be + * computed by doing a background computation request. It may also ask to be + * notified of the completion of its request by doing a notify request. + * + * Before asking for the computation, it must check for its readiness. If it is + * ready, the information has already been computed, so it is ready to use. If + * the information is not ready, in must check whether or not the processing of + * this task is in progress. If it is, it must not do any background computation + * request. It must only do a background notification request of the current + * processing to be informed of its completion. If the information is not ready + * and not being processed, then the viewer may do a background computation + * request and add a notify request to the notify queue. + * + * When a context takes control of a trace, it must lock the trace. This is a + * way of ensuring that not conflict will occur between two traceset contexts + * and shared traces. It will generate an error if a context try to get a lock + * on a trace what is not unlocked. Upon every trace locking, + * lttv_process_traceset_synchronize_tracefiles should be used to resynchronize + * the traces with the trace context information. + * + * The usefulness of the lock in this framework can be questionable in a + * single threaded environment, but can be great in the eventuality of + * multiple threads. + * + */ + + + +#ifndef LTTVWINDOWTRACES_H +#define LTTVWINDOWTRACES_H + +#include +#include +#include + +typedef GQuark LttvTraceInfo; + +extern LttvTraceInfo LTTV_TRACES, + LTTV_COMPUTATION, + LTTV_REQUESTS_QUEUE, + LTTV_REQUESTS_CURRENT, + LTTV_NOTIFY_QUEUE, + LTTV_NOTIFY_CURRENT, + LTTV_COMPUTATION_TRACESET, + LTTV_COMPUTATION_TRACESET_CONTEXT, + LTTV_COMPUTATION_SYNC_POSITION, + LTTV_BEFORE_CHUNK_TRACESET, + LTTV_BEFORE_CHUNK_TRACE, + LTTV_BEFORE_CHUNK_TRACEFILE, + LTTV_AFTER_CHUNK_TRACESET, + LTTV_AFTER_CHUNK_TRACE, + LTTV_AFTER_CHUNK_TRACEFILE, + LTTV_BEFORE_REQUEST, + LTTV_AFTER_REQUEST, + LTTV_EVENT_HOOK, + LTTV_EVENT_HOOK_BY_ID, + LTTV_HOOK_ADDER, + LTTV_HOOK_REMOVER, + LTTV_IN_PROGRESS, + LTTV_READY, + LTTV_LOCK; + + + +/* Get a trace by its path name. + * + * @param path path of the trace on the virtual file system. + * @return Pointer to trace if found + * NULL is returned if the trace is not present + */ + +LttvTrace *lttvwindowtraces_get_trace_by_name(gchar *path); + +/* Get a trace by its number identifier */ + +LttvTrace *lttvwindowtraces_get_trace(guint num); + +/* Total number of traces */ + +guint lttvwindowtraces_get_number(); + +/* Add a trace to the global attributes */ + +void lttvwindowtraces_add_trace(LttvTrace *trace); + +/* Remove a trace from the global attributes */ + +void lttvwindowtraces_remove_trace(LttvTrace *trace); + + +/** + * Function to request data from a specific trace + * + * The memory allocated for the request will be managed by the API. + * + * @param tab parent Window + * @param trace the trace to compute + * @param module_name the name of the module which registered global computation + * hooks. + */ + +void lttvwindowtraces_background_request_queue + (GtkWidget *widget, LttvTrace *trace, gchar *module_name); + +/** + * Remove a background request from a trace. + * + * This should ONLY be used by the modules which registered the global hooks + * (module_name). If this is called by the viewers, it may lead to incomplete + * and incoherent background processing information. + * + * Even if the module which deals with the hooks removes the background + * requests, it may cause a problem if the module gets loaded again in the + * session : the data will be partially calculated. The calculation function + * must deal with this case correctly. + * + * @param trace the trace to compute + * @param module_name the name of the module which registered global computation + * hooks. + */ + +void lttvwindowtraces_background_request_remove + (LttvTrace *trace, gchar *module_name); + + + +/** + * Find a background request in a trace + * + */ + +gboolean lttvwindowtraces_background_request_find + (LttvTrace *trace, gchar *module_name); + +/** + * Register a callback to be called when requested data is passed in the next + * queued background processing. + * + * @param owner owner of the background notification + * @param trace the trace computed + * @param notify_time time when notification hooks must be called + * @param notify_position position when notification hooks must be called + * @param notify Hook to call when the notify position is passed + */ + +void lttvwindowtraces_background_notify_queue + (gpointer owner, + LttvTrace *trace, + LttTime notify_time, + const LttvTracesetContextPosition *notify_position, + const LttvHooks *notify); + +/** + * Register a callback to be called when requested data is passed in the current + * background processing. + * + * @param owner owner of the background notification + * @param trace the trace computed + * @param notify_time time when notification hooks must be called + * @param notify_position position when notification hooks must be called + * @param notify Hook to call when the notify position is passed + */ + +void lttvwindowtraces_background_notify_current + (gpointer owner, + LttvTrace *trace, + LttTime notify_time, + const LttvTracesetContextPosition *notify_position, + const LttvHooks *notify); + +/** + * Removes all the notifications requests from a specific viewer. + * + * @param owner owner of the background notification + */ + +void lttvwindowtraces_background_notify_remove(gpointer owner); + + +/** + * Tells if the information computed by a module for a trace is ready. + * + * Must be checked before a background processing request. + * + * @param module_name A GQuark : the name of the module which computes the + * information. + * @param trace The trace for which the information is verified. + */ + +gboolean lttvwindowtraces_get_ready(LttvAttributeName module_name, + LttvTrace *trace); + +/** + * Tells if the information computed by a module for a trace is being processed. + * + * Must be checked before a background processing request. + * + * If it is effectively processed, the typical thing to do is to call + * lttvwindowtraces_background_notify_current to be notified when the current + * processing will be over. + * + * @param module_name A GQuark : the name of the module which computes the + * information. + * @param trace The trace for which the information is verified. + */ + +gboolean lttvwindowtraces_get_in_progress(LttvAttributeName module_name, + LttvTrace *trace); + +/** + * Register the background computation hooks for a specific module. It adds the + * computation hooks to the global attrubutes, under "computation/module name" + * + * @param module_name A GQuark : the name of the module which computes the + * information. + */ +void lttvwindowtraces_register_computation_hooks(LttvAttributeName module_name, + LttvHooks *before_chunk_traceset, + LttvHooks *before_chunk_trace, + LttvHooks *before_chunk_tracefile, + LttvHooks *after_chunk_traceset, + LttvHooks *after_chunk_trace, + LttvHooks *after_chunk_tracefile, + LttvHooks *before_request, + LttvHooks *after_request, + LttvHooks *event_hook, + LttvHooksById *event_hook_by_id, + LttvHooks *hook_adder, + LttvHooks *hook_remover); +/** + * Unregister the background computation hooks for a specific module. + * + * It also removes all the requests than can be currently processed by the + * background computation algorithm for all the traces (list_in and list_out). + * + * @param module_name A GQuark : the name of the module which computes the + * information. + */ + +void lttvwindowtraces_unregister_computation_hooks + (LttvAttributeName module_name); + + +/** + * It removes all the requests than can be currently processed by the + * background computation algorithm for all the traces (list_in and list_out). + * + * Leaves the flag to in_progress or none.. depending if current or queue + * + * @param module_name A GQuark : the name of the module which computes the + * information. + */ +void lttvwindowtraces_unregister_requests(LttvAttributeName module_name); + + +/** + * Lock a trace so no other instance can use it. + * + * @param trace The trace to lock. + * @return 0 on success, -1 if cannot get lock. + */ +gint lttvwindowtraces_lock(LttvTrace *trace); + + +/** + * Unlock a trace. + * + * @param trace The trace to unlock. + * @return 0 on success, -1 if cannot unlock (not locked ?). + */ +gint lttvwindowtraces_unlock(LttvTrace *trace); + +/** + * Verify if a trace is locked. + * + * @param trace The trace to verify. + * @return TRUE if locked, FALSE is unlocked. + */ +gint lttvwindowtraces_get_lock_state(LttvTrace *trace); + + +#endif //LTTVWINDOWTRACES_H diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/mainwindow-private.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/mainwindow-private.h new file mode 100644 index 00000000..5bd9705e --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/mainwindow-private.h @@ -0,0 +1,141 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Xiangxiu Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 _MAIN_WINDOW_PRIVATE_ +#define _MAIN_WINDOW_PRIVATE_ + +#include + +#include +#include +#include +#include +#include +#include +#include +//#include +#include + +#define SCROLL_STEP_PER_PAGE 10.0 + +struct _TracesetInfo { + //FIXME? TracesetContext and stats in same or different variable ? + LttvTracesetStats * traceset_context; + LttvTraceset * traceset; +}; + +struct _MainWindow{ + GtkWidget* mwindow; /* Main Window */ + int window_width; + + /* Status bar information */ + // guint MainSBarContextID; /* Context ID of main status bar */ + // guint BegTimeSBarContextID; /* Context ID of BegTime status bar */ + // guint EndTimeSBarContextID; /* Context ID of EndTime status bar */ + + /* Child windows */ + //openTracesetWindow* OpenTracesetWindow;/* Window to get prof and proc file*/ + //viewTimeFrameWindow* ViewTimeFrameWindow;/*Window to select time frame */ + //gotoEventWindow* GotoEventWindow; /*search for event description*/ + //openFilterWindow* OpenFilterWindow; /* Open a filter selection window */ + GtkWidget* help_contents;/* Window to display help contents */ + GtkWidget* about_box; /* Window about information */ + + // lttv_trace_filter * filter; /* trace filter associated with the window */ + + /* Attributes for trace reading hooks local to the main window */ + LttvIAttribute * attributes; + + //Tab * tab; + //Tab * current_tab; + +}; + + +struct _Tab{ + GtkWidget *label; + GtkWidget *top_widget; + GtkWidget *vbox; /* contains viewer_container and scrollbar */ + //GtkWidget *multivpaned; + GtkWidget *viewer_container; + GtkWidget *scrollbar; + + /* Paste zones */ + GtkTooltips *tooltips; + + /* time bar */ + GtkWidget *MTimebar; + GtkWidget *MEventBox1a; + GtkWidget *MText1a; + GtkWidget *MEventBox1b; + GtkWidget *MText1b; + GtkWidget *MEntry1; + GtkWidget *MText2; + GtkWidget *MEntry2; + GtkWidget *MText3a; + GtkWidget *MEventBox3b; + GtkWidget *MText3b; + GtkWidget *MEntry3; + GtkWidget *MText4; + GtkWidget *MEntry4; + GtkWidget *MText5a; + GtkWidget *MEventBox5b; + GtkWidget *MText5b; + GtkWidget *MEntry5; + GtkWidget *MText6; + GtkWidget *MEntry6; + GtkWidget *MText7; + GtkWidget *MEventBox8; + GtkWidget *MText8; + GtkWidget *MEntry7; + GtkWidget *MText9; + GtkWidget *MEntry8; + GtkWidget *MText10; + + // startTime is the left of the visible area. Corresponds to the scrollbar + // value. + // Time_Width is a zoom dependant value (corresponding to page size) + TimeWindow time_window; + gboolean time_manager_lock; + + // The current time is the time selected in the visible area by the user, + // not the scrollbar value. + LttTime current_time; + gboolean current_time_manager_lock; + + LttvIAttribute * attributes; + + //struct _Tab * next; + MainWindow * mw; + + /* Traceset related information */ + TracesetInfo * traceset_info; + + /* Filter to apply to the tab's traceset */ + LttvFilter *filter; + + /* A list of time requested for the next process trace */ + GSList *events_requests; + gboolean events_request_pending; + LttvAttribute *interrupted_state; + gboolean stop_foreground; +}; + +#endif /* _MAIN_WINDOW_PRIVATE_ */ + + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/mainwindow.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/mainwindow.h new file mode 100644 index 00000000..9874d0d9 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/mainwindow.h @@ -0,0 +1,41 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Xiangxiu Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 _MAIN_WINDOW_ +#define _MAIN_WINDOW_ + +#include +#include + +typedef struct _MainWindow MainWindow; +typedef struct _TimeWindow TimeWindow; +typedef struct _Tab Tab; +typedef struct _TracesetInfo TracesetInfo; + +struct _TimeWindow { + LttTime start_time; + LttTime time_width; + double time_width_double; + LttTime end_time; +}; + + + +#endif /* _MAIN_WINDOW_ */ + + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/menu.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/menu.c new file mode 100644 index 00000000..5235acaa --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/menu.c @@ -0,0 +1,79 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 XangXiu Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + + +LttvMenus *lttv_menus_new() { + return g_array_new(FALSE, FALSE, sizeof(LttvMenuClosure)); +} + +/* MD: delete elements of the array also, but don't free pointed addresses + * (functions). + */ +void lttv_menus_destroy(LttvMenus *h) { + g_debug("lttv_menus_destroy()"); + g_array_free(h, TRUE); +} + +LttvMenuClosure lttv_menus_add(LttvMenus *h, + lttvwindow_viewer_constructor f, + char* menu_path, char* menu_text, GtkWidget *widget) +{ + LttvMenuClosure c; + + c.con = f; + c.menu_path = menu_path; + c.menu_text = menu_text; + c.widget = widget; + if(h != NULL) g_array_append_val(h,c); + + return c; +} + +GtkWidget *lttv_menus_remove(LttvMenus *h, lttvwindow_viewer_constructor f) +{ + LttvMenuClosure * tmp; + guint i; + GtkWidget *widget; + + for(i=0;ilen;i++){ + tmp = & g_array_index(h, LttvMenuClosure, i); + if(tmp->con == f) { + widget = tmp->widget; + break; + } + } + if(ilen){ + g_array_remove_index(h, i); + return widget; + }else return NULL; + +} + +unsigned lttv_menus_number(LttvMenus *h) +{ + return h->len; +} + + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/menu.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/menu.h new file mode 100644 index 00000000..87ccdee4 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/menu.h @@ -0,0 +1,46 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Xiangxiu Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 MENU_H +#define MENU_H + +#include +#include + + +typedef GArray LttvMenus; + +typedef struct _LttvMenuClosure { + lttvwindow_viewer_constructor con; + char * menu_path; + char * menu_text; + GtkWidget *widget; +} LttvMenuClosure; + + +LttvMenus *lttv_menus_new(); + +void lttv_menus_destroy(LttvMenus *h); + +LttvMenuClosure lttv_menus_add(LttvMenus *h, lttvwindow_viewer_constructor f, char* menuPath, char * menuText, GtkWidget *widget); + +GtkWidget *lttv_menus_remove(LttvMenus *h, lttvwindow_viewer_constructor f); + +unsigned lttv_menus_number(LttvMenus *h); + +#endif // MENU_H diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/support.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/support.c new file mode 100644 index 00000000..ce92ef05 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/support.c @@ -0,0 +1,164 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 XangXiu Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#include + +#include + +#include "support.h" + +__EXPORT GtkWidget* +lookup_widget (GtkWidget *widget, + const gchar *widget_name) +{ + GtkWidget *parent, *found_widget; + + for (;;) + { + if (GTK_IS_MENU (widget)) + parent = gtk_menu_get_attach_widget (GTK_MENU (widget)); + else + parent = widget->parent; + if (!parent) + parent = (GtkWidget*) g_object_get_data (G_OBJECT (widget), "GladeParentKey"); + if (parent == NULL) + break; + widget = parent; + } + + found_widget = (GtkWidget*) g_object_get_data (G_OBJECT (widget), + widget_name); + if (!found_widget) + g_warning ("Widget not found: %s", widget_name); + return found_widget; +} + +static GList *pixmaps_directories = NULL; + +/* Use this function to set the directory containing installed pixmaps. */ +void +add_pixmap_directory (const gchar *directory) +{ + pixmaps_directories = g_list_prepend (pixmaps_directories, + g_strdup (directory)); +} + +/* This is an internally used function to find pixmap files. */ +static gchar* +find_pixmap_file (const gchar *filename) +{ + GList *elem; + + /* We step through each of the pixmaps directory to find it. */ + elem = pixmaps_directories; + while (elem) + { + gchar *pathname = g_strdup_printf ("%s%s%s", (gchar*)elem->data, + G_DIR_SEPARATOR_S, filename); + if (g_file_test (pathname, G_FILE_TEST_EXISTS)) + return pathname; + g_free (pathname); + elem = elem->next; + } + return NULL; +} + +/* This is an internally used function to create pixmaps. */ +__EXPORT GtkWidget* +create_pixmap (GtkWidget *widget, + const gchar *filename) +{ + gchar *pathname = NULL; + GtkWidget *pixmap; + + if (!filename || !filename[0]) + return gtk_image_new (); + + pathname = find_pixmap_file (filename); + + if (!pathname) + { + g_warning ("Couldn't find pixmap file: %s", filename); + return gtk_image_new (); + } + + pixmap = gtk_image_new_from_file (pathname); + g_free (pathname); + return pixmap; +} + +/* This is an internally used function to create pixmaps. */ +GdkPixbuf* +create_pixbuf (const gchar *filename) +{ + gchar *pathname = NULL; + GdkPixbuf *pixbuf; + GError *error = NULL; + + if (!filename || !filename[0]) + return NULL; + + pathname = find_pixmap_file (filename); + + if (!pathname) + { + g_warning ("Couldn't find pixmap file: %s", filename); + return NULL; + } + + pixbuf = gdk_pixbuf_new_from_file (pathname, &error); + if (!pixbuf) + { + fprintf (stderr, "Failed to load pixbuf file: %s: %s\n", + pathname, error->message); + g_error_free (error); + } + g_free (pathname); + return pixbuf; +} + +/* This is used to set ATK action descriptions. */ +void +glade_set_atk_action_description (AtkAction *action, + const gchar *action_name, + const gchar *description) +{ + gint n_actions, i; + + n_actions = atk_action_get_n_actions (action); + for (i = 0; i < n_actions; i++) + { + if (!strcmp (atk_action_get_name (action, i), action_name)) + atk_action_set_description (action, i, description); + } +} + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/support.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/support.h new file mode 100644 index 00000000..62975193 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/support.h @@ -0,0 +1,62 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 XangXiu Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +/* + * Public Functions. + */ + +/* + * This function returns a widget in a component created by Glade. + * Call it with the toplevel widget in the component (i.e. a window/dialog), + * or alternatively any widget in the component, and the name of the widget + * you want returned. + */ +GtkWidget* lookup_widget (GtkWidget *widget, + const gchar *widget_name); + + +/* Use this function to set the directory containing installed pixmaps. */ +void add_pixmap_directory (const gchar *directory); + + +/* + * Private Functions. + */ + +/* This is used to create the pixmaps used in the interface. */ +GtkWidget* create_pixmap (GtkWidget *widget, + const gchar *filename); + +/* This is used to create the pixbufs used in the interface. */ +GdkPixbuf* create_pixbuf (const gchar *filename); + +/* This is used to set ATK action descriptions. */ +void glade_set_atk_action_description (AtkAction *action, + const gchar *action_name, + const gchar *description); + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/test_main.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/test_main.c new file mode 100644 index 00000000..02ea36c3 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/test_main.c @@ -0,0 +1,95 @@ +/* + * Initial main.c file generated by Glade. Edit as required. + * Glade will not overwrite this file. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include "interface.h" +#include "support.h" +#include +#include "callbacks.h" + +/* global variable */ +systemView * gSysView; + +typedef void (*call_Event_Selected_Hook)(void * call_data); +call_Event_Selected_Hook selected_hook = NULL; +GModule *gm; +view_constructor gConstructor = NULL; + +int +main (int argc, char *argv[]) +{ + GModule *gm; + GtkWidget * ToolMenuTitle_menu, *insert_view; + GtkWidget *window1; + mainWindow * mw = g_new(mainWindow, 1); + gSysView = g_new(systemView, 1); + +#ifdef ENABLE_NLS + bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); +#endif + + gtk_set_locale (); + gtk_init (&argc, &argv); + + add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps"); + add_pixmap_directory ("pixmaps"); + add_pixmap_directory ("../pixmaps"); + + /* + * The following code was added by Glade to create one of each component + * (except popup menus), just so that you see something after building + * the project. Delete any components that you don't want shown initially. + */ + window1 = create_MWindow (); + gtk_widget_show (window1); + + mw->MWindow = window1; + mw->SystemView = gSysView; + mw->Tab = NULL; + mw->CurrentTab = NULL; + // mw->Attributes = lttv_attributes_new(); + + //test + + gm = g_module_open("/home1/yangxx/poly/lttv/modules/libguiEvents.la",0); + printf("Main : the address of gm : %d\n", gm); + if(!g_module_symbol(gm, "get_constructor", (gpointer)&get_constructor)){ + g_error("can not get constructor\n"); + } + if(!g_module_symbol(gm, "call_Event_Selected_Hook", (gpointer)&selected_hook)){ + g_error("can not get selected hook\n"); + } + + gConstructor = get_constructor(); + ToolMenuTitle_menu = lookup_widget(mw->MWindow,"ToolMenuTitle_menu"); + insert_view = gtk_menu_item_new_with_mnemonic ("insert_view"); + gtk_widget_show (insert_view); + gtk_container_add (GTK_CONTAINER (ToolMenuTitle_menu), insert_view); + g_signal_connect ((gpointer) insert_view, "activate", + G_CALLBACK (insertViewTest), + NULL); + //end + + gSysView->EventDB = NULL; + gSysView->SystemInfo = NULL; + gSysView->Options = NULL; + gSysView->Window = mw; + gSysView->Next = NULL; + + g_object_set_data(G_OBJECT(window1), "systemView", (gpointer)gSysView); + g_object_set_data(G_OBJECT(window1), "mainWindow", (gpointer)mw); + + gtk_main (); + return 0; +} + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/toolbar.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/toolbar.c new file mode 100644 index 00000000..a01986d9 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/toolbar.c @@ -0,0 +1,77 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 XangXiu Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +LttvToolbars *lttv_toolbars_new() { + return g_array_new(FALSE, FALSE, sizeof(LttvToolbarClosure)); +} + +/* MD: delete elements of the array also, but don't free pointed addresses + * (functions). + */ +void lttv_toolbars_destroy(LttvToolbars *h) { + g_debug("lttv_toolbars_destroy"); + g_array_free(h, TRUE); +} + +LttvToolbarClosure lttv_toolbars_add(LttvToolbars *h, + lttvwindow_viewer_constructor f, + char* tooltip, char ** pixmap, GtkWidget *widget) +{ + LttvToolbarClosure c; + + c.con = f; + c.tooltip = tooltip; + c.pixmap = pixmap; + c.widget = widget; + if(h != NULL) g_array_append_val(h,c); + + return c; +} + +GtkWidget *lttv_toolbars_remove(LttvToolbars *h, lttvwindow_viewer_constructor f) +{ + LttvToolbarClosure * tmp; + guint i; + GtkWidget *widget; + + for(i=0;ilen;i++){ + tmp = & g_array_index(h, LttvToolbarClosure, i); + if(tmp->con == f) { + widget = tmp->widget; + break; + } + } + if(ilen){ + g_array_remove_index(h, i); + return widget; + }else return NULL; +} + +unsigned lttv_toolbars_number(LttvToolbars *h) +{ + return h->len; +} + + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/toolbar.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/toolbar.h new file mode 100644 index 00000000..2fb4b395 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/lttvwindow/toolbar.h @@ -0,0 +1,48 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Xiangxiu Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 TOOLBAR_H +#define TOOLBAR_H + +#include +#include + +typedef GArray LttvToolbars; + +typedef struct _LttvToolbarClosure { + lttvwindow_viewer_constructor con; + char * tooltip; + char ** pixmap; + GtkWidget *widget; +} LttvToolbarClosure; + +LttvToolbars *lttv_toolbars_new(); + +void lttv_toolbars_destroy(LttvToolbars *h); + +LttvToolbarClosure lttv_toolbars_add(LttvToolbars *h, + lttvwindow_viewer_constructor f, + char* tooltip, + char ** pixmap, + GtkWidget *widget); + +GtkWidget *lttv_toolbars_remove(LttvToolbars *h, lttvwindow_viewer_constructor f); + +unsigned lttv_toolbars_number(LttvToolbars *h); + +#endif // TOOLBAR_H diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/mainwin.glade b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/mainwin.glade new file mode 100644 index 00000000..0d4cd97d --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/mainwin.glade @@ -0,0 +1,760 @@ + + + + + + + 100 + 50 + True + Main window + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + 600 + 400 + True + False + + + + + True + False + 0 + + + + True + False + 0 + + + + True + + + + True + _File + True + + + + + + + True + New + True + + + + + + + True + Empty trace set + True + + + + + + + True + Clone trace set + True + + + + + + + True + + + + + + True + Tab + True + + + + + + + + + + + True + Open + True + + + + + + + True + Close + True + + + + + + + True + Close Tab + True + + + + + + + True + + + + + + True + Add Trace + True + + + + + + + True + Remove Trace + True + + + + + + + True + Save + True + + + + + + + True + Save As + True + + + + + + + True + + + + + + True + Quit + True + + + + + + + + + + + True + _Edit + True + + + + + + + True + gtk-cut + True + + + + + + + True + gtk-copy + True + + + + + + + True + gtk-paste + True + + + + + + + True + gtk-delete + True + + + + + + + + + + + True + _View + True + + + + + + + True + Zoom in + True + + + + + + + True + Zoom out + True + + + + + + + True + Zoom extended + True + + + + + + + True + + + + + + True + Go to time + True + + + + + + + True + Show time frame + True + + + + + + + + + + + True + Tools + True + + + + + + + True + Move viewer up + True + + + + + + + True + Move viewer down + True + + + + + + + True + Remove viewer + True + + + + + + + True + + + + + + True + Insert viewer test + True + + + + + + + + + + + True + Plugins + True + + + + + + + True + Load module + True + + + + + + + True + Unload module + True + + + + + + + True + Add module search path + True + + + + + + + + + + + True + Options + True + + + + + + + True + Color + True + + + + + + + True + + + + + + True + Filter + True + + + + + + + True + Save configuration + True + + + + + + + + + + 0 + False + False + + + + + + True + + + + True + _Help + True + + + + + + + True + Content + True + + + + + + + True + + + + + + True + About... + True + + + + + + + + + + 0 + False + False + GTK_PACK_END + + + + + 0 + False + False + + + + + + True + GTK_ORIENTATION_HORIZONTAL + GTK_TOOLBAR_ICONS + True + + + + 1 + True + New window with empty trace set + + True + filenew.png + + + + + + + 1 + True + open a trace set + + True + fileopen.png + + + + + + + 1 + True + Add a trace + + True + edit_add_22.png + + + + + + + 1 + True + Remove a trace + + True + edit_remove_22.png + + + + + + + 1 + True + save the current trace set + + True + filesave.png + + + + + + + 1 + True + save as + + True + filesaveas.png + + + + + + + 1 + True + Zoom in + + True + stock_zoom_in_24.png + True + + + + True + + + + + + 1 + True + Zoom out + + True + stock_zoom_out_24.png + + + + + + + 1 + True + Zoom extended + + True + stock_zoom_fit_24.png + + + + + + + 1 + True + Go to time + + True + gtk-jump-to.png + + + + + + + 1 + True + Show time frame + + True + mini-display.xpm + + + + + + + 1 + True + Move up current viewer + + True + 1uparrow.png + True + + + + True + + + + + + 1 + True + Move down current viewer + + True + 1downarrow.png + + + + + + + 1 + True + Delete current viewer + + True + remove.png + + + + + + 0 + False + False + + + + + + True + GTK_ORIENTATION_HORIZONTAL + GTK_TOOLBAR_BOTH + True + + + + + + + 0 + False + False + + + + + + True + True + True + True + GTK_POS_TOP + False + False + + + + + + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + tab + + + + + 0 + True + True + + + + + + True + True + + + 0 + False + False + + + + + + + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/mainwin.gladep b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/mainwin.gladep new file mode 100644 index 00000000..31586846 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/mainwin.gladep @@ -0,0 +1,11 @@ + + + + + MainWin + mainwin + FALSE + FALSE + FALSE + FALSE + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/1downarrow.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/1downarrow.png new file mode 100644 index 00000000..30e06024 Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/1downarrow.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/1uparrow.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/1uparrow.png new file mode 100644 index 00000000..7bb1b688 Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/1uparrow.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/Makefile.am b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/Makefile.am new file mode 100644 index 00000000..30ce5f88 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/Makefile.am @@ -0,0 +1,34 @@ +# +# Makefile for LTT New generation user interface +# +# Created by Mathieu Desnoyers on September 30, 2003 +# + +EXTRA_DIST = \ + 1downarrow.png\ + 1uparrow.png\ + edit_add_22.png\ + edit_remove_22.png\ + filenew.png\ + fileopen.png\ + filesave.png\ + filesaveas.png\ + gtk-add.png\ + gtk-jump-to.png\ + mini-display.xpm\ + move_message.xpm\ + remove.png\ + remove1.png\ + stock_zoom_fit_24.png\ + stock_zoom_in_24.png\ + stock_zoom_out_24.png\ + stock_stop_24.png\ + stock_redo_24.png\ + stock_refresh_24.png\ + close.png\ + stock_jump_to_24.png\ + properties.png\ + controlflow-legend.png\ + guifilter22x22.png\ + guifilter16x16.png\ + qmark.png diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/close.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/close.png new file mode 100644 index 00000000..3506f4b7 Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/close.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/controlflow-legend.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/controlflow-legend.png new file mode 100644 index 00000000..480848b0 Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/controlflow-legend.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/edit_add_22.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/edit_add_22.png new file mode 100644 index 00000000..c46aed2b Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/edit_add_22.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/edit_remove_22.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/edit_remove_22.png new file mode 100644 index 00000000..1a2f87c5 Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/edit_remove_22.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/filenew.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/filenew.png new file mode 100644 index 00000000..cba0053d Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/filenew.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/fileopen.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/fileopen.png new file mode 100644 index 00000000..f92ef901 Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/fileopen.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/filesave.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/filesave.png new file mode 100644 index 00000000..6a9adc13 Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/filesave.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/filesaveas.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/filesaveas.png new file mode 100644 index 00000000..ef0e8c7f Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/filesaveas.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/gtk-add.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/gtk-add.png new file mode 100644 index 00000000..e517f411 Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/gtk-add.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/gtk-jump-to.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/gtk-jump-to.png new file mode 100644 index 00000000..8039c0e3 Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/gtk-jump-to.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/guifilter16x16.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/guifilter16x16.png new file mode 100644 index 00000000..6e183c53 Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/guifilter16x16.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/guifilter16x16.xpm b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/guifilter16x16.xpm new file mode 100644 index 00000000..99103247 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/guifilter16x16.xpm @@ -0,0 +1,25 @@ +/* XPM */ +static char * guifilter16x16_xpm[] = { +"16 16 6 1", +" c None", +". c #000000", +"+ c #FF0000", +"@ c #440000", +"# c #510000", +"$ c #4B0000", +" ", +" ... ", +" .. .. ", +" .. .. ", +" ++@. .. ", +" ++#. . ", +" .. .. ", +" .. .$++ ", +" .. .$++ ", +" .. .. ", +" ++#. . ", +" ++@. .. ", +" .. .. ", +" .. .. ", +" ... ", +" "}; diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/guifilter22x22.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/guifilter22x22.png new file mode 100644 index 00000000..d46803dc Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/guifilter22x22.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/guifilter22x22.xpm b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/guifilter22x22.xpm new file mode 100644 index 00000000..2b3e7f85 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/guifilter22x22.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static char * hGuiFilterInsert_xpm[] = { +"22 22 3 1", +" c None", +". c #000000", +"r c #FF0000", +" ", +" ", +" ..... ", +" .. .. ", +" .. .. ", +" .. .. ", +" rrr.. .. ", +" rrr.. . ", +" .. .. ", +" .. .. ", +" .. ..rrr ", +" .. ..rrr ", +" .. .. ", +" .. .. ", +" rrr.. . ", +" rrr.. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" ..... ", +" ", +" "}; diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/mini-display.xpm b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/mini-display.xpm new file mode 100644 index 00000000..e1e34a51 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/mini-display.xpm @@ -0,0 +1,25 @@ +/* XPM */ +static char * mini-display_xpm[] = { +"16 16 6 1", +" c None s None", +". c #808080", +"X c white", +"o c black", +"O c blue", +"+ c #c0c0c0", +" ", +" ............ ", +" .XXXXXXXXXXXXo ", +" .Xooooooooo.Xo ", +" .XoOOOOOOOO.Xo ", +" .XoOXOOOOOO.Xo ", +" .XoOOOOOOOO.Xo ", +" .XoOOOOOOOO.Xo ", +" .XoOOOOOOOO.Xo ", +" .XoOOOOOOOO.Xo ", +" .Xo.........Xo ", +" .XXXXXXXXXXXXo ", +" oooooooooooo ", +" .XXXX+.o ", +" oooooooooooo ", +" "}; diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/move_message.xpm b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/move_message.xpm new file mode 100644 index 00000000..846febbe --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/move_message.xpm @@ -0,0 +1,59 @@ +/* XPM */ +static char * 16_move_message_xpm[] = { +"16 16 40 1", +" c None", +". c #010101", +"+ c #2F2F2F", +"@ c #A7A4A0", +"# c #FCFCFC", +"$ c #64625F", +"% c #95938E", +"& c #FBFBFB", +"* c #F9F8F6", +"= c #FBFAFA", +"- c #F6F5F1", +"; c #7D7B78", +"> c #EAE8E3", +", c #969491", +"' c #F6F4F1", +") c #81807C", +"! c #E5E3DE", +"~ c #000000", +"{ c #928F8B", +"] c #FBFBFA", +"^ c #F7F5F2", +"/ c #F5F3F0", +"( c #F7F6F4", +"_ c #B5B2AC", +": c #A09C97", +"< c #F0EFEB", +"[ c #E4E2DD", +"} c #AEABA6", +"| c #92908B", +"1 c #E8E6E1", +"2 c #B0ADA7", +"3 c #F7F6F2", +"4 c #F5F4F0", +"5 c #6B6A68", +"6 c #83817E", +"7 c #E1DFDA", +"8 c #E2DFD9", +"9 c #E2E0DB", +"0 c #E3E1DC", +"a c #E6E4DF", +" ", +" . . . . . . ", +". . ", +" ", +". . ", +" .+......... ", +". .@#########$.", +" .#%&*#*=*-;>.", +". .#*,=*=*')'!.", +" ~ .#**{]*^)*/!.", +" .#*(_)#):-<[.", +" .#(}**)^^|1[.", +" .#2*/34^/>51.", +" .678887990a$.", +" ........... ", +" "}; diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/properties.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/properties.png new file mode 100644 index 00000000..e81b562f Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/properties.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/qmark.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/qmark.png new file mode 100644 index 00000000..9d2043ff Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/qmark.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/remove.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/remove.png new file mode 100644 index 00000000..8127ee49 Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/remove.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/remove1.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/remove1.png new file mode 100644 index 00000000..cc6338b5 Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/remove1.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/stock_jump_to_24.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/stock_jump_to_24.png new file mode 100644 index 00000000..52f7f60a Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/stock_jump_to_24.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/stock_redo_24.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/stock_redo_24.png new file mode 100644 index 00000000..9a5ef573 Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/stock_redo_24.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/stock_refresh_24.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/stock_refresh_24.png new file mode 100644 index 00000000..c7e691b9 Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/stock_refresh_24.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/stock_stop_24.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/stock_stop_24.png new file mode 100644 index 00000000..89c21249 Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/stock_stop_24.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/stock_zoom_fit_24.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/stock_zoom_fit_24.png new file mode 100644 index 00000000..585e9707 Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/stock_zoom_fit_24.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/stock_zoom_in_24.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/stock_zoom_in_24.png new file mode 100644 index 00000000..1ac4864d Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/stock_zoom_in_24.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/stock_zoom_out_24.png b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/stock_zoom_out_24.png new file mode 100644 index 00000000..d67a87de Binary files /dev/null and b/tags/lttv-0.11.3-23102008/lttv/modules/gui/lttvwindow/pixmaps/stock_zoom_out_24.png differ diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/Makefile.am b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/Makefile.am new file mode 100644 index 00000000..52bf12cb --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/Makefile.am @@ -0,0 +1,39 @@ +# This file is part of the Linux Trace Toolkit viewer +# Copyright (C) 2003-2004 Mathieu Desnoyers +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License Version 2 as +# published by the Free Software Foundation; +# +# 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. + + +# +# Makefile for LTT New generation user interface : plugins. +# +# Created by Mathieu Desnoyers on September 27, 2003 +# + +AM_CFLAGS = $(GLIB_CFLAGS) +AM_CFLAGS += $(GTK_CFLAGS) +AM_CFLAGS += -fvisibility=hidden +LIBS += $(GLIB_LIBS) +LIBS += $(GTK_LIBS) -L${top_builddir}/lttv/modules/gui/lttvwindow/lttvwindow -llttvwindow + +libdir = ${lttvplugindir} + +lib_LTLIBRARIES = libresourceview.la +libresourceview_la_LDFLAGS = -module -avoid-version +libresourceview_la_SOURCES = module.c eventhooks.c cfv.c processlist.c drawing.c drawitem.c lttv_plugin_cfv.c + +noinst_HEADERS = eventhooks.h cfv.h processlist.h drawing.h drawitem.h lttv_plugin_cfv.h + +EXTRA_DIST = resourceview_icon.xpm hLegendInsert.xpm diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/cfv.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/cfv.c new file mode 100644 index 00000000..183be8bb --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/cfv.c @@ -0,0 +1,320 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "cfv.h" +#include "drawing.h" +#include "processlist.h" +#include "eventhooks.h" +#include "lttv_plugin_cfv.h" + +extern GSList *g_control_flow_data_list; + +static gboolean +header_size_allocate(GtkWidget *widget, + GtkAllocation *allocation, + gpointer user_data) +{ + Drawing_t *drawing = (Drawing_t*)user_data; + + gtk_widget_set_size_request(drawing->ruler, -1, allocation->height); + //gtk_widget_queue_resize(drawing->padding); + //gtk_widget_queue_resize(drawing->ruler); + gtk_container_check_resize(GTK_CONTAINER(drawing->ruler_hbox)); + return 0; +} + +gboolean cfv_scroll_event(GtkWidget *widget, GdkEventScroll *event, + gpointer data) +{ + ControlFlowData *control_flow_data = (ControlFlowData*)data; + unsigned int cell_height = + get_cell_height( + GTK_TREE_VIEW(control_flow_data->process_list->process_list_widget)); + gdouble new; + + switch(event->direction) { + case GDK_SCROLL_UP: + { + new = gtk_adjustment_get_value(control_flow_data->v_adjust) + - cell_height; + } + break; + case GDK_SCROLL_DOWN: + { + new = gtk_adjustment_get_value(control_flow_data->v_adjust) + + cell_height; + } + break; + default: + return FALSE; + } + if(new >= control_flow_data->v_adjust->lower && + new <= control_flow_data->v_adjust->upper + - control_flow_data->v_adjust->page_size) + gtk_adjustment_set_value(control_flow_data->v_adjust, new); + return TRUE; +} + + +/* Toolbar callbacks */ +static void property_button (GtkToolButton *toolbutton, + gpointer user_data) +{ + ControlFlowData *control_flow_data = (ControlFlowData*)user_data; + + g_printf("CFV Property button clicked\n"); + +} + +/***************************************************************************** + * Control Flow Viewer class implementation * + *****************************************************************************/ +/** + * Control Flow Viewer's constructor + * + * This constructor is given as a parameter to the menuitem and toolbar button + * registration. It creates the drawing widget. + * @param ParentWindow A pointer to the parent window. + * @return The widget created. + */ +ControlFlowData * +resourceview(LttvPluginTab *ptab) +{ + Tab *tab = ptab->tab; + GtkWidget *tmp_toolbar_icon; + GtkWidget *process_list_widget, *drawing_widget, *drawing_area; + //ControlFlowData* control_flow_data = g_new(ControlFlowData,1) ; + LttvPluginCFV *plugin_cfv = g_object_new(LTTV_TYPE_PLUGIN_CFV, NULL); + GtkTooltips *tooltips = gtk_tooltips_new(); + ControlFlowData* control_flow_data = plugin_cfv->cfd; + control_flow_data->ptab = ptab; + control_flow_data->tab = ptab->tab; + + control_flow_data->v_adjust = + GTK_ADJUSTMENT(gtk_adjustment_new( 0.0, /* Value */ + 0.0, /* Lower */ + 0.0, /* Upper */ + 0.0, /* Step inc. */ + 0.0, /* Page inc. */ + 0.0)); /* page size */ + + /* Create the drawing */ + control_flow_data->drawing = drawing_construct(control_flow_data); + + drawing_widget = + drawing_get_widget(control_flow_data->drawing); + + drawing_area = + drawing_get_drawing_area(control_flow_data->drawing); + + control_flow_data->number_of_process = 0; + control_flow_data->background_info_waiting = 0; + + /* Create the Process list */ + control_flow_data->process_list = processlist_construct(); + + process_list_widget = + processlist_get_widget(control_flow_data->process_list); + + gtk_tree_view_set_vadjustment(GTK_TREE_VIEW(process_list_widget), + GTK_ADJUSTMENT( + control_flow_data->v_adjust)); + + g_signal_connect (G_OBJECT(process_list_widget), + "scroll-event", + G_CALLBACK (cfv_scroll_event), + (gpointer)control_flow_data); + g_signal_connect (G_OBJECT(drawing_area), + "scroll-event", + G_CALLBACK (cfv_scroll_event), + (gpointer)control_flow_data); + + g_signal_connect (G_OBJECT(control_flow_data->process_list->button), + "size-allocate", + G_CALLBACK(header_size_allocate), + (gpointer)control_flow_data->drawing); +#if 0 /* not ready */ + g_signal_connect ( + // G_OBJECT(control_flow_data->process_list->process_list_widget), + G_OBJECT(control_flow_data->process_list->list_store), + "row-changed", + G_CALLBACK (tree_row_activated), + (gpointer)control_flow_data); +#endif //0 + + control_flow_data->hbox = gtk_hbox_new(FALSE, 1); + control_flow_data->toolbar = gtk_toolbar_new(); + gtk_toolbar_set_orientation(GTK_TOOLBAR(control_flow_data->toolbar), + GTK_ORIENTATION_VERTICAL); + +// tmp_toolbar_icon = create_pixmap (main_window_get_widget(tab), +// "properties.png"); +// gtk_widget_show(tmp_toolbar_icon); +// control_flow_data->button_prop = gtk_tool_button_new(tmp_toolbar_icon, +// "Properties"); +// g_signal_connect (G_OBJECT(control_flow_data->button_prop), +// "clicked", +// G_CALLBACK (property_button), +// (gpointer)control_flow_data); +// gtk_toolbar_insert(GTK_TOOLBAR(control_flow_data->toolbar), +// control_flow_data->button_prop, +// 1); + + gtk_toolbar_set_style(GTK_TOOLBAR(control_flow_data->toolbar), + GTK_TOOLBAR_ICONS); + + gtk_box_pack_start(GTK_BOX(control_flow_data->hbox), + control_flow_data->toolbar, + FALSE, FALSE, 0); + control_flow_data->h_paned = gtk_hpaned_new(); + control_flow_data->box = gtk_event_box_new(); + gtk_box_pack_end(GTK_BOX(control_flow_data->hbox), + control_flow_data->box, + TRUE, TRUE, 0); + control_flow_data->top_widget = control_flow_data->hbox; + plugin_cfv->parent.top_widget = control_flow_data->top_widget; + gtk_container_add(GTK_CONTAINER(control_flow_data->box), + control_flow_data->h_paned); + + gtk_paned_pack1(GTK_PANED(control_flow_data->h_paned), + process_list_widget, FALSE, TRUE); + gtk_paned_pack2(GTK_PANED(control_flow_data->h_paned), + drawing_widget, TRUE, TRUE); + + gtk_container_set_border_width(GTK_CONTAINER(control_flow_data->box), 1); + + /* Set the size of the drawing area */ + //drawing_Resize(drawing, h, w); + + /* Get trace statistics */ + //control_flow_data->Trace_Statistics = get_trace_statistics(Trace); + + gtk_widget_show(drawing_widget); + gtk_widget_show(process_list_widget); + gtk_widget_show(control_flow_data->h_paned); + gtk_widget_show(control_flow_data->box); + gtk_widget_show(control_flow_data->toolbar); +// gtk_widget_show(GTK_WIDGET(control_flow_data->button_prop)); + gtk_widget_show(control_flow_data->hbox); + + g_object_set_data_full( + G_OBJECT(control_flow_data->top_widget), + "plugin_data", + plugin_cfv, + (GDestroyNotify)guicontrolflow_destructor); + + g_object_set_data( + G_OBJECT(drawing_area), + "resourceview_data", + control_flow_data); + + g_object_set_data( + G_OBJECT(control_flow_data->process_list->process_list_widget), + "resourceview_data", + control_flow_data); + + g_control_flow_data_list = g_slist_append( + g_control_flow_data_list, + plugin_cfv); + + control_flow_data->filter = NULL; + + //WARNING : The widget must be + //inserted in the main window before the drawing area + //can be configured (and this must happen before sending + //data) + + return control_flow_data; + +} + +/* Destroys widget also */ +void +guicontrolflow_destructor_full(gpointer data) +{ + LttvPluginCFV *plugin_cfv = (LttvPluginCFV*)data; + g_info("CFV.c : guicontrolflow_destructor_full, %p", plugin_cfv); + /* May already have been done by GTK window closing */ + if(GTK_IS_WIDGET(guicontrolflow_get_widget(plugin_cfv->cfd))) + gtk_widget_destroy(guicontrolflow_get_widget(plugin_cfv->cfd)); + //control_flow_data->mw = NULL; + //FIXME guicontrolflow_destructor(control_flow_data); +} + +/* When this destructor is called, the widgets are already disconnected */ +void +guicontrolflow_destructor(gpointer data) +{ + LttvPluginCFV *plugin_cfv = (LttvPluginCFV*)data; + Tab *tab = plugin_cfv->cfd->tab; + ControlFlowData *control_flow_data = plugin_cfv->cfd; + + g_info("CFV.c : guicontrolflow_destructor, %p", plugin_cfv); + g_info("%p, %p, %p", update_time_window_hook, plugin_cfv, tab); + if(GTK_IS_WIDGET(guicontrolflow_get_widget(plugin_cfv->cfd))) + g_info("widget still exists"); + + lttv_filter_destroy(plugin_cfv->cfd->filter); + /* Process List is removed with it's widget */ + //ProcessList_destroy(control_flow_data->process_list); + if(tab != NULL) + { + /* Delete reading hooks */ + lttvwindow_unregister_traceset_notify(tab, + traceset_notify, + control_flow_data); + + lttvwindow_unregister_time_window_notify(tab, + update_time_window_hook, + control_flow_data); + + lttvwindow_unregister_current_time_notify(tab, + update_current_time_hook, + control_flow_data); + + lttvwindow_unregister_redraw_notify(tab, redraw_notify, control_flow_data); + lttvwindow_unregister_continue_notify(tab, + continue_notify, + control_flow_data); + + lttvwindow_events_request_remove_all(control_flow_data->tab, + control_flow_data); + + } + lttvwindowtraces_background_notify_remove(control_flow_data); + g_control_flow_data_list = + g_slist_remove(g_control_flow_data_list, control_flow_data); + + g_info("CFV.c : guicontrolflow_destructor end, %p", control_flow_data); + //g_free(control_flow_data); + g_object_unref(plugin_cfv); +} + + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/cfv.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/cfv.h new file mode 100644 index 00000000..46bc8043 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/cfv.h @@ -0,0 +1,98 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 _CFV_H +#define _CFV_H + +#include +#include +#include +#include + +extern GQuark LTT_NAME_CPU; + +#ifndef TYPE_DRAWING_T_DEFINED +#define TYPE_DRAWING_T_DEFINED +typedef struct _Drawing_t Drawing_t; +#endif //TYPE_DRAWING_T_DEFINED + +#ifndef TYPE_CONTROLFLOWDATA_DEFINED +#define TYPE_CONTROLFLOWDATA_DEFINED +typedef struct _ControlFlowData ControlFlowData; +#endif //TYPE_CONTROLFLOWDATA_DEFINED + +#ifndef TYPE_PROCESSLIST_DEFINED +#define TYPE_PROCESSLIST_DEFINED +typedef struct _ProcessList ProcessList; +#endif //TYPE_PROCESSLIST_DEFINED + +struct _ControlFlowData { + + GtkWidget *top_widget; + Tab *tab; + LttvPluginTab *ptab; + + GtkWidget *hbox; + GtkWidget *toolbar; /* Vbox that contains the viewer's toolbar */ + GtkToolItem *button_prop; /* Properties button. */ + GtkToolItem *button_filter; /* Properties button. */ + GtkWidget *box; /* box that contains the hpaned. necessary for it to work */ + GtkWidget *h_paned; + + ProcessList *process_list; + + Drawing_t *drawing; + GtkAdjustment *v_adjust ; + + /* Shown events information */ +// TimeWindow time_window; +// LttTime current_time; + + //guint currently_Selected_Event ; + guint number_of_process; + guint background_info_waiting; /* Number of background requests waited for + in order to have all the info ready. */ + + LttvFilter *filter; + +} ; + +/* Control Flow Data constructor */ +ControlFlowData *guicontrolflow(LttvPluginTab *ptab); +void +guicontrolflow_destructor_full(gpointer data); +void +guicontrolflow_destructor(gpointer data); + +static inline GtkWidget *guicontrolflow_get_widget( + ControlFlowData *control_flow_data) +{ + return control_flow_data->top_widget ; +} + +static inline ProcessList *guicontrolflow_get_process_list + (ControlFlowData *control_flow_data) +{ + return control_flow_data->process_list ; +} + + + +#endif // _CFV_H diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/drawing.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/drawing.c new file mode 100644 index 00000000..9908029f --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/drawing.c @@ -0,0 +1,1448 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "drawing.h" +#include "eventhooks.h" +#include "cfv.h" + +//#define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format) + +//FIXME +// fixed #define TRACE_NUMBER 0 +#define EXTRA_ALLOC 1024 // pixels + + +#if 0 /* colors for two lines representation */ +GdkColor drawing_colors[NUM_COLORS] = +{ /* Pixel, R, G, B */ + { 0, 0, 0, 0 }, /* COL_BLACK */ + { 0, 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_WHITE */ + { 0, 0x0FFF, 0xFFFF, 0xFFFF }, /* COL_WAIT_FORK : pale blue */ + { 0, 0xFFFF, 0xFFFF, 0x0000 }, /* COL_WAIT_CPU : yellow */ + { 0, 0xFFFF, 0xA000, 0xFCFF }, /* COL_EXIT : pale magenta */ + { 0, 0xFFFF, 0x0000, 0xFFFF }, /* COL_ZOMBIE : purple */ + { 0, 0xFFFF, 0x0000, 0x0000 }, /* COL_WAIT : red */ + { 0, 0x0000, 0xFFFF, 0x0000 }, /* COL_RUN : green */ + { 0, 0x8800, 0xFFFF, 0x8A00 }, /* COL_USER_MODE : pale green */ + { 0, 0x09FF, 0x01FF, 0xFFFF }, /* COL_SYSCALL : blue */ + { 0, 0xF900, 0x4200, 0xFF00 }, /* COL_TRAP : pale purple */ + { 0, 0xFFFF, 0x5AFF, 0x01FF }, /* COL_IRQ : orange */ + { 0, 0xFFFF, 0xFFFF, 0xFFFF } /* COL_MODE_UNKNOWN : white */ + +}; +#endif //0 + + +GdkColor drawing_colors[NUM_COLORS] = +{ /* Pixel, R, G, B */ + { 0, 0, 0, 0 }, /* COL_BLACK */ + { 0, 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_WHITE */ + { 0, 0x0000, 0xFF00, 0x0000 }, /* COL_RUN_USER_MODE : green */ + { 0, 0x0100, 0x9E00, 0xFFFF }, /* COL_RUN_SYSCALL : pale blue */ + { 0, 0xFF00, 0xFF00, 0x0100 }, /* COL_RUN_TRAP : yellow */ + { 0, 0xFFFF, 0x5E00, 0x0000 }, /* COL_RUN_IRQ : orange */ + { 0, 0xFFFF, 0x9400, 0x9600 }, /* COL_RUN_SOFT_IRQ : pink */ + { 0, 0x6600, 0x0000, 0x0000 }, /* COL_WAIT : dark red */ + { 0, 0x7700, 0x7700, 0x0000 }, /* COL_WAIT_CPU : dark yellow */ + { 0, 0x6400, 0x0000, 0x5D00 }, /* COL_ZOMBIE : dark purple */ + { 0, 0x0700, 0x6400, 0x0000 }, /* COL_WAIT_FORK : dark green */ + { 0, 0x8900, 0x0000, 0x8400 }, /* COL_EXIT : "less dark" magenta */ + { 0, 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_MODE_UNKNOWN : white */ + { 0, 0xFFFF, 0xFFFF, 0xFFFF } /* COL_UNNAMED : white */ + +}; + +GdkColor drawing_colors_cpu[NUM_COLORS_CPU] = +{ /* Pixel, R, G, B */ + { 0, 0x0000, 0x0000, 0x0000 }, /* COL_CPU_UNKNOWN */ + { 0, 0xBBBB, 0xBBBB, 0xBBBB }, /* COL_CPU_IDLE */ + { 0, 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_CPU_BUSY */ + { 0, 0xFFFF, 0x5E00, 0x0000 }, /* COL_CPU_IRQ */ + { 0, 0xFFFF, 0x9400, 0x9600 }, /* COL_CPU_SOFT_IRQ */ + { 0, 0xFF00, 0xFF00, 0x0100 }, /* COL_CPU_TRAP */ +}; + +GdkColor drawing_colors_irq[NUM_COLORS_IRQ] = +{ /* Pixel, R, G, B */ + { 0, 0x0000, 0x0000, 0x0000 }, /* COL_IRQ_UNKNOWN */ + { 0, 0xBBBB, 0xBBBB, 0xBBBB }, /* COL_IRQ_IDLE */ + { 0, 0xFFFF, 0x5E00, 0x0000 }, /* COL_IRQ_BUSY */ +}; + +GdkColor drawing_colors_soft_irq[NUM_COLORS_SOFT_IRQ] = +{ /* Pixel, R, G, B */ + { 0, 0x0000, 0x0000, 0x0000 }, /* COL_SOFT_IRQ_UNKNOWN */ + { 0, 0x0000, 0x0000, 0x0000 }, /* COL_SOFT_IRQ_IDLE */ + { 0, 0xFFFF, 0xD400, 0xD400 }, /* COL_SOFT_IRQ_PENDING */ + { 0, 0xFFFF, 0x9400, 0x9600 }, /* COL_SOFT_IRQ_BUSY */ +}; + +GdkColor drawing_colors_trap[NUM_COLORS_TRAP] = +{ /* Pixel, R, G, B */ + { 0, 0x0000, 0x0000, 0x0000 }, /* COL_TRAP_UNKNOWN */ + { 0, 0x0000, 0x0000, 0x0000 }, /* COL_TRAP_IDLE */ + { 0, 0xFF00, 0xFF00, 0x0100 }, /* COL_TRAP_BUSY */ +}; + +GdkColor drawing_colors_bdev[NUM_COLORS_BDEV] = +{ /* Pixel, R, G, B */ + { 0, 0x0000, 0x0000, 0x0000 }, /* COL_BDEV_UNKNOWN */ + { 0, 0xBBBB, 0xBBBB, 0xBBBB }, /* COL_BDEV_IDLE */ + { 0, 0x0000, 0x0000, 0xFFFF }, /* COL_BDEV_BUSY_READING */ + { 0, 0xFFFF, 0x0000, 0x0000 }, /* COL_BDEV_BUSY_WRITING */ +}; + +/***************************************************************************** + * drawing functions * + *****************************************************************************/ + +static gboolean +expose_ruler( GtkWidget *widget, GdkEventExpose *event, gpointer user_data ); + +static gboolean +motion_notify_ruler(GtkWidget *widget, GdkEventMotion *event, gpointer user_data); + + +/* Function responsible for updating the exposed area. + * It must do an events request to the lttvwindow API to ask for this update. + * Note : this function cannot clear the background, because it may + * erase drawing already present (SAFETY). + */ +void drawing_data_request(Drawing_t *drawing, + gint x, gint y, + gint width, + gint height) +{ + if(width < 0) return ; + if(height < 0) return ; + + + Tab *tab = drawing->control_flow_data->tab; + TimeWindow time_window = + lttvwindow_get_time_window(tab); + + ControlFlowData *control_flow_data = drawing->control_flow_data; + // (ControlFlowData*)g_object_get_data( + // G_OBJECT(drawing->drawing_area), "resourceview_data"); + + LttTime start, time_end; + LttTime window_end = time_window.end_time; + + g_debug("req : window start_time : %lu, %lu", time_window.start_time.tv_sec, + time_window.start_time.tv_nsec); + + g_debug("req : window time width : %lu, %lu", time_window.time_width.tv_sec, + time_window.time_width.tv_nsec); + + g_debug("req : window_end : %lu, %lu", window_end.tv_sec, + window_end.tv_nsec); + + g_debug("x is : %i, x+width is : %i", x, x+width); + + convert_pixels_to_time(drawing->width, x, + time_window, + &start); + + convert_pixels_to_time(drawing->width, x+width, + time_window, + &time_end); + time_end = ltt_time_add(time_end, ltt_time_one); // because main window + // doesn't deliver end time. + + lttvwindow_events_request_remove_all(tab, + control_flow_data); + + { + /* find the tracehooks */ + LttvTracesetContext *tsc = lttvwindow_get_traceset_context(tab); + + LttvTraceset *traceset = tsc->ts; + + guint i, k, l, nb_trace; + + LttvTraceState *ts; + + LttvTracefileState *tfs; + + GArray *hooks; + + LttvTraceHook *hook; + + LttvTraceHook *th; + + guint ret; + guint first_after; + + nb_trace = lttv_traceset_number(traceset); + // FIXME (fixed) : eventually request for more traces + for(i = 0 ; i < nb_trace ; i++) { + EventsRequest *events_request = g_new(EventsRequest, 1); + // Create the hooks + //LttvHooks *event = lttv_hooks_new(); + LttvHooksById *event_by_id = lttv_hooks_by_id_new(); + LttvHooks *before_chunk_traceset = lttv_hooks_new(); + LttvHooks *after_chunk_traceset = lttv_hooks_new(); + LttvHooks *before_request_hook = lttv_hooks_new(); + LttvHooks *after_request_hook = lttv_hooks_new(); + + lttv_hooks_add(before_chunk_traceset, + before_chunk, + events_request, + LTTV_PRIO_DEFAULT); + + lttv_hooks_add(after_chunk_traceset, + after_chunk, + events_request, + LTTV_PRIO_DEFAULT); + + lttv_hooks_add(before_request_hook, + before_request, + events_request, + LTTV_PRIO_DEFAULT); + + lttv_hooks_add(after_request_hook, + after_request, + events_request, + LTTV_PRIO_DEFAULT); + + + ts = (LttvTraceState *)tsc->traces[i]; + + /* Find the eventtype id for the following events and register the + associated by id hooks. */ + + hooks = g_array_sized_new(FALSE, FALSE, sizeof(LttvTraceHook), 18); + + /* before hooks */ + +// lttv_trace_find_hook(ts->parent.t, +// LTT_FACILITY_ARCH, +// LTT_EVENT_SYSCALL_ENTRY, +// FIELD_ARRAY(LTT_FIELD_SYSCALL_ID), +// before_execmode_hook, +// events_request, +// &hooks); +// +// lttv_trace_find_hook(ts->parent.t, +// LTT_FACILITY_ARCH, +// LTT_EVENT_SYSCALL_EXIT, +// NULL, +// before_execmode_hook, +// events_request, +// &hooks); +// + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL_ARCH, + LTT_EVENT_TRAP_ENTRY, + FIELD_ARRAY(LTT_FIELD_TRAP_ID), + before_execmode_hook, + events_request, + &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL_ARCH, + LTT_EVENT_TRAP_EXIT, + NULL, + before_execmode_hook, + events_request, + &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_IRQ_ENTRY, + FIELD_ARRAY(LTT_FIELD_IRQ_ID), + before_execmode_hook, + events_request, + &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_IRQ_EXIT, + NULL, + before_execmode_hook, + events_request, + &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SOFT_IRQ_RAISE, + FIELD_ARRAY(LTT_FIELD_SOFT_IRQ_ID), + before_execmode_hook, + events_request, + &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SOFT_IRQ_ENTRY, + FIELD_ARRAY(LTT_FIELD_SOFT_IRQ_ID), + before_execmode_hook, + events_request, + &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SOFT_IRQ_EXIT, + NULL, + before_execmode_hook, + events_request, + &hooks); + + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SCHED_SCHEDULE, + FIELD_ARRAY(LTT_FIELD_PREV_PID, LTT_FIELD_NEXT_PID, LTT_FIELD_PREV_STATE), + before_schedchange_hook, + events_request, + &hooks); + +// lttv_trace_find_hook(ts->parent.t, +// LTT_FACILITY_KERNEL, +// LTT_EVENT_PROCESS_EXIT, +// FIELD_ARRAY(LTT_FIELD_PID), +// before_process_exit_hook, +// events_request, +// &hooks); +// +// lttv_trace_find_hook(ts->parent.t, +// LTT_FACILITY_KERNEL, +// LTT_EVENT_PROCESS_FREE, +// FIELD_ARRAY(LTT_FIELD_PID), +// before_process_release_hook, +// events_request, +// &hooks); +// +// lttv_trace_find_hook(ts->parent.t, +// LTT_FACILITY_LIST, +// LTT_EVENT_STATEDUMP_END, +// NULL, +// before_statedump_end, +// events_request, +// &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_REQUEST_ISSUE, + FIELD_ARRAY(LTT_FIELD_MAJOR, LTT_FIELD_MINOR, LTT_FIELD_OPERATION), + before_bdev_event_hook, + events_request, + &hooks); + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_REQUEST_COMPLETE, + FIELD_ARRAY(LTT_FIELD_MAJOR, LTT_FIELD_MINOR, LTT_FIELD_OPERATION), + before_bdev_event_hook, + events_request, + &hooks); + + /* After hooks */ + first_after = hooks->len; + + lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, + LTT_EVENT_SCHED_SCHEDULE, + FIELD_ARRAY(LTT_FIELD_PREV_PID, LTT_FIELD_NEXT_PID, LTT_FIELD_PREV_STATE), + after_schedchange_hook, + events_request, + &hooks); + +// lttv_trace_find_hook(ts->parent.t, +// LTT_FACILITY_KERNEL, +// LTT_EVENT_PROCESS_FORK, +// FIELD_ARRAY(LTT_FIELD_PARENT_PID, LTT_FIELD_CHILD_PID), +// after_process_fork_hook, +// events_request, +// &hooks); +// +// lttv_trace_find_hook(ts->parent.t, +// LTT_FACILITY_KERNEL, +// LTT_EVENT_PROCESS_EXIT, +// FIELD_ARRAY(LTT_FIELD_PID), +// after_process_exit_hook, +// events_request, +// &hooks); +// +// lttv_trace_find_hook(ts->parent.t, +// LTT_FACILITY_KERNEL, +// LTT_EVENT_EXEC, +// NULL, +// after_fs_exec_hook, +// events_request, +// &hooks); +// +// lttv_trace_find_hook(ts->parent.t, +// LTT_FACILITY_USER_GENERIC, +// LTT_EVENT_THREAD_BRAND, +// FIELD_ARRAY(LTT_FIELD_NAME), +// after_user_generic_thread_brand_hook, +// events_request, +// &hooks); +// +// lttv_trace_find_hook(ts->parent.t, +// LTT_FACILITY_LIST, +// LTT_EVENT_PROCESS_STATE, +// FIELD_ARRAY(LTT_FIELD_PID, LTT_FIELD_PARENT_PID, LTT_FIELD_NAME), +// after_event_enum_process_hook, +// events_request, +// &hooks); + + + /* Add these hooks to each event_by_id hooks list */ + /* add before */ + for(k = 0 ; k < first_after ; k++) { + th = &g_array_index(hooks, LttvTraceHook, k); + lttv_hooks_add(lttv_hooks_by_id_find(event_by_id, th->id), + th->h, + th, + LTTV_PRIO_STATE-5); + } + + /* add after */ + for(k = first_after ; k < hooks->len ; k++) { + th = &g_array_index(hooks, LttvTraceHook, k); + lttv_hooks_add(lttv_hooks_by_id_find(event_by_id, th->id), + th->h, + th, + LTTV_PRIO_STATE+5); + } + + events_request->hooks = hooks; + + // Fill the events request + events_request->owner = control_flow_data; + events_request->viewer_data = control_flow_data; + events_request->servicing = FALSE; + events_request->start_time = start; + events_request->start_position = NULL; + events_request->stop_flag = FALSE; + events_request->end_time = time_end; + events_request->num_events = G_MAXUINT; + events_request->end_position = NULL; + events_request->trace = i; //fixed /* FIXME */ + events_request->before_chunk_traceset = before_chunk_traceset; + events_request->before_chunk_trace = NULL; + events_request->before_chunk_tracefile = NULL; + events_request->event = NULL; + events_request->event_by_id = event_by_id; + events_request->after_chunk_tracefile = NULL; + events_request->after_chunk_trace = NULL; + events_request->after_chunk_traceset = after_chunk_traceset; + events_request->before_request = before_request_hook; + events_request->after_request = after_request_hook; + + g_debug("req : start : %lu, %lu", start.tv_sec, + start.tv_nsec); + + g_debug("req : end : %lu, %lu", time_end.tv_sec, + time_end.tv_nsec); + + lttvwindow_events_request(tab, events_request); + + } + } +} + + +static void set_last_start(gpointer key, gpointer value, gpointer user_data) +{ + //ResourceInfo *process_info = (ResourceInfo*)key; + HashedResourceData *hashed_process_data = (HashedResourceData*)value; + guint x = (guint)user_data; + + hashed_process_data->x.over = x; + hashed_process_data->x.over_used = FALSE; + hashed_process_data->x.over_marked = FALSE; + hashed_process_data->x.middle = x; + hashed_process_data->x.middle_used = FALSE; + hashed_process_data->x.middle_marked = FALSE; + hashed_process_data->x.under = x; + hashed_process_data->x.under_used = FALSE; + hashed_process_data->x.under_marked = FALSE; + hashed_process_data->next_good_time = ltt_time_zero; + + return; +} + +void drawing_data_request_begin(EventsRequest *events_request, LttvTracesetState *tss) +{ + int i; + + g_debug("Begin of data request"); + ControlFlowData *cfd = events_request->viewer_data; + LttvTracesetContext *tsc = LTTV_TRACESET_CONTEXT(tss); + TimeWindow time_window = + lttvwindow_get_time_window(cfd->tab); + + guint width = cfd->drawing->width; + guint x=0; + + cfd->drawing->last_start = events_request->start_time; + + convert_time_to_pixels( + time_window, + events_request->start_time, + width, + &x); + + for(i=0; iprocess_list->restypes[i].hash_table, set_last_start, + (gpointer)x); + } + +} + +void drawing_chunk_begin(EventsRequest *events_request, LttvTracesetState *tss) +{ + g_debug("Begin of chunk"); + ControlFlowData *cfd = events_request->viewer_data; + LttvTracesetContext *tsc = &tss->parent; + //LttTime current_time = lttv_traceset_context_get_current_tfc(tsc)->timestamp; + guint i; + LttvTraceset *traceset = tsc->ts; + guint nb_trace = lttv_traceset_number(traceset); + + if(!cfd->process_list->current_hash_data) { + cfd->process_list->current_hash_data = g_new(HashedResourceData**,nb_trace); + for(i = 0 ; i < nb_trace ; i++) { + guint num_cpu = ltt_trace_get_num_cpu(tss->parent.traces[i]->t); + cfd->process_list->current_hash_data[i] = g_new(HashedResourceData*,num_cpu); + memset(cfd->process_list->current_hash_data[i], 0, + sizeof(HashedResourceData*)*num_cpu); + } + } + //cfd->drawing->last_start = LTT_TIME_MIN(current_time, + // events_request->end_time); +} + + +void drawing_request_expose(EventsRequest *events_request, + LttvTracesetState *tss, + LttTime end_time) +{ + gint x, width; + guint x_end; + + ControlFlowData *cfd = events_request->viewer_data; + LttvTracesetContext *tsc = (LttvTracesetContext*)tss; + Drawing_t *drawing = cfd->drawing; + + TimeWindow time_window = + lttvwindow_get_time_window(cfd->tab); + + g_debug("request expose"); + + convert_time_to_pixels( + time_window, + end_time, + drawing->width, + &x_end); + x = drawing->damage_begin; + + width = x_end - x; + + drawing->damage_begin = x+width; + + // FIXME ? + gtk_widget_queue_draw_area ( drawing->drawing_area, + x, 0, + width, drawing->drawing_area->allocation.height); + + /* Update directly when scrolling */ + gdk_window_process_updates(drawing->drawing_area->window, + TRUE); +} + + +/* Callbacks */ + + +/* Create a new backing pixmap of the appropriate size */ +/* As the scaling will always change, it's of no use to copy old + * pixmap. + * + * Only change the size if width changes. The height is specified and changed + * when process ID are added or removed from the process list. + */ +static gboolean +configure_event( GtkWidget *widget, GdkEventConfigure *event, + gpointer user_data) +{ + Drawing_t *drawing = (Drawing_t*)user_data; + + + /* First, get the new time interval of the main window */ + /* we assume (see documentation) that the main window + * has updated the time interval before this configure gets + * executed. + */ + //lttvwindow_get_time_window(drawing->control_flow_data->mw, + // &drawing->control_flow_data->time_window); + + /* New pixmap, size of the configure event */ + //GdkPixmap *pixmap = gdk_pixmap_new(widget->window, + // widget->allocation.width + SAFETY, + // widget->allocation.height + SAFETY, + // -1); + + if(widget->allocation.width != drawing->width) { + g_debug("drawing configure event"); + g_debug("New alloc draw size : %i by %i",widget->allocation.width, + widget->allocation.height); + + drawing->width = widget->allocation.width; + + if(drawing->alloc_width < widget->allocation.width) { + //if(drawing->pixmap) + // gdk_pixmap_unref(drawing->pixmap); + + //drawing->pixmap = gdk_pixmap_new(widget->window, + // drawing->width + SAFETY + EXTRA_ALLOC, + // drawing->height + EXTRA_ALLOC, + // -1); + drawing->alloc_width = drawing->width + SAFETY + EXTRA_ALLOC; + drawing->alloc_height = drawing->height + EXTRA_ALLOC; + update_pixmap_size(drawing->control_flow_data->process_list, + drawing->alloc_width); + update_index_to_pixmap(drawing->control_flow_data->process_list); + } + //drawing->height = widget->allocation.height; + + //ProcessList_get_height + // (GuiControlFlow_get_process_list(drawing->control_flow_data)), + + + // Clear the image + //gdk_draw_rectangle (drawing->pixmap, + // widget->style->black_gc, + // TRUE, + // 0, 0, + // drawing->width+SAFETY, + // drawing->height); + + //g_info("init data request"); + + + /* Initial data request */ + /* no, do initial data request in the expose event */ + // Do not need to ask for data of 1 pixel : not synchronized with + // main window time at this moment. + //drawing_data_request(drawing, &drawing->pixmap, 0, 0, + // widget->allocation.width, + // widget->allocation.height); + + //drawing->width = widget->allocation.width; + //drawing->height = widget->allocation.height; + + drawing->damage_begin = 0; + drawing->damage_end = widget->allocation.width; + + if((widget->allocation.width != 1 && + widget->allocation.height != 1) + && drawing->damage_begin < drawing->damage_end) + { + + rectangle_pixmap (drawing->control_flow_data->process_list, + drawing->drawing_area->style->black_gc, + TRUE, + 0, 0, + drawing->alloc_width, // do not overlap + -1); + + + drawing_data_request(drawing, + drawing->damage_begin, + 0, + drawing->damage_end - drawing->damage_begin, + drawing->height); + } + } + return TRUE; +} + + +/* Redraw the screen from the backing pixmap */ +static gboolean +expose_event( GtkWidget *widget, GdkEventExpose *event, gpointer user_data ) +{ + Drawing_t *drawing = (Drawing_t*)user_data; + + ControlFlowData *control_flow_data = + (ControlFlowData*)g_object_get_data( + G_OBJECT(widget), + "resourceview_data"); +#if 0 + if(unlikely(drawing->gc == NULL)) { + drawing->gc = gdk_gc_new(drawing->drawing_area->window); + gdk_gc_copy(drawing->gc, drawing->drawing_area->style->black_gc); + } +#endif //0 + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + LttTime current_time = + lttvwindow_get_current_time(control_flow_data->tab); + + guint cursor_x=0; + + LttTime window_end = time_window.end_time; + + /* update the screen from the pixmap buffer */ +#if 0 + gdk_draw_pixmap(widget->window, + widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + drawing->pixmap, + event->area.x, event->area.y, + event->area.x, event->area.y, + event->area.width, event->area.height); +#endif //0 + drawing->height = processlist_get_height(control_flow_data->process_list); +#if 0 + copy_pixmap_to_screen(control_flow_data->process_list, + widget->window, + widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + event->area.x, event->area.y, + event->area.width, event->area.height); +#endif //0 + copy_pixmap_to_screen(control_flow_data->process_list, + widget->window, + drawing->gc, + event->area.x, event->area.y, + event->area.width, event->area.height); + + + /* Erase the dotted lines left.. */ + if(widget->allocation.height > drawing->height) + { + gdk_draw_rectangle (widget->window, + drawing->drawing_area->style->black_gc, + TRUE, + event->area.x, drawing->height, + event->area.width, // do not overlap + widget->allocation.height - drawing->height); + } + if(ltt_time_compare(time_window.start_time, current_time) <= 0 && + ltt_time_compare(window_end, current_time) >= 0) + { + /* Draw the dotted lines */ + convert_time_to_pixels( + time_window, + current_time, + drawing->width, + &cursor_x); + +#if 0 + if(drawing->dotted_gc == NULL) { + + drawing->dotted_gc = gdk_gc_new(drawing->drawing_area->window); + gdk_gc_copy(drawing->dotted_gc, widget->style->white_gc); + + gint8 dash_list[] = { 1, 2 }; + gdk_gc_set_line_attributes(drawing->dotted_gc, + 1, + GDK_LINE_ON_OFF_DASH, + GDK_CAP_BUTT, + GDK_JOIN_MITER); + gdk_gc_set_dashes(drawing->dotted_gc, + 0, + dash_list, + 2); + } +#endif //0 + gint height_tot = MAX(widget->allocation.height, drawing->height); + gdk_draw_line(widget->window, + drawing->dotted_gc, + cursor_x, 0, + cursor_x, height_tot); + } + return FALSE; +} + +static gboolean +after_expose_event( GtkWidget *widget, GdkEventExpose *event, gpointer user_data ) +{ + //g_assert(0); + g_debug("AFTER EXPOSE"); + + return FALSE; + + +} + +#if 0 +void +tree_row_activated(GtkTreeModel *treemodel, + GtkTreePath *arg1, + GtkTreeViewColumn *arg2, + gpointer user_data) +{ + ControlFlowData *cfd = (ControlFlowData*)user_data; + Drawing_t *drawing = cfd->drawing; + GtkTreeView *treeview = cfd->process_list->process_list_widget; + gint *path_indices; + gint height; + + path_indices = gtk_tree_path_get_indices (arg1); + + height = get_cell_height(cfd->process_list, + GTK_TREE_VIEW(treeview)); + drawing->horizontal_sel = height * path_indices[0]; + g_critical("new hor sel : %i", drawing->horizontal_sel); +} +#endif //0 + +/* mouse click */ +static gboolean +button_press_event( GtkWidget *widget, GdkEventButton *event, gpointer user_data ) +{ + ControlFlowData *control_flow_data = + (ControlFlowData*)g_object_get_data( + G_OBJECT(widget), + "resourceview_data"); + Drawing_t *drawing = control_flow_data->drawing; + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + + g_debug("click"); + if(event->button == 1) + { + LttTime time; + + /* left mouse button click */ + g_debug("x click is : %f", event->x); + + convert_pixels_to_time(drawing->width, (guint)event->x, + time_window, + &time); + + lttvwindow_report_current_time(control_flow_data->tab, time); + + } + + return FALSE; +} + +static gboolean +scrollbar_size_allocate(GtkWidget *widget, + GtkAllocation *allocation, + gpointer user_data) +{ + Drawing_t *drawing = (Drawing_t*)user_data; + + gtk_widget_set_size_request(drawing->padding, allocation->width, -1); + //gtk_widget_queue_resize(drawing->padding); + //gtk_widget_queue_resize(drawing->ruler); + gtk_container_check_resize(GTK_CONTAINER(drawing->ruler_hbox)); + return 0; +} + + + +Drawing_t *drawing_construct(ControlFlowData *control_flow_data) +{ + Drawing_t *drawing = g_new(Drawing_t, 1); + + drawing->control_flow_data = control_flow_data; + + drawing->vbox = gtk_vbox_new(FALSE, 1); + + + drawing->ruler_hbox = gtk_hbox_new(FALSE, 1); + drawing->ruler = gtk_drawing_area_new (); + //gtk_widget_set_size_request(drawing->ruler, -1, 27); + + drawing->padding = gtk_drawing_area_new (); + //gtk_widget_set_size_request(drawing->padding, -1, 27); + gtk_box_pack_start(GTK_BOX(drawing->ruler_hbox), drawing->ruler, + TRUE, TRUE, 0); + gtk_box_pack_end(GTK_BOX(drawing->ruler_hbox), drawing->padding, + FALSE, FALSE, 0); + + + + drawing->drawing_area = gtk_drawing_area_new (); + + drawing->gc = NULL; + + drawing->hbox = gtk_hbox_new(FALSE, 1); + drawing->viewport = gtk_viewport_new(NULL, control_flow_data->v_adjust); + drawing->scrollbar = gtk_vscrollbar_new(control_flow_data->v_adjust); + gtk_box_pack_start(GTK_BOX(drawing->hbox), drawing->viewport, + TRUE, TRUE, 0); + gtk_box_pack_end(GTK_BOX(drawing->hbox), drawing->scrollbar, + FALSE, FALSE, 0); + + //drawing->scrolled_window = + // gtk_scrolled_window_new (NULL, + // control_flow_data->v_adjust); + + //gtk_scrolled_window_set_policy( + // GTK_SCROLLED_WINDOW(drawing->scrolled_window), + // GTK_POLICY_NEVER, + // GTK_POLICY_AUTOMATIC); + + gtk_container_add(GTK_CONTAINER(drawing->viewport), + drawing->drawing_area); + //gtk_scrolled_window_add_with_viewport( + // GTK_SCROLLED_WINDOW(drawing->scrolled_window), + // drawing->drawing_area); + + gtk_box_pack_start(GTK_BOX(drawing->vbox), drawing->ruler_hbox, + FALSE, FALSE, 0); + gtk_box_pack_end(GTK_BOX(drawing->vbox), drawing->hbox, + TRUE, TRUE, 0); + + drawing->pango_layout = + gtk_widget_create_pango_layout(drawing->drawing_area, NULL); + + drawing->height = 1; + drawing->width = 1; + drawing->depth = 0; + drawing->alloc_height = 1; + drawing->alloc_width = 1; + + drawing->damage_begin = 0; + drawing->damage_end = 0; + drawing->horizontal_sel = -1; + + //gtk_widget_set_size_request(drawing->drawing_area->window, 50, 50); + g_object_set_data_full( + G_OBJECT(drawing->drawing_area), + "Link_drawing_Data", + drawing, + (GDestroyNotify)drawing_destroy); + + g_object_set_data( + G_OBJECT(drawing->ruler), + "drawing", + drawing); + + + //gtk_widget_modify_bg( drawing->drawing_area, + // GTK_STATE_NORMAL, + // &CF_Colors[BLACK]); + + //gdk_window_get_geometry(drawing->drawing_area->window, + // NULL, NULL, + // &(drawing->width), + // &(drawing->height), + // -1); + + //drawing->pixmap = gdk_pixmap_new( + // drawing->drawing_area->window, + // drawing->width, + // drawing->height, + // drawing->depth); + + //drawing->pixmap = NULL; + +// drawing->pixmap = gdk_pixmap_new(drawing->drawing_area->window, +// drawing->drawing_area->allocation.width, +// drawing->drawing_area->allocation.height, +// -1); + + g_signal_connect (G_OBJECT(drawing->drawing_area), + "configure_event", + G_CALLBACK (configure_event), + (gpointer)drawing); + + g_signal_connect (G_OBJECT(drawing->ruler), + "expose_event", + G_CALLBACK(expose_ruler), + (gpointer)drawing); + + gtk_widget_add_events(drawing->ruler, GDK_POINTER_MOTION_MASK); + + g_signal_connect (G_OBJECT(drawing->ruler), + "motion-notify-event", + G_CALLBACK(motion_notify_ruler), + (gpointer)drawing); + + + g_signal_connect (G_OBJECT(drawing->scrollbar), + "size-allocate", + G_CALLBACK(scrollbar_size_allocate), + (gpointer)drawing); + + + + g_signal_connect (G_OBJECT(drawing->drawing_area), + "expose_event", + G_CALLBACK (expose_event), + (gpointer)drawing); + + g_signal_connect_after (G_OBJECT(drawing->drawing_area), + "expose_event", + G_CALLBACK (after_expose_event), + (gpointer)drawing); + + g_signal_connect (G_OBJECT(drawing->drawing_area), + "button-press-event", + G_CALLBACK (button_press_event), + (gpointer)drawing); + + + gtk_widget_show(drawing->ruler); + gtk_widget_show(drawing->padding); + gtk_widget_show(drawing->ruler_hbox); + + gtk_widget_show(drawing->drawing_area); + //gtk_widget_show(drawing->scrolled_window); + gtk_widget_show(drawing->viewport); + gtk_widget_show(drawing->scrollbar); + gtk_widget_show(drawing->hbox); + + /* Allocate the colors */ + GdkColormap* colormap = gdk_colormap_get_system(); + gboolean success[NUM_COLORS]; + gdk_colormap_alloc_colors(colormap, drawing_colors, NUM_COLORS, FALSE, + TRUE, success); + gdk_colormap_alloc_colors(colormap, drawing_colors_cpu, NUM_COLORS_CPU, FALSE, + TRUE, success); + gdk_colormap_alloc_colors(colormap, drawing_colors_irq, NUM_COLORS_IRQ, FALSE, + TRUE, success); + gdk_colormap_alloc_colors(colormap, drawing_colors_soft_irq, NUM_COLORS_SOFT_IRQ, FALSE, + TRUE, success); + gdk_colormap_alloc_colors(colormap, drawing_colors_trap, NUM_COLORS_TRAP, FALSE, + TRUE, success); + gdk_colormap_alloc_colors(colormap, drawing_colors_bdev, NUM_COLORS_BDEV, FALSE, + TRUE, success); + + drawing->gc = + gdk_gc_new(GDK_DRAWABLE(main_window_get_widget(control_flow_data->tab)->window)); + drawing->dotted_gc = + gdk_gc_new(GDK_DRAWABLE(main_window_get_widget(control_flow_data->tab)->window)); + + gdk_gc_copy(drawing->gc, + main_window_get_widget(control_flow_data->tab)->style->black_gc); + gdk_gc_copy(drawing->dotted_gc, + main_window_get_widget(control_flow_data->tab)->style->white_gc); + + gint8 dash_list[] = { 1, 2 }; + gdk_gc_set_line_attributes(drawing->dotted_gc, + 1, + GDK_LINE_ON_OFF_DASH, + GDK_CAP_BUTT, + GDK_JOIN_MITER); + gdk_gc_set_dashes(drawing->dotted_gc, + 0, + dash_list, + 2); + + drawing->ruler_gc_butt = + gdk_gc_new(GDK_DRAWABLE(main_window_get_widget(control_flow_data->tab)->window)); + gdk_gc_copy(drawing->ruler_gc_butt, + main_window_get_widget(control_flow_data->tab)->style->black_gc); + drawing->ruler_gc_round = + gdk_gc_new(GDK_DRAWABLE(main_window_get_widget(control_flow_data->tab)->window)); + gdk_gc_copy(drawing->ruler_gc_round, + main_window_get_widget(control_flow_data->tab)->style->black_gc); + + + gdk_gc_set_line_attributes(drawing->ruler_gc_butt, + 2, + GDK_LINE_SOLID, + GDK_CAP_BUTT, + GDK_JOIN_MITER); + + gdk_gc_set_line_attributes(drawing->ruler_gc_round, + 2, + GDK_LINE_SOLID, + GDK_CAP_ROUND, + GDK_JOIN_ROUND); + + + return drawing; +} + +void drawing_destroy(Drawing_t *drawing) +{ + g_info("drawing_destroy %p", drawing); + + /* Free the colors */ + GdkColormap* colormap = gdk_colormap_get_system(); + + gdk_colormap_free_colors(colormap, drawing_colors, NUM_COLORS); + gdk_colormap_free_colors(colormap, drawing_colors_cpu, NUM_COLORS_CPU); + gdk_colormap_free_colors(colormap, drawing_colors_irq, NUM_COLORS_IRQ); + gdk_colormap_free_colors(colormap, drawing_colors_soft_irq, NUM_COLORS_IRQ); + gdk_colormap_free_colors(colormap, drawing_colors_trap, NUM_COLORS_TRAP); + gdk_colormap_free_colors(colormap, drawing_colors_bdev, NUM_COLORS_BDEV); + + // Do not unref here, Drawing_t destroyed by it's widget. + //g_object_unref( G_OBJECT(drawing->drawing_area)); + if(drawing->gc != NULL) + gdk_gc_unref(drawing->gc); + + g_object_unref(drawing->pango_layout); + if(drawing->dotted_gc != NULL) gdk_gc_unref(drawing->dotted_gc); + if(drawing->ruler_gc_butt != NULL) gdk_gc_unref(drawing->ruler_gc_butt); + if(drawing->ruler_gc_round != NULL) gdk_gc_unref(drawing->ruler_gc_round); + + g_free(drawing); + g_info("drawing_destroy end"); +} + +GtkWidget *drawing_get_drawing_area(Drawing_t *drawing) +{ + return drawing->drawing_area; +} + +GtkWidget *drawing_get_widget(Drawing_t *drawing) +{ + return drawing->vbox; +} + +void drawing_draw_line( Drawing_t *drawing, + GdkPixmap *pixmap, + guint x1, guint y1, + guint x2, guint y2, + GdkGC *GC) +{ + gdk_draw_line (pixmap, + GC, + x1, y1, x2, y2); +} + +void drawing_clear(Drawing_t *drawing) +{ + //if (drawing->pixmap) + // gdk_pixmap_unref(drawing->pixmap); + ControlFlowData *cfd = drawing->control_flow_data; + + + rectangle_pixmap(cfd->process_list, + drawing->drawing_area->style->black_gc, + TRUE, + 0, 0, + drawing->alloc_width, // do not overlap + -1); + + //drawing->height = 1; + /* Allocate a new pixmap with new height */ + //drawing->pixmap = gdk_pixmap_new(drawing->drawing_area->window, + // drawing->width + SAFETY + EXTRA_ALLOC, + // drawing->height + EXTRA_ALLOC, + // -1); + //drawing->alloc_width = drawing->width + SAFETY + EXTRA_ALLOC; + //drawing->alloc_height = drawing->height + EXTRA_ALLOC; + + //gtk_widget_set_size_request(drawing->drawing_area, + // -1, + // drawing->height); + //gtk_widget_queue_resize_no_redraw(drawing->drawing_area); + + /* ask for the buffer to be redrawn */ + gtk_widget_queue_draw ( drawing->drawing_area); +} + +#if 0 +/* Insert a square corresponding to a new process in the list */ +/* Applies to whole drawing->width */ +void drawing_insert_square(Drawing_t *drawing, + guint y, + guint height) +{ + //GdkRectangle update_rect; + gboolean reallocate = FALSE; + GdkPixmap *new_pixmap; + + /* Allocate a new pixmap with new height */ + if(drawing->alloc_height < drawing->height + height) { + + new_pixmap = gdk_pixmap_new(drawing->drawing_area->window, + drawing->width + SAFETY + EXTRA_ALLOC, + drawing->height + height + EXTRA_ALLOC, + -1); + drawing->alloc_width = drawing->width + SAFETY + EXTRA_ALLOC; + drawing->alloc_height = drawing->height + height + EXTRA_ALLOC; + reallocate = TRUE; + + /* Copy the high region */ + gdk_draw_pixmap (new_pixmap, + drawing->drawing_area->style->black_gc, + drawing->pixmap, + 0, 0, + 0, 0, + drawing->width + SAFETY, y); + + } else { + new_pixmap = drawing->pixmap; + } + + //GdkPixmap *pixmap = gdk_pixmap_new(drawing->drawing_area->window, + // drawing->width + SAFETY, + // drawing->height + height, + // -1); + + /* add an empty square */ + gdk_draw_rectangle (new_pixmap, + drawing->drawing_area->style->black_gc, + TRUE, + 0, y, + drawing->width + SAFETY, // do not overlap + height); + + /* copy the bottom of the region */ + gdk_draw_pixmap (new_pixmap, + drawing->drawing_area->style->black_gc, + drawing->pixmap, + 0, y, + 0, y + height, + drawing->width+SAFETY, drawing->height - y); + + + if(reallocate && likely(drawing->pixmap)) { + gdk_pixmap_unref(drawing->pixmap); + drawing->pixmap = new_pixmap; + } + + if(unlikely(drawing->height==1)) drawing->height = height; + else drawing->height += height; + + gtk_widget_set_size_request(drawing->drawing_area, + -1, + drawing->height); + gtk_widget_queue_resize_no_redraw(drawing->drawing_area); + + /* ask for the buffer to be redrawn */ + gtk_widget_queue_draw_area ( drawing->drawing_area, + 0, y, + drawing->width, drawing->height-y); +} + + +/* Remove a square corresponding to a removed process in the list */ +void drawing_remove_square(Drawing_t *drawing, + guint y, + guint height) +{ + GdkPixmap *pixmap; + + if(unlikely((guint)drawing->height == height)) { + //pixmap = gdk_pixmap_new( + // drawing->drawing_area->window, + // drawing->width + SAFETY, + // 1, + // -1); + pixmap = drawing->pixmap; + drawing->height=1; + } else { + /* Allocate a new pixmap with new height */ + //pixmap = gdk_pixmap_new( + // drawing->drawing_area->window, + // drawing->width + SAFETY, + // drawing->height - height, + // -1); + /* Keep the same preallocated pixmap */ + pixmap = drawing->pixmap; + + /* Copy the high region */ + gdk_draw_pixmap (pixmap, + drawing->drawing_area->style->black_gc, + drawing->pixmap, + 0, 0, + 0, 0, + drawing->width + SAFETY, y); + + /* Copy up the bottom of the region */ + gdk_draw_pixmap (pixmap, + drawing->drawing_area->style->black_gc, + drawing->pixmap, + 0, y + height, + 0, y, + drawing->width, drawing->height - y - height); + + drawing->height-=height; + } + + //if(likely(drawing->pixmap)) + // gdk_pixmap_unref(drawing->pixmap); + + //drawing->pixmap = pixmap; + + gtk_widget_set_size_request(drawing->drawing_area, + -1, + drawing->height); + gtk_widget_queue_resize_no_redraw(drawing->drawing_area); + /* ask for the buffer to be redrawn */ + gtk_widget_queue_draw_area ( drawing->drawing_area, + 0, y, + drawing->width, MAX(drawing->height-y, 1)); +} +#endif //0 + +void drawing_update_ruler(Drawing_t *drawing, TimeWindow *time_window) +{ + GtkRequisition req; + GdkRectangle rect; + + req.width = drawing->ruler->allocation.width; + req.height = drawing->ruler->allocation.height; + + + rect.x = 0; + rect.y = 0; + rect.width = req.width; + rect.height = req.height; + + gtk_widget_queue_draw(drawing->ruler); + //gtk_widget_draw( drawing->ruler, &rect); +} + +/* Redraw the ruler */ +static gboolean +expose_ruler( GtkWidget *widget, GdkEventExpose *event, gpointer user_data ) +{ + Drawing_t *drawing = (Drawing_t*)user_data; + TimeWindow time_window = lttvwindow_get_time_window(drawing->control_flow_data->tab); + gchar text[255]; + + PangoContext *context; + PangoLayout *layout; + PangoFontDescription *FontDesc; + PangoRectangle ink_rect; + gint global_width=0; + GdkColor foreground = { 0, 0, 0, 0 }; + GdkColor background = { 0, 0xffff, 0xffff, 0xffff }; + + LttTime window_end = time_window.end_time; + LttTime half_width = + ltt_time_div(time_window.time_width,2.0); + LttTime window_middle = + ltt_time_add(half_width, + time_window.start_time); + g_debug("ruler expose event"); + + gdk_draw_rectangle (drawing->ruler->window, + drawing->ruler->style->white_gc, + TRUE, + event->area.x, event->area.y, + event->area.width, + event->area.height); + + gdk_draw_line (drawing->ruler->window, + drawing->ruler_gc_butt, + event->area.x, 1, + event->area.x + event->area.width, 1); + + + snprintf(text, 255, "%lus\n%luns", + time_window.start_time.tv_sec, + time_window.start_time.tv_nsec); + + layout = gtk_widget_create_pango_layout(drawing->drawing_area, NULL); + + context = pango_layout_get_context(layout); + FontDesc = pango_context_get_font_description(context); + + pango_font_description_set_size(FontDesc, 6*PANGO_SCALE); + pango_layout_context_changed(layout); + + pango_layout_set_text(layout, text, -1); + pango_layout_get_pixel_extents(layout, &ink_rect, NULL); + global_width += ink_rect.width; + + gdk_draw_layout_with_colors(drawing->ruler->window, + drawing->ruler_gc_butt, + 0, + 6, + layout, &foreground, &background); + + gdk_draw_line (drawing->ruler->window, + drawing->ruler_gc_round, + 1, 1, + 1, 7); + + + snprintf(text, 255, "%lus\n%luns", window_end.tv_sec, + window_end.tv_nsec); + + pango_layout_set_text(layout, text, -1); + pango_layout_get_pixel_extents(layout, &ink_rect, NULL); + global_width += ink_rect.width; + + if(global_width <= drawing->ruler->allocation.width) + { + gdk_draw_layout_with_colors(drawing->ruler->window, + drawing->ruler_gc_butt, + drawing->ruler->allocation.width - ink_rect.width, + 6, + layout, &foreground, &background); + + gdk_draw_line (drawing->ruler->window, + drawing->ruler_gc_butt, + drawing->ruler->allocation.width-1, 1, + drawing->ruler->allocation.width-1, 7); + } + + + snprintf(text, 255, "%lus\n%luns", window_middle.tv_sec, + window_middle.tv_nsec); + + pango_layout_set_text(layout, text, -1); + pango_layout_get_pixel_extents(layout, &ink_rect, NULL); + global_width += ink_rect.width; + + if(global_width <= drawing->ruler->allocation.width) + { + gdk_draw_layout_with_colors(drawing->ruler->window, + drawing->ruler_gc_butt, + (drawing->ruler->allocation.width - ink_rect.width)/2, + 6, + layout, &foreground, &background); + + gdk_draw_line (drawing->ruler->window, + drawing->ruler_gc_butt, + drawing->ruler->allocation.width/2, 1, + drawing->ruler->allocation.width/2, 7); + + + + + } + + g_object_unref(layout); + + return FALSE; +} + + +/* notify mouse on ruler */ +static gboolean +motion_notify_ruler(GtkWidget *widget, GdkEventMotion *event, gpointer user_data) +{ + //g_debug("motion"); + //eventually follow mouse and show time here + return 0; +} diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/drawing.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/drawing.h new file mode 100644 index 00000000..8b0070bd --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/drawing.h @@ -0,0 +1,266 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 _DRAWING_H +#define _DRAWING_H + +#include +#include +#include +#include +#include +#include +#include +#include "cfv.h" +#include "drawitem.h" + + +#define SAFETY 50 // safety pixels at right and bottom of pixmap buffer + +typedef enum _draw_color { + COL_BLACK, + COL_WHITE, + COL_RUN_USER_MODE,/* green */ + COL_RUN_SYSCALL, /* pale blue */ + COL_RUN_TRAP, /* yellow */ + COL_RUN_IRQ, /* orange */ + COL_RUN_SOFT_IRQ, /* red */ + COL_WAIT, /* dark red */ + COL_WAIT_CPU, /* dark yellow */ + COL_ZOMBIE, /* dark purple */ + COL_WAIT_FORK, /* dark green */ + COL_EXIT, /* "less dark" magenta */ + COL_MODE_UNKNOWN, /* white */ + COL_UNNAMED, /* white */ + NUM_COLORS } draw_color; + +typedef enum _draw_color_cpu { + COL_CPU_UNKNOWN, + COL_CPU_IDLE, + COL_CPU_BUSY, + COL_CPU_IRQ, + COL_CPU_SOFT_IRQ, + COL_CPU_TRAP, + NUM_COLORS_CPU +} draw_color_cpu; + +typedef enum _draw_color_irq { + COL_IRQ_UNKNOWN, + COL_IRQ_IDLE, + COL_IRQ_BUSY, + NUM_COLORS_IRQ +} draw_color_irq; + +typedef enum _draw_color_soft_irq { + COL_SOFT_IRQ_UNKNOWN, + COL_SOFT_IRQ_IDLE, + COL_SOFT_IRQ_PENDING, + COL_SOFT_IRQ_BUSY, + NUM_COLORS_SOFT_IRQ +} draw_color_soft_irq; + +typedef enum _draw_color_trap { + COL_TRAP_UNKNOWN, + COL_TRAP_IDLE, + COL_TRAP_BUSY, + NUM_COLORS_TRAP +} draw_color_trap; + +typedef enum _draw_color_bdev { + COL_BDEV_UNKNOWN, + COL_BDEV_IDLE, + COL_BDEV_BUSY_READING, + COL_BDEV_BUSY_WRITING, + NUM_COLORS_BDEV +} draw_color_bdev; + +extern GdkColor drawing_colors[NUM_COLORS]; +extern GdkColor drawing_colors_cpu[NUM_COLORS_CPU]; +extern GdkColor drawing_colors_irq[NUM_COLORS_IRQ]; +extern GdkColor drawing_colors_soft_irq[NUM_COLORS_SOFT_IRQ]; +extern GdkColor drawing_colors_trap[NUM_COLORS_TRAP]; +extern GdkColor drawing_colors_bdev[NUM_COLORS_BDEV]; + +/* This part of the viewer does : + * Draw horizontal lines, getting graphic context as arg. + * Copy region of the screen into another. + * Modify the boundaries to reflect a scale change. (resize) + * Refresh the physical screen with the pixmap + * A helper function is provided here to convert from time to process + * identifier to pixels and the contrary (will be useful for mouse selection). + * Insert an empty square in the drawing, moving the bottom part. + * + * Note: The last point is exactly why it would not be so easy to add the + * vertical line functionnality as in the original version of LTT. In order + * to do so, we should keep all processes in the list for the duration of + * all the trace instead of dynamically adding and removing them when we + * scroll. Another possibility is to redraw all the visible area when a new + * process is added to the list. The second solution seems more appropriate + * to me. + * + * + * The pixmap used has the width of the physical window, but the height + * of the shown processes. + */ + +#ifndef TYPE_DRAWING_T_DEFINED +#define TYPE_DRAWING_T_DEFINED +typedef struct _Drawing_t Drawing_t; +#endif //TYPE_DRAWING_T_DEFINED + +#ifndef TYPE_CONTROLFLOWDATA_DEFINED +#define TYPE_CONTROLFLOWDATA_DEFINED +typedef struct _ControlFlowData ControlFlowData; +#endif //TYPE_CONTROLFLOWDATA_DEFINED + +struct _Drawing_t { + GtkWidget *vbox; + GtkWidget *drawing_area; + //GtkWidget *scrolled_window; + GtkWidget *hbox; + GtkWidget *viewport; + GtkWidget *scrollbar; + + GtkWidget *ruler_hbox; + GtkWidget *ruler; + GtkWidget *padding; + //GdkPixmap *pixmap; + ControlFlowData *control_flow_data; + + PangoLayout *pango_layout; + + gint height, width, depth; + /* height and width of allocated buffer pixmap */ + gint alloc_height, alloc_width; + + /* X coordinate of damaged region */ + gint damage_begin, damage_end; /* damaged region to be exposed, + updated per chunk */ + LttTime last_start; + GdkGC *dotted_gc; + GdkGC *gc; + GdkGC *ruler_gc_butt; + GdkGC *ruler_gc_round; + + /* Position of the horizontal selector, -1 for none */ + gint horizontal_sel; +}; + +Drawing_t *drawing_construct(ControlFlowData *control_flow_data); +void drawing_destroy(Drawing_t *drawing); + +GtkWidget *drawing_get_widget(Drawing_t *drawing); +GtkWidget *drawing_get_drawing_area(Drawing_t *drawing); + + +void drawing_data_request(Drawing_t *drawing, + gint x, gint y, + gint width, + gint height); + +void drawing_draw_line( Drawing_t *drawing, + GdkPixmap *pixmap, + guint x1, guint y1, + guint x2, guint y2, + GdkGC *GC); + +//void drawing_copy( Drawing_t *drawing, +// guint xsrc, guint ysrc, +// guint xdest, guint ydest, +// guint width, guint height); + +/* Clear the drawing : make it 1xwidth. */ +void drawing_clear(Drawing_t *drawing); + +/* Insert a square corresponding to a new process in the list */ +void drawing_insert_square(Drawing_t *drawing, + guint y, + guint height); + +/* Remove a square corresponding to a removed process in the list */ +void drawing_remove_square(Drawing_t *drawing, + guint y, + guint height); + +void drawing_update_ruler(Drawing_t *drawing, TimeWindow *time_window); + +void drawing_request_expose(EventsRequest *events_request, + LttvTracesetState *tss, + LttTime end_time); + +void drawing_data_request_begin(EventsRequest *events_request, + LttvTracesetState *tss); +void drawing_chunk_begin(EventsRequest *events_request, LttvTracesetState *tss); + + + +void +tree_row_activated(GtkTreeModel *treemodel, + GtkTreePath *arg1, + GtkTreeViewColumn *arg2, + gpointer user_data); + + +/* convert_pixels_to_time + * + * Convert from window pixel and time interval to an absolute time. + */ +static inline void convert_pixels_to_time( + gint width, + guint x, + TimeWindow time_window, + LttTime *time) +{ + double time_d; + + time_d = time_window.time_width_double; + time_d = time_d / (double)width * (double)x; + *time = ltt_time_from_double(time_d); + *time = ltt_time_add(time_window.start_time, *time); +} + + +static inline void convert_time_to_pixels( + TimeWindow time_window, + LttTime time, + int width, + guint *x) +{ + double time_d; +#ifdef EXTRA_CHECK + g_assert(ltt_time_compare(window_time_begin, time) <= 0 && + ltt_time_compare(window_time_end, time) >= 0); +#endif //EXTRA_CHECK + + time = ltt_time_sub(time, time_window.start_time); + + time_d = ltt_time_to_double(time); + + if(time_window.time_width_double == 0.0) { + g_assert(time_d == 0.0); + *x = 0; + } else { + *x = (guint)(time_d / time_window.time_width_double * (double)width); + } + +} + + + +#endif // _DRAWING_H diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/drawitem.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/drawitem.c new file mode 100644 index 00000000..d97628b0 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/drawitem.c @@ -0,0 +1,465 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + + + +/****************************************************************************** + * drawitem.c + * + * This file contains methods responsible for drawing a generic type of data + * in a drawable. Doing this generically will permit user defined drawing + * behavior in a later time. + * + * This file provides an API which is meant to be reusable for all viewers that + * need to show information in line, icon, text, background or point form in + * a drawable area having time for x axis. The y axis, in the control flow + * viewer case, is corresponding to the different processes, but it can be + * reused integrally for cpu, and eventually locks, buffers, network + * interfaces... What will differ between the viewers is the precise + * information which interests us. We may think that the most useful + * information for control flow are some specific events, like schedule + * change, and processes'states. It may differ for a cpu viewer : the + * interesting information could be more the execution mode of each cpu. + * This API in meant to make viewer's writers life easier : it will become + * a simple choice of icons and line types for the precise information + * the viewer has to provide (agremented with keeping supplementary records + * and modifying slightly the DrawContext to suit the needs.) + * + * We keep each data type in attributes, keys to specific information + * being formed from the GQuark corresponding to the information received. + * (facilities / facility_name / events / eventname.) + * (cpus/cpu_name, process_states/ps_name, + * execution_modes/em_name, execution_submodes/es_name). + * The goal is then to provide a generic way to print information on the + * screen for all this different information. + * + * Information can be printed as + * + * - text (text + color + size + position (over or under line) + * - icon (icon filename, corresponding to a loaded icon, accessible through + * a GQuark. Icons are loaded statically at the guiControlFlow level during + * module initialization and can be added on the fly if not present in the + * GQuark.) The habitual place for xpm icons is in + * ${prefix}/share/LinuxTraceToolkit.) + position (over or under line) + * - line (color, width, style) + * - Arc (big points) (color, size) + * - background color (color) + * + * An item is a leaf of the attributes tree. It is, in that case, including + * all kind of events categories we can have. It then associates each category + * with one or more actions (drawing something) or nothing. + * + * Each item has an array of hooks (hook list). Each hook represents an + * operation to perform. We seek the array each time we want to + * draw an item. We execute each operation in order. An operation type + * is associated with each hook to permit user listing and modification + * of these operations. The operation type is also used to find the + * corresponding priority for the sorting. Operation type and priorities + * are enum and a static int table. + * + * The array has to be sorted by priority each time we add a task in it. + * A priority is associated with each operation type. It permits + * to perform background color selection before line or text drawing. We also + * draw lines before text, so the text appears over the lines. + * + * Executing all the arrays of operations for a specific event (which + * implies information for state, event, cpu, execution mode and submode) + * has to be done in a same DrawContext. The goal there is to keep the offset + * of the text and icons over and under the middle line, so a specific + * event could be printed as ( R Si 0 for running, scheduled in, cpu 0 ), + * text being easy to replace with icons. The DrawContext is passed as + * call_data for the operation hooks. + * + * We use the lttv global attributes to keep track of the loaded icons. + * If we need an icon, we look for it in the icons / icon name pathname. + * If found, we use the pointer to it. If not, we load the pixmap in + * memory and set the pointer to the GdkPixmap in the attributes. The + * structure pointed to contains the pixmap and the mask bitmap. + * + * Author : Mathieu Desnoyers, October 2003 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "drawing.h" +#include "drawitem.h" + + +#define MAX_PATH_LEN 256 + +/* drawing hook functions */ +gboolean draw_text( void *hook_data, void *call_data) +{ + PropertiesText *properties = (PropertiesText*)hook_data; + DrawContext *draw_context = (DrawContext*)call_data; + + PangoContext *context; + PangoLayout *layout; + PangoFontDescription *font_desc;// = pango_font_description_new(); + PangoRectangle ink_rect; + + layout = draw_context->pango_layout; + + context = pango_layout_get_context(layout); + font_desc = pango_context_get_font_description(context); + + pango_font_description_set_size(font_desc, properties->size*PANGO_SCALE); + pango_layout_context_changed(layout); + + pango_layout_set_text(layout, properties->text, -1); + pango_layout_get_pixel_extents(layout, &ink_rect, NULL); + + gint x=0, y=0; + gint *offset=NULL; + gboolean enough_space = FALSE; + gint width = ink_rect.width; + + switch(properties->position.x) { + case POS_START: + x = draw_context->drawinfo.start.x; + switch(properties->position.y) { + case OVER: + offset = &draw_context->drawinfo.start.offset.over; + x += draw_context->drawinfo.start.offset.over; + y = draw_context->drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->drawinfo.start.offset.middle; + x += draw_context->drawinfo.start.offset.middle; + y = draw_context->drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->drawinfo.start.offset.under; + x += draw_context->drawinfo.start.offset.under; + y = draw_context->drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x + width <= draw_context->drawinfo.end.x)) { + enough_space = TRUE; + *offset += width; + } + break; + case POS_END: + x = draw_context->drawinfo.end.x; + switch(properties->position.y) { + case OVER: + offset = &draw_context->drawinfo.end.offset.over; + x += draw_context->drawinfo.end.offset.over; + y = draw_context->drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->drawinfo.end.offset.middle; + x += draw_context->drawinfo.end.offset.middle; + y = draw_context->drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->drawinfo.end.offset.under; + x += draw_context->drawinfo.end.offset.under; + y = draw_context->drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x - width >= draw_context->drawinfo.start.x)) { + enough_space = TRUE; + *offset -= width; + } + break; + } + + if(unlikely(enough_space)) + gdk_draw_layout_with_colors(draw_context->drawable, + draw_context->gc, + x, + y, + layout, properties->foreground, properties->background); + + return 0; +} + + +/* To speed up the process, search in already loaded icons list first. Only + * load it if not present. + */ +gboolean draw_icon( void *hook_data, void *call_data) +{ + PropertiesIcon *properties = (PropertiesIcon*)hook_data; + DrawContext *draw_context = (DrawContext*)call_data; + + LttvIAttribute *attributes = LTTV_IATTRIBUTE(lttv_global_attributes()); + LttvAttributeValue value; + gchar icon_name[MAX_PATH_LEN] = "icons/"; + IconStruct *icon_info; + + strcat(icon_name, properties->icon_name); + + g_assert(lttv_iattribute_find_by_path(attributes, icon_name, + LTTV_POINTER, &value)); + if(unlikely(*(value.v_pointer) == NULL)) + { + *(value.v_pointer) = icon_info = g_new(IconStruct,1); + + icon_info->pixmap = gdk_pixmap_create_from_xpm(draw_context->drawable, + &icon_info->mask, NULL, properties->icon_name); + } + else + { + icon_info = *(value.v_pointer); + } + + gint x=0, y=0; + gint *offset=NULL; + gboolean enough_space = FALSE; + gint width = properties->width; + + switch(properties->position.x) { + case POS_START: + x = draw_context->drawinfo.start.x; + switch(properties->position.y) { + case OVER: + offset = &draw_context->drawinfo.start.offset.over; + x += draw_context->drawinfo.start.offset.over; + y = draw_context->drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->drawinfo.start.offset.middle; + x += draw_context->drawinfo.start.offset.middle; + y = draw_context->drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->drawinfo.start.offset.under; + x += draw_context->drawinfo.start.offset.under; + y = draw_context->drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x + width <= draw_context->drawinfo.end.x)) { + enough_space = TRUE; + *offset += width; + } + break; + case POS_END: + x = draw_context->drawinfo.end.x; + switch(properties->position.y) { + case OVER: + offset = &draw_context->drawinfo.end.offset.over; + x += draw_context->drawinfo.end.offset.over; + y = draw_context->drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->drawinfo.end.offset.middle; + x += draw_context->drawinfo.end.offset.middle; + y = draw_context->drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->drawinfo.end.offset.under; + x += draw_context->drawinfo.end.offset.under; + y = draw_context->drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x - width >= draw_context->drawinfo.start.x)) { + enough_space = TRUE; + *offset -= width; + } + break; + } + + if(unlikely(enough_space)) { + gdk_gc_set_clip_mask(draw_context->gc, icon_info->mask); + + gdk_gc_set_clip_origin( + draw_context->gc, + x, + y); + gdk_draw_drawable(draw_context->drawable, + draw_context->gc, + icon_info->pixmap, + 0, 0, + x, + y, + properties->width, properties->height); + + gdk_gc_set_clip_origin(draw_context->gc, 0, 0); + gdk_gc_set_clip_mask(draw_context->gc, NULL); + } + return 0; +} + +gboolean draw_line( void *hook_data, void *call_data) +{ + PropertiesLine *properties = (PropertiesLine*)hook_data; + DrawContext *draw_context = (DrawContext*)call_data; + + gdk_gc_set_foreground(draw_context->gc, &properties->color); + //gdk_gc_set_rgb_fg_color(draw_context->gc, &properties->color); + gdk_gc_set_line_attributes( draw_context->gc, + properties->line_width, + properties->style, + GDK_CAP_BUTT, + GDK_JOIN_MITER); + //g_critical("DRAWING LINE : x1: %i, y1: %i, x2:%i, y2:%i", + // draw_context->previous->middle->x, + // draw_context->previous->middle->y, + // draw_context->drawinfo.middle.x, + // draw_context->drawinfo.middle.y); + + gint x_begin=0, x_end=0, y=0; + + x_begin = draw_context->drawinfo.start.x; + x_end = draw_context->drawinfo.end.x; + + switch(properties->y) { + case OVER: + y = draw_context->drawinfo.y.over; + break; + case MIDDLE: + y = draw_context->drawinfo.y.middle; + break; + case UNDER: + y = draw_context->drawinfo.y.under; + break; + } + + drawing_draw_line( + NULL, draw_context->drawable, + x_begin, + y, + x_end, + y, + draw_context->gc); + + return 0; +} + +gboolean draw_arc( void *hook_data, void *call_data) +{ + PropertiesArc *properties = (PropertiesArc*)hook_data; + DrawContext *draw_context = (DrawContext*)call_data; + + gdk_gc_set_foreground(draw_context->gc, properties->color); + //gdk_gc_set_rgb_fg_color(draw_context->gc, properties->color); + + gint x=0, y=0; + gint *offset=NULL; + gboolean enough_space = FALSE; + gint width = properties->size; + + switch(properties->position.x) { + case POS_START: + x = draw_context->drawinfo.start.x; + switch(properties->position.y) { + case OVER: + offset = &draw_context->drawinfo.start.offset.over; + x += draw_context->drawinfo.start.offset.over; + y = draw_context->drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->drawinfo.start.offset.middle; + x += draw_context->drawinfo.start.offset.middle; + y = draw_context->drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->drawinfo.start.offset.under; + x += draw_context->drawinfo.start.offset.under; + y = draw_context->drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x + width <= draw_context->drawinfo.end.x)) { + enough_space = TRUE; + *offset += width; + } + break; + case POS_END: + x = draw_context->drawinfo.end.x; + switch(properties->position.y) { + case OVER: + offset = &draw_context->drawinfo.end.offset.over; + x += draw_context->drawinfo.end.offset.over; + y = draw_context->drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->drawinfo.end.offset.middle; + x += draw_context->drawinfo.end.offset.middle; + y = draw_context->drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->drawinfo.end.offset.under; + x += draw_context->drawinfo.end.offset.under; + y = draw_context->drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x - width >= draw_context->drawinfo.start.x)) { + enough_space = TRUE; + *offset -= width; + } + break; + } + + if(unlikely(enough_space)) + gdk_draw_arc(draw_context->drawable, draw_context->gc, + properties->filled, + x, + y, + properties->size, properties->size, 0, 360*64); + + return 0; +} + +gboolean draw_bg( void *hook_data, void *call_data) +{ + PropertiesBG *properties = (PropertiesBG*)hook_data; + DrawContext *draw_context = (DrawContext*)call_data; + + gdk_gc_set_foreground(draw_context->gc, properties->color); + //gdk_gc_set_rgb_fg_color(draw_context->gc, properties->color); + + //g_critical("DRAWING RECT : x: %i, y: %i, w:%i, h:%i, val1 :%i, val2:%i ", + // draw_context->previous->over->x, + // draw_context->previous->over->y, + // draw_context->drawinfo.over.x - draw_context->previous->over->x, + // draw_context->previous->under->y-draw_context->previous->over->y, + // draw_context->drawinfo.over.x, + // draw_context->previous->over->x); + gdk_draw_rectangle(draw_context->drawable, draw_context->gc, + TRUE, + draw_context->drawinfo.start.x, + draw_context->drawinfo.y.over, + draw_context->drawinfo.end.x - draw_context->drawinfo.start.x, + draw_context->drawinfo.y.under - draw_context->drawinfo.y.over); + + return 0; +} + + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/drawitem.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/drawitem.h new file mode 100644 index 00000000..28fdc183 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/drawitem.h @@ -0,0 +1,279 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 _DRAW_ITEM_H +#define _DRAW_ITEM_H + +#include + +typedef struct _DrawContext DrawContext; +typedef struct _DrawInfo DrawInfo; +typedef struct _ItemInfo ItemInfo; + +typedef struct _IconStruct IconStruct; + +typedef struct _DrawOperation DrawOperation; + + +typedef struct _PropertiesText PropertiesText; +typedef struct _PropertiesIcon PropertiesIcon; +typedef struct _PropertiesLine PropertiesLine; +typedef struct _PropertiesArc PropertiesArc; +typedef struct _PropertiesBG PropertiesBG; + +typedef enum _DrawableItems DrawableItems; +enum _DrawableItems { + ITEM_TEXT, ITEM_ICON, ITEM_LINE, ITEM_POINT, ITEM_BACKGROUND +}; + +typedef enum _RelPosX { + POS_START, POS_END +} RelPosX; + +typedef enum _RelPosY { + OVER, MIDDLE, UNDER +} RelPosY; + + +/* The DrawContext keeps information about the current drawing position and + * the previous one, so we can use both to draw lines. + * + * over : position for drawing over the middle line. + * middle : middle line position. + * under : position for drawing under the middle line. + * + * the modify_* are used to take into account that we should go forward + * when we draw a text, an arc or an icon, while it's unneeded when we + * draw a line or background. + * + * The modify_* positions are altered by the draw item functions. + * + */ + + +struct _DrawContext { + GdkDrawable *drawable; + GdkGC *gc; + PangoLayout *pango_layout; + + struct { + struct { + gint x; + struct { + gint over; + gint middle; + gint under; + } offset; + } start; + + struct { + gint x; + struct { + gint over; + gint middle; + gint under; + } offset; + } end; + + struct { + gint over; + gint middle; + gint under; + } y; + + } drawinfo; +}; + + + + +/* + * Structure used to keep information about icons. + */ +struct _IconStruct { + GdkPixmap *pixmap; + GdkBitmap *mask; +}; + + +/* + * The Item element is only used so the DrawOperation is modifiable by users. + * During drawing, only the Hook is needed. + */ +struct _DrawOperation { + DrawableItems item; + LttvHooks *hook; +}; +#if 0 +/* + * We define here each items that can be drawn, together with their + * associated priority. Many item types can have the same priority, + * it's only used for quicksorting the operations when we add a new one + * to the array of operations to perform. Lower priorities are executed + * first. So, for example, we may want to give background color a value + * of 10 while a line would have 20, so the background color, which + * is in fact a rectangle, does not hide the line. + */ + +static int Items_Priorities[] = { + 50, /* ITEM_TEXT */ + 40, /* ITEM_ICON */ + 20, /* ITEM_LINE */ + 30, /* ITEM_POINT */ + 10 /* ITEM_BACKGROUND */ +}; +#endif //0 + +/* + * Here are the different structures describing each item type that can be + * drawn. They contain the information necessary to draw the item : not the + * position (this is provided by the DrawContext), but the text, icon name, + * line width, color; all the properties of the specific items. + */ + +struct _PropertiesText { + GdkColor *foreground; + GdkColor *background; + gint size; + gchar *text; + struct { + RelPosX x; + RelPosY y; + } position; +}; + + +struct _PropertiesIcon { + gchar *icon_name; + gint width; + gint height; + struct { + RelPosX x; + RelPosY y; + } position; +}; + +struct _PropertiesLine { + GdkColor color; + gint line_width; + GdkLineStyle style; + RelPosY y; +}; + +struct _PropertiesArc { + GdkColor *color; + gint size; /* We force circle by width = height */ + gboolean filled; + struct { + RelPosX x; + RelPosY y; + } position; +}; + +struct _PropertiesBG { + GdkColor *color; +}; + + + +void draw_item( GdkDrawable *drawable, + gint x, + gint y, + LttvTraceState *ts, + LttvTracefileState *tfs, + LttvIAttribute *attributes); + +/* + * The tree of attributes used to store drawing operations goes like this : + * + * event_types/ + * "facility-event_type" + * cpus/ + * "cpu name" + * mode_types/ + * "execution mode"/ + * submodes/ + * "submode" + * process_states/ + * "state name" + * + * So if, for example, we want to add a hook to get called each time we + * receive an event that is in state LTTV_STATE_SYSCALL, we put the + * pointer to the GArray of DrawOperation in + * process_states/ "name associated with LTTV_STATE_SYSCALL" + */ + + +#if 0 +/* + * The add_operation has to do a quick sort by priority to keep the operations + * in the right order. + */ +void add_operation( LttvIAttribute *attributes, + gchar *pathname, + DrawOperation *operation); + +/* + * The del_operation seeks the array present at pathname (if any) and + * removes the DrawOperation if present. It returns 0 on success, -1 + * if it fails. + */ +gint del_operation( LttvIAttribute *attributes, + gchar *pathname, + DrawOperation *operation); + +/* + * The clean_operations removes all operations present at a pathname. + * returns 0 on success, -1 if it fails. + */ +gint clean_operations( LttvIAttribute *attributes, + gchar *pathname ); + + +/* + * The list_operations gives a pointer to the operation array associated + * with the pathname. It will be NULL if no operation is present. + */ +void list_operations( LttvIAttribute *attributes, + gchar *pathname, + GArray **operation); + + + +/* + * exec_operation executes the operations if present in the attributes, or + * do nothing if not present. + */ +void exec_operations( LttvIAttribute *attributes, + gchar *pathname); +#endif //0 + +/* + * Here follow the prototypes of the hook functions used to draw the + * different items. + */ + +gboolean draw_text( void *hook_data, void *call_data); +gboolean draw_icon( void *hook_data, void *call_data); +gboolean draw_line( void *hook_data, void *call_data); +gboolean draw_arc( void *hook_data, void *call_data); +gboolean draw_bg( void *hook_data, void *call_data); + + +#endif // _DRAW_ITEM_H diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/eventhooks.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/eventhooks.c new file mode 100644 index 00000000..6c74bdbb --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/eventhooks.c @@ -0,0 +1,2225 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + + +/***************************************************************************** + * Hooks to be called by the main window * + *****************************************************************************/ + + +/* Event hooks are the drawing hooks called during traceset read. They draw the + * icons, text, lines and background color corresponding to the events read. + * + * Two hooks are used for drawing : before_schedchange and after_schedchange hooks. The + * before_schedchange is called before the state update that occurs with an event and + * the after_schedchange hook is called after this state update. + * + * The before_schedchange hooks fulfill the task of drawing the visible objects that + * corresponds to the data accumulated by the after_schedchange hook. + * + * The after_schedchange hook accumulates the data that need to be shown on the screen + * (items) into a queue. Then, the next before_schedchange hook will draw what that + * queue contains. That's the Right Way (TM) of drawing items on the screen, + * because we need to draw the background first (and then add icons, text, ... + * over it), but we only know the length of a background region once the state + * corresponding to it is over, which happens to be at the next before_schedchange + * hook. + * + * We also have a hook called at the end of a chunk to draw the information left + * undrawn in each process queue. We use the current time as end of + * line/background. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +//#define PANGO_ENABLE_BACKEND +#include +#include +#include +#include +#include +#include + +//#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +#include "eventhooks.h" +#include "cfv.h" +#include "processlist.h" +#include "drawing.h" + + +#define MAX_PATH_LEN 256 +#define STATE_LINE_WIDTH 6 +#define COLLISION_POSITION(height) (((height - STATE_LINE_WIDTH)/2) -3) + +extern GSList *g_legend_list; + + +/* Action to do when background computation completed. + * + * Wait for all the awaited computations to be over. + */ + +static gint background_ready(void *hook_data, void *call_data) +{ + ControlFlowData *resourceview_data = (ControlFlowData *)hook_data; + LttvTrace *trace = (LttvTrace*)call_data; + + resourceview_data->background_info_waiting--; + + if(resourceview_data->background_info_waiting == 0) { + g_message("control flow viewer : background computation data ready."); + + drawing_clear(resourceview_data->drawing); + processlist_clear(resourceview_data->process_list); + gtk_widget_set_size_request( + resourceview_data->drawing->drawing_area, + -1, processlist_get_height(resourceview_data->process_list)); + redraw_notify(resourceview_data, NULL); + } + + return 0; +} + + +/* Request background computation. Verify if it is in progress or ready first. + * Only for each trace in the tab's traceset. + */ +static void request_background_data(ControlFlowData *resourceview_data) +{ + LttvTracesetContext * tsc = + lttvwindow_get_traceset_context(resourceview_data->tab); + gint num_traces = lttv_traceset_number(tsc->ts); + gint i; + LttvTrace *trace; + LttvTraceState *tstate; + + LttvHooks *background_ready_hook = + lttv_hooks_new(); + lttv_hooks_add(background_ready_hook, background_ready, resourceview_data, + LTTV_PRIO_DEFAULT); + resourceview_data->background_info_waiting = 0; + + for(i=0;its, i); + tstate = LTTV_TRACE_STATE(tsc->traces[i]); + + if(lttvwindowtraces_get_ready(g_quark_from_string("state"),trace)==FALSE + && !tstate->has_precomputed_states) { + + if(lttvwindowtraces_get_in_progress(g_quark_from_string("state"), + trace) == FALSE) { + /* We first remove requests that could have been done for the same + * information. Happens when two viewers ask for it before servicing + * starts. + */ + if(!lttvwindowtraces_background_request_find(trace, "state")) + lttvwindowtraces_background_request_queue( + main_window_get_widget(resourceview_data->tab), trace, "state"); + lttvwindowtraces_background_notify_queue(resourceview_data, + trace, + ltt_time_infinite, + NULL, + background_ready_hook); + resourceview_data->background_info_waiting++; + } else { /* in progress */ + + lttvwindowtraces_background_notify_current(resourceview_data, + trace, + ltt_time_infinite, + NULL, + background_ready_hook); + resourceview_data->background_info_waiting++; + } + } else { + /* Data ready. By its nature, this viewer doesn't need to have + * its data ready hook called there, because a background + * request is always linked with a redraw. + */ + } + } + + lttv_hooks_destroy(background_ready_hook); +} + + +/** + * Event Viewer's constructor hook + * + * This constructor is given as a parameter to the menuitem and toolbar button + * registration. It creates the list. + * @param tab A pointer to the parent tab. + * @return The widget created. + */ +GtkWidget * +h_resourceview(LttvPlugin *plugin) +{ + LttvPluginTab *ptab = LTTV_PLUGIN_TAB(plugin); + Tab *tab = ptab->tab; + g_info("h_guicontrolflow, %p", tab); + ControlFlowData *resourceview_data = resourceview(ptab); + + resourceview_data->tab = tab; + + // Unreg done in the GuiControlFlow_Destructor + lttvwindow_register_traceset_notify(tab, + traceset_notify, + resourceview_data); + + lttvwindow_register_time_window_notify(tab, + update_time_window_hook, + resourceview_data); + lttvwindow_register_current_time_notify(tab, + update_current_time_hook, + resourceview_data); + lttvwindow_register_redraw_notify(tab, + redraw_notify, + resourceview_data); + lttvwindow_register_continue_notify(tab, + continue_notify, + resourceview_data); + request_background_data(resourceview_data); + + + return guicontrolflow_get_widget(resourceview_data) ; + +} + +void legend_destructor(GtkWindow *legend) +{ + g_legend_list = g_slist_remove(g_legend_list, legend); +} + +/* Create a popup legend */ +GtkWidget * +h_legend(LttvPlugin *plugin) +{ + LttvPluginTab *ptab = LTTV_PLUGIN_TAB(plugin); + Tab *tab = ptab->tab; + g_info("h_legend, %p", tab); + + GtkWindow *legend = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); + + g_legend_list = g_slist_append( + g_legend_list, + legend); + + g_object_set_data_full( + G_OBJECT(legend), + "legend", + legend, + (GDestroyNotify)legend_destructor); + + gtk_window_set_title(legend, "Control Flow View Legend"); + + GtkWidget *pixmap = create_pixmap(GTK_WIDGET(legend), "lttv-color-list.png"); + + gtk_container_add(GTK_CONTAINER(legend), GTK_WIDGET(pixmap)); + + gtk_widget_show(GTK_WIDGET(pixmap)); + gtk_widget_show(GTK_WIDGET(legend)); + + + return NULL; /* This is a popup window */ +} + + +int event_selected_hook(void *hook_data, void *call_data) +{ + ControlFlowData *resourceview_data = (ControlFlowData*) hook_data; + guint *event_number = (guint*) call_data; + + g_debug("DEBUG : event selected by main window : %u", *event_number); + + return 0; +} + +static void cpu_set_line_color(PropertiesLine *prop_line, LttvCPUState *s) +{ + GQuark present_state; + + if(s->mode_stack->len == 0) + present_state = LTTV_CPU_UNKNOWN; + else + present_state = ((GQuark*)s->mode_stack->data)[s->mode_stack->len-1]; + + if(present_state == LTTV_CPU_IDLE) { + prop_line->color = drawing_colors_cpu[COL_CPU_IDLE]; + } + else if(present_state == LTTV_CPU_BUSY) { + prop_line->color = drawing_colors_cpu[COL_CPU_BUSY]; + } + else if(present_state == LTTV_CPU_IRQ) { + prop_line->color = drawing_colors_cpu[COL_CPU_IRQ]; + } + else if(present_state == LTTV_CPU_SOFT_IRQ) { + prop_line->color = drawing_colors_cpu[COL_CPU_SOFT_IRQ]; + } + else if(present_state == LTTV_CPU_TRAP) { + prop_line->color = drawing_colors_cpu[COL_CPU_TRAP]; + } else { + prop_line->color = drawing_colors_cpu[COL_CPU_UNKNOWN]; + } +} + +static void irq_set_line_color(PropertiesLine *prop_line, LttvIRQState *s) +{ + GQuark present_state; + if(s->mode_stack->len == 0) + present_state = LTTV_IRQ_UNKNOWN; + else + present_state = ((GQuark*)s->mode_stack->data)[s->mode_stack->len-1]; + + if(present_state == LTTV_IRQ_IDLE) { + prop_line->color = drawing_colors_irq[COL_IRQ_IDLE]; + } + else if(present_state == LTTV_IRQ_BUSY) { + prop_line->color = drawing_colors_irq[COL_IRQ_BUSY]; + } + else { + prop_line->color = drawing_colors_irq[COL_IRQ_UNKNOWN]; + } +} + +static void soft_irq_set_line_color(PropertiesLine *prop_line, LttvSoftIRQState *s) +{ + GQuark present_state; + if(s->running) + prop_line->color = drawing_colors_soft_irq[COL_SOFT_IRQ_BUSY]; + else if(s->pending) + prop_line->color = drawing_colors_soft_irq[COL_SOFT_IRQ_PENDING]; + else + prop_line->color = drawing_colors_soft_irq[COL_SOFT_IRQ_IDLE]; +} + +static void trap_set_line_color(PropertiesLine *prop_line, LttvTrapState *s) +{ + GQuark present_state; + if(s->running == 0) + prop_line->color = drawing_colors_trap[COL_TRAP_IDLE]; + else + prop_line->color = drawing_colors_trap[COL_TRAP_BUSY]; +} + +static void bdev_set_line_color(PropertiesLine *prop_line, LttvBdevState *s) +{ + GQuark present_state; + if(s == 0 || s->mode_stack->len == 0) + present_state = LTTV_BDEV_UNKNOWN; + else + present_state = ((GQuark*)s->mode_stack->data)[s->mode_stack->len-1]; + + if(present_state == LTTV_BDEV_IDLE) { + prop_line->color = drawing_colors_bdev[COL_BDEV_IDLE]; + } + else if(present_state == LTTV_BDEV_BUSY_READING) { + prop_line->color = drawing_colors_bdev[COL_BDEV_BUSY_READING]; + } + else if(present_state == LTTV_BDEV_BUSY_WRITING) { + prop_line->color = drawing_colors_bdev[COL_BDEV_BUSY_WRITING]; + } + else { + prop_line->color = drawing_colors_bdev[COL_BDEV_UNKNOWN]; + } +} + +/* before_schedchange_hook + * + * This function basically draw lines and icons. Two types of lines are drawn : + * one small (3 pixels?) representing the state of the process and the second + * type is thicker (10 pixels?) representing on which CPU a process is running + * (and this only in running state). + * + * Extremums of the lines : + * x_min : time of the last event context for this process kept in memory. + * x_max : time of the current event. + * y : middle of the process in the process list. The process is found in the + * list, therefore is it's position in pixels. + * + * The choice of lines'color is defined by the context of the last event for this + * process. + */ + + +int before_schedchange_hook(void *hook_data, void *call_data) +{ + LttvTraceHook *th = (LttvTraceHook*)hook_data; + EventsRequest *events_request = (EventsRequest*)th->hook_data; + ControlFlowData *resourceview_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + gint target_pid_saved = tfc->target_pid; + + LttTime evtime = ltt_event_time(e); + LttvFilter *filter = resourceview_data->filter; + + /* we are in a schedchange, before the state update. We must draw the + * items corresponding to the state before it changes : now is the right + * time to do it. + */ + + guint pid_out; + guint pid_in; + pid_out = ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 0)); + pid_in = ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 1)); +// TODO: can't we reenable this? pmf +// if(pid_in != 0 && pid_out != 0) { +// /* not a transition to/from idle */ +// return 0; +// } + + tfc->target_pid = pid_out; + + guint cpu = tfs->cpu; + + guint trace_num = ts->parent.index; + /* Add process to process list (if not present) */ + guint pl_height = 0; + HashedResourceData *hashed_process_data = NULL; + ProcessList *process_list = resourceview_data->process_list; + + hashed_process_data = resourcelist_obtain_cpu(resourceview_data, trace_num, cpu); + + /* Now, the process is in the state hash and our own process hash. + * We definitely can draw the items related to the ending state. + */ + + if(ltt_time_compare(hashed_process_data->next_good_time, + evtime) > 0) + { + if(hashed_process_data->x.middle_marked == FALSE) { + + TimeWindow time_window = + lttvwindow_get_time_window(resourceview_data->tab); +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = resourceview_data->drawing; + guint width = drawing->width; + guint x; + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + } else { + TimeWindow time_window = + lttvwindow_get_time_window(resourceview_data->tab); +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = resourceview_data->drawing; + guint width = drawing->width; + guint x; + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + /* Jump over draw if we are at the same x position */ + if(x == hashed_process_data->x.middle && + hashed_process_data->x.middle_used) + { + if(hashed_process_data->x.middle_marked == FALSE) { + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + /* jump */ + } else { + DrawContext draw_context; + + /* Now create the drawing context that will be used to draw + * items related to the last state. */ + draw_context.drawable = hashed_process_data->pixmap; + draw_context.gc = drawing->gc; + draw_context.pango_layout = drawing->pango_layout; + draw_context.drawinfo.start.x = hashed_process_data->x.middle; + draw_context.drawinfo.end.x = x; + + draw_context.drawinfo.y.over = 1; + draw_context.drawinfo.y.middle = (hashed_process_data->height/2); + draw_context.drawinfo.y.under = hashed_process_data->height; + + draw_context.drawinfo.start.offset.over = 0; + draw_context.drawinfo.start.offset.middle = 0; + draw_context.drawinfo.start.offset.under = 0; + draw_context.drawinfo.end.offset.over = 0; + draw_context.drawinfo.end.offset.middle = 0; + draw_context.drawinfo.end.offset.under = 0; + + { + /* Draw the line */ + //PropertiesLine prop_line = prepare_s_e_line(process); + PropertiesLine prop_line; + prop_line.line_width = STATE_LINE_WIDTH; + prop_line.style = GDK_LINE_SOLID; + prop_line.y = MIDDLE; + cpu_set_line_color(&prop_line, tfs->cpu_state); + draw_line((void*)&prop_line, (void*)&draw_context); + + } + /* become the last x position */ + hashed_process_data->x.middle = x; + hashed_process_data->x.middle_used = TRUE; + hashed_process_data->x.middle_marked = FALSE; + + /* Calculate the next good time */ + convert_pixels_to_time(width, x+1, time_window, + &hashed_process_data->next_good_time); + } + } + + return 0; +} + +/* after_schedchange_hook + * + * The draw after hook is called by the reading API to have a + * particular event drawn on the screen. + * @param hook_data ControlFlowData structure of the viewer. + * @param call_data Event context. + * + * This function adds items to be drawn in a queue for each process. + * + */ +int after_schedchange_hook(void *hook_data, void *call_data) +{ + LttvTraceHook *th = (LttvTraceHook*)hook_data; + EventsRequest *events_request = (EventsRequest*)th->hook_data; + ControlFlowData *resourceview_data = events_request->viewer_data; + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + LttEvent *e; + + e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *filter = resourceview_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + LttTime evtime = ltt_event_time(e); + + /* Add process to process list (if not present) */ + LttvProcessState *process_in; + LttTime birth; + guint pl_height = 0; + HashedResourceData *hashed_process_data_in = NULL; + + ProcessList *process_list = resourceview_data->process_list; + + guint pid_in; + { + guint pid_out; + pid_out = ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 0)); + pid_in = ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 1)); + } + + + /* Find process pid_in in the list... */ + //process_in = lttv_state_find_process(ts, ANY_CPU, pid_in); + //process_in = tfs->process; + guint cpu = tfs->cpu; + guint trace_num = ts->parent.index; + process_in = ts->running_process[cpu]; + /* It should exist, because we are after the state update. */ +#ifdef EXTRA_CHECK + g_assert(process_in != NULL); +#endif //EXTRA_CHECK + birth = process_in->creation_time; + + //hashed_process_data_in = processlist_get_process_data(process_list, cpuq, trace_num); + hashed_process_data_in = resourcelist_obtain_cpu(resourceview_data, trace_num, cpu); + + /* Set the current process */ + process_list->current_hash_data[trace_num][process_in->cpu] = + hashed_process_data_in; + + if(ltt_time_compare(hashed_process_data_in->next_good_time, + evtime) <= 0) + { + TimeWindow time_window = + lttvwindow_get_time_window(resourceview_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = resourceview_data->drawing; + guint width = drawing->width; + guint new_x; + + convert_time_to_pixels( + time_window, + evtime, + width, + &new_x); + + if(hashed_process_data_in->x.middle != new_x) { + hashed_process_data_in->x.middle = new_x; + hashed_process_data_in->x.middle_used = FALSE; + hashed_process_data_in->x.middle_marked = FALSE; + } + } + return 0; +} + +/* before_execmode_hook + * + * This function basically draw lines and icons. Two types of lines are drawn : + * one small (3 pixels?) representing the state of the process and the second + * type is thicker (10 pixels?) representing on which CPU a process is running + * (and this only in running state). + * + * Extremums of the lines : + * x_min : time of the last event context for this process kept in memory. + * x_max : time of the current event. + * y : middle of the process in the process list. The process is found in the + * list, therefore is it's position in pixels. + * + * The choice of lines'color is defined by the context of the last event for this + * process. + */ + +int before_execmode_hook(void *hook_data, void *call_data) +{ + LttvTraceHook *th = (LttvTraceHook*)hook_data; + EventsRequest *events_request = (EventsRequest*)th->hook_data; + ControlFlowData *resourceview_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttTime evtime = ltt_event_time(e); + + before_execmode_hook_irq(hook_data, call_data); + before_execmode_hook_soft_irq(hook_data, call_data); + before_execmode_hook_trap(hook_data, call_data); + + /* we are in a execmode, before the state update. We must draw the + * items corresponding to the state before it changes : now is the right + * time to do it. + */ + /* For the pid */ + guint cpu = tfs->cpu; + + guint trace_num = ts->parent.index; + LttvProcessState *process = ts->running_process[cpu]; + g_assert(process != NULL); + + /* Well, the process_out existed : we must get it in the process hash + * or add it, and draw its items. + */ + /* Add process to process list (if not present) */ + guint pl_height = 0; + HashedResourceData *hashed_process_data = NULL; + ProcessList *process_list = resourceview_data->process_list; + + LttTime birth = process->creation_time; + + if(likely(process_list->current_hash_data[trace_num][cpu] != NULL)) { + hashed_process_data = process_list->current_hash_data[trace_num][cpu]; + } else { + hashed_process_data = resourcelist_obtain_cpu(resourceview_data, trace_num, cpu); + + /* Set the current process */ + process_list->current_hash_data[trace_num][process->cpu] = + hashed_process_data; + } + + /* Now, the process is in the state hash and our own process hash. + * We definitely can draw the items related to the ending state. + */ + + if(likely(ltt_time_compare(hashed_process_data->next_good_time, + evtime) > 0)) + { + if(unlikely(hashed_process_data->x.middle_marked == FALSE)) { + TimeWindow time_window = + lttvwindow_get_time_window(resourceview_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = resourceview_data->drawing; + guint width = drawing->width; + guint x; + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + } + else { + TimeWindow time_window = + lttvwindow_get_time_window(resourceview_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = resourceview_data->drawing; + guint width = drawing->width; + guint x; + + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + + /* Jump over draw if we are at the same x position */ + if(unlikely(x == hashed_process_data->x.middle && + hashed_process_data->x.middle_used)) + { + if(unlikely(hashed_process_data->x.middle_marked == FALSE)) { + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + /* jump */ + } + else { + + DrawContext draw_context; + /* Now create the drawing context that will be used to draw + * items related to the last state. */ + draw_context.drawable = hashed_process_data->pixmap; + draw_context.gc = drawing->gc; + draw_context.pango_layout = drawing->pango_layout; + draw_context.drawinfo.start.x = hashed_process_data->x.middle; + draw_context.drawinfo.end.x = x; + + draw_context.drawinfo.y.over = 1; + draw_context.drawinfo.y.middle = (hashed_process_data->height/2); + draw_context.drawinfo.y.under = hashed_process_data->height; + + draw_context.drawinfo.start.offset.over = 0; + draw_context.drawinfo.start.offset.middle = 0; + draw_context.drawinfo.start.offset.under = 0; + draw_context.drawinfo.end.offset.over = 0; + draw_context.drawinfo.end.offset.middle = 0; + draw_context.drawinfo.end.offset.under = 0; + + { + /* Draw the line */ + PropertiesLine prop_line; + prop_line.line_width = STATE_LINE_WIDTH; + prop_line.style = GDK_LINE_SOLID; + prop_line.y = MIDDLE; + cpu_set_line_color(&prop_line, tfs->cpu_state); + draw_line((void*)&prop_line, (void*)&draw_context); + } + /* become the last x position */ + hashed_process_data->x.middle = x; + hashed_process_data->x.middle_used = TRUE; + hashed_process_data->x.middle_marked = FALSE; + + /* Calculate the next good time */ + convert_pixels_to_time(width, x+1, time_window, + &hashed_process_data->next_good_time); + } + } + + return 0; +} + +int before_execmode_hook_irq(void *hook_data, void *call_data) +{ + LttvTraceHook *th = (LttvTraceHook*)hook_data; + EventsRequest *events_request = (EventsRequest*)th->hook_data; + ControlFlowData *resourceview_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttTime evtime = ltt_event_time(e); + + LttTrace *trace = tfc->t_context->t; + + /* we are in a execmode, before the state update. We must draw the + * items corresponding to the state before it changes : now is the right + * time to do it. + */ + /* For the pid */ + + guint64 irq; + guint cpu = tfs->cpu; + + guint16 ev_id_entry = marker_get_id_from_info(trace, marker_get_info_from_name(trace, lttv_merge_facility_event_name(LTT_FACILITY_KERNEL, LTT_EVENT_IRQ_ENTRY))); + guint16 ev_id_exit = marker_get_id_from_info(trace, marker_get_info_from_name(trace, lttv_merge_facility_event_name(LTT_FACILITY_KERNEL, LTT_EVENT_IRQ_EXIT))); + if(ev_id_entry == e->event_id) { + irq = ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 0)); + } + else if(ev_id_exit == e->event_id) { + irq = ts->cpu_states[cpu].last_irq; + } + else { + return 0; + } + + guint trace_num = ts->parent.index; + + /* Well, the process_out existed : we must get it in the process hash + * or add it, and draw its items. + */ + /* Add process to process list (if not present) */ + guint pl_height = 0; + HashedResourceData *hashed_process_data = NULL; + ProcessList *process_list = resourceview_data->process_list; + + hashed_process_data = resourcelist_obtain_irq(resourceview_data, trace_num, irq); + // TODO: fix this, it's ugly and slow: + GQuark name; + { + gchar *str; + str = g_strdup_printf("IRQ %llu [%s]", irq, (char*)g_quark_to_string(ts->irq_names[irq])); + name = g_quark_from_string(str); + g_free(str); + } + gtk_tree_store_set(resourceview_data->process_list->list_store, &hashed_process_data->y_iter, NAME_COLUMN, g_quark_to_string(name), -1); + + /* Now, the process is in the state hash and our own process hash. + * We definitely can draw the items related to the ending state. + */ + + if(likely(ltt_time_compare(hashed_process_data->next_good_time, + evtime) > 0)) + { + if(unlikely(hashed_process_data->x.middle_marked == FALSE)) { + TimeWindow time_window = + lttvwindow_get_time_window(resourceview_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = resourceview_data->drawing; + guint width = drawing->width; + guint x; + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + } + else { + TimeWindow time_window = + lttvwindow_get_time_window(resourceview_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = resourceview_data->drawing; + guint width = drawing->width; + guint x; + + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + + /* Jump over draw if we are at the same x position */ + if(unlikely(x == hashed_process_data->x.middle && + hashed_process_data->x.middle_used)) + { + if(unlikely(hashed_process_data->x.middle_marked == FALSE)) { + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + /* jump */ + } + else { + + DrawContext draw_context; + /* Now create the drawing context that will be used to draw + * items related to the last state. */ + draw_context.drawable = hashed_process_data->pixmap; + draw_context.gc = drawing->gc; + draw_context.pango_layout = drawing->pango_layout; + draw_context.drawinfo.start.x = hashed_process_data->x.middle; + draw_context.drawinfo.end.x = x; + + draw_context.drawinfo.y.over = 1; + draw_context.drawinfo.y.middle = (hashed_process_data->height/2); + draw_context.drawinfo.y.under = hashed_process_data->height; + + draw_context.drawinfo.start.offset.over = 0; + draw_context.drawinfo.start.offset.middle = 0; + draw_context.drawinfo.start.offset.under = 0; + draw_context.drawinfo.end.offset.over = 0; + draw_context.drawinfo.end.offset.middle = 0; + draw_context.drawinfo.end.offset.under = 0; + + { + /* Draw the line */ + PropertiesLine prop_line; + prop_line.line_width = STATE_LINE_WIDTH; + prop_line.style = GDK_LINE_SOLID; + prop_line.y = MIDDLE; + irq_set_line_color(&prop_line, &ts->irq_states[irq]); + draw_line((void*)&prop_line, (void*)&draw_context); + } + /* become the last x position */ + hashed_process_data->x.middle = x; + hashed_process_data->x.middle_used = TRUE; + hashed_process_data->x.middle_marked = FALSE; + + /* Calculate the next good time */ + convert_pixels_to_time(width, x+1, time_window, + &hashed_process_data->next_good_time); + } + } + + return 0; +} + +int before_execmode_hook_soft_irq(void *hook_data, void *call_data) +{ + LttvTraceHook *th = (LttvTraceHook*)hook_data; + EventsRequest *events_request = (EventsRequest*)th->hook_data; + ControlFlowData *resourceview_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttTime evtime = ltt_event_time(e); + + LttTrace *trace = tfc->t_context->t; + + /* we are in a execmode, before the state update. We must draw the + * items corresponding to the state before it changes : now is the right + * time to do it. + */ + /* For the pid */ + + guint64 softirq; + guint cpu = tfs->cpu; + + guint16 ev_id_raise = marker_get_id_from_info(trace, marker_get_info_from_name(trace, lttv_merge_facility_event_name(LTT_FACILITY_KERNEL, LTT_EVENT_SOFT_IRQ_RAISE))); + guint16 ev_id_entry = marker_get_id_from_info(trace, marker_get_info_from_name(trace, lttv_merge_facility_event_name(LTT_FACILITY_KERNEL, LTT_EVENT_SOFT_IRQ_ENTRY))); + guint16 ev_id_exit = marker_get_id_from_info(trace, marker_get_info_from_name(trace, lttv_merge_facility_event_name(LTT_FACILITY_KERNEL, LTT_EVENT_SOFT_IRQ_EXIT))); + if(ev_id_entry == e->event_id || ev_id_raise == e->event_id) { + softirq = ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 0)); + } + else if(ev_id_exit == e->event_id) { + softirq = ts->cpu_states[cpu].last_soft_irq; + } + else { + return 0; + } + + guint trace_num = ts->parent.index; + + /* Well, the process_out existed : we must get it in the process hash + * or add it, and draw its items. + */ + /* Add process to process list (if not present) */ + guint pl_height = 0; + HashedResourceData *hashed_process_data = NULL; + ProcessList *process_list = resourceview_data->process_list; + + hashed_process_data = resourcelist_obtain_soft_irq(resourceview_data, trace_num, softirq); + + /* Now, the process is in the state hash and our own process hash. + * We definitely can draw the items related to the ending state. + */ + + if(likely(ltt_time_compare(hashed_process_data->next_good_time, + evtime) > 0)) + { + if(unlikely(hashed_process_data->x.middle_marked == FALSE)) { + TimeWindow time_window = + lttvwindow_get_time_window(resourceview_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = resourceview_data->drawing; + guint width = drawing->width; + guint x; + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + } + else { + TimeWindow time_window = + lttvwindow_get_time_window(resourceview_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = resourceview_data->drawing; + guint width = drawing->width; + guint x; + + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + + /* Jump over draw if we are at the same x position */ + if(unlikely(x == hashed_process_data->x.middle && + hashed_process_data->x.middle_used)) + { + if(unlikely(hashed_process_data->x.middle_marked == FALSE)) { + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + /* jump */ + } + else { + + DrawContext draw_context; + /* Now create the drawing context that will be used to draw + * items related to the last state. */ + draw_context.drawable = hashed_process_data->pixmap; + draw_context.gc = drawing->gc; + draw_context.pango_layout = drawing->pango_layout; + draw_context.drawinfo.start.x = hashed_process_data->x.middle; + draw_context.drawinfo.end.x = x; + + draw_context.drawinfo.y.over = 1; + draw_context.drawinfo.y.middle = (hashed_process_data->height/2); + draw_context.drawinfo.y.under = hashed_process_data->height; + + draw_context.drawinfo.start.offset.over = 0; + draw_context.drawinfo.start.offset.middle = 0; + draw_context.drawinfo.start.offset.under = 0; + draw_context.drawinfo.end.offset.over = 0; + draw_context.drawinfo.end.offset.middle = 0; + draw_context.drawinfo.end.offset.under = 0; + + { + /* Draw the line */ + PropertiesLine prop_line; + prop_line.line_width = STATE_LINE_WIDTH; + prop_line.style = GDK_LINE_SOLID; + prop_line.y = MIDDLE; + soft_irq_set_line_color(&prop_line, &ts->soft_irq_states[softirq]); + draw_line((void*)&prop_line, (void*)&draw_context); + } + /* become the last x position */ + hashed_process_data->x.middle = x; + hashed_process_data->x.middle_used = TRUE; + hashed_process_data->x.middle_marked = FALSE; + + /* Calculate the next good time */ + convert_pixels_to_time(width, x+1, time_window, + &hashed_process_data->next_good_time); + } + } + + return 0; +} + +int before_execmode_hook_trap(void *hook_data, void *call_data) +{ + LttvTraceHook *th = (LttvTraceHook*)hook_data; + EventsRequest *events_request = (EventsRequest*)th->hook_data; + ControlFlowData *resourceview_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttTime evtime = ltt_event_time(e); + + LttTrace *trace = tfc->t_context->t; + + /* we are in a execmode, before the state update. We must draw the + * items corresponding to the state before it changes : now is the right + * time to do it. + */ + /* For the pid */ + + guint64 trap; + guint cpu = tfs->cpu; + + guint16 ev_id_entry = marker_get_id_from_info(trace, marker_get_info_from_name(trace, lttv_merge_facility_event_name(LTT_FACILITY_KERNEL_ARCH, LTT_EVENT_TRAP_ENTRY))); + guint16 ev_id_exit = marker_get_id_from_info(trace, marker_get_info_from_name(trace, lttv_merge_facility_event_name(LTT_FACILITY_KERNEL_ARCH, LTT_EVENT_TRAP_EXIT))); + if(ev_id_entry == e->event_id) { + trap = ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 0)); + } + else if(ev_id_exit == e->event_id) { + trap = ts->cpu_states[cpu].last_trap; + } + else { + return 0; + } + + guint trace_num = ts->parent.index; + + /* Well, the process_out existed : we must get it in the process hash + * or add it, and draw its items. + */ + /* Add process to process list (if not present) */ + guint pl_height = 0; + HashedResourceData *hashed_process_data = NULL; + ProcessList *process_list = resourceview_data->process_list; + + hashed_process_data = resourcelist_obtain_trap(resourceview_data, trace_num, trap); + + /* Now, the process is in the state hash and our own process hash. + * We definitely can draw the items related to the ending state. + */ + + if(likely(ltt_time_compare(hashed_process_data->next_good_time, + evtime) > 0)) + { + if(unlikely(hashed_process_data->x.middle_marked == FALSE)) { + TimeWindow time_window = + lttvwindow_get_time_window(resourceview_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = resourceview_data->drawing; + guint width = drawing->width; + guint x; + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + } + else { + TimeWindow time_window = + lttvwindow_get_time_window(resourceview_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = resourceview_data->drawing; + guint width = drawing->width; + guint x; + + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + + /* Jump over draw if we are at the same x position */ + if(unlikely(x == hashed_process_data->x.middle && + hashed_process_data->x.middle_used)) + { + if(unlikely(hashed_process_data->x.middle_marked == FALSE)) { + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + /* jump */ + } + else { + + DrawContext draw_context; + /* Now create the drawing context that will be used to draw + * items related to the last state. */ + draw_context.drawable = hashed_process_data->pixmap; + draw_context.gc = drawing->gc; + draw_context.pango_layout = drawing->pango_layout; + draw_context.drawinfo.start.x = hashed_process_data->x.middle; + draw_context.drawinfo.end.x = x; + + draw_context.drawinfo.y.over = 1; + draw_context.drawinfo.y.middle = (hashed_process_data->height/2); + draw_context.drawinfo.y.under = hashed_process_data->height; + + draw_context.drawinfo.start.offset.over = 0; + draw_context.drawinfo.start.offset.middle = 0; + draw_context.drawinfo.start.offset.under = 0; + draw_context.drawinfo.end.offset.over = 0; + draw_context.drawinfo.end.offset.middle = 0; + draw_context.drawinfo.end.offset.under = 0; + + { + /* Draw the line */ + PropertiesLine prop_line; + prop_line.line_width = STATE_LINE_WIDTH; + prop_line.style = GDK_LINE_SOLID; + prop_line.y = MIDDLE; + trap_set_line_color(&prop_line, &ts->trap_states[trap]); + draw_line((void*)&prop_line, (void*)&draw_context); + } + /* become the last x position */ + hashed_process_data->x.middle = x; + hashed_process_data->x.middle_used = TRUE; + hashed_process_data->x.middle_marked = FALSE; + + /* Calculate the next good time */ + convert_pixels_to_time(width, x+1, time_window, + &hashed_process_data->next_good_time); + } + } + + return 0; +} + + +int before_bdev_event_hook(void *hook_data, void *call_data) +{ + LttvTraceHook *th = (LttvTraceHook*)hook_data; + EventsRequest *events_request = (EventsRequest*)th->hook_data; + ControlFlowData *resourceview_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttTime evtime = ltt_event_time(e); + + /* we are in a execmode, before the state update. We must draw the + * items corresponding to the state before it changes : now is the right + * time to do it. + */ + /* For the pid */ + + guint cpu = tfs->cpu; + guint8 major = ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 0)); + guint8 minor = ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 1)); + guint oper = ltt_event_get_long_unsigned(e, lttv_trace_get_hook_field(th, 2)); + gint devcode_gint = MKDEV(major,minor); + + guint trace_num = ts->parent.index; + + LttvBdevState *bdev = g_hash_table_lookup(ts->bdev_states, &devcode_gint); + /* the result of the lookup might be NULL. that's ok, the rest of the function + should understand it was not found and that its state is unknown */ + + /* Well, the process_out existed : we must get it in the process hash + * or add it, and draw its items. + */ + /* Add process to process list (if not present) */ + guint pl_height = 0; + HashedResourceData *hashed_process_data = NULL; + ProcessList *process_list = resourceview_data->process_list; +// LttTime birth = process->creation_time; + +// if(likely(process_list->current_hash_data[trace_num][cpu] != NULL)) { +// hashed_process_data = process_list->current_hash_data[trace_num][cpu]; +// } else { + hashed_process_data = resourcelist_obtain_bdev(resourceview_data, trace_num, devcode_gint); + ////hashed_process_data = processlist_get_process_data(process_list, resourceq, trace_num); +// hashed_process_data = processlist_get_process_data(process_list, +// pid, +// process->cpu, +// &birth, +// trace_num); +// + /* Set the current process */ +// process_list->current_hash_data[trace_num][process->cpu] = +// hashed_process_data; +// } + + /* Now, the process is in the state hash and our own process hash. + * We definitely can draw the items related to the ending state. + */ + + if(likely(ltt_time_compare(hashed_process_data->next_good_time, + evtime) > 0)) + { + if(unlikely(hashed_process_data->x.middle_marked == FALSE)) { + TimeWindow time_window = + lttvwindow_get_time_window(resourceview_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = resourceview_data->drawing; + guint width = drawing->width; + guint x; + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + } + else { + TimeWindow time_window = + lttvwindow_get_time_window(resourceview_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = resourceview_data->drawing; + guint width = drawing->width; + guint x; + + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + + /* Jump over draw if we are at the same x position */ + if(unlikely(x == hashed_process_data->x.middle && + hashed_process_data->x.middle_used)) + { + if(unlikely(hashed_process_data->x.middle_marked == FALSE)) { + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + /* jump */ + } + else { + + DrawContext draw_context; + /* Now create the drawing context that will be used to draw + * items related to the last state. */ + draw_context.drawable = hashed_process_data->pixmap; + draw_context.gc = drawing->gc; + draw_context.pango_layout = drawing->pango_layout; + draw_context.drawinfo.start.x = hashed_process_data->x.middle; + draw_context.drawinfo.end.x = x; + + draw_context.drawinfo.y.over = 1; + draw_context.drawinfo.y.middle = (hashed_process_data->height/2); + draw_context.drawinfo.y.under = hashed_process_data->height; + + draw_context.drawinfo.start.offset.over = 0; + draw_context.drawinfo.start.offset.middle = 0; + draw_context.drawinfo.start.offset.under = 0; + draw_context.drawinfo.end.offset.over = 0; + draw_context.drawinfo.end.offset.middle = 0; + draw_context.drawinfo.end.offset.under = 0; + + { + /* Draw the line */ + PropertiesLine prop_line; + prop_line.line_width = STATE_LINE_WIDTH; + prop_line.style = GDK_LINE_SOLID; + prop_line.y = MIDDLE; + bdev_set_line_color(&prop_line, bdev); + draw_line((void*)&prop_line, (void*)&draw_context); + } + /* become the last x position */ + hashed_process_data->x.middle = x; + hashed_process_data->x.middle_used = TRUE; + hashed_process_data->x.middle_marked = FALSE; + + /* Calculate the next good time */ + convert_pixels_to_time(width, x+1, time_window, + &hashed_process_data->next_good_time); + } + } + + return 0; +} + +gint update_time_window_hook(void *hook_data, void *call_data) +{ + ControlFlowData *resourceview_data = (ControlFlowData*) hook_data; + Drawing_t *drawing = resourceview_data->drawing; + ProcessList *process_list = resourceview_data->process_list; + + const TimeWindowNotifyData *time_window_nofify_data = + ((const TimeWindowNotifyData *)call_data); + + TimeWindow *old_time_window = + time_window_nofify_data->old_time_window; + TimeWindow *new_time_window = + time_window_nofify_data->new_time_window; + + /* Update the ruler */ + drawing_update_ruler(resourceview_data->drawing, + new_time_window); + + + /* Two cases : zoom in/out or scrolling */ + + /* In order to make sure we can reuse the old drawing, the scale must + * be the same and the new time interval being partly located in the + * currently shown time interval. (reuse is only for scrolling) + */ + + g_info("Old time window HOOK : %lu, %lu to %lu, %lu", + old_time_window->start_time.tv_sec, + old_time_window->start_time.tv_nsec, + old_time_window->time_width.tv_sec, + old_time_window->time_width.tv_nsec); + + g_info("New time window HOOK : %lu, %lu to %lu, %lu", + new_time_window->start_time.tv_sec, + new_time_window->start_time.tv_nsec, + new_time_window->time_width.tv_sec, + new_time_window->time_width.tv_nsec); + + if( new_time_window->time_width.tv_sec == old_time_window->time_width.tv_sec + && new_time_window->time_width.tv_nsec == old_time_window->time_width.tv_nsec) + { + /* Same scale (scrolling) */ + g_info("scrolling"); + LttTime *ns = &new_time_window->start_time; + LttTime *nw = &new_time_window->time_width; + LttTime *os = &old_time_window->start_time; + LttTime *ow = &old_time_window->time_width; + LttTime old_end = old_time_window->end_time; + LttTime new_end = new_time_window->end_time; + //if(nsdrawing->width; + convert_time_to_pixels( + *old_time_window, + *ns, + width, + &x); + + /* Copy old data to new location */ + copy_pixmap_region(process_list, + NULL, + resourceview_data->drawing->drawing_area->style->black_gc, + NULL, + x, 0, + 0, 0, + resourceview_data->drawing->width-x+SAFETY, -1); + + if(drawing->damage_begin == drawing->damage_end) + drawing->damage_begin = resourceview_data->drawing->width-x; + else + drawing->damage_begin = 0; + + drawing->damage_end = resourceview_data->drawing->width; + + /* Clear the data request background, but not SAFETY */ + rectangle_pixmap(process_list, + resourceview_data->drawing->drawing_area->style->black_gc, + TRUE, + drawing->damage_begin+SAFETY, 0, + drawing->damage_end - drawing->damage_begin, // do not overlap + -1); + gtk_widget_queue_draw(drawing->drawing_area); + + /* Get new data for the rest. */ + drawing_data_request(resourceview_data->drawing, + drawing->damage_begin, 0, + drawing->damage_end - drawing->damage_begin, + resourceview_data->drawing->height); + } else { + if(ltt_time_compare(*ns,*os) == -1 + && ltt_time_compare(*os,new_end) == -1) + { + g_info("scrolling near left"); + /* Scroll left, keep left part of the screen */ + guint x = 0; + guint width = resourceview_data->drawing->width; + convert_time_to_pixels( + *new_time_window, + *os, + width, + &x); + + /* Copy old data to new location */ + copy_pixmap_region (process_list, + NULL, + resourceview_data->drawing->drawing_area->style->black_gc, + NULL, + 0, 0, + x, 0, + -1, -1); + + if(drawing->damage_begin == drawing->damage_end) + drawing->damage_end = x; + else + drawing->damage_end = + resourceview_data->drawing->width; + + drawing->damage_begin = 0; + + rectangle_pixmap (process_list, + resourceview_data->drawing->drawing_area->style->black_gc, + TRUE, + drawing->damage_begin, 0, + drawing->damage_end - drawing->damage_begin, // do not overlap + -1); + + gtk_widget_queue_draw(drawing->drawing_area); + + /* Get new data for the rest. */ + drawing_data_request(resourceview_data->drawing, + drawing->damage_begin, 0, + drawing->damage_end - drawing->damage_begin, + resourceview_data->drawing->height); + + } else { + if(ltt_time_compare(*ns,*os) == 0) + { + g_info("not scrolling"); + } else { + g_info("scrolling far"); + /* Cannot reuse any part of the screen : far jump */ + + + rectangle_pixmap (process_list, + resourceview_data->drawing->drawing_area->style->black_gc, + TRUE, + 0, 0, + resourceview_data->drawing->width+SAFETY, // do not overlap + -1); + + gtk_widget_queue_draw(drawing->drawing_area); + + drawing->damage_begin = 0; + drawing->damage_end = resourceview_data->drawing->width; + + drawing_data_request(resourceview_data->drawing, + 0, 0, + resourceview_data->drawing->width, + resourceview_data->drawing->height); + + } + } + } + } else { + /* Different scale (zoom) */ + g_info("zoom"); + + rectangle_pixmap (process_list, + resourceview_data->drawing->drawing_area->style->black_gc, + TRUE, + 0, 0, + resourceview_data->drawing->width+SAFETY, // do not overlap + -1); + + gtk_widget_queue_draw(drawing->drawing_area); + + drawing->damage_begin = 0; + drawing->damage_end = resourceview_data->drawing->width; + + drawing_data_request(resourceview_data->drawing, + 0, 0, + resourceview_data->drawing->width, + resourceview_data->drawing->height); + } + + /* Update directly when scrolling */ + gdk_window_process_updates(resourceview_data->drawing->drawing_area->window, + TRUE); + + return 0; +} + +gint traceset_notify(void *hook_data, void *call_data) +{ + ControlFlowData *resourceview_data = (ControlFlowData*) hook_data; + Drawing_t *drawing = resourceview_data->drawing; + + if(unlikely(drawing->gc == NULL)) { + return FALSE; + } + if(drawing->dotted_gc == NULL) { + return FALSE; + } + + drawing_clear(resourceview_data->drawing); + processlist_clear(resourceview_data->process_list); + gtk_widget_set_size_request( + resourceview_data->drawing->drawing_area, + -1, processlist_get_height(resourceview_data->process_list)); + redraw_notify(resourceview_data, NULL); + + request_background_data(resourceview_data); + + return FALSE; +} + +gint redraw_notify(void *hook_data, void *call_data) +{ + ControlFlowData *resourceview_data = (ControlFlowData*) hook_data; + Drawing_t *drawing = resourceview_data->drawing; + GtkWidget *widget = drawing->drawing_area; + + drawing->damage_begin = 0; + drawing->damage_end = drawing->width; + + /* fun feature, to be separated someday... */ + drawing_clear(resourceview_data->drawing); + processlist_clear(resourceview_data->process_list); + gtk_widget_set_size_request( + resourceview_data->drawing->drawing_area, + -1, processlist_get_height(resourceview_data->process_list)); + // Clear the images + rectangle_pixmap (resourceview_data->process_list, + widget->style->black_gc, + TRUE, + 0, 0, + drawing->alloc_width, + -1); + + gtk_widget_queue_draw(drawing->drawing_area); + + if(drawing->damage_begin < drawing->damage_end) + { + drawing_data_request(drawing, + drawing->damage_begin, + 0, + drawing->damage_end-drawing->damage_begin, + drawing->height); + } + + return FALSE; + +} + + +gint continue_notify(void *hook_data, void *call_data) +{ + ControlFlowData *resourceview_data = (ControlFlowData*) hook_data; + Drawing_t *drawing = resourceview_data->drawing; + + if(drawing->damage_begin < drawing->damage_end) + { + drawing_data_request(drawing, + drawing->damage_begin, + 0, + drawing->damage_end-drawing->damage_begin, + drawing->height); + } + + return FALSE; +} + + +gint update_current_time_hook(void *hook_data, void *call_data) +{ + ControlFlowData *resourceview_data = (ControlFlowData*)hook_data; + Drawing_t *drawing = resourceview_data->drawing; + + LttTime current_time = *((LttTime*)call_data); + + TimeWindow time_window = + lttvwindow_get_time_window(resourceview_data->tab); + + LttTime time_begin = time_window.start_time; + LttTime width = time_window.time_width; + LttTime half_width; + { + guint64 time_ll = ltt_time_to_uint64(width); + time_ll = time_ll >> 1; /* divide by two */ + half_width = ltt_time_from_uint64(time_ll); + } + LttTime time_end = ltt_time_add(time_begin, width); + + LttvTracesetContext * tsc = + lttvwindow_get_traceset_context(resourceview_data->tab); + + LttTime trace_start = tsc->time_span.start_time; + LttTime trace_end = tsc->time_span.end_time; + + g_info("New current time HOOK : %lu, %lu", current_time.tv_sec, + current_time.tv_nsec); + + /* If current time is inside time interval, just move the highlight + * bar */ + + /* Else, we have to change the time interval. We have to tell it + * to the main window. */ + /* The time interval change will take care of placing the current + * time at the center of the visible area, or nearest possible if we are + * at one end of the trace. */ + + + if(ltt_time_compare(current_time, time_begin) < 0) + { + TimeWindow new_time_window; + + if(ltt_time_compare(current_time, + ltt_time_add(trace_start,half_width)) < 0) + time_begin = trace_start; + else + time_begin = ltt_time_sub(current_time,half_width); + + new_time_window.start_time = time_begin; + new_time_window.time_width = width; + new_time_window.time_width_double = ltt_time_to_double(width); + new_time_window.end_time = ltt_time_add(time_begin, width); + + lttvwindow_report_time_window(resourceview_data->tab, new_time_window); + } + else if(ltt_time_compare(current_time, time_end) > 0) + { + TimeWindow new_time_window; + + if(ltt_time_compare(current_time, ltt_time_sub(trace_end, half_width)) > 0) + time_begin = ltt_time_sub(trace_end,width); + else + time_begin = ltt_time_sub(current_time,half_width); + + new_time_window.start_time = time_begin; + new_time_window.time_width = width; + new_time_window.time_width_double = ltt_time_to_double(width); + new_time_window.end_time = ltt_time_add(time_begin, width); + + lttvwindow_report_time_window(resourceview_data->tab, new_time_window); + + } + gtk_widget_queue_draw(resourceview_data->drawing->drawing_area); + + /* Update directly when scrolling */ + gdk_window_process_updates(resourceview_data->drawing->drawing_area->window, + TRUE); + + return 0; +} + +typedef struct _ClosureData { + EventsRequest *events_request; + LttvTracesetState *tss; + LttTime end_time; + guint x_end; +} ClosureData; + +/* Draw line until end of the screen */ + +void draw_closure(gpointer key, gpointer value, gpointer user_data) +{ + ResourceUniqueNumeric *process_info = (ResourceUniqueNumeric*)key; + HashedResourceData *hashed_process_data = (HashedResourceData*)value; + ClosureData *closure_data = (ClosureData*)user_data; + + EventsRequest *events_request = closure_data->events_request; + ControlFlowData *resourceview_data = events_request->viewer_data; + + LttvTracesetState *tss = closure_data->tss; + LttvTracesetContext *tsc = (LttvTracesetContext*)tss; + + LttTime evtime = closure_data->end_time; + + gboolean dodraw = TRUE; + + if(hashed_process_data->type == RV_RESOURCE_MACHINE) + return; + + { + /* For the process */ + /* First, check if the current process is in the state computation + * process list. If it is there, that means we must add it right now and + * draw items from the beginning of the read for it. If it is not + * present, it's a new process and it was not present : it will + * be added after the state update. */ +#ifdef EXTRA_CHECK + g_assert(lttv_traceset_number(tsc->ts) > 0); +#endif //EXTRA_CHECK + LttvTraceContext *tc = tsc->traces[process_info->trace_num]; + LttvTraceState *ts = (LttvTraceState*)tc; + + /* Only draw for processes that are currently in the trace states */ + + ProcessList *process_list = resourceview_data->process_list; +#ifdef EXTRA_CHECK + /* Should be alike when background info is ready */ + if(resourceview_data->background_info_waiting==0) + g_assert(ltt_time_compare(process->creation_time, + process_info->birth) == 0); +#endif //EXTRA_CHECK + + /* Now, the process is in the state hash and our own process hash. + * We definitely can draw the items related to the ending state. + */ + + if(unlikely(ltt_time_compare(hashed_process_data->next_good_time, + evtime) <= 0)) + { + TimeWindow time_window = + lttvwindow_get_time_window(resourceview_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = resourceview_data->drawing; + guint width = drawing->width; + + guint x = closure_data->x_end; + + DrawContext draw_context; + + /* Now create the drawing context that will be used to draw + * items related to the last state. */ + draw_context.drawable = hashed_process_data->pixmap; + draw_context.gc = drawing->gc; + draw_context.pango_layout = drawing->pango_layout; + draw_context.drawinfo.end.x = x; + + draw_context.drawinfo.y.over = 1; + draw_context.drawinfo.y.middle = (hashed_process_data->height/2); + draw_context.drawinfo.y.under = hashed_process_data->height; + + draw_context.drawinfo.start.offset.over = 0; + draw_context.drawinfo.start.offset.middle = 0; + draw_context.drawinfo.start.offset.under = 0; + draw_context.drawinfo.end.offset.over = 0; + draw_context.drawinfo.end.offset.middle = 0; + draw_context.drawinfo.end.offset.under = 0; +#if 0 + /* Jump over draw if we are at the same x position */ + if(x == hashed_process_data->x.over) + { + /* jump */ + } else { + draw_context.drawinfo.start.x = hashed_process_data->x.over; + /* Draw the line */ + PropertiesLine prop_line = prepare_execmode_line(process); + draw_line((void*)&prop_line, (void*)&draw_context); + + hashed_process_data->x.over = x; + } +#endif //0 + + if(unlikely(x == hashed_process_data->x.middle && + hashed_process_data->x.middle_used)) { +#if 0 /* do not mark closure : not missing information */ + if(hashed_process_data->x.middle_marked == FALSE) { + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(drawing->pixmap, + drawing->gc, + x, + y+(height/2)-3); + hashed_process_data->x.middle_marked = TRUE; + } +#endif //0 + /* Jump */ + } else { + draw_context.drawinfo.start.x = hashed_process_data->x.middle; + /* Draw the line */ + if(dodraw) { + PropertiesLine prop_line; + prop_line.line_width = STATE_LINE_WIDTH; + prop_line.style = GDK_LINE_SOLID; + prop_line.y = MIDDLE; + if(hashed_process_data->type == RV_RESOURCE_CPU) + cpu_set_line_color(&prop_line, &ts->cpu_states[process_info->id]); + else if(hashed_process_data->type == RV_RESOURCE_IRQ) + irq_set_line_color(&prop_line, &ts->irq_states[process_info->id]); + else if(hashed_process_data->type == RV_RESOURCE_SOFT_IRQ) + soft_irq_set_line_color(&prop_line, &ts->soft_irq_states[process_info->id]); + else if(hashed_process_data->type == RV_RESOURCE_TRAP) + trap_set_line_color(&prop_line, &ts->trap_states[process_info->id]); + else if(hashed_process_data->type == RV_RESOURCE_BDEV) { + gint devcode_gint = process_info->id; + LttvBdevState *bdev = g_hash_table_lookup(ts->bdev_states, &devcode_gint); + // the lookup may return null; bdev_set_line_color must act appropriately + bdev_set_line_color(&prop_line, bdev); + } + + draw_line((void*)&prop_line, (void*)&draw_context); + } + + /* become the last x position */ + if(likely(x != hashed_process_data->x.middle)) { + hashed_process_data->x.middle = x; + /* but don't use the pixel */ + hashed_process_data->x.middle_used = FALSE; + + /* Calculate the next good time */ + convert_pixels_to_time(width, x+1, time_window, + &hashed_process_data->next_good_time); + } + } + } + } +} + +int before_chunk(void *hook_data, void *call_data) +{ + EventsRequest *events_request = (EventsRequest*)hook_data; + LttvTracesetState *tss = (LttvTracesetState*)call_data; + ControlFlowData *cfd = (ControlFlowData*)events_request->viewer_data; +#if 0 + /* Deactivate sort */ + gtk_tree_sortable_set_sort_column_id( + GTK_TREE_SORTABLE(cfd->process_list->list_store), + TRACE_COLUMN, + GTK_SORT_ASCENDING); +#endif //0 + drawing_chunk_begin(events_request, tss); + + return 0; +} + +/* before_request + * + * This gets executed just before an events request is executed + */ + +int before_request(void *hook_data, void *call_data) +{ + EventsRequest *events_request = (EventsRequest*)hook_data; + LttvTracesetState *tss = (LttvTracesetState*)call_data; + + drawing_data_request_begin(events_request, tss); + + return 0; +} + + +/* + * after request is necessary in addition of after chunk in order to draw + * lines until the end of the screen. after chunk just draws lines until + * the last event. + * + * for each process + * draw closing line + * expose + */ +int after_request(void *hook_data, void *call_data) +{ + guint i; + EventsRequest *events_request = (EventsRequest*)hook_data; + ControlFlowData *resourceview_data = events_request->viewer_data; + LttvTracesetState *tss = (LttvTracesetState*)call_data; + + ProcessList *process_list = resourceview_data->process_list; + LttTime end_time = events_request->end_time; + + ClosureData closure_data; + closure_data.events_request = (EventsRequest*)hook_data; + closure_data.tss = tss; + closure_data.end_time = end_time; + + TimeWindow time_window = + lttvwindow_get_time_window(resourceview_data->tab); + guint width = resourceview_data->drawing->width; + convert_time_to_pixels( + time_window, + end_time, + width, + &closure_data.x_end); + + + /* Draw last items */ + for(i=0; iviewer_data; + LttvTracesetState *tss = (LttvTracesetState*)call_data; + LttvTracesetContext *tsc = (LttvTracesetContext*)call_data; + LttvTracefileContext *tfc = lttv_traceset_context_get_current_tfc(tsc); + LttTime end_time; + + ProcessList *process_list = resourceview_data->process_list; + guint i; + LttvTraceset *traceset = tsc->ts; + guint nb_trace = lttv_traceset_number(traceset); + + /* Only execute when called for the first trace's events request */ + if(!process_list->current_hash_data) return; + + for(i = 0 ; i < nb_trace ; i++) { + g_free(process_list->current_hash_data[i]); + } + g_free(process_list->current_hash_data); + process_list->current_hash_data = NULL; + + if(tfc != NULL) + end_time = LTT_TIME_MIN(tfc->timestamp, events_request->end_time); + else /* end of traceset, or position now out of request : end */ + end_time = events_request->end_time; + + ClosureData closure_data; + closure_data.events_request = (EventsRequest*)hook_data; + closure_data.tss = tss; + closure_data.end_time = end_time; + + TimeWindow time_window = + lttvwindow_get_time_window(resourceview_data->tab); + guint width = resourceview_data->drawing->width; + convert_time_to_pixels( + time_window, + end_time, + width, + &closure_data.x_end); + + /* Draw last items */ + for(i=0; iprocess_list->list_store), + GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, + GTK_SORT_ASCENDING); + + update_index_to_pixmap(resourceview_data->process_list); + /* Request a full expose : drawing scrambled */ + gtk_widget_queue_draw(resourceview_data->drawing->drawing_area); +#endif //0 + /* Request expose (updates damages zone also) */ + drawing_request_expose(events_request, tss, end_time); + + return 0; +} + +/* after_statedump_end + * + * @param hook_data ControlFlowData structure of the viewer. + * @param call_data Event context. + * + * This function adds items to be drawn in a queue for each process. + * + */ +int before_statedump_end(void *hook_data, void *call_data) +{ + gint i; + + LttvTraceHook *th = (LttvTraceHook*)hook_data; + EventsRequest *events_request = (EventsRequest*)th->hook_data; + ControlFlowData *resourceview_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttvTracesetState *tss = (LttvTracesetState*)tfc->t_context->ts_context; + ProcessList *process_list = resourceview_data->process_list; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *filter = resourceview_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + LttTime evtime = ltt_event_time(e); + + ClosureData closure_data; + closure_data.events_request = events_request; + closure_data.tss = tss; + closure_data.end_time = evtime; + + TimeWindow time_window = + lttvwindow_get_time_window(resourceview_data->tab); + guint width = resourceview_data->drawing->width; + convert_time_to_pixels( + time_window, + evtime, + width, + &closure_data.x_end); + + /* Draw last items */ + + for(i=0; iprocess_list->list_store), + GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, + GTK_SORT_ASCENDING); + + update_index_to_pixmap(resourceview_data->process_list); + /* Request a full expose : drawing scrambled */ + gtk_widget_queue_draw(resourceview_data->drawing->drawing_area); +#endif //0 + /* Request expose (updates damages zone also) */ + drawing_request_expose(events_request, tss, evtime); + + return 0; +} diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/eventhooks.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/eventhooks.h new file mode 100644 index 00000000..4be0cffc --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/eventhooks.h @@ -0,0 +1,123 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + + +/* eventhooks.h defines the hooks that are given to processTrace as parameter. + * These hooks call the drawing API to draw the information on the screen, + * using information from Context, but mostly state (running, waiting...). + */ + + +#ifndef _EVENT_HOOKS_H +#define _EVENT_HOOKS_H + +#include +#include +#include + +#include "processlist.h" +#include "drawing.h" +#include "cfv.h" + + +/* Structure used to store and use information relative to one events refresh + * request. Typically filled in by the expose event callback, then passed to the + * library call, then used by the drawing hooks. Then, once all the events are + * sent, it is freed by the hook called after the reading. + */ +//typedef struct _EventRequest +//{ +// ControlFlowData *control_flow_data; +// LttTime time_begin, time_end; +// gint x_begin, x_end; + /* Fill the Events_Context during the initial expose, before calling for + * events. + */ + //GArray Events_Context; //FIXME +//} EventRequest ; + + + + + +void send_test_data(ProcessList *process_list, Drawing_t *drawing); + +GtkWidget *h_resourceview(LttvPlugin *plugin); + +GtkWidget *h_legend(LttvPlugin *plugin); + +int event_selected_hook(void *hook_data, void *call_data); + +/* + * The draw event hook is called by the reading API to have a + * particular event drawn on the screen. + * @param hook_data ControlFlowData structure of the viewer. + * @param call_data Event context with state. + * + * This function basically draw lines and icons. Two types of lines are drawn : + * one small (3 pixels?) representing the state of the process and the second + * type is thicker (10 pixels?) representing on which CPU a process is running + * (and this only in running state). + * + * Extremums of the lines : + * x_min : time of the last event context for this process kept in memory. + * x_max : time of the current event. + * y : middle of the process in the process list. The process is found in the + * list, therefore is it's position in pixels. + * + * The choice of lines'color is defined by the context of the last event for this + * process. + */ +int before_schedchange_hook(void *hook_data, void *call_data); +int after_schedchange_hook(void *hook_data, void *call_data); +int before_execmode_hook(void *hook_data, void *call_data); +int after_execmode_hook(void *hook_data, void *call_data); + + +int before_process_exit_hook(void *hook_data, void *call_data); +int before_process_release_hook(void *hook_data, void *call_data); +int after_process_exit_hook(void *hook_data, void *call_data); +int after_process_fork_hook(void *hook_data, void *call_data); +int after_fs_exec_hook(void *hook_data, void *call_data); +int after_user_generic_thread_brand_hook(void *hook_data, void *call_data); +int after_event_enum_process_hook(void *hook_data, void *call_data); + +#if 0 +int before_process_hook(void *hook_data, void *call_data); +int after_process_hook(void *hook_data, void *call_data); +#endif //0 + +void draw_closure(gpointer key, gpointer value, gpointer user_data); + +int before_chunk(void *hook_data, void *call_data); +int after_chunk(void *hook_data, void *call_data); +int before_request(void *hook_data, void *call_data); +int after_request(void *hook_data, void *call_data); +int before_statedump_end(void *hook_data, void *call_data); +int before_bdev_event_hook(void *hook_data, void *call_data); + + +gint update_time_window_hook(void *hook_data, void *call_data); +gint update_current_time_hook(void *hook_data, void *call_data); +gint traceset_notify(void *hook_data, void *call_data); +gint redraw_notify(void *hook_data, void *call_data); +gint continue_notify(void *hook_data, void *call_data); + +void legend_destructor(GtkWindow *legend); + +#endif // _EVENT_HOOKS_H diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/hLegendInsert.xpm b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/hLegendInsert.xpm new file mode 100644 index 00000000..a6ff0f39 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/hLegendInsert.xpm @@ -0,0 +1,45 @@ +/* XPM */ +static char * hLegendInsert_xpm[] = { +"22 22 20 1", +" c None", +". c #0DF904", +"+ c #000000", +"@ c #000500", +"# c #000B00", +"$ c #034001", +"% c #000200", +"& c #0CF403", +"* c #0BD603", +"= c #034901", +"- c #F90404", +"; c #0AC503", +"> c #000F00", +", c #034601", +"' c #0CF503", +") c #D60303", +"! c #001000", +"~ c #044E01", +"{ c #0CF203", +"] c #E40303", +" ", +" . ", +" .. ", +" ++@#$ ", +" ++++++% ", +" + &*=++ ", +" .. ++ ", +" . ++ ", +" ++ ", +"----------;>+,'.......", +"---------)!+~{........", +" +++ ", +" ++]---- ", +" ++----- ", +" ++----- ", +" ------ ", +" ++----- ", +" ++----- ", +" ", +"..........------------", +"..........------------", +" "}; diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/lttv_plugin_cfv.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/lttv_plugin_cfv.c new file mode 100644 index 00000000..ca96455a --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/lttv_plugin_cfv.c @@ -0,0 +1,84 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2006 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 "lttv_plugin_cfv.h" +#include +#include "drawing.h" + +/* + * forward definitions + */ + +/* + * Implementation + */ + +static void cfv_update_filter(LttvPlugin *parent, LttvFilter *filter) +{ + LttvPluginCFV *self = LTTV_PLUGIN_CFV(parent); + g_message("In CFV update filter."); + lttv_filter_destroy(self->cfd->filter); + self->cfd->filter = filter; + redraw_notify(self->cfd, NULL); +} + + +static void +lttv_plugin_cfv_class_init (LttvPluginCFVClass *klass) +{ + LttvPluginClass *parent_klass; + parent_klass = &klass->parent; + parent_klass->update_filter = cfv_update_filter; + g_type_class_add_private (klass, sizeof (ControlFlowData)); +} + + +static void +lttv_plugin_cfv_init (GTypeInstance *instance, gpointer g_class) +{ + LttvPluginCFV *self = LTTV_PLUGIN_CFV (instance); + self->cfd = G_TYPE_INSTANCE_GET_PRIVATE (self, + LTTV_TYPE_PLUGIN_CFV, ControlFlowData); +} + + +GType +lttv_plugin_cfv_get_type (void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (LttvPluginCFVClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + lttv_plugin_cfv_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (LttvPluginCFV), + 0, /* n_preallocs */ + lttv_plugin_cfv_init /* instance_init */ + }; + type = g_type_register_static (G_TYPE_OBJECT, + "LttvPluginRVType", + &info, 0); + } + return type; +} + + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/lttv_plugin_cfv.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/lttv_plugin_cfv.h new file mode 100644 index 00000000..45445da9 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/lttv_plugin_cfv.h @@ -0,0 +1,63 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2006 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 LTTV_PLUGIN_CFV_H +#define LTTV_PLUGIN_CFV_H + +#include +#include +#include "cfv.h" + +/* + * Type macros. + */ + +#define LTTV_TYPE_PLUGIN_CFV (lttv_plugin_cfv_get_type ()) +#define LTTV_PLUGIN_CFV(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LTTV_TYPE_PLUGIN_CFV, LttvPluginCFV)) +#define LTTV_PLUGIN_CFV_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), LTTV_TYPE_PLUGIN_CFV, LttvPluginCFVClass)) +#define LTTV_IS_PLUGIN_CFV(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LTTV_TYPE_PLUGIN_CFV)) +#define LTTV_IS_PLUGIN_CFV_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), LTTV_TYPE_PLUGIN_CFV)) +#define LTTV_PLUGIN_CFV_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), LTTV_TYPE_PLUGIN_CFV, LttvPluginCFVClass)) + +typedef struct _LttvPluginCFV LttvPluginCFV; +typedef struct _LttvPluginCFVClass LttvPluginCFVClass; + +struct _LttvPluginCFV { + LttvPlugin parent; + + /* instance members */ + ControlFlowData *cfd; + + /* private */ +}; + +struct _LttvPluginCFVClass { + LttvPluginClass parent; + + /* class members */ +}; + +/* used by LTTV_PLUGIN_TAB_TYPE */ +GType lttv_plugin_cfv_get_type (void); + +/* + * Method definitions. + */ + + +#endif diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/module.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/module.c new file mode 100644 index 00000000..33191988 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/module.c @@ -0,0 +1,95 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2007 Pierre-Marc Fournier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "cfv.h" +#include "lttv_plugin_cfv.h" +#include "eventhooks.h" + +#include "resourceview_icon.xpm" +#include "hLegendInsert.xpm" + +GQuark LTT_NAME_CPU; + +/** Array containing instanced objects. Used when module is unloaded */ +GSList *g_control_flow_data_list = NULL ; + +GSList *g_legend_list = NULL ; + +/***************************************************************************** + * Functions for module loading/unloading * + *****************************************************************************/ +/** + * plugin's init function + * + * This function initializes the Control Flow Viewer functionnality through the + * gtkTraceSet API. + */ +static void init() { + + g_info("Resource usage viewer init()"); + + /* Register the toolbar insert button and menu entry*/ + lttvwindow_register_constructor("resourceview", + "/", + "Insert Resource Viewer", + resourceview_icon_xpm, + "Insert Resource Viewer", + h_resourceview); + + + LTT_NAME_CPU = g_quark_from_string("/cpu"); +} + +void destroy_walk(gpointer data, gpointer user_data) +{ + g_info("Walk destroy Resource Viewer"); + guicontrolflow_destructor_full((LttvPluginCFV*)data); +} + + +/** + * plugin's destroy function + * + * This function releases the memory reserved by the module and unregisters + * everything that has been registered in the gtkTraceSet API. + */ +static void destroy() { + g_info("GUI resource viewer destroy()"); + + g_slist_foreach(g_control_flow_data_list, destroy_walk, NULL ); + + g_slist_free(g_control_flow_data_list); + + /* Unregister the toolbar insert button and menu entry */ + lttvwindow_unregister_constructor(h_resourceview); +} + + +LTTV_MODULE("resourceview", "Resource viewer", \ + "Graphical module to view usage of resources", \ + init, destroy, "lttvwindow") diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/processlist.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/processlist.c new file mode 100644 index 00000000..dec03cfc --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/processlist.c @@ -0,0 +1,820 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "processlist.h" +#include "drawing.h" +#include "drawitem.h" + +#define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format) +//#define g_debug(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format) + +/* Preallocated Size of the index_to_pixmap array */ +#define ALLOCATE_PROCESSES 1000 + +/***************************************************************************** + * Methods to synchronize process list * + *****************************************************************************/ + + +gint resource_sort_func ( GtkTreeModel *model, + GtkTreeIter *it_a, + GtkTreeIter *it_b, + gpointer user_data) +{ + gchar *a_name; + gchar *b_name; + + gtk_tree_model_get(model, it_a, NAME_COLUMN, &a_name, -1); + + gtk_tree_model_get(model, it_b, NAME_COLUMN, &b_name, -1); + + return strcmp(a_name, b_name); +} + +static guint ru_numeric_hash_fct(gconstpointer key) +{ + ResourceUniqueNumeric *ru = (ResourceUniqueNumeric *)key; + int tmp = (ru->trace_num << 8) ^ ru->id; + + return g_int_hash(&tmp); +} + +static gboolean ru_numeric_equ_fct(gconstpointer a, gconstpointer b) +{ + const ResourceUniqueNumeric *pa = (const ResourceUniqueNumeric *)a; + const ResourceUniqueNumeric *pb = (const ResourceUniqueNumeric *)b; + + if(pa->id == pb->id && pa->trace_num == pb->trace_num) + return TRUE; + + return FALSE; +} + +void destroy_hash_key(gpointer key); + +void destroy_hash_data(gpointer data); + + +gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, gpointer data) +{ + ControlFlowData *control_flow_data = + (ControlFlowData*)g_object_get_data( + G_OBJECT(widget), + "resourceview_data"); + Drawing_t *drawing = control_flow_data->drawing; + unsigned int cell_height = + get_cell_height(GTK_TREE_VIEW(control_flow_data->process_list->process_list_widget)); + + switch(event->direction) { + case GDK_SCROLL_UP: + gtk_adjustment_set_value(control_flow_data->v_adjust, + gtk_adjustment_get_value(control_flow_data->v_adjust) - cell_height); + break; + case GDK_SCROLL_DOWN: + gtk_adjustment_set_value(control_flow_data->v_adjust, + gtk_adjustment_get_value(control_flow_data->v_adjust) + cell_height); + break; + default: + g_error("should only scroll up and down."); + } + return TRUE; +} + +void expand_event(GtkTreeView *treeview, GtkTreeIter *iter, GtkTreePath *arg2, gpointer user_data) +{ + ControlFlowData *resourceview_data = + (ControlFlowData*)g_object_get_data( + G_OBJECT(treeview), + "resourceview_data"); + ProcessList *process_list = (ProcessList *) user_data; + ResourceUnique *rup; + HashedResourceData *hrd; + gboolean result; + + GtkTreeModel *model; + GtkTreeIter child; + + /* Determine which trace has been expanded */ + model = gtk_tree_view_get_model(treeview); + + /* mark each of the trace's resources invisible */ + result = gtk_tree_model_iter_children(model, &child, iter); + + /* for each child of the collapsed row */ + while(result) { + /* hide the item */ + gtk_tree_model_get(model, &child, DATA_COLUMN, &hrd, -1); + hrd->hidden=0; + + /* find next */ + result = gtk_tree_model_iter_next(model, &child); + } + + update_index_to_pixmap(process_list); + + gtk_widget_queue_draw(resourceview_data->drawing->drawing_area); +} + +void collapse_event(GtkTreeView *treeview, GtkTreeIter *iter, GtkTreePath *arg2, gpointer user_data) +{ + ControlFlowData *resourceview_data = + (ControlFlowData*)g_object_get_data( + G_OBJECT(treeview), + "resourceview_data"); + ProcessList *process_list = (ProcessList *) user_data; + ResourceUnique *rup; + HashedResourceData *hrd; + gboolean result; + + GtkTreeModel *model; + GtkTreeIter child; + + /* Determine which trace has been expanded */ + model = gtk_tree_view_get_model(treeview); + + /* mark each of the trace's resources invisible */ + result = gtk_tree_model_iter_children(model, &child, iter); + + /* for each child of the collapsed row */ + while(result) { + char *name; + /* hide the item */ + gtk_tree_model_get(model, &child, NAME_COLUMN, &name, DATA_COLUMN, &hrd, -1); + hrd->hidden=1; + + /* find next */ + result = gtk_tree_model_iter_next(model, &child); + } + + update_index_to_pixmap(process_list); + + gtk_widget_queue_draw(resourceview_data->drawing->drawing_area); +} + +static gboolean update_index_to_pixmap_each (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, UpdateIndexPixmapArg *arg) +{ + guint array_index = arg->count; + HashedResourceData *hdata; + gchar *name; + + gtk_tree_model_get(model, iter, NAME_COLUMN, &name, DATA_COLUMN, &hdata, -1); + + g_assert(array_index < arg->process_list->index_to_pixmap->len); + + if(hdata->hidden == 0) { + GdkPixmap **pixmap = + (GdkPixmap**)&g_ptr_array_index(arg->process_list->index_to_pixmap, array_index); + *pixmap = hdata->pixmap; + + arg->count++; + } + + return FALSE; +} + +void update_index_to_pixmap(ProcessList *process_list) +{ + int i, items=0; + UpdateIndexPixmapArg arg; + + for(i=0; irestypes[i].hash_table); + } + + /* we don't know the exact number of items there will be, + * so set an upper bound */ + g_ptr_array_set_size(process_list->index_to_pixmap, items); + + arg.count = 0; + arg.process_list = process_list; + + /* If cell_height is still 0, the only element in the tree is a temporary + * element that has no pixmap, see also processlist_construct() */ + if (process_list->cell_height != 0) { + gtk_tree_model_foreach(GTK_TREE_MODEL(process_list->list_store), + (GtkTreeModelForeachFunc)update_index_to_pixmap_each, &arg); + } + + /* now that we know the exact number of items, set it */ + g_ptr_array_set_size(process_list->index_to_pixmap, arg.count); +} + + +static void update_pixmap_size_each(void *key, + HashedResourceData *value, + guint width) +{ + GdkPixmap *old_pixmap = value->pixmap; + + value->pixmap = + gdk_pixmap_new(old_pixmap, + width, + value->height, + -1); + + gdk_pixmap_unref(old_pixmap); +} + + +void update_pixmap_size(ProcessList *process_list, guint width) +{ + int i; + for(i=0; irestypes[i].hash_table, + (GHFunc)update_pixmap_size_each, + (gpointer)width); + } +} + + +typedef struct _CopyPixmap { + GdkDrawable *dest; + GdkGC *gc; + GdkDrawable *src; + gint xsrc, ysrc, xdest, ydest, width, height; +} CopyPixmap; + +static void copy_pixmap_region_each(void *key, + HashedResourceData *value, + CopyPixmap *cp) +{ + GdkPixmap *src = cp->src; + GdkPixmap *dest = cp->dest; + + if(dest == NULL) + dest = value->pixmap; + if(src == NULL) + src = value->pixmap; + + gdk_draw_drawable (dest, + cp->gc, + src, + cp->xsrc, cp->ysrc, + cp->xdest, cp->ydest, + cp->width, cp->height); +} + +void copy_pixmap_region(ProcessList *process_list, GdkDrawable *dest, + GdkGC *gc, GdkDrawable *src, + gint xsrc, gint ysrc, + gint xdest, gint ydest, gint width, gint height) +{ + int i; + CopyPixmap cp = { dest, gc, src, xsrc, ysrc, xdest, ydest, width, height }; + + for(i=0; irestypes[i].hash_table, + (GHFunc)copy_pixmap_region_each, + &cp); + } +} + + + +typedef struct _RectanglePixmap { + gboolean filled; + gint x, y, width, height; + GdkGC *gc; +} RectanglePixmap; + +static void rectangle_pixmap_each(void *key, + HashedResourceData *value, + RectanglePixmap *rp) +{ + if(rp->height == -1) + rp->height = value->height; + + gdk_draw_rectangle (value->pixmap, + rp->gc, + rp->filled, + rp->x, rp->y, + rp->width, rp->height); +} + +void rectangle_pixmap(ProcessList *process_list, GdkGC *gc, + gboolean filled, gint x, gint y, gint width, gint height) +{ + int i; + RectanglePixmap rp = { filled, x, y, width, height, gc }; + + for(i=0; irestypes[i].hash_table, + (GHFunc)rectangle_pixmap_each, + &rp); + } +} + +/* Renders each pixmaps into on big drawable */ +void copy_pixmap_to_screen(ProcessList *process_list, + GdkDrawable *dest, + GdkGC *gc, + gint x, gint y, + gint width, gint height) +{ + if(process_list->index_to_pixmap->len == 0) return; + guint cell_height = process_list->cell_height; + + /* Get indexes */ + gint begin = floor(y/(double)cell_height); + gint end = MIN(ceil((y+height)/(double)cell_height), + process_list->index_to_pixmap->len); + gint i; + + for(i=begin; iindex_to_pixmap->len); + /* Render the pixmap to the screen */ + GdkPixmap *pixmap = + //(GdkPixmap*)g_ptr_array_index(process_list->index_to_pixmap, i); + GDK_PIXMAP(g_ptr_array_index(process_list->index_to_pixmap, i)); + + gdk_draw_drawable (dest, + gc, + pixmap, + x, 0, + x, i*cell_height, + width, cell_height); + + } +} + +ProcessList *processlist_construct(void) +{ + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + ProcessList* process_list = g_new(ProcessList,1); + + process_list->number_of_process = 0; + + process_list->current_hash_data = NULL; + + /* Create the Process list */ + process_list->list_store = gtk_tree_store_new ( N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER); + + process_list->process_list_widget = + gtk_tree_view_new_with_model + (GTK_TREE_MODEL (process_list->list_store)); + g_object_set(process_list->process_list_widget, "enable-tree-lines", TRUE, NULL); + + g_object_unref (G_OBJECT (process_list->list_store)); + + gtk_tree_sortable_set_default_sort_func( + GTK_TREE_SORTABLE(process_list->list_store), + resource_sort_func, + NULL, + NULL); + + + gtk_tree_sortable_set_sort_column_id( + GTK_TREE_SORTABLE(process_list->list_store), + GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, + GTK_SORT_ASCENDING); + + gtk_tree_view_set_headers_visible( + GTK_TREE_VIEW(process_list->process_list_widget), TRUE); + + /* Create a column, associating the "text" attribute of the + * cell_renderer to the first column of the model */ + /* Columns alignment : 0.0 : Left 0.5 : Center 1.0 : Right */ + renderer = gtk_cell_renderer_text_new (); + process_list->renderer = renderer; + + g_signal_connect(process_list->process_list_widget, "row-expanded", G_CALLBACK(expand_event), process_list); + g_signal_connect(process_list->process_list_widget, "row-collapsed", G_CALLBACK(collapse_event), process_list); + + /* Add a temporary row to the model to get the cell size when the first + * real process is added. */ + GtkTreeIter iter; + GtkTreePath *path; + path = gtk_tree_path_new_first(); + gtk_tree_model_get_iter (gtk_tree_view_get_model(GTK_TREE_VIEW( + process_list->process_list_widget)), &iter, path); + gtk_tree_store_append(process_list->list_store, &iter, NULL); + gtk_tree_path_free(path); + + process_list->cell_height = 0; // not ready to get size yet. + + column = gtk_tree_view_column_new_with_attributes ( "Resource", + renderer, + "text", + NAME_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_fixed_width (column, 45); + gtk_tree_view_append_column ( + GTK_TREE_VIEW (process_list->process_list_widget), column); + + process_list->button = column->button; + + g_object_set_data_full( + G_OBJECT(process_list->process_list_widget), + "process_list_Data", + process_list, + (GDestroyNotify)processlist_destroy); + + process_list->index_to_pixmap = g_ptr_array_sized_new(ALLOCATE_PROCESSES); + + process_list->restypes[RV_RESOURCE_MACHINE].hash_table = g_hash_table_new(ru_numeric_hash_fct, ru_numeric_equ_fct); + process_list->restypes[RV_RESOURCE_CPU].hash_table = g_hash_table_new(ru_numeric_hash_fct, ru_numeric_equ_fct); + process_list->restypes[RV_RESOURCE_IRQ].hash_table = g_hash_table_new(ru_numeric_hash_fct, ru_numeric_equ_fct); + process_list->restypes[RV_RESOURCE_SOFT_IRQ].hash_table = g_hash_table_new(ru_numeric_hash_fct, ru_numeric_equ_fct); + process_list->restypes[RV_RESOURCE_TRAP].hash_table = g_hash_table_new(ru_numeric_hash_fct, ru_numeric_equ_fct); + process_list->restypes[RV_RESOURCE_BDEV].hash_table = g_hash_table_new(ru_numeric_hash_fct, ru_numeric_equ_fct); + + return process_list; +} + +void processlist_destroy(ProcessList *process_list) +{ + int i; + + g_debug("processlist_destroy %p", process_list); + + for(i=0; irestypes[i].hash_table); + process_list->restypes[i].hash_table = NULL; + } + g_ptr_array_free(process_list->index_to_pixmap, TRUE); + + g_free(process_list); + g_debug("processlist_destroy end"); +} + +static gboolean remove_hash_item(void *key, + HashedResourceData *hashed_process_data, + ProcessList *process_list) +{ + GtkTreeIter iter; + + iter = hashed_process_data->y_iter; + + gtk_tree_store_remove (process_list->list_store, &iter); + gdk_pixmap_unref(hashed_process_data->pixmap); + +// TODO pmf: check this; might be needed +// if(likely(process_list->current_hash_data != NULL)) { +// if(likely(hashed_process_data == +// process_list->current_hash_data[process_info->trace_num][process_info->cpu])) +// process_list->current_hash_data[process_info->trace_num][process_info->cpu] = NULL; +// } + return TRUE; /* remove the element from the hash table */ +} + +void processlist_clear(ProcessList *process_list) +{ + int i; + + g_info("processlist_clear %p", process_list); + + for(i=RV_RESOURCE_COUNT-1; i>=0; i--) { + g_hash_table_foreach_remove(process_list->restypes[i].hash_table, + (GHRFunc)remove_hash_item, + (gpointer)process_list); + } + process_list->number_of_process = 0; + update_index_to_pixmap(process_list); +} + + +GtkWidget *processlist_get_widget(ProcessList *process_list) +{ + return process_list->process_list_widget; +} + + +void destroy_hash_key(gpointer key) +{ + g_free(key); +} + +void destroy_hash_data(gpointer data) +{ + g_free(data); +} + +GQuark make_cpu_name(ControlFlowData *resourceview_data, guint trace_num, guint id) +{ + GQuark name; + gchar *str; + + str = g_strdup_printf("CPU%u", id); + name = g_quark_from_string(str); + g_free(str); + + return name; +} + +GQuark make_irq_name(ControlFlowData *resourceview_data, guint trace_num, guint id) +{ + GQuark name; + gchar *str; + + str = g_strdup_printf("IRQ %u", id); + name = g_quark_from_string(str); + g_free(str); + + return name; +} + +GQuark make_soft_irq_name(ControlFlowData *resourceview_data, guint trace_num, guint id) +{ + GQuark name; + gchar *str; + + str = g_strdup_printf("SOFTIRQ %u", id); + name = g_quark_from_string(str); + g_free(str); + + return name; +} + +GQuark make_trap_name(ControlFlowData *resourceview_data, guint trace_num, guint id) +{ + GQuark name; + gchar *str; + + str = g_strdup_printf("Trap %u", id); + name = g_quark_from_string(str); + g_free(str); + + return name; +} + +GQuark make_bdev_name(ControlFlowData *resourceview_data, guint trace_num, guint id) +{ + GQuark name; + gchar *str; + + str = g_strdup_printf("Block (%u,%u)", MAJOR(id), MINOR(id)); + name = g_quark_from_string(str); + g_free(str); + + return name; +} + +HashedResourceData *resourcelist_obtain_machine(ControlFlowData *resourceview_data, guint trace_num, guint id) +{ + ResourceUniqueNumeric *ru = g_new(ResourceUniqueNumeric, 1); + HashedResourceData *data = g_new(HashedResourceData, 1); + + /* Prepare hash key */ + ru->trace_num = trace_num; + ru->id = id; + + /* Search within hash table */ + GHashTable *ht = resourceview_data->process_list->restypes[RV_RESOURCE_MACHINE].hash_table; + data = g_hash_table_lookup(ht, ru); + + /* If not found in hash table, add it */ + if(data == NULL) { + GQuark name; + + data = g_malloc(sizeof(HashedResourceData)); + /* Prepare hashed data */ + data->type = RV_RESOURCE_MACHINE; + data->x.over = 0; + data->x.over_used = FALSE; + data->x.over_marked = FALSE; + data->x.middle = 0; // last + data->x.middle_used = FALSE; + data->x.middle_marked = FALSE; + data->x.under = 0; + data->x.under_used = FALSE; + data->x.under_marked = FALSE; + data->next_good_time = ltt_time_zero; + data->hidden = 0; + + if (resourceview_data->process_list->cell_height == 0) { + GtkTreePath *path; + GdkRectangle rect; + GtkTreeIter iter; + + path = gtk_tree_path_new_first(); + gtk_tree_model_get_iter(gtk_tree_view_get_model(GTK_TREE_VIEW( + resourceview_data->process_list->process_list_widget)), + &iter, path); + gtk_tree_view_get_background_area(GTK_TREE_VIEW( + resourceview_data->process_list->process_list_widget), + path, NULL, &rect); + gtk_tree_store_remove(resourceview_data->process_list->list_store, + &iter); + gtk_tree_path_free(path); + resourceview_data->process_list->cell_height = rect.height; + } + + data->height = resourceview_data->process_list->cell_height; + data->pixmap = + gdk_pixmap_new(resourceview_data->drawing->drawing_area->window, + resourceview_data->drawing->alloc_width, + data->height, + -1); + g_assert(data->pixmap); + + gdk_draw_rectangle (data->pixmap, + resourceview_data->drawing->drawing_area->style->black_gc, + TRUE, + 0, 0, + resourceview_data->drawing->alloc_width, + data->height); + + /* add to hash table */ + g_hash_table_insert(ht, ru, data); + resourceview_data->process_list->number_of_process++; // TODO: check + + /* add to process list */ + { + gchar *str; + str = g_strdup_printf("Trace %u", id); + name = g_quark_from_string(str); + g_free(str); + } + + gtk_tree_store_append(resourceview_data->process_list->list_store, &data->y_iter, NULL); + gtk_tree_store_set(resourceview_data->process_list->list_store, &data->y_iter, + NAME_COLUMN, g_quark_to_string(name), DATA_COLUMN, data, + -1); + + update_index_to_pixmap(resourceview_data->process_list); + + int heightall = data->height * resourceview_data->process_list->number_of_process; + + gtk_widget_set_size_request(resourceview_data->drawing->drawing_area, + -1, + heightall); + + gtk_widget_queue_draw(resourceview_data->drawing->drawing_area); + } + + /* expand the newly added machine */ + { + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(resourceview_data->process_list->process_list_widget)); + GtkTreePath *path; + path = gtk_tree_model_get_path(model, &data->y_iter); + + gtk_tree_view_expand_row(GTK_TREE_VIEW(resourceview_data->process_list->process_list_widget), path, FALSE); + + gtk_tree_path_free(path); + } + + return data; +} + +HashedResourceData *resourcelist_obtain_generic(ControlFlowData *resourceview_data, gint res_type, guint trace_num, guint id, GQuark (*make_name_func)(ControlFlowData *resourceview_data, guint trace_num, guint id)) +{ + ResourceUniqueNumeric *ru = g_new(ResourceUniqueNumeric, 1); + HashedResourceData *data = g_new(HashedResourceData, 1); + + /* Prepare hash key */ + ru->ru.type = &(resourceview_data->process_list->restypes[res_type]); + ru->trace_num = trace_num; + ru->id = id; + + /* Search within hash table */ + GHashTable *ht = resourceview_data->process_list->restypes[res_type].hash_table; + data = g_hash_table_lookup(ht, ru); + + /* If not found in hash table, add it */ + if(data == NULL) { + GQuark name; + HashedResourceData *parent; + + /* Find the parent machine */ + parent = resourcelist_obtain_machine(resourceview_data, trace_num, trace_num); + + /* Prepare hashed data */ + data = g_malloc(sizeof(HashedResourceData)); + + data->type = res_type; + data->x.over = 0; + data->x.over_used = FALSE; + data->x.over_marked = FALSE; + data->x.middle = 0; // last + data->x.middle_used = FALSE; + data->x.middle_marked = FALSE; + data->x.under = 0; + data->x.under_used = FALSE; + data->x.under_marked = FALSE; + data->next_good_time = ltt_time_zero; + + if (resourceview_data->process_list->cell_height == 0) { + GtkTreePath *path; + GdkRectangle rect; + GtkTreeIter iter; + + path = gtk_tree_path_new_first(); + gtk_tree_model_get_iter(gtk_tree_view_get_model(GTK_TREE_VIEW( + resourceview_data->process_list->process_list_widget)), &iter, + path); + gtk_tree_view_get_background_area(GTK_TREE_VIEW( + resourceview_data->process_list->process_list_widget), path, + NULL, &rect); + gtk_tree_store_remove(resourceview_data->process_list->list_store, + &iter); + gtk_tree_path_free(path); + resourceview_data->process_list->cell_height = rect.height; + } + + data->height = resourceview_data->process_list->cell_height; + data->pixmap = + gdk_pixmap_new(resourceview_data->drawing->drawing_area->window, + resourceview_data->drawing->alloc_width, + data->height, + -1); + + gdk_draw_rectangle (data->pixmap, + resourceview_data->drawing->drawing_area->style->black_gc, + TRUE, + 0, 0, + resourceview_data->drawing->alloc_width, + data->height); + + /* add to hash table */ + g_hash_table_insert(ht, ru, data); + resourceview_data->process_list->number_of_process++; // TODO: check + + /* add to process list */ + name = make_name_func(resourceview_data, trace_num, id); + + gtk_tree_store_append(resourceview_data->process_list->list_store, &data->y_iter, &parent->y_iter); + gtk_tree_store_set(resourceview_data->process_list->list_store, &data->y_iter, + NAME_COLUMN, g_quark_to_string(name), DATA_COLUMN, data, + -1); + + /* Determine if we should add it hidden or not */ + { + gboolean result; + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(resourceview_data->process_list->process_list_widget)); + GtkTreeIter parent_iter; + + result = gtk_tree_model_iter_parent(model, &parent_iter, &data->y_iter); + GtkTreePath *path = gtk_tree_model_get_path(model, &parent_iter); + data->hidden = gtk_tree_view_row_expanded(GTK_TREE_VIEW(resourceview_data->process_list->process_list_widget), path)?0:1; + gtk_tree_path_free(path); + } + + + update_index_to_pixmap(resourceview_data->process_list); + + int heightall = data->height * resourceview_data->process_list->number_of_process; + + gtk_widget_set_size_request(resourceview_data->drawing->drawing_area, + -1, + heightall); + + gtk_widget_queue_draw(resourceview_data->drawing->drawing_area); + } + + return data; +} + +HashedResourceData *resourcelist_obtain_cpu(ControlFlowData *resourceview_data, guint trace_num, guint id) +{ + return resourcelist_obtain_generic(resourceview_data, RV_RESOURCE_CPU, trace_num, id, make_cpu_name); +} + +HashedResourceData *resourcelist_obtain_irq(ControlFlowData *resourceview_data, guint trace_num, guint id) +{ + return resourcelist_obtain_generic(resourceview_data, RV_RESOURCE_IRQ, trace_num, id, make_irq_name); +} + +HashedResourceData *resourcelist_obtain_soft_irq(ControlFlowData *resourceview_data, guint trace_num, guint id) +{ + return resourcelist_obtain_generic(resourceview_data, RV_RESOURCE_SOFT_IRQ, trace_num, id, make_soft_irq_name); +} + +HashedResourceData *resourcelist_obtain_trap(ControlFlowData *resourceview_data, guint trace_num, guint id) +{ + return resourcelist_obtain_generic(resourceview_data, RV_RESOURCE_TRAP, trace_num, id, make_trap_name); +} + +HashedResourceData *resourcelist_obtain_bdev(ControlFlowData *resourceview_data, guint trace_num, guint id) +{ + return resourcelist_obtain_generic(resourceview_data, RV_RESOURCE_BDEV, trace_num, id, make_bdev_name); +} + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/processlist.h b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/processlist.h new file mode 100644 index 00000000..2070a9c6 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/processlist.h @@ -0,0 +1,304 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 _PROCESS_LIST_H +#define _PROCESS_LIST_H + +#include +#include +#include +#include + +#include "drawitem.h" +#include "cfv.h" + +/* The process list + * + * Tasks : + * Create a process list + * contains the data for the process list + * tells the height of the process list widget + * provides methods to add/remove process from the list + * note : the sync with drawing is left to the caller. + * provides helper function to convert a process unique identifier to + * pixels (in height). + * + */ + +/* Unique identifiers for resource types */ +#define RV_RESOURCE_MACHINE 0 +#define RV_RESOURCE_CPU 1 +#define RV_RESOURCE_IRQ 2 +#define RV_RESOURCE_SOFT_IRQ 3 +#define RV_RESOURCE_TRAP 4 +#define RV_RESOURCE_BDEV 5 +#define RV_RESOURCE_COUNT 6 + +/* Enumeration of the columns */ +enum +{ + NAME_COLUMN, + DATA_COLUMN, + N_COLUMNS +}; + +/* +typedef struct _ResourceInfo { + GQuark name; + guint trace_num; + guint type; + guint64 id; +} ResourceInfo; +*/ + +struct _ResourceType { + /* a hashtable containing the data of each resource of this type */ + GHashTable *hash_table; +}; +typedef struct _ResourceType ResourceType; + +typedef struct _ResourceUnique { + ResourceType *type; + void *priv; +} ResourceUnique; + +typedef struct _ResourceUniqueNumeric { + ResourceUnique ru; + guint trace_num; + guint id; +} ResourceUniqueNumeric; + +typedef struct _HashedResourceData { + guint type; + + GdkPixmap *pixmap; // Pixmap slice containing drawing buffer for the PID + gint height; // height of the pixmap + GtkTreeIter y_iter; // Access quickly to y pos. + // DrawContext *draw_context; + /* Information on current drawing */ + struct { + guint over; + gboolean over_used; /* inform the user that information is incomplete */ + gboolean over_marked; /* inform the user that information is incomplete */ + guint middle; + gboolean middle_used; /* inform the user that information is incomplete */ + gboolean middle_marked;/* inform the user that information is incomplete */ + guint under; + gboolean under_used; /* inform the user that information is incomplete */ + gboolean under_marked; /* inform the user that information is incomplete */ + } x; /* last x position saved by after state update */ + + LttTime next_good_time; /* precalculate the next time where the next + pixel is.*/ + gint hidden; + +} HashedResourceData; + +struct _ProcessList { + + GtkWidget *process_list_widget; + GtkTreeStore *list_store; + GtkWidget *button; /* one button of the tree view */ + GtkCellRenderer *renderer; + + /* A hash table by PID to speed up process position find in the list */ +// GHashTable *process_hash; + + guint number_of_process; + gint cell_height; + + /* Current process pointer, one per cpu, one per trace */ + HashedResourceData ***current_hash_data; + + /* Array containing index -> pixmap correspondance. Must be updated + * every time the process list is reordered, process added or removed */ + GPtrArray * index_to_pixmap; + + ResourceType restypes[RV_RESOURCE_COUNT]; +}; + +typedef struct _UpdateIndexPixmapArg { + ProcessList *process_list; + guint count; +} UpdateIndexPixmapArg; + +#ifndef TYPE_PROCESSLIST_DEFINED +#define TYPE_PROCESSLIST_DEFINED +typedef struct _ProcessList ProcessList; +#endif //TYPE_PROCESSLIST_DEFINED + + +#ifndef TYPE_DRAWING_T_DEFINED +#define TYPE_DRAWING_T_DEFINED +typedef struct _Drawing_t Drawing_t; +#endif //TYPE_DRAWING_T_DEFINED + +ProcessList *processlist_construct(void); +void processlist_destroy(ProcessList *process_list); +GtkWidget *processlist_get_widget(ProcessList *process_list); + +void processlist_clear(ProcessList *process_list); + +// out : success (0) and height +/* CPU num is only used for PID 0 */ +//int resourcelist_add( ProcessList *process_list, Drawing_t *drawing, guint trace_num, +// GQuark name, guint type, guint id, guint *height, ResourceInfo **pm_resource_info, +// HashedResourceData **pm_hashed_resource_data, GQuark parent); + +// out : success (0) and height +int processlist_remove(ProcessList *process_list, guint pid, guint cpu, + LttTime *birth, guint trace_num); + +/* Set the name of a process */ +void processlist_set_name(ProcessList *process_list, + GQuark name, + HashedResourceData *hashed_process_data); + +void processlist_set_brand(ProcessList *process_list, + GQuark brand, + HashedResourceData *hashed_process_data); + +/* Set the ppid of a process */ +void processlist_set_tgid(ProcessList *process_list, + guint tgid, + HashedResourceData *hashed_process_data); +void processlist_set_ppid(ProcessList *process_list, + guint ppid, + HashedResourceData *hashed_process_data); + + +/* Synchronize the list at the left and the drawing */ +void update_index_to_pixmap(ProcessList *process_list); + +/* Update the width of each pixmap buffer for each process */ +void update_pixmap_size(ProcessList *process_list, guint width); + + +/* Put src and/or dest to NULL to copy from/to the each PID specific pixmap */ +void copy_pixmap_region(ProcessList *process_list, GdkDrawable *dest, + GdkGC *gc, GdkDrawable *src, + gint xsrc, gint ysrc, + gint xdest, gint ydest, gint width, gint height); + +/* If height is -1, the height of each pixmap is used */ +void rectangle_pixmap(ProcessList *process_list, GdkGC *gc, + gboolean filled, gint x, gint y, gint width, gint height); + +/* Renders each pixmaps into on big drawable */ +void copy_pixmap_to_screen(ProcessList *process_list, + GdkDrawable *dest, + GdkGC *gc, + gint x, gint y, + gint width, gint height); + +static inline gint get_cell_height(GtkTreeView *TreeView) +{ + gint height; + GtkTreeViewColumn *column = gtk_tree_view_get_column(TreeView, 0); + + gtk_tree_view_column_cell_get_size(column, NULL, NULL, NULL, NULL, &height); + + gint vertical_separator; + gtk_widget_style_get (GTK_WIDGET (TreeView), + "vertical-separator", &vertical_separator, + NULL); + height += vertical_separator; + + return height; +} + +static inline guint processlist_get_height(ProcessList *process_list) +{ + return process_list->cell_height * process_list->number_of_process ; +} + + +//static inline HashedResourceData *processlist_get_process_data( +// ProcessList *process_list, GQuark resource_name, guint trace_num) +//{ +// ResourceInfo resource_info; +// +//// process_info.pid = pid; +//// if(pid == 0) +//// process_info.cpu = cpu; +//// else +//// process_info.cpu = ANY_CPU; +//// process_info.birth = *birth; +//// process_info.trace_num = trace_num; +// resource_info.name = resource_name; +// resource_info.trace_num = trace_num; +// +// return (HashedResourceData*)g_hash_table_lookup( +// process_list->process_hash, +// &resource_info); +//} +// + +static inline gint processlist_get_pixels_from_data( ProcessList *process_list, + HashedResourceData *hashed_process_data, + guint *y, + guint *height) +{ + gint *path_indices; + GtkTreePath *tree_path; + + tree_path = gtk_tree_model_get_path((GtkTreeModel*)process_list->list_store, + &hashed_process_data->y_iter); + path_indices = gtk_tree_path_get_indices (tree_path); + + *height = get_cell_height((GtkTreeView*)process_list->process_list_widget); + *y = *height * path_indices[0]; + gtk_tree_path_free(tree_path); + + return 0; + +} + +static inline guint processlist_get_index_from_data(ProcessList *process_list, + HashedResourceData *hashed_process_data) +{ + gint *path_indices; + GtkTreePath *tree_path; + guint ret; + gint depth; + + tree_path = gtk_tree_model_get_path((GtkTreeModel*)process_list->list_store, + &hashed_process_data->y_iter); + path_indices = gtk_tree_path_get_indices (tree_path); + depth = gtk_tree_path_get_depth(tree_path); + +// ret = path_indices[1]+path_indices[0]+1; + ret = path_indices[0]; + if(depth>1) + ret+= 1+path_indices[1]; + + gtk_tree_path_free(tree_path); + + return ret; +} + +/* Return the hash table used to store the data of each resource of a given resource type */ + +static inline GHashTable *resourcelist_get_resource_hash_table(ControlFlowData *resourceview_data, guint type) +{ + return resourceview_data->process_list->restypes[type].hash_table; +} + + + +#endif // _PROCESS_LIST_H diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/resourceview_icon.xpm b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/resourceview_icon.xpm new file mode 100644 index 00000000..fcb7a09a --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/resourceview_icon.xpm @@ -0,0 +1,47 @@ +/* XPM */ +static char * resourceview_icon_xpm[] = { +"22 22 22 1", +" c None", +". c #0DF904", +"+ c #F90404", +"@ c #000000", +"# c #870202", +"$ c #580101", +"% c #EA0303", +"& c #0C0000", +"* c #060000", +"= c #E60303", +"- c #6D0101", +"; c #D90303", +"> c #020000", +", c #240000", +"' c #F80303", +") c #530101", +"! c #B60202", +"~ c #C30303", +"{ c #510101", +"] c #068202", +"^ c #045101", +"/ c #A90202", +" ", +" . ", +" .. ", +" ... ", +" .... ", +" ... ", +" .. ", +" . ", +" ", +"++++++++++............", +"++++++++++............", +" @@@@@@ @@ @@ ", +" @@ @@ +#@$++ @@ ", +" @@ @@ +%&*=+ @@ ", +" @@ @@ ++-@#+ @@ ", +" @@ @@ ++;>,' @@ ", +" @@@@@@ +++)@! @@ ", +" @@ @@ +++~@{@@ ", +" @@ @@ @@@@ ", +"..@@...]@^++++/@@/++++", +"..........++++++++++++", +" "}; diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/test.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/test.c new file mode 100644 index 00000000..8a71a52e --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/resourceview/test.c @@ -0,0 +1,578 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +static void destroy_cb( GtkWidget *widget, + gpointer data ) +{ + gtk_main_quit (); +} + + + +int main(int argc, char **argv) +{ + GtkWidget *Window; + GtkWidget *CF_Viewer; + GtkWidget *VBox_V; + GtkWidget *HScroll_VC; + ControlFlowData *control_flow_data; + guint ev_sel = 444 ; + /* Horizontal scrollbar and it's adjustment */ + GtkWidget *VScroll_VC; + GtkAdjustment *v_adjust ; + + /* Initialize i18n support */ + gtk_set_locale (); + + /* Initialize the widget set */ + gtk_init (&argc, &argv); + + init(); + + Window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (Window), ("Test Window")); + + g_signal_connect (G_OBJECT (Window), "destroy", + G_CALLBACK (destroy_cb), NULL); + + + VBox_V = gtk_vbox_new(0, 0); + gtk_container_add (GTK_CONTAINER (Window), VBox_V); + + //ListViewer = hGuiEvents(Window); + //gtk_box_pack_start(GTK_BOX(VBox_V), ListViewer, TRUE, TRUE, 0); + + //ListViewer = hGuiEvents(Window); + //gtk_box_pack_start(GTK_BOX(VBox_V), ListViewer, FALSE, TRUE, 0); + + control_flow_data = guicontrolflow(); + CF_Viewer = control_flow_data->scrolled_window; + gtk_box_pack_start(GTK_BOX(VBox_V), CF_Viewer, TRUE, TRUE, 0); + + /* Create horizontal scrollbar and pack it */ + HScroll_VC = gtk_hscrollbar_new(NULL); + gtk_box_pack_start(GTK_BOX(VBox_V), HScroll_VC, FALSE, TRUE, 0); + + + gtk_widget_show (HScroll_VC); + gtk_widget_show (VBox_V); + gtk_widget_show (Window); + + //Event_Selected_Hook(control_flow_data, &ev_sel); + + gtk_main (); + + g_critical("main loop finished"); + + //h_guievents_destructor(ListViewer); + + //g_critical("GuiEvents Destructor finished"); + destroy(); + + return 0; +} + + + +void add_test_process(ControlFlowData *control_flow_data) +{ + GtkTreeIter iter; + int i; + gchar *process[] = { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" }; + + for(i=0; inumber_of_process; i++) + { + /* Add a new row to the model */ + gtk_list_store_append (control_flow_data->list_store, &iter); + gtk_list_store_set ( control_flow_data->list_store, &iter, + PROCESS_COLUMN, process[i], + -1); + } + +} + + + + + + +void test_draw(ControlFlowData *control_flow_data) +{ + /* Draw event states using available height, Number of process, cell height + * (don't forget to remove two pixels at beginning and end). + * For horizontal : use width, Time_Begin, Time_End. + * This function calls the reading library to get the draw_hook called + * for the desired period of time. */ + + drawingAreaInfo *drawing_Area_Info = &control_flow_data->drawing_Area_Info; + + +} + +#ifdef DEBUG +void test_draw() { + gint cell_height = get_cell_height(GTK_TREE_VIEW(control_flow_data->process_list_widget)); + GdkGC *GC = gdk_gc_new(widget->window); + GdkColor color = CF_Colors[GREEN]; + + gdk_color_alloc (gdk_colormap_get_system () , &color); + + g_critical("expose"); + + /* When redrawing, use widget->allocation.width to get the width of + * drawable area. */ + control_flow_data->drawing_Area_Info.width = widget->allocation.width; + + test_draw(control_flow_data); + + gdk_gc_copy(GC,widget->style->black_gc); + gdk_gc_set_foreground(GC,&color); + + //gdk_draw_arc (widget->window, + // widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + // TRUE, + // //0, 0, widget->allocation.width, widget->allocation.height, + // 0, 0, widget->allocation.width, + // control_flow_data->drawing_Area_Info.height, + // 0, 64 * 360); + + + //drawing_Area_Init(control_flow_data); + + // 2 pixels for the box around the drawing area, 1 pixel for off-by-one + // (starting from 0) + //gdk_gc_copy (&GC, widget->style->fg_gc[GTK_WIDGET_STATE (widget)]); + + gdk_gc_set_line_attributes(GC,12, GDK_LINE_SOLID, GDK_CAP_NOT_LAST,GDK_JOIN_MITER); + + gdk_draw_line (widget->window, + GC, + 0, (cell_height-1)/2, + widget->allocation.width, (cell_height-1)/2); + + color = CF_Colors[BLUE]; + + gdk_color_alloc (gdk_colormap_get_system () , &color); + + gdk_gc_set_foreground(GC,&color); + + + gdk_gc_set_line_attributes(GC,3, GDK_LINE_SOLID, GDK_CAP_NOT_LAST,GDK_JOIN_MITER); + + gdk_draw_line (widget->window, + GC, + 0, (cell_height-1)/2, + widget->allocation.width,(cell_height-1)/2); + + + + + + + g_object_unref(GC); + + //gdk_colormap_alloc_colors(gdk_colormap_get_system(), TRUE, + + //gdk_gc_set_line_attributes(GC,5, GDK_LINE_SOLID, GDK_CAP_NOT_LAST,GDK_JOIN_MITER); + //gdk_gc_set_foreground(GC, + + //gdk_draw_line (widget->window, + // GC, + // 0, (2*cell_height)-2-1, + // 50, (2*cell_height)-2-1); + +} +#endif //DEBUG + + +/* Event_Hook.c tests */ + +void test_draw_item(Drawing_t *drawing, + GdkPixmap *pixmap) +{ + PropertiesIcon properties_icon; + DrawContext draw_context; + + DrawInfo current, previous; + ItemInfo over, middle, under, modify_over, modify_middle, modify_under; + + int i=0,j=0; + + //for(i=0; i<1024;i=i+15) + { + // for(j=0;j<768;j=j+15) + { + over.x = i; + over.y = j; + + current.modify_over = &over; + + draw_context.drawable = pixmap; + draw_context.gc = drawing->drawing_area->style->black_gc; + + draw_context.current = ¤t; + draw_context.previous = NULL; + + properties_icon.icon_name = g_new(char, MAX_PATH_LEN); + strncpy(properties_icon.icon_name, + "/home/compudj/local/share/LinuxTraceToolkit/pixmaps/mini-display.xpm", + MAX_PATH_LEN); + properties_icon.width = -1; + properties_icon.height = -1; + properties_icon.position = OVER; + draw_icon(&properties_icon, &draw_context); + g_free(properties_icon.icon_name); + } + } + +} + +#ifdef NOTUSE +/* NOTE : no drawing data should be sent there, since the drawing widget + * has not been initialized */ +void send_test_drawing(ProcessList *process_list, + Drawing_t *drawing, + GdkPixmap *pixmap, + gint x, gint y, // y not used here? + gint width, + gint height) // height won't be used here ? +{ + int i,j; + ProcessInfo Process_Info = {10000, 12000, 55600}; + //ProcessInfo Process_Info = {156, 14000, 55500}; + GtkTreeRowReference *row_ref; + PangoContext *context; + PangoLayout *layout; + PangoFontDescription *FontDesc;// = pango_font_description_new(); + gint Font_Size; + + //icon + //GdkBitmap *mask = g_new(GdkBitmap, 1); + //GdkPixmap *icon_pixmap = g_new(GdkPixmap, 1); + GdkGC * gc; + // rectangle + GdkColor color = { 0, 0xffff, 0x0000, 0x0000 }; + + gc = gdk_gc_new(pixmap); + /* Sent text data */ + layout = gtk_widget_create_pango_layout(drawing->drawing_area, + NULL); + context = pango_layout_get_context(layout); + FontDesc = pango_context_get_font_description(context); + Font_Size = pango_font_description_get_size(FontDesc); + pango_font_description_set_size(FontDesc, Font_Size-3*PANGO_SCALE); + + + + + LttTime birth; + birth.tv_sec = 12000; + birth.tv_nsec = 55500; + g_info("we have : x : %u, y : %u, width : %u, height : %u", x, y, width, height); + processlist_get_process_pixels(process_list, + 1, + &birth, + &y, + &height); + + g_info("we draw : x : %u, y : %u, width : %u, height : %u", x, y, width, height); + drawing_draw_line( + drawing, pixmap, x, + y+(height/2), x + width, y+(height/2), + drawing->drawing_area->style->black_gc); + + pango_layout_set_text(layout, "Test", -1); + gdk_draw_layout(pixmap, drawing->drawing_area->style->black_gc, + 0, y+height, layout); + + birth.tv_sec = 14000; + birth.tv_nsec = 55500; + + processlist_get_process_pixels(process_list, + 156, + &birth, + &y, + &height); + + + drawing_draw_line( + drawing, pixmap, x, + y+(height/2), x + width, y+(height/2), + drawing->drawing_area->style->black_gc); + + g_info("y : %u, height : %u", y, height); + + + + birth.tv_sec = 12000; + birth.tv_nsec = 55700; + + processlist_get_process_pixels(process_list, + 10, + &birth, + &y, + &height); + + /* Draw rectangle (background color) */ + gdk_gc_copy(gc, drawing->drawing_area->style->black_gc); + gdk_gc_set_rgb_fg_color(gc, &color); + gdk_draw_rectangle(pixmap, gc, + TRUE, + x, y, width, height); + + drawing_draw_line( + drawing, pixmap, x, + y+(height/2), x + width, y+(height/2), + drawing->drawing_area->style->black_gc); + + + /* Draw arc */ + gdk_draw_arc(pixmap, drawing->drawing_area->style->black_gc, + TRUE, 100, y, height/2, height/2, 0, 360*64); + + g_info("y : %u, height : %u", y, height); + + for(i=0; i<10; i++) + { + birth.tv_sec = i*12000; + birth.tv_nsec = i*55700; + + processlist_get_process_pixels(process_list, + i, + &birth, + &y, + &height); + + + drawing_draw_line( + drawing, pixmap, x, + y+(height/2), x + width, y+(height/2), + drawing->drawing_area->style->black_gc); + + g_critical("y : %u, height : %u", y, height); + + } + + birth.tv_sec = 12000; + birth.tv_nsec = 55600; + + processlist_get_process_pixels(process_list, + 10, + &birth, + &y, + &height); + + + drawing_draw_line( + drawing, pixmap, x, + y+(height/2), x + width, y+(height/2), + drawing->drawing_area->style->black_gc); + + g_info("y : %u, height : %u", y, height); + + + /* IMPORTANT : This action uses the cpu heavily! */ + //icon_pixmap = gdk_pixmap_create_from_xpm(pixmap, &mask, NULL, +// "/home/compudj/local/share/LinuxTraceToolkit/pixmaps/move_message.xpm"); + // "/home/compudj/local/share/LinuxTraceToolkit/pixmaps/mini-display.xpm"); + + // gdk_gc_set_clip_mask(drawing->drawing_area->style->black_gc, mask); + +// for(i=x;idrawing_area->style->black_gc); +// gdk_gc_set_clip_origin(drawing->drawing_area->style->black_gc, i, j); +// gdk_draw_drawable(pixmap, +// drawing->drawing_area->style->black_gc, +// icon_pixmap, +// 0, 0, i, j, -1, -1); + +// } +// } + + test_draw_item(drawing,pixmap); + + //gdk_gc_set_clip_origin(drawing->drawing_area->style->black_gc, 0, 0); + //gdk_gc_set_clip_mask(drawing->drawing_area->style->black_gc, NULL); + + //g_free(icon_pixmap); + //g_free(mask); + + + + + + + pango_font_description_set_size(FontDesc, Font_Size); + g_object_unref(layout); + g_free(gc); +} + +void send_test_process(ProcessList *process_list, Drawing_t *drawing) +{ + guint height, y; + int i; + ProcessInfo Process_Info = {10000, 12000, 55600}; + //ProcessInfo Process_Info = {156, 14000, 55500}; + GtkTreeRowReference *row_ref; + + LttTime birth; + + if(process_list->Test_Process_Sent) return; + + birth.tv_sec = 12000; + birth.tv_nsec = 55500; + + processlist_add(process_list, + 1, + &birth, + &y); + processlist_get_process_pixels(process_list, + 1, + &birth, + &y, + &height); + drawing_insert_square( drawing, y, height); + + //g_critical("y : %u, height : %u", y, height); + + birth.tv_sec = 14000; + birth.tv_nsec = 55500; + + processlist_add(process_list, + 156, + &birth, + &y); + processlist_get_process_pixels(process_list, + 156, + &birth, + &y, + &height); + drawing_insert_square( drawing, y, height); + + //g_critical("y : %u, height : %u", y, height); + + birth.tv_sec = 12000; + birth.tv_nsec = 55700; + + processlist_add(process_list, + 10, + &birth, + &height); + processlist_get_process_pixels(process_list, + 10, + &birth, + &y, + &height); + drawing_insert_square( drawing, y, height); + + //g_critical("y : %u, height : %u", y, height); + + //drawing_insert_square( drawing, height, 5); + + for(i=0; i<10; i++) + { + birth.tv_sec = i*12000; + birth.tv_nsec = i*55700; + + processlist_add(process_list, + i, + &birth, + &height); + processlist_get_process_pixels(process_list, + i, + &birth, + &y, + &height); + drawing_insert_square( drawing, y, height); + + // g_critical("y : %u, height : %u", y, height); + + } + //g_critical("height : %u", height); + + birth.tv_sec = 12000; + birth.tv_nsec = 55600; + + processlist_add(process_list, + 10, + &birth, + &y); + processlist_get_process_pixels(process_list, + 10, + &birth, + &y, + &height); + drawing_insert_square( drawing, y, height); + + //g_critical("y : %u, height : %u", y, height); + + processlist_add(process_list, + 10000, + &birth, + &height); + processlist_get_process_pixels(process_list, + 10000, + &birth, + &y, + &height); + drawing_insert_square( drawing, y, height); + + //g_critical("y : %u, height : %u", y, height); + + //drawing_insert_square( drawing, height, 5); + //g_critical("height : %u", height); + + + processlist_get_process_pixels(process_list, + 10000, + &birth, + &y, &height); + processlist_remove( process_list, + 10000, + &birth); + + drawing_remove_square( drawing, y, height); + + if(row_ref = + (GtkTreeRowReference*)g_hash_table_lookup( + process_list->process_hash, + &Process_Info)) + { + g_critical("key found"); + g_critical("position in the list : %s", + gtk_tree_path_to_string ( + gtk_tree_row_reference_get_path( + (GtkTreeRowReference*)row_ref) + )); + + } + + process_list->Test_Process_Sent = TRUE; + +} +#endif//NOTUSE + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/statistics/Makefile.am b/tags/lttv-0.11.3-23102008/lttv/modules/gui/statistics/Makefile.am new file mode 100644 index 00000000..912dfba2 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/statistics/Makefile.am @@ -0,0 +1,20 @@ +# +# Makefile for LTT New generation user interface : plugins. +# +# Created by Mathieu Desnoyers on May 6, 2003 +# + +AM_CFLAGS = $(GLIB_CFLAGS) +AM_CFLAGS += $(GTK_CFLAGS) +AM_CFLAGS += -fvisibility=hidden +LIBS += $(GLIB_LIBS) +LIBS += $(GTK_LIBS) -L${top_builddir}/lttv/modules/gui/lttvwindow/lttvwindow -llttvwindow + +libdir = ${lttvplugindir} + +lib_LTLIBRARIES = libguistatistics.la +libguistatistics_la_LDFLAGS = -module -avoid-version +libguistatistics_la_SOURCES = statistics.c + +EXTRA_DIST = \ + hGuiStatisticInsert.xpm diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/statistics/hGuiStatisticInsert.xpm b/tags/lttv-0.11.3-23102008/lttv/modules/gui/statistics/hGuiStatisticInsert.xpm new file mode 100644 index 00000000..1a78b4de --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/statistics/hGuiStatisticInsert.xpm @@ -0,0 +1,27 @@ +/* XPM */ +static char * hGuiStatisticInsert_xpm[] = { +"22 22 2 1", +" c None", +". c #800080", +" ", +" ", +" ", +" .............. ", +" .... ... ", +" .... . ", +" .... . ", +" .... ", +" .... ", +" .... ", +" .... ", +" ... ", +" ... ", +" ... ", +" ... ", +" ... ", +" ... . ", +" ... . ", +" ... ... ", +" .............. ", +" ", +" "}; diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/statistics/statistics.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/statistics/statistics.c new file mode 100644 index 00000000..3f5c51d5 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/statistics/statistics.c @@ -0,0 +1,738 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 XangXiu Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "hGuiStatisticInsert.xpm" + +#define PATH_LENGTH 256 /* CHECK */ +#define MAX_NUMBER_EVENT "max_number_event" + +//???????????????6 +//static GPtrArray * statistic_traceset; + +/** Array containing instanced objects. Used when module is unloaded */ +static GSList *g_statistic_viewer_data_list = NULL ; + +typedef struct _StatisticViewerData StatisticViewerData; + +static void request_background_data(StatisticViewerData *svd); +GtkWidget *guistatistic_get_widget(StatisticViewerData *svd); + +//! Statistic Viewer's constructor hook +GtkWidget *h_gui_statistic(LttvPlugin *plugin); +//! Statistic Viewer's constructor +StatisticViewerData *gui_statistic(LttvPluginTab *ptab); +//! Statistic Viewer's destructor +void gui_statistic_destructor(StatisticViewerData *statistic_viewer_data); + +static void tree_selection_changed_cb (GtkTreeSelection *selection, gpointer data); + +void statistic_destroy_hash_key(gpointer key); +void statistic_destroy_hash_data(gpointer data); + +void show_traceset_stats(StatisticViewerData * statistic_viewer_data); +void show_tree(StatisticViewerData * statistic_viewer_data, + LttvAttribute* stats, GtkTreeIter* parent); +void show_statistic(StatisticViewerData * statistic_viewer_data, + LttvAttribute* stats, GtkTextBuffer* buf); + + +gboolean statistic_traceset_changed(void * hook_data, void * call_data); +//void statistic_add_context_hooks(StatisticViewerData * statistic_viewer_data, +// LttvTracesetContext * tsc); +//void statistic_remove_context_hooks(StatisticViewerData *statistic_viewer_data, +// LttvTracesetContext * tsc); + +//gboolean statistic_insert_traceset_stats(void * stats); + +enum +{ + NAME_COLUMN, + N_COLUMNS +}; + +struct _StatisticViewerData{ + Tab *tab; + LttvPluginTab *ptab; + //LttvTracesetStats * stats; + int size; + + //gboolean shown; //indicate if the statistic is shown or not + //char * filter_key; + + GtkWidget * hpaned_v; + GtkTreeStore * store_m; + GtkWidget * tree_v; + + //scroll window containing Tree View + GtkWidget * scroll_win_tree; + + GtkWidget * text_v; + //scroll window containing Text View + GtkWidget * scroll_win_text; + + // Selection handler + GtkTreeSelection *select_c; + + //hash + GHashTable *statistic_hash; + + guint background_info_waiting; +}; + + + + +/* Action to do when background computation completed. + * + * Eventually, will have to check that every requested traces are finished + * before doing the redraw. It will save unnecessary processor usage. + */ + +static gint background_ready(void *hook_data, void *call_data) +{ + StatisticViewerData *svd = (StatisticViewerData *)hook_data; + Tab *tab = svd->tab; + LttvTrace *trace = (LttvTrace*)call_data; + + svd->background_info_waiting--; + + if(svd->background_info_waiting == 0) { + g_message("statistics viewer : background computation data ready."); + + gtk_tree_store_clear (svd->store_m); + + lttv_stats_sum_traceset(lttvwindow_get_traceset_stats(tab), + ltt_time_infinite); + show_traceset_stats(svd); + } + + return 0; +} + +/* Request background computation. Verify if it is in progress or ready first. + * + * Right now, for all loaded traces. + * + * Later : must be only for each trace in the tab's traceset. + */ +static void request_background_data(StatisticViewerData *svd) +{ + gint num_traces = lttvwindowtraces_get_number(); + gint i; + LttvTrace *trace; + GtkTextBuffer* buf; + + LttvHooks *background_ready_hook = + lttv_hooks_new(); + lttv_hooks_add(background_ready_hook, background_ready, svd, + LTTV_PRIO_DEFAULT); + svd->background_info_waiting = num_traces; + buf = gtk_text_view_get_buffer((GtkTextView*)svd->text_v); + gtk_text_buffer_set_text(buf,"", -1); + + for(i=0;itab), trace, "stats"); + lttvwindowtraces_background_notify_queue(svd, + trace, + ltt_time_infinite, + NULL, + background_ready_hook); + } else { /* in progress */ + lttvwindowtraces_background_notify_current(svd, + trace, + ltt_time_infinite, + NULL, + background_ready_hook); + + } + } else { + /* ready */ + lttv_hooks_call(background_ready_hook, NULL); + } + } + + if(num_traces == 0) { + svd->background_info_waiting = 1; + lttv_hooks_call(background_ready_hook, NULL); + } + lttv_hooks_destroy(background_ready_hook); +} + + +GtkWidget *guistatistic_get_widget(StatisticViewerData *svd) +{ + return svd->hpaned_v; +} + + +void +gui_statistic_destructor(StatisticViewerData *statistic_viewer_data) +{ + Tab *tab = statistic_viewer_data->tab; + + /* May already been done by GTK window closing */ + if(GTK_IS_WIDGET(guistatistic_get_widget(statistic_viewer_data))){ + g_info("widget still exists"); + } + if(tab != NULL) { + lttvwindow_unregister_traceset_notify(statistic_viewer_data->tab, + statistic_traceset_changed, + statistic_viewer_data); + } + lttvwindowtraces_background_notify_remove(statistic_viewer_data); + + g_hash_table_destroy(statistic_viewer_data->statistic_hash); + g_statistic_viewer_data_list = + g_slist_remove(g_statistic_viewer_data_list, statistic_viewer_data); + g_free(statistic_viewer_data); +} + + +/** + * Statistic Viewer's constructor hook + * + * This constructor is given as a parameter to the menuitem and toolbar button + * registration. It creates the list. + * @param parent_window A pointer to the parent window. + * @return The widget created. + */ +GtkWidget * +h_gui_statistic(LttvPlugin *plugin) +{ + LttvPluginTab *ptab = LTTV_PLUGIN_TAB(plugin); + StatisticViewerData* statistic_viewer_data = gui_statistic(ptab) ; + + if(statistic_viewer_data) + return guistatistic_get_widget(statistic_viewer_data); + else return NULL; + +} + +#if 0 +gboolean statistic_insert_traceset_stats(void * stats) +{ + int i, len; + gpointer s; + + len = statistic_traceset->len; + for(i=0;itab; + statistic_viewer_data->tab = tab; + statistic_viewer_data->ptab = ptab; + // statistic_viewer_data->stats = + // lttvwindow_get_traceset_stats(statistic_viewer_data->tab); + // statistic_viewer_data->calculate_stats = + // statistic_insert_traceset_stats((void *)statistic_viewer_data->stats); + + lttvwindow_register_traceset_notify(statistic_viewer_data->tab, + statistic_traceset_changed, + statistic_viewer_data); + + statistic_viewer_data->statistic_hash = g_hash_table_new_full(g_str_hash, + g_str_equal, + statistic_destroy_hash_key, + NULL); + + statistic_viewer_data->hpaned_v = gtk_hpaned_new(); + statistic_viewer_data->store_m = gtk_tree_store_new (N_COLUMNS, G_TYPE_STRING); + statistic_viewer_data->tree_v = + gtk_tree_view_new_with_model ( + GTK_TREE_MODEL (statistic_viewer_data->store_m)); + g_object_unref (G_OBJECT (statistic_viewer_data->store_m)); + + // Setup the selection handler + statistic_viewer_data->select_c = gtk_tree_view_get_selection (GTK_TREE_VIEW (statistic_viewer_data->tree_v)); + gtk_tree_selection_set_mode (statistic_viewer_data->select_c, GTK_SELECTION_SINGLE); + g_signal_connect (G_OBJECT (statistic_viewer_data->select_c), "changed", + G_CALLBACK (tree_selection_changed_cb), + statistic_viewer_data); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Statistic Name", + renderer, + "text", NAME_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + // gtk_tree_view_column_set_fixed_width (column, 45); + gtk_tree_view_append_column (GTK_TREE_VIEW (statistic_viewer_data->tree_v), column); + + + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW (statistic_viewer_data->tree_v), FALSE); + + statistic_viewer_data->scroll_win_tree = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(statistic_viewer_data->scroll_win_tree), + GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); + + gtk_container_add (GTK_CONTAINER (statistic_viewer_data->scroll_win_tree), statistic_viewer_data->tree_v); + gtk_paned_pack1(GTK_PANED(statistic_viewer_data->hpaned_v),statistic_viewer_data->scroll_win_tree, TRUE, FALSE); + gtk_paned_set_position(GTK_PANED(statistic_viewer_data->hpaned_v), 160); + + statistic_viewer_data->scroll_win_text = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(statistic_viewer_data->scroll_win_text), + GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); + + statistic_viewer_data->text_v = gtk_text_view_new (); + + gtk_text_view_set_editable(GTK_TEXT_VIEW(statistic_viewer_data->text_v),FALSE); + gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(statistic_viewer_data->text_v),FALSE); + gtk_container_add (GTK_CONTAINER (statistic_viewer_data->scroll_win_text), statistic_viewer_data->text_v); + gtk_paned_pack2(GTK_PANED(statistic_viewer_data->hpaned_v), statistic_viewer_data->scroll_win_text, TRUE, FALSE); + + gtk_container_set_border_width( + GTK_CONTAINER(statistic_viewer_data->hpaned_v), 1); + + gtk_widget_show(statistic_viewer_data->scroll_win_tree); + gtk_widget_show(statistic_viewer_data->scroll_win_text); + gtk_widget_show(statistic_viewer_data->tree_v); + gtk_widget_show(statistic_viewer_data->text_v); + gtk_widget_show(statistic_viewer_data->hpaned_v); + + g_object_set_data_full( + G_OBJECT(guistatistic_get_widget(statistic_viewer_data)), + "statistic_viewer_data", + statistic_viewer_data, + (GDestroyNotify)gui_statistic_destructor); + + /* Add the object's information to the module's array */ + g_statistic_viewer_data_list = g_slist_append( + g_statistic_viewer_data_list, + statistic_viewer_data); + + request_background_data(statistic_viewer_data); + + return statistic_viewer_data; +} + +static void +tree_selection_changed_cb (GtkTreeSelection *selection, gpointer data) +{ + StatisticViewerData *statistic_viewer_data = (StatisticViewerData*)data; + GtkTreeIter iter; + GtkTreeModel *model = GTK_TREE_MODEL(statistic_viewer_data->store_m); + gchar *event; + GtkTextBuffer* buf; + gchar * str; + GtkTreePath * path; + GtkTextIter text_iter; + LttvAttribute * stats; + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) + { + gtk_tree_model_get (model, &iter, NAME_COLUMN, &event, -1); + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(model),&iter); + str = gtk_tree_path_to_string (path); + stats = (LttvAttribute*)g_hash_table_lookup (statistic_viewer_data->statistic_hash,str); + g_free(str); + + buf = gtk_text_view_get_buffer((GtkTextView*)statistic_viewer_data->text_v); + gtk_text_buffer_set_text(buf,"Statistic for '", -1); + gtk_text_buffer_get_end_iter(buf, &text_iter); + gtk_text_buffer_insert(buf, &text_iter, event, strlen(event)); + gtk_text_buffer_get_end_iter(buf, &text_iter); + gtk_text_buffer_insert(buf, &text_iter, "' :\n\n",5); + + show_statistic(statistic_viewer_data, stats, buf); + + g_free (event); + } +} + +void statistic_destroy_hash_key(gpointer key) +{ + g_free(key); +} + +#ifdef DEBUG +#include +extern FILE *stdin; +extern FILE *stdout; +extern FILE *stderr; +#endif //DEBUG + +void show_traceset_stats(StatisticViewerData * statistic_viewer_data) +{ + Tab *tab = statistic_viewer_data->tab; + int i, nb; + LttvTraceset *ts; + LttvTraceStats *tcs; + LttSystemDescription *desc; + LttvTracesetStats * tscs = lttvwindow_get_traceset_stats(tab); + gchar * str, trace_str[PATH_LENGTH]; + GtkTreePath * path; + GtkTreeIter iter; + GtkTreeStore * store = statistic_viewer_data->store_m; + + if(tscs->stats == NULL) return; +#ifdef DEBUG + lttv_attribute_write_xml(tscs->stats, stdout, 1, 4); +#endif //DEBUG + + ts = tscs->parent.parent.ts; + nb = lttv_traceset_number(ts); + if(nb == 0) return; + + gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_set (store, &iter, + NAME_COLUMN, "Traceset statistics", + -1); + path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter); + str = gtk_tree_path_to_string (path); + g_hash_table_insert(statistic_viewer_data->statistic_hash, + (gpointer)str, tscs->stats); + show_tree(statistic_viewer_data, tscs->stats, &iter); + + //show stats for all traces + for(i = 0 ; i < nb ; i++) { + tcs = (LttvTraceStats *)(LTTV_TRACESET_CONTEXT(tscs)->traces[i]); +#if 0 //FIXME + desc = ltt_trace_system_description(tcs->parent.parent.t); + LttTime start_time = ltt_trace_system_description_trace_start_time(desc); + sprintf(trace_str, "Trace on system %s at time %lu.%09lu", + ltt_trace_system_description_node_name(desc), + start_time.tv_sec, + start_time.tv_nsec); +#endif //0 + sprintf(trace_str, g_quark_to_string(ltt_trace_name(tcs->parent.parent.t))); + gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_set (store, &iter,NAME_COLUMN,trace_str,-1); + path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter); + str = gtk_tree_path_to_string (path); + g_hash_table_insert(statistic_viewer_data->statistic_hash, + (gpointer)str,tcs->stats); + show_tree(statistic_viewer_data, tcs->stats, &iter); +#ifdef DEBUG + lttv_attribute_write_xml(tcs->stats, stdout, 3, 4); +#endif //DEBUG + } +} + +void show_tree(StatisticViewerData * statistic_viewer_data, + LttvAttribute* stats, GtkTreeIter* parent) +{ + int i, nb; + LttvAttribute *subtree; + LttvAttributeName name; + LttvAttributeValue value; + LttvAttributeType type; + gboolean is_named; + gchar * str, dir_str[PATH_LENGTH]; + GtkTreePath * path; + GtkTreeIter iter; + GtkTreeStore * store = statistic_viewer_data->store_m; + + nb = lttv_attribute_get_number(stats); + for(i = 0 ; i < nb ; i++) { + type = lttv_attribute_get(stats, i, &name, &value, &is_named); + switch(type) { + case LTTV_GOBJECT: + if(LTTV_IS_ATTRIBUTE(*(value.v_gobject))) { + subtree = (LttvAttribute *)*(value.v_gobject); + if(is_named) + sprintf(dir_str, "%s", g_quark_to_string(name)); + else + sprintf(dir_str, "%u", name); + gtk_tree_store_append (store, &iter, parent); + gtk_tree_store_set (store, &iter,NAME_COLUMN,dir_str,-1); + path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter); + str = gtk_tree_path_to_string (path); + g_hash_table_insert(statistic_viewer_data->statistic_hash, + (gpointer)str, subtree); + show_tree(statistic_viewer_data, subtree, &iter); + } + break; + default: + break; + } + } +} + +void show_statistic(StatisticViewerData * statistic_viewer_data, + LttvAttribute* stats, GtkTextBuffer* buf) +{ + int i, nb , flag; + LttvAttributeName name; + LttvAttributeValue value; + LttvAttributeType type; + gboolean is_named; + gchar type_name[PATH_LENGTH], type_value[PATH_LENGTH]; + GtkTextIter text_iter; + + flag = 0; + nb = lttv_attribute_get_number(stats); + for(i = 0 ; i < nb ; i++) { + type = lttv_attribute_get(stats, i, &name, &value, &is_named); + if(is_named) + sprintf(type_name,"%s", g_quark_to_string(name)); + else + sprintf(type_name,"%u", name); + type_value[0] = '\0'; + switch(type) { + case LTTV_INT: + sprintf(type_value, " : %d\n", *value.v_int); + break; + case LTTV_UINT: + sprintf(type_value, " : %u\n", *value.v_uint); + break; + case LTTV_LONG: + sprintf(type_value, " : %ld\n", *value.v_long); + break; + case LTTV_ULONG: + sprintf(type_value, " : %lu\n", *value.v_ulong); + break; + case LTTV_FLOAT: + sprintf(type_value, " : %f\n", (double)*value.v_float); + break; + case LTTV_DOUBLE: + sprintf(type_value, " : %f\n", *value.v_double); + break; + case LTTV_TIME: + sprintf(type_value, " : %10lu.%09lu\n", value.v_time->tv_sec, + value.v_time->tv_nsec); + break; + case LTTV_POINTER: + sprintf(type_value, " : POINTER\n"); + break; + case LTTV_STRING: + sprintf(type_value, " : %s\n", *value.v_string); + break; + default: + break; + } + if(strlen(type_value)){ + flag = 1; + strcat(type_name,type_value); + gtk_text_buffer_get_end_iter(buf, &text_iter); + gtk_text_buffer_insert(buf, &text_iter, type_name, strlen(type_name)); + } + } + + if(flag == 0){ + sprintf(type_value, "No statistic information in this directory.\nCheck in subdirectories please.\n"); + gtk_text_buffer_get_end_iter(buf, &text_iter); + gtk_text_buffer_insert(buf, &text_iter, type_value, strlen(type_value)); + + } +} + +gboolean statistic_traceset_changed(void * hook_data, void * call_data) +{ + StatisticViewerData *statistic_viewer_data = (StatisticViewerData*) hook_data; + + request_background_data(statistic_viewer_data); + + return FALSE; +} + +#if 0 +void statistic_add_context_hooks(StatisticViewerData * statistic_viewer_data, + LttvTracesetContext * tsc) +{ + gint i, j, nbi, nb_tracefile; + LttTrace *trace; + LttvTraceContext *tc; + LttvTracefileContext *tfc; + LttvTracesetSelector * ts_s; + LttvTraceSelector * t_s; + LttvTracefileSelector * tf_s; + gboolean selected; + + ts_s = (LttvTracesetSelector*)g_object_get_data(G_OBJECT(statistic_viewer_data->hpaned_v), + statistic_viewer_data->filter_key); + + //if there are hooks for traceset, add them here + + nbi = lttv_traceset_number(tsc->ts); + for(i = 0 ; i < nbi ; i++) { + t_s = lttv_traceset_selector_trace_get(ts_s,i); + selected = lttv_trace_selector_get_selected(t_s); + if(!selected) continue; + tc = tsc->traces[i]; + trace = tc->t; + //if there are hooks for trace, add them here + + nb_tracefile = ltt_trace_control_tracefile_number(trace) + + ltt_trace_per_cpu_tracefile_number(trace); + + for(j = 0 ; j < nb_tracefile ; j++) { + tf_s = lttv_trace_selector_tracefile_get(t_s,j); + selected = lttv_tracefile_selector_get_selected(tf_s); + if(!selected) continue; + tfc = tc->tracefiles[j]; + + //if there are hooks for tracefile, add them here + // lttv_tracefile_context_add_hooks(tfc, NULL,NULL,NULL,NULL, + // statistic_viewer_data->before_event_hooks,NULL); + } + } + + lttv_stats_add_event_hooks(LTTV_TRACESET_STATS(tsc)); + +} + +void statistic_remove_context_hooks(StatisticViewerData * statistic_viewer_data, + LttvTracesetContext * tsc) +{ + gint i, j, nbi, nb_tracefile; + LttTrace *trace; + LttvTraceContext *tc; + LttvTracefileContext *tfc; + LttvTracesetSelector * ts_s; + LttvTraceSelector * t_s; + LttvTracefileSelector * tf_s; + gboolean selected; + + ts_s = (LttvTracesetSelector*)g_object_get_data(G_OBJECT(statistic_viewer_data->hpaned_v), + statistic_viewer_data->filter_key); + + //if there are hooks for traceset, remove them here + + nbi = lttv_traceset_number(tsc->ts); + for(i = 0 ; i < nbi ; i++) { + t_s = lttv_traceset_selector_trace_get(ts_s,i); + selected = lttv_trace_selector_get_selected(t_s); + if(!selected) continue; + tc = tsc->traces[i]; + trace = tc->t; + //if there are hooks for trace, remove them here + + nb_tracefile = ltt_trace_control_tracefile_number(trace) + + ltt_trace_per_cpu_tracefile_number(trace); + + for(j = 0 ; j < nb_tracefile ; j++) { + tf_s = lttv_trace_selector_tracefile_get(t_s,j); + selected = lttv_tracefile_selector_get_selected(tf_s); + if(!selected) continue; + tfc = tc->tracefiles[j]; + + //if there are hooks for tracefile, remove them here + // lttv_tracefile_context_remove_hooks(tfc, NULL,NULL,NULL,NULL, + // statistic_viewer_data->before_event_hooks,NULL); + } + } + + lttv_stats_remove_event_hooks(LTTV_TRACESET_STATS(tsc)); +} +#endif //0 + +/** + * plugin's init function + * + * This function initializes the Statistic Viewer functionnality through the + * gtkTraceSet API. + */ +static void init() { + + lttvwindow_register_constructor("guistatistics", + "/", + "Insert Statistic Viewer", + hGuiStatisticInsert_xpm, + "Insert Statistic Viewer", + h_gui_statistic); +} + +void statistic_destroy_walk(gpointer data, gpointer user_data) +{ + StatisticViewerData *svd = (StatisticViewerData*)data; + + g_debug("CFV.c : statistic_destroy_walk, %p", svd); + /* May already have been done by GTK window closing */ + if(GTK_IS_WIDGET(guistatistic_get_widget(svd))) + gtk_widget_destroy(guistatistic_get_widget(svd)); +} + +/** + * plugin's destroy function + * + * This function releases the memory reserved by the module and unregisters + * everything that has been registered in the gtkTraceSet API. + */ +static void destroy() { + + g_slist_foreach(g_statistic_viewer_data_list, statistic_destroy_walk, NULL ); + g_slist_free(g_statistic_viewer_data_list); + + lttvwindow_unregister_constructor(h_gui_statistic); + +} + + +LTTV_MODULE("guistatistics", "Statistics viewer", \ + "Graphical module to view statistics about processes, CPUs and systems", \ + init, destroy, "lttvwindow") + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/tracecontrol/Makefile.am b/tags/lttv-0.11.3-23102008/lttv/modules/gui/tracecontrol/Makefile.am new file mode 100644 index 00000000..42925da7 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/tracecontrol/Makefile.am @@ -0,0 +1,23 @@ +# +# Makefile for LTT New generation user interface : plugins. +# +# Created by Mathieu Desnoyers on May 6, 2003 +# + +AM_CFLAGS = $(GLIB_CFLAGS) +AM_CFLAGS += $(GTK_CFLAGS) +AM_CFLAGS += -DPACKAGE_DATA_DIR=\""$(datadir)"\" -DPACKAGE_BIN_DIR=\""$(bindir)"\" +AM_CFLAGS += -fvisibility=hidden +LIBS += $(GLIB_LIBS) +LIBS += $(GTK_LIBS) -L${top_builddir}/lttv/modules/gui/lttvwindow/lttvwindow -llttvwindow +LIBS += $(UTIL_LIBS) + +libdir = ${lttvplugindir} + +lib_LTLIBRARIES = libguitracecontrol.la +libguitracecontrol_la_LDFLAGS = -module -avoid-version +libguitracecontrol_la_SOURCES = tracecontrol.c + +EXTRA_DIST = \ + hTraceControlInsert.xpm TraceControlStart.xpm TraceControlPause.xpm \ + TraceControlStop.xpm diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/tracecontrol/TraceControlPause.xpm b/tags/lttv-0.11.3-23102008/lttv/modules/gui/tracecontrol/TraceControlPause.xpm new file mode 100644 index 00000000..ac2c2734 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/tracecontrol/TraceControlPause.xpm @@ -0,0 +1,30 @@ +/* XPM */ +static char * TraceControlPause_xpm[] = { +"22 22 5 1", +" c None", +". c #000000", +"+ c #570404", +"@ c #EE8100", +"# c #053C0A", +" ..... ", +" ....... ", +" ...+++... ", +" ..+++++.. ", +" ..+++++.. ", +" ..+++++.. ", +" ...+++... ", +" ......... ", +" ...@@@... ", +" ..@@@@@.. ", +" ..@@@@@.. ", +" ..@@@@@.. ", +" ...@@@... ", +" ......... ", +" ...###... ", +" ..#####.. ", +" ..#####.. ", +" ..#####.. ", +" ..###.. ", +" ....... ", +" ....... ", +" ..... "}; diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/tracecontrol/TraceControlStart.xpm b/tags/lttv-0.11.3-23102008/lttv/modules/gui/tracecontrol/TraceControlStart.xpm new file mode 100644 index 00000000..fc225d01 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/tracecontrol/TraceControlStart.xpm @@ -0,0 +1,30 @@ +/* XPM */ +static char * TraceControlStart_xpm[] = { +"22 22 5 1", +" c None", +". c #000000", +"+ c #570404", +"@ c #6B3A00", +"# c #12E826", +" ..... ", +" ....... ", +" ...+++... ", +" ..+++++.. ", +" ..+++++.. ", +" ..+++++.. ", +" ...+++... ", +" ......... ", +" ...@@@... ", +" ..@@@@@.. ", +" ..@@@@@.. ", +" ..@@@@@.. ", +" ...@@@... ", +" ......... ", +" ...###... ", +" ..#####.. ", +" ..#####.. ", +" ..#####.. ", +" ..###.. ", +" ....... ", +" ....... ", +" ..... "}; diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/tracecontrol/TraceControlStop.xpm b/tags/lttv-0.11.3-23102008/lttv/modules/gui/tracecontrol/TraceControlStop.xpm new file mode 100644 index 00000000..b01067be --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/tracecontrol/TraceControlStop.xpm @@ -0,0 +1,30 @@ +/* XPM */ +static char * TraceControlStop_xpm[] = { +"22 22 5 1", +" c None", +". c #000000", +"+ c #D00A0A", +"@ c #6B3A00", +"# c #053C0A", +" ..... ", +" ....... ", +" ...+++... ", +" ..+++++.. ", +" ..+++++.. ", +" ..+++++.. ", +" ...+++... ", +" ......... ", +" ...@@@... ", +" ..@@@@@.. ", +" ..@@@@@.. ", +" ..@@@@@.. ", +" ...@@@... ", +" ......... ", +" ...###... ", +" ..#####.. ", +" ..#####.. ", +" ..#####.. ", +" ..###.. ", +" ....... ", +" ....... ", +" ..... "}; diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/tracecontrol/hTraceControlInsert.xpm b/tags/lttv-0.11.3-23102008/lttv/modules/gui/tracecontrol/hTraceControlInsert.xpm new file mode 100644 index 00000000..fecc6653 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/tracecontrol/hTraceControlInsert.xpm @@ -0,0 +1,30 @@ +/* XPM */ +static char * hTraceControlInsert_xpm[] = { +"22 22 5 1", +" c None", +". c #000000", +"+ c #D00A0A", +"@ c #EE8100", +"# c #12E826", +" ..... ", +" ....... ", +" ...+++... ", +" ..+++++.. ", +" ..+++++.. ", +" ..+++++.. ", +" ...+++... ", +" ......... ", +" ...@@@... ", +" ..@@@@@.. ", +" ..@@@@@.. ", +" ..@@@@@.. ", +" ...@@@... ", +" ......... ", +" ...###... ", +" ..#####.. ", +" ..#####.. ", +" ..#####.. ", +" ..###.. ", +" ....... ", +" ....... ", +" ..... "}; diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/tracecontrol/tracecontrol.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/tracecontrol/tracecontrol.c new file mode 100644 index 00000000..768095cd --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/tracecontrol/tracecontrol.c @@ -0,0 +1,1158 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2005 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "hTraceControlInsert.xpm" +#include "TraceControlStart.xpm" +#include "TraceControlPause.xpm" +#include "TraceControlStop.xpm" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_ARGS_LEN PATH_MAX * 10 + +GSList *g_control_list = NULL ; + +/*! \file lttv/modules/gui/tracecontrol/tracecontrol.c + * \brief Graphic trace start/stop control interface. + * + * This plugin interacts with lttctl to start/stop tracing. It needs to take the + * root password to be able to interact with lttctl. + * + */ + +typedef struct _ControlData ControlData; + +/* + * Prototypes + */ +GtkWidget *guicontrol_get_widget(ControlData *tcd); +ControlData *gui_control(LttvPluginTab *ptab); +void gui_control_destructor(ControlData *tcd); +GtkWidget* h_guicontrol(LttvPlugin *plugin); +void control_destroy_walk(gpointer data, gpointer user_data); + +/* + * Callback functions + */ + +static void start_clicked (GtkButton *button, gpointer user_data); +static void pause_clicked (GtkButton *button, gpointer user_data); +static void unpause_clicked (GtkButton *button, gpointer user_data); +static void stop_clicked (GtkButton *button, gpointer user_data); + + +/** + * @struct _ControlData + * + * @brief Main structure of gui control + */ +struct _ControlData { + Tab *tab; /**< current tab of module */ + + GtkWidget *window; /**< window */ + + GtkWidget *main_box; /**< main container */ + GtkWidget *start_button; + GtkWidget *pause_button; + GtkWidget *unpause_button; + GtkWidget *stop_button; + GtkWidget *username_label; + GtkWidget *username_entry; + GtkWidget *password_label; + GtkWidget *password_entry; + GtkWidget *channel_dir_label; + GtkWidget *channel_dir_entry; + GtkWidget *trace_dir_label; + GtkWidget *trace_dir_entry; + GtkWidget *trace_name_label; + GtkWidget *trace_name_entry; + GtkWidget *trace_mode_label; + GtkWidget *trace_mode_combo; + GtkWidget *start_daemon_label; + GtkWidget *start_daemon_check; + GtkWidget *append_label; + GtkWidget *append_check; + GtkWidget *optional_label; + GtkWidget *subbuf_size_label; + GtkWidget *subbuf_size_entry; + GtkWidget *subbuf_num_label; + GtkWidget *subbuf_num_entry; + GtkWidget *lttd_threads_label; + GtkWidget *lttd_threads_entry; + GtkWidget *lttctl_path_label; + GtkWidget *lttctl_path_entry; + GtkWidget *lttd_path_label; + GtkWidget *lttd_path_entry; + GtkWidget *fac_path_label; + GtkWidget *fac_path_entry; +}; + +/** + * @fn GtkWidget* guicontrol_get_widget(ControlData*) + * + * This function returns the current main widget + * used by this module + * @param tcd the module struct + * @return The main widget + */ +GtkWidget* +guicontrol_get_widget(ControlData *tcd) +{ + return tcd->window; +} + +/** + * @fn ControlData* gui_control(Tab*) + * + * Constructor is used to create ControlData data structure. + * @param tab The tab structure used by the widget + * @return The Filter viewer data created. + */ +ControlData* +gui_control(LttvPluginTab *ptab) +{ + Tab *tab = ptab->tab; + g_debug("filter::gui_control()"); + + unsigned i; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + ControlData* tcd = g_new(ControlData,1); + + tcd->tab = tab; + + tcd->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(tcd->window), "LTTng Trace Control"); + /* + * Initiating GtkTable layout + * starts with 2 rows and 5 columns and + * expands when expressions added + */ + tcd->main_box = gtk_table_new(14,7,FALSE); + gtk_table_set_row_spacings(GTK_TABLE(tcd->main_box),5); + gtk_table_set_col_spacings(GTK_TABLE(tcd->main_box),5); + + gtk_container_add(GTK_CONTAINER(tcd->window), GTK_WIDGET(tcd->main_box)); + + GList *focus_chain = NULL; + + /* + * start/pause/stop buttons + */ + GdkPixbuf *pixbuf; + GtkWidget *image; + pixbuf = gdk_pixbuf_new_from_xpm_data((const char **)TraceControlStart_xpm); + image = gtk_image_new_from_pixbuf(pixbuf); + tcd->start_button = gtk_button_new_with_label("start"); + //2.6 gtk_button_set_image(GTK_BUTTON(tcd->start_button), image); + g_object_set(G_OBJECT(tcd->start_button), "image", image, NULL); + gtk_button_set_alignment(GTK_BUTTON(tcd->start_button), 0.0, 0.0); + gtk_widget_show (tcd->start_button); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->start_button,6,7,0,1,GTK_FILL,GTK_FILL,2,2); + + pixbuf = gdk_pixbuf_new_from_xpm_data((const char **)TraceControlPause_xpm); + image = gtk_image_new_from_pixbuf(pixbuf); + tcd->pause_button = gtk_button_new_with_label("pause"); + //2.6 gtk_button_set_image(GTK_BUTTON(tcd->pause_button), image); + g_object_set(G_OBJECT(tcd->pause_button), "image", image, NULL); + gtk_button_set_alignment(GTK_BUTTON(tcd->pause_button), 0.0, 0.0); + gtk_widget_show (tcd->pause_button); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->pause_button,6,7,1,2,GTK_FILL,GTK_FILL,2,2); + + pixbuf = gdk_pixbuf_new_from_xpm_data((const char **)TraceControlPause_xpm); + image = gtk_image_new_from_pixbuf(pixbuf); + tcd->unpause_button = gtk_button_new_with_label("unpause"); + //2.6 gtk_button_set_image(GTK_BUTTON(tcd->unpause_button), image); + g_object_set(G_OBJECT(tcd->unpause_button), "image", image, NULL); + gtk_button_set_alignment(GTK_BUTTON(tcd->unpause_button), 0.0, 0.0); + gtk_widget_show (tcd->unpause_button); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->unpause_button,6,7,2,3,GTK_FILL,GTK_FILL,2,2); + + pixbuf = gdk_pixbuf_new_from_xpm_data((const char **)TraceControlStop_xpm); + image = gtk_image_new_from_pixbuf(pixbuf); + tcd->stop_button = gtk_button_new_with_label("stop"); + //2.6 gtk_button_set_image(GTK_BUTTON(tcd->stop_button), image); + g_object_set(G_OBJECT(tcd->stop_button), "image", image, NULL); + gtk_button_set_alignment(GTK_BUTTON(tcd->stop_button), 0.0, 0.0); + gtk_widget_show (tcd->stop_button); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->stop_button,6,7,3,4,GTK_FILL,GTK_FILL,2,2); + + /* + * First half of the filter window + * - textual entry of filter expression + * - processing button + */ + tcd->username_label = gtk_label_new("Username:"); + gtk_widget_show (tcd->username_label); + tcd->username_entry = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(tcd->username_entry),"root"); + gtk_widget_show (tcd->username_entry); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->username_label,0,2,0,1,GTK_FILL,GTK_FILL,2,2); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->username_entry,2,6,0,1,GTK_FILL|GTK_EXPAND|GTK_SHRINK,GTK_FILL,0,0); + + + + tcd->password_label = gtk_label_new("Password:"); + gtk_widget_show (tcd->password_label); + tcd->password_entry = gtk_entry_new(); + gtk_entry_set_visibility(GTK_ENTRY(tcd->password_entry), FALSE); + gtk_widget_show (tcd->password_entry); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->password_label,0,2,1,2,GTK_FILL,GTK_FILL,2,2); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->password_entry,2,6,1,2,GTK_FILL|GTK_EXPAND|GTK_SHRINK,GTK_FILL,0,0); + + + tcd->channel_dir_label = gtk_label_new("Channel directory:"); + gtk_widget_show (tcd->channel_dir_label); + tcd->channel_dir_entry = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(tcd->channel_dir_entry),"/mnt/debugfs/ltt"); + gtk_widget_show (tcd->channel_dir_entry); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->channel_dir_label,0,2,2,3,GTK_FILL,GTK_FILL,2,2); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->channel_dir_entry,2,6,2,3,GTK_FILL|GTK_EXPAND|GTK_SHRINK,GTK_FILL,0,0); + + tcd->trace_dir_label = gtk_label_new("Trace directory:"); + gtk_widget_show (tcd->trace_dir_label); + tcd->trace_dir_entry = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(tcd->trace_dir_entry),"/tmp/trace1"); + gtk_widget_show (tcd->trace_dir_entry); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->trace_dir_label,0,2,3,4,GTK_FILL,GTK_FILL,2,2); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->trace_dir_entry,2,6,3,4,GTK_FILL|GTK_EXPAND|GTK_SHRINK,GTK_FILL,0,0); + + tcd->trace_name_label = gtk_label_new("Trace name:"); + gtk_widget_show (tcd->trace_name_label); + tcd->trace_name_entry = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(tcd->trace_name_entry),"trace"); + gtk_widget_show (tcd->trace_name_entry); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->trace_name_label,0,2,4,5,GTK_FILL,GTK_FILL,2,2); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->trace_name_entry,2,6,4,5,GTK_FILL|GTK_EXPAND|GTK_SHRINK,GTK_FILL,0,0); + + tcd->trace_mode_label = gtk_label_new("Trace mode "); + gtk_widget_show (tcd->trace_mode_label); + tcd->trace_mode_combo = gtk_combo_box_new_text(); + gtk_combo_box_append_text(GTK_COMBO_BOX(tcd->trace_mode_combo), + "normal"); + gtk_combo_box_append_text(GTK_COMBO_BOX(tcd->trace_mode_combo), + "flight recorder"); + gtk_combo_box_set_active(GTK_COMBO_BOX(tcd->trace_mode_combo), 0); + gtk_widget_show (tcd->trace_mode_combo); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->trace_mode_label,0,2,5,6,GTK_FILL,GTK_FILL,2,2); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->trace_mode_combo,2,6,5,6,GTK_FILL|GTK_EXPAND|GTK_SHRINK,GTK_FILL,0,0); + + tcd->start_daemon_label = gtk_label_new("Start daemon "); + gtk_widget_show (tcd->start_daemon_label); + tcd->start_daemon_check = gtk_check_button_new(); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tcd->start_daemon_check), TRUE); + gtk_widget_show (tcd->start_daemon_check); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->start_daemon_label,0,2,6,7,GTK_FILL,GTK_FILL,2,2); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->start_daemon_check,2,6,6,7,GTK_FILL,GTK_FILL,0,0); + + tcd->append_label = gtk_label_new("Append to trace "); + gtk_widget_show (tcd->append_label); + tcd->append_check = gtk_check_button_new(); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tcd->append_check), FALSE); + gtk_widget_show (tcd->append_check); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->append_label,0,2,7,8,GTK_FILL,GTK_FILL,2,2); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->append_check,2,6,7,8,GTK_FILL,GTK_FILL,0,0); + + + tcd->optional_label = gtk_label_new("Optional fields "); + gtk_widget_show (tcd->optional_label); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->optional_label,0,6,8,9,GTK_FILL,GTK_FILL,2,2); + + tcd->subbuf_size_label = gtk_label_new("Subbuffer size:"); + gtk_widget_show (tcd->subbuf_size_label); + tcd->subbuf_size_entry = gtk_entry_new(); + gtk_widget_show (tcd->subbuf_size_entry); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->subbuf_size_label,0,2,9,10,GTK_FILL,GTK_FILL,2,2); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->subbuf_size_entry,2,6,9,10,GTK_FILL|GTK_EXPAND|GTK_SHRINK,GTK_FILL,0,0); + + tcd->subbuf_num_label = gtk_label_new("Number of subbuffers:"); + gtk_widget_show (tcd->subbuf_num_label); + tcd->subbuf_num_entry = gtk_entry_new(); + gtk_widget_show (tcd->subbuf_num_entry); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->subbuf_num_label,0,2,10,11,GTK_FILL,GTK_FILL,2,2); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->subbuf_num_entry,2,6,10,11,GTK_FILL|GTK_EXPAND|GTK_SHRINK,GTK_FILL,0,0); + + tcd->lttd_threads_label = gtk_label_new("Number of lttd threads:"); + gtk_widget_show (tcd->lttd_threads_label); + tcd->lttd_threads_entry = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(tcd->lttd_threads_entry), "1"); + gtk_widget_show (tcd->lttd_threads_entry); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->lttd_threads_label,0,2,11,12,GTK_FILL,GTK_FILL,2,2); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->lttd_threads_entry,2,6,11,12,GTK_FILL|GTK_EXPAND|GTK_SHRINK,GTK_FILL,0,0); + + tcd->lttctl_path_label = gtk_label_new("path to lttctl:"); + gtk_widget_show (tcd->lttctl_path_label); + tcd->lttctl_path_entry = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(tcd->lttctl_path_entry),PACKAGE_BIN_DIR "/lttctl"); + gtk_widget_show (tcd->lttctl_path_entry); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->lttctl_path_label,0,2,12,13,GTK_FILL,GTK_FILL,2,2); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->lttctl_path_entry,2,6,12,13,GTK_FILL|GTK_EXPAND|GTK_SHRINK,GTK_FILL,0,0); + + + tcd->lttd_path_label = gtk_label_new("path to lttd:"); + gtk_widget_show (tcd->lttd_path_label); + tcd->lttd_path_entry = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(tcd->lttd_path_entry),PACKAGE_BIN_DIR "/lttd"); + gtk_widget_show (tcd->lttd_path_entry); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->lttd_path_label,0,2,13,14,GTK_FILL,GTK_FILL,2,2); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->lttd_path_entry,2,6,13,14,GTK_FILL|GTK_EXPAND|GTK_SHRINK,GTK_FILL,0,0); + + + tcd->fac_path_label = gtk_label_new("path to facilities:"); + gtk_widget_show (tcd->fac_path_label); + tcd->fac_path_entry = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(tcd->fac_path_entry),PACKAGE_DATA_DIR "/" "ltt-control" "/facilities"); + gtk_widget_set_size_request(tcd->fac_path_entry, 250, -1); + gtk_widget_show (tcd->fac_path_entry); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->fac_path_label,0,2,14,15,GTK_FILL,GTK_FILL,2,2); + gtk_table_attach( GTK_TABLE(tcd->main_box),tcd->fac_path_entry,2,6,14,15,GTK_FILL|GTK_EXPAND|GTK_SHRINK,GTK_FILL,0,0); + + focus_chain = g_list_append (focus_chain, tcd->username_entry); + focus_chain = g_list_append (focus_chain, tcd->password_entry); + focus_chain = g_list_append (focus_chain, tcd->start_button); + focus_chain = g_list_append (focus_chain, tcd->pause_button); + focus_chain = g_list_append (focus_chain, tcd->unpause_button); + focus_chain = g_list_append (focus_chain, tcd->stop_button); + focus_chain = g_list_append (focus_chain, tcd->channel_dir_entry); + focus_chain = g_list_append (focus_chain, tcd->trace_dir_entry); + focus_chain = g_list_append (focus_chain, tcd->trace_name_entry); + focus_chain = g_list_append (focus_chain, tcd->trace_mode_combo); + focus_chain = g_list_append (focus_chain, tcd->start_daemon_check); + focus_chain = g_list_append (focus_chain, tcd->append_check); + focus_chain = g_list_append (focus_chain, tcd->subbuf_size_entry); + focus_chain = g_list_append (focus_chain, tcd->subbuf_num_entry); + focus_chain = g_list_append (focus_chain, tcd->lttd_threads_entry); + focus_chain = g_list_append (focus_chain, tcd->lttctl_path_entry); + focus_chain = g_list_append (focus_chain, tcd->lttd_path_entry); + focus_chain = g_list_append (focus_chain, tcd->fac_path_entry); + + gtk_container_set_focus_chain(GTK_CONTAINER(tcd->main_box), focus_chain); + + g_list_free(focus_chain); + + g_signal_connect(G_OBJECT(tcd->start_button), "clicked", + (GCallback)start_clicked, tcd); + g_signal_connect(G_OBJECT(tcd->pause_button), "clicked", + (GCallback)pause_clicked, tcd); + g_signal_connect(G_OBJECT(tcd->unpause_button), "clicked", + (GCallback)unpause_clicked, tcd); + g_signal_connect(G_OBJECT(tcd->stop_button), "clicked", + (GCallback)stop_clicked, tcd); + + /* + * show main container + */ + gtk_widget_show(tcd->main_box); + gtk_widget_show(tcd->window); + + + g_object_set_data_full( + G_OBJECT(guicontrol_get_widget(tcd)), + "control_viewer_data", + tcd, + (GDestroyNotify)gui_control_destructor); + + g_control_list = g_slist_append( + g_control_list, + tcd); + + return tcd; +} + + +/** + * @fn void gui_control_destructor(ControlData*) + * + * Destructor for the filter gui module + * @param tcd The module structure + */ +void +gui_control_destructor(ControlData *tcd) +{ + Tab *tab = tcd->tab; + + /* May already been done by GTK window closing */ + if(GTK_IS_WIDGET(guicontrol_get_widget(tcd))){ + g_info("widget still exists"); + } +// if(tab != NULL) { +// lttvwindow_unregister_traceset_notify(tcd->tab, +// filter_traceset_changed, +// filter_viewer_data); +// } + lttvwindowtraces_background_notify_remove(tcd); + + g_control_list = g_slist_remove(g_control_list, tcd); + + g_free(tcd); +} + +static int execute_command(const gchar *command, const gchar *username, + const gchar *password, const gchar *lttd_path, const gchar *fac_path) +{ + pid_t pid; + int fdpty; + pid = forkpty(&fdpty, NULL, NULL, NULL); + int retval = 0; + + if(pid > 0) { + /* parent */ + gchar buf[256]; + int status; + ssize_t count; + /* discuss with su */ + struct timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + struct pollfd pollfd; + int num_rdy; + int num_hup = 0; + enum read_state { GET_LINE, GET_SEMI, GET_SPACE } read_state = GET_LINE; + + retval = fcntl(fdpty, F_SETFL, O_WRONLY); + if(retval == -1) { + perror("Error in fcntl"); + goto wait_child; + } + + /* Read the output from the child terminal before the prompt. If no data in + * 200 ms, we stop reading to give the password */ + g_info("Reading from child console..."); + while(1) { + pollfd.fd = fdpty; + pollfd.events = POLLIN|POLLPRI|POLLERR|POLLHUP|POLLNVAL; + + num_rdy = poll(&pollfd, 1, -1); + if(num_rdy == -1) { + perror("Poll error"); + goto wait_child; + } + + /* Timeout : Stop waiting for chars */ + if(num_rdy == 0) goto wait_child; + + switch(pollfd.revents) { + case POLLERR: + g_warning("Error returned in polling fd\n"); + num_hup++; + break; + case POLLHUP: + g_info("Polling FD : hung up."); + num_hup++; + break; + case POLLNVAL: + g_warning("Polling fd tells it is not open"); + num_hup++; + break; + case POLLPRI: + case POLLIN: + count = read (fdpty, buf, 256); + if(count > 0) { + unsigned int i; + buf[count] = '\0'; + g_printf("%s", buf); + for(i=0; i 0) { + g_warning("Child hung up too fast"); + goto wait_child; + } + } +write_password: + fsync(fdpty); + pollfd.fd = fdpty; + pollfd.events = POLLOUT|POLLERR|POLLHUP|POLLNVAL; + + num_rdy = poll(&pollfd, 1, -1); + if(num_rdy == -1) { + perror("Poll error"); + goto wait_child; + } + + /* Write the password */ + g_info("Got su prompt, now writing password..."); + int ret; + sleep(1); + ret = write(fdpty, password, strlen(password)); + if(ret < 0) perror("Error in write"); + ret = write(fdpty, "\n", 1); + if(ret < 0) perror("Error in write"); + fsync(fdpty); + /* Take the output from the terminal and show it on the real console */ + g_info("Getting data from child terminal..."); + while(1) { + int num_hup = 0; + pollfd.fd = fdpty; + pollfd.events = POLLIN|POLLPRI|POLLERR|POLLHUP|POLLNVAL; + + num_rdy = poll(&pollfd, 1, -1); + if(num_rdy == -1) { + perror("Poll error"); + goto wait_child; + } + if(num_rdy == 0) break; + + if(pollfd.revents & (POLLERR|POLLNVAL)) { + g_warning("Error returned in polling fd\n"); + num_hup++; + } + + if(pollfd.revents & (POLLIN|POLLPRI) ) { + count = read (fdpty, buf, 256); + if(count > 0) { + buf[count] = '\0'; + printf("%s", buf); + } else if(count == -1) { + perror("Error in read"); + goto wait_child; + } + } + + if(pollfd.revents & POLLHUP) { + g_info("Polling FD : hung up."); + num_hup++; + } + + if(num_hup > 0) goto wait_child; + } +wait_child: + g_info("Waiting for child exit..."); + + ret = waitpid(pid, &status, 0); + + if(ret == -1) { + g_warning("An error occured in wait : %s", + strerror(errno)); + } else { + if(WIFEXITED(status)) + if(WEXITSTATUS(status) != 0) { + retval = WEXITSTATUS(status); + g_warning("An error occured in the su command : %s", + strerror(retval)); + } + } + + g_info("Child exited."); + + } else if(pid == 0) { + /* Setup environment variables */ + if(strcmp(lttd_path, "") != 0) + setenv("LTT_DAEMON", lttd_path, 1); + if(strcmp(fac_path, "") != 0) + setenv("LTT_FACILITIES", fac_path, 1); + + /* One comment line (must be only one) */ + g_printf("Executing (as %s) : %s\n", username, command); + + execlp("su", "su", "-p", "-c", command, username, NULL); + exit(-1); /* not supposed to happen! */ + + //gint ret = execvp(); + + } else { + /* error */ + g_warning("Error happened when forking for su"); + } + + return retval; +} + + +/* Callbacks */ + +void start_clicked (GtkButton *button, gpointer user_data) +{ + ControlData *tcd = (ControlData*)user_data; + + const gchar *username = gtk_entry_get_text(GTK_ENTRY(tcd->username_entry)); + const gchar *password = gtk_entry_get_text(GTK_ENTRY(tcd->password_entry)); + const gchar *channel_dir = + gtk_entry_get_text(GTK_ENTRY(tcd->channel_dir_entry)); + const gchar *trace_dir = gtk_entry_get_text(GTK_ENTRY(tcd->trace_dir_entry)); + const gchar *trace_name = + gtk_entry_get_text(GTK_ENTRY(tcd->trace_name_entry)); + + const gchar *trace_mode_sel; + GtkTreeIter iter; + + gtk_combo_box_get_active_iter(GTK_COMBO_BOX(tcd->trace_mode_combo), &iter); + gtk_tree_model_get( + gtk_combo_box_get_model(GTK_COMBO_BOX(tcd->trace_mode_combo)), + &iter, 0, &trace_mode_sel, -1); + //const gchar *trace_mode_sel = + //2.6+ gtk_combo_box_get_active_text(GTK_COMBO_BOX(tcd->trace_mode_combo)); + const gchar *trace_mode; + if(strcmp(trace_mode_sel, "normal") == 0) + trace_mode = "normal"; + else + trace_mode = "flight"; + + gboolean start_daemon = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tcd->start_daemon_check)); + + gboolean append = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tcd->append_check)); + + const gchar *subbuf_size = + gtk_entry_get_text(GTK_ENTRY(tcd->subbuf_size_entry)); + const gchar *subbuf_num = + gtk_entry_get_text(GTK_ENTRY(tcd->subbuf_num_entry)); + const gchar *threads_num = + gtk_entry_get_text(GTK_ENTRY(tcd->lttd_threads_entry)); + const gchar *lttctl_path = + gtk_entry_get_text(GTK_ENTRY(tcd->lttctl_path_entry)); + const gchar *lttd_path = gtk_entry_get_text(GTK_ENTRY(tcd->lttd_path_entry)); + const gchar *fac_path = gtk_entry_get_text(GTK_ENTRY(tcd->fac_path_entry)); + + + /* Setup arguments to su */ + /* child */ + gchar args[MAX_ARGS_LEN]; + gint args_left = MAX_ARGS_LEN - 1; /* for \0 */ + + args[0] = '\0'; + + /* Command */ + strncat(args, "exec", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* space */ + strncat(args, " ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + if(strcmp(lttctl_path, "") == 0) + strncat(args, "lttctl", args_left); + else + strncat(args, lttctl_path, args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* space */ + strncat(args, " ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* channel dir */ + strncat(args, "-l ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + strncat(args, channel_dir, args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* space */ + strncat(args, " ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* trace dir */ + strncat(args, "-t ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + strncat(args, trace_dir, args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* space */ + strncat(args, " ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* name */ + strncat(args, "-n ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + strncat(args, trace_name, args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* space */ + strncat(args, " ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* trace mode */ + strncat(args, "-m ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + strncat(args, trace_mode, args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* space */ + strncat(args, " ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* Start daemon ? */ + if(start_daemon) { + strncat(args, "-d", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + } else { + /* Simply create the channel and then start tracing */ + strncat(args, "-b", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + } + + + /* Append to trace ? */ + if(append) { + /* space */ + strncat(args, " ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + strncat(args, "-a", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + } + + /* optional arguments */ + /* subbuffer size */ + if(strcmp(subbuf_size, "") != 0) { + /* space */ + strncat(args, " ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + strncat(args, "-z ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + strncat(args, subbuf_size, args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + } + + /* number of subbuffers */ + if(strcmp(subbuf_num, "") != 0) { + /* space */ + strncat(args, " ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + strncat(args, "-x ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + strncat(args, subbuf_num, args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + } + + /* number of lttd threads */ + if(strcmp(threads_num, "") != 0) { + /* space */ + strncat(args, " ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + strncat(args, "-N ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + strncat(args, threads_num, args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + } + + + int retval = execute_command(args, username, password, lttd_path, fac_path); + + if(retval) { + gchar msg[256]; + guint msg_left = 256; + + strcpy(msg, "A problem occured when executing the su command : "); + msg_left = 256 - strlen(msg) - 1; + strncat(msg, strerror(retval), msg_left); + GtkWidget *dialogue = + gtk_message_dialog_new( + GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(button))), + GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + msg); + gtk_dialog_run(GTK_DIALOG(dialogue)); + gtk_widget_destroy(dialogue); + } + +} + + +void pause_clicked (GtkButton *button, gpointer user_data) +{ + ControlData *tcd = (ControlData*)user_data; + + const gchar *username = gtk_entry_get_text(GTK_ENTRY(tcd->username_entry)); + const gchar *password = gtk_entry_get_text(GTK_ENTRY(tcd->password_entry)); + const gchar *trace_name = + gtk_entry_get_text(GTK_ENTRY(tcd->trace_name_entry)); + const gchar *lttd_path = ""; + const gchar *fac_path = ""; + + const gchar *lttctl_path = + gtk_entry_get_text(GTK_ENTRY(tcd->lttctl_path_entry)); + + /* Setup arguments to su */ + /* child */ + gchar args[MAX_ARGS_LEN]; + gint args_left = MAX_ARGS_LEN - 1; /* for \0 */ + + args[0] = '\0'; + + /* Command */ + strncat(args, "exec", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* space */ + strncat(args, " ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + if(strcmp(lttctl_path, "") == 0) + strncat(args, "lttctl", args_left); + else + strncat(args, lttctl_path, args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* space */ + strncat(args, " ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* name */ + strncat(args, "-n ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + strncat(args, trace_name, args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* space */ + strncat(args, " ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* Simply pause tracing */ + strncat(args, "-q", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + int retval = execute_command(args, username, password, lttd_path, fac_path); + if(retval) { + gchar msg[256]; + guint msg_left = 256; + + strcpy(msg, "A problem occured when executing the su command : "); + msg_left = 256 - strlen(msg) - 1; + strncat(msg, strerror(retval), msg_left); + GtkWidget *dialogue = + gtk_message_dialog_new( + GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(button))), + GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + msg); + gtk_dialog_run(GTK_DIALOG(dialogue)); + gtk_widget_destroy(dialogue); + } + +} + +void unpause_clicked (GtkButton *button, gpointer user_data) +{ + ControlData *tcd = (ControlData*)user_data; + + const gchar *username = gtk_entry_get_text(GTK_ENTRY(tcd->username_entry)); + const gchar *password = gtk_entry_get_text(GTK_ENTRY(tcd->password_entry)); + const gchar *trace_name = + gtk_entry_get_text(GTK_ENTRY(tcd->trace_name_entry)); + const gchar *lttd_path = ""; + const gchar *fac_path = ""; + + const gchar *lttctl_path = + gtk_entry_get_text(GTK_ENTRY(tcd->lttctl_path_entry)); + + /* Setup arguments to su */ + /* child */ + gchar args[MAX_ARGS_LEN]; + gint args_left = MAX_ARGS_LEN - 1; /* for \0 */ + + args[0] = '\0'; + + /* Command */ + strncat(args, "exec", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* space */ + strncat(args, " ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + if(strcmp(lttctl_path, "") == 0) + strncat(args, "lttctl", args_left); + else + strncat(args, lttctl_path, args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* space */ + strncat(args, " ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* name */ + strncat(args, "-n ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + strncat(args, trace_name, args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* space */ + strncat(args, " ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* Simply unpause tracing */ + strncat(args, "-s", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + int retval = execute_command(args, username, password, lttd_path, fac_path); + if(retval) { + gchar msg[256]; + guint msg_left = 256; + + strcpy(msg, "A problem occured when executing the su command : "); + msg_left = 256 - strlen(msg) - 1; + strncat(msg, strerror(retval), msg_left); + GtkWidget *dialogue = + gtk_message_dialog_new( + GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(button))), + GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + msg); + gtk_dialog_run(GTK_DIALOG(dialogue)); + gtk_widget_destroy(dialogue); + } + +} + +void stop_clicked (GtkButton *button, gpointer user_data) +{ + ControlData *tcd = (ControlData*)user_data; + + const gchar *username = gtk_entry_get_text(GTK_ENTRY(tcd->username_entry)); + const gchar *password = gtk_entry_get_text(GTK_ENTRY(tcd->password_entry)); + const gchar *trace_name = + gtk_entry_get_text(GTK_ENTRY(tcd->trace_name_entry)); + const gchar *lttd_path = ""; + const gchar *fac_path = ""; + + const gchar *lttctl_path = + gtk_entry_get_text(GTK_ENTRY(tcd->lttctl_path_entry)); + gchar *trace_dir = gtk_entry_get_text(GTK_ENTRY(tcd->trace_dir_entry)); + GSList * trace_list = NULL; + + trace_list = g_slist_append(trace_list, trace_dir); + + /* Setup arguments to su */ + /* child */ + gchar args[MAX_ARGS_LEN]; + gint args_left = MAX_ARGS_LEN - 1; /* for \0 */ + + args[0] = '\0'; + + /* Command */ + strncat(args, "exec", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* space */ + strncat(args, " ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + if(strcmp(lttctl_path, "") == 0) + strncat(args, "lttctl", args_left); + else + strncat(args, lttctl_path, args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* space */ + strncat(args, " ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* name */ + strncat(args, "-n ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + strncat(args, trace_name, args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* space */ + strncat(args, " ", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + /* Simply stop tracing and destroy channel */ + strncat(args, "-R", args_left); + args_left = MAX_ARGS_LEN - strlen(args) - 1; + + int retval = execute_command(args, username, password, lttd_path, fac_path); + if(retval) { + gchar msg[256]; + guint msg_left = 256; + + strcpy(msg, "A problem occured when executing the su command : "); + msg_left = 256 - strlen(msg) - 1; + strncat(msg, strerror(retval), msg_left); + GtkWidget *dialogue = + gtk_message_dialog_new( + GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(button))), + GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + msg); + gtk_dialog_run(GTK_DIALOG(dialogue)); + gtk_widget_destroy(dialogue); + return; + } + + + /* Ask to the user if he wants to open the trace in a new window */ + GtkWidget *dialogue; + GtkWidget *label; + gint id; + + dialogue = gtk_dialog_new_with_buttons("Open trace ?", + GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(button))), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_YES,GTK_RESPONSE_ACCEPT, + GTK_STOCK_NO,GTK_RESPONSE_REJECT, + NULL); + label = gtk_label_new("Do you want to open the trace in LTTV ?"); + gtk_widget_show(label); + + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialogue)->vbox), + label); + + id = gtk_dialog_run(GTK_DIALOG(dialogue)); + + switch(id){ + case GTK_RESPONSE_ACCEPT: + { + create_main_window_with_trace_list(trace_list); + } + break; + case GTK_RESPONSE_REJECT: + default: + break; + } + gtk_widget_destroy(dialogue); + g_slist_free(trace_list); +} + + +/** + * @fn GtkWidget* h_guicontrol(Tab*) + * + * Control Module's constructor hook + * + * This constructor is given as a parameter to the menuitem and toolbar button + * registration. It creates the list. + * @param tab A pointer to the parent window. + * @return The widget created. + */ +GtkWidget * +h_guicontrol(LttvPlugin *plugin) +{ + LttvPluginTab *ptab = LTTV_PLUGIN_TAB(plugin); + ControlData* f = gui_control(ptab); + + return NULL; +} + +/** + * @fn static void init() + * + * This function initializes the Filter Viewer functionnality through the + * gtkTraceSet API. + */ +static void init() { + + lttvwindow_register_constructor("guicontrol", + "/", + "Insert Tracing Control Module", + hTraceControlInsert_xpm, + "Insert Tracing Control Module", + h_guicontrol); +} + +/** + * @fn void control_destroy_walk(gpointer,gpointer) + * + * Initiate the destruction of the current gui module + * on the GTK Interface + */ +void +control_destroy_walk(gpointer data, gpointer user_data) +{ + ControlData *tcd = (ControlData*)data; + + g_debug("traceontrol.c : control_destroy_walk, %p", tcd); + + /* May already have been done by GTK window closing */ + if(GTK_IS_WIDGET(guicontrol_get_widget(tcd))) + gtk_widget_destroy(guicontrol_get_widget(tcd)); +} + +/** + * @fn static void destroy() + * @brief plugin's destroy function + * + * This function releases the memory reserved by the module and unregisters + * everything that has been registered in the gtkTraceSet API. + */ +static void destroy() { + + g_slist_foreach(g_control_list, control_destroy_walk, NULL ); + + lttvwindow_unregister_constructor(h_guicontrol); + +} + + +LTTV_MODULE("guitracecontrol", "Trace Control Window", \ + "Graphical module that let user control kernel tracing", \ + init, destroy, "lttvwindow") + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/tutorial/Makefile.am b/tags/lttv-0.11.3-23102008/lttv/modules/gui/tutorial/Makefile.am new file mode 100644 index 00000000..ab5513bc --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/tutorial/Makefile.am @@ -0,0 +1,39 @@ +# This file is part of the Linux Trace Toolkit viewer +# Copyright (C) 2003-2004 Mathieu Desnoyers +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License Version 2 as +# published by the Free Software Foundation; +# +# 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. + + + +# +# Makefile for LTT New generation user interface : plugins. +# +# Created by Mathieu Desnoyers on May 6, 2003 +# + +AM_CFLAGS = $(GLIB_CFLAGS) +AM_CFLAGS += $(GTK_CFLAGS) +AM_CFLAGS += -fvisibility=hidden +LIBS += $(GLIB_LIBS) +LIBS += $(GTK_LIBS) -L${top_builddir}/lttv/modules/gui/lttvwindow/lttvwindow -llttvwindow + +libdir = ${lttvplugindir} + +lib_LTLIBRARIES = libtutorial.la +libtutorial_la_LDFLAGS = -module -avoid-version +libtutorial_la_SOURCES = tutorial.c + +EXTRA_DIST = \ + hTutorialInsert.xpm diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/tutorial/hTutorialInsert.xpm b/tags/lttv-0.11.3-23102008/lttv/modules/gui/tutorial/hTutorialInsert.xpm new file mode 100644 index 00000000..4e50c5c3 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/tutorial/hTutorialInsert.xpm @@ -0,0 +1,27 @@ +/* XPM */ +static char * hTutorialInsert_xpm[] = { +"22 22 2 1", +" c None", +". c #800080", +" ", +" ", +" .......... ", +" ............. ", +" ..... ", +" ..... ", +" ..... ", +" ..... ", +" ..... ", +" ..... ", +" ..... ", +" ..... ", +" ..... ", +" ..... ", +" ..... ", +" ..... ", +" ..... ", +" ..... ", +" ............. ", +" .......... ", +" ", +" "}; diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/gui/tutorial/tutorial.c b/tags/lttv-0.11.3-23102008/lttv/modules/gui/tutorial/tutorial.c new file mode 100644 index 00000000..4f786f30 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/gui/tutorial/tutorial.c @@ -0,0 +1,679 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2005 Peter Ho + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ +/****************************************************************** + Each field of the interrupt viewer is summarized as follows: + +- CPUID: processor ID + +- IrqId: IRQ ID + +- Frequency (Hz): the number of interrupts per second (Hz). + We compute the total number of interrupts. Then + we divide it by the time interval. + +- Total Duration (nsec): the sum of each interrupt duration in nsec. + For a given Irq ID, we sum the duration of each interrupt + to give us the total duration + + +*******************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hTutorialInsert.xpm" + +#define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format) +#define g_debug(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format) +#define NO_ITEMS 0 + +typedef struct +{ + guint cpu_id; + guint id; + guint TotalNumberOfInterrupts; + LttTime total_duration; +}Irq; + +typedef struct +{ + guint id; + guint cpu_id; + LttTime event_time; +}irq_entry; + +enum type_t +{ + IRQ_ENTRY, + IRQ_EXIT +}; + +/** Array containing instanced objects. Used when module is unloaded */ +static GSList *interrupt_data_list = NULL ; + + +#define TRACE_NUMBER 0 + +typedef struct _InterruptEventData +{ + + /* Graphical Widgets */ + GtkWidget * ScrollWindow; + GtkListStore *ListStore; + GtkWidget *Hbox; + GtkWidget *TreeView; + GtkTreeSelection *SelectionTree; + + Tab * tab; /* tab that contains this plug-in*/ + LttvPluginTab * ptab; + LttvHooks * event_hooks; + LttvHooks * hooks_trace_after; + LttvHooks * hooks_trace_before; + TimeWindow time_window; + LttvHooksById * event_by_id_hooks; + GArray *IrqExit; + GArray *IrqEntry; +} InterruptEventData ; + + +/* Function prototypes */ + +static gboolean interrupt_update_time_window(void * hook_data, void * call_data); +static GtkWidget *interrupts(LttvPlugin *plugin); +static InterruptEventData *system_info(LttvPluginTab *ptab); +void interrupt_destructor(InterruptEventData *event_viewer_data); +static void request_event(InterruptEventData *event_data ); +static guint64 get_interrupt_id(LttEvent *e); +static gboolean trace_header(void *hook_data, void *call_data); +static gboolean interrupt_display (void *hook_data, void *call_data); +static void calcul_duration(LttTime time_exit, guint cpu_id, InterruptEventData *event_data); +static void sum_interrupt_data(irq_entry *e, LttTime time_exit, GArray *interrupt_counters); +static gboolean irq_entry_callback(void *hook_data, void *call_data); +static gboolean irq_exit_callback(void *hook_data, void *call_data); +static void InterruptFree(InterruptEventData *event_viewer_data); +static int FrequencyInHZ(gint NumerofInterruptions, TimeWindow time_window); +/* Enumeration of the columns */ +enum{ + CPUID_COLUMN, + IRQ_ID_COLUMN, + FREQUENCY_COLUMN, + DURATION_COLUMN, + N_COLUMNS +}; + + + +/** + * init function + * + * + * This is the entry point of the viewer. + * + */ +static void init() +{ + g_info("interrupts: init()"); + + lttvwindow_register_constructor("tutorial", + "/", + "Insert Interrupts View", + hTutorialInsert_xpm, + "Insert Interrupts View", + interrupts); +} + + +/** + * Constructor hook + * + */ +static GtkWidget *interrupts(LttvPlugin *plugin) +{ + LttvPluginTab *ptab = LTTV_PLUGIN_TAB(plugin); + InterruptEventData* event_data = system_info(ptab) ; + if(event_data) + return event_data->Hbox; + else + return NULL; +} + +/** + * This function initializes the Event Viewer functionnality through the + * GTK API. + */ +InterruptEventData *system_info(LttvPluginTab *ptab) +{ + LttTime end; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + InterruptEventData* event_viewer_data = g_new(InterruptEventData,1) ; + Tab *tab = ptab->tab; + + event_viewer_data->tab = tab; + event_viewer_data->ptab = ptab; + + /*Get the current time frame from the main window */ + event_viewer_data->time_window = lttvwindow_get_time_window(tab); + event_viewer_data->IrqExit = g_array_new(FALSE, FALSE, sizeof(Irq)); + event_viewer_data->IrqEntry = g_array_new(FALSE, FALSE, sizeof(irq_entry)); + + /*Create tha main window for the viewer */ + event_viewer_data->ScrollWindow = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (event_viewer_data->ScrollWindow); + gtk_scrolled_window_set_policy( + GTK_SCROLLED_WINDOW(event_viewer_data->ScrollWindow), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + + /* Create a model for storing the data list */ + event_viewer_data->ListStore = gtk_list_store_new ( + N_COLUMNS, /* Total number of columns */ + G_TYPE_INT, /* CPUID */ + G_TYPE_INT, /* IRQ_ID */ + G_TYPE_INT, /* Frequency */ + G_TYPE_UINT64 /* Duration */ + ); + + event_viewer_data->TreeView = gtk_tree_view_new_with_model (GTK_TREE_MODEL (event_viewer_data->ListStore)); + + g_object_unref (G_OBJECT (event_viewer_data->ListStore)); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("CPU ID", + renderer, + "text", CPUID_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_fixed_width (column, 45); + gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column); + + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("IRQ ID", + renderer, + "text", IRQ_ID_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_fixed_width (column, 220); + gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Frequency (HZ)", + renderer, + "text", FREQUENCY_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 1.0); + gtk_tree_view_column_set_fixed_width (column, 220); + gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Total Duration (nsec)", + renderer, + "text", DURATION_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_fixed_width (column, 145); + gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column); + + event_viewer_data->SelectionTree = gtk_tree_view_get_selection (GTK_TREE_VIEW (event_viewer_data->TreeView)); + gtk_tree_selection_set_mode (event_viewer_data->SelectionTree, GTK_SELECTION_SINGLE); + + gtk_container_add (GTK_CONTAINER (event_viewer_data->ScrollWindow), event_viewer_data->TreeView); + + event_viewer_data->Hbox = gtk_hbox_new(0, 0); + gtk_box_pack_start(GTK_BOX(event_viewer_data->Hbox), event_viewer_data->ScrollWindow, TRUE, TRUE, 0); + + gtk_widget_show(event_viewer_data->Hbox); + gtk_widget_show(event_viewer_data->TreeView); + + interrupt_data_list = g_slist_append(interrupt_data_list, event_viewer_data); + + /* Registration for time notification */ + lttvwindow_register_time_window_notify(tab, + interrupt_update_time_window, + event_viewer_data); + + g_object_set_data_full(G_OBJECT(event_viewer_data->Hbox), + "event_data", + event_viewer_data, + (GDestroyNotify) InterruptFree); + + + request_event(event_viewer_data ); + return event_viewer_data; +} + +/** + * + * For each trace in the traceset, this function: + * - registers a callback function to each hook + * - calls lttv_trace_find_hook() registers a hook function to event_by_id_hooks + * - calls lttvwindow_events_request() to request data in a specific + * time interval to the main window + * + */ +static void request_event(InterruptEventData *event_data ) +{ + guint i, k, l, nb_trace; + + LttvTraceHook *hook; + + guint ret; + + LttvTraceState *ts; + + GArray *hooks; + + EventsRequest *events_request; + + LttvTraceHookByFacility *thf; + + LttvTracesetContext *tsc = lttvwindow_get_traceset_context(event_data->tab); + + + /* Get the traceset */ + LttvTraceset *traceset = tsc->ts; + + nb_trace = lttv_traceset_number(traceset); + + /* There are many traces in a traceset. Iteration for each trace. */ + for(i = 0; ihooks_trace_before = lttv_hooks_new(); + + /* Registers a hook function */ + lttv_hooks_add(event_data->hooks_trace_before, trace_header, event_data, LTTV_PRIO_DEFAULT); + + event_data->hooks_trace_after = lttv_hooks_new(); + /* Registers a hook function */ + lttv_hooks_add(event_data->hooks_trace_after, interrupt_display, event_data, LTTV_PRIO_DEFAULT); + /* Get a trace state */ + ts = (LttvTraceState *)tsc->traces[i]; + /* Create event_by_Id hooks */ + event_data->event_by_id_hooks = lttv_hooks_by_id_new(); + + /*Register event_by_id_hooks with a callback function*/ + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, LTT_EVENT_IRQ_ENTRY, + LTT_FIELD_IRQ_ID, 0, 0, + irq_entry_callback, + events_request, + &g_array_index(hooks, LttvTraceHook, 0)); + + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, LTT_EVENT_IRQ_EXIT, + LTT_FIELD_IRQ_ID, 0, 0, + irq_exit_callback, + events_request, + &g_array_index(hooks, LttvTraceHook, 1)); + + g_assert(!ret); + + /*iterate through the facility list*/ + for(k = 0 ; k < hooks->len; k++) + { + hook = &g_array_index(hooks, LttvTraceHook, k); + for(l=0; lfac_list->len; l++) + { + thf = g_array_index(hook->fac_list, LttvTraceHookByFacility*, l); + lttv_hooks_add(lttv_hooks_by_id_find(event_data->event_by_id_hooks, thf->id), + thf->h, + event_data, + LTTV_PRIO_DEFAULT); + + } + } + + /* Initalize the EventsRequest structure */ + events_request->owner = event_data; + events_request->viewer_data = event_data; + events_request->servicing = FALSE; + events_request->start_time = event_data->time_window.start_time; + events_request->start_position = NULL; + events_request->stop_flag = FALSE; + events_request->end_time = event_data->time_window.end_time; + events_request->num_events = G_MAXUINT; + events_request->end_position = NULL; + events_request->trace = i; + + events_request->hooks = hooks; + + events_request->before_chunk_traceset = NULL; + events_request->before_chunk_trace = event_data->hooks_trace_before; + events_request->before_chunk_tracefile= NULL; + events_request->event = NULL; + events_request->event_by_id = event_data->event_by_id_hooks; + events_request->after_chunk_tracefile = NULL; + events_request->after_chunk_trace = NULL; + events_request->after_chunk_traceset = NULL; + events_request->before_request = NULL; + events_request->after_request = event_data->hooks_trace_after; + + lttvwindow_events_request(event_data->tab, events_request); + } + +} + +/** + * This function is called whenever an irq_entry event occurs. + * + */ +static gboolean irq_entry_callback(void *hook_data, void *call_data) +{ + + LttTime event_time; + unsigned cpu_id; + irq_entry entry; + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + InterruptEventData *event_data = (InterruptEventData *)hook_data; + GArray* IrqEntry = event_data->IrqEntry; + LttEvent *e = ltt_tracefile_get_event(tfc->tf); + event_time = ltt_event_time(e); + cpu_id = ltt_event_cpu_id(e); + + if ((ltt_time_compare(event_time,event_data->time_window.start_time) == TRUE) && + (ltt_time_compare(event_data->time_window.end_time,event_time) == TRUE)) + { + entry.id =get_interrupt_id(e); + entry.cpu_id = cpu_id; + entry.event_time = event_time; + g_array_append_val (IrqEntry, entry); + } + return FALSE; +} + +/** + * This function gets the id of the interrupt. The id is stored in a dynamic structure. + * Refer to the print.c file for howto extract data from a dynamic structure. + */ +static guint64 get_interrupt_id(LttEvent *e) +{ + guint i, num_fields; + LttEventType *event_type; + LttField *element; + LttField *field; + guint64 irq_id; + event_type = ltt_event_eventtype(e); + num_fields = ltt_eventtype_num_fields(event_type); + for(i = 0 ; i < num_fields-1 ; i++) + { + field = ltt_eventtype_field(event_type, i); + irq_id = ltt_event_get_long_unsigned(e,field); + } + return irq_id; + +} +/** + * This function is called whenever an irq_exit event occurs. + * + */ +gboolean irq_exit_callback(void *hook_data, void *call_data) +{ + LttTime event_time; + unsigned cpu_id; + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + InterruptEventData *event_data = (InterruptEventData *)hook_data; + LttEvent *e = ltt_tracefile_get_event(tfc->tf); + LttEventType *type = ltt_event_eventtype(e); + event_time = ltt_event_time(e); + cpu_id = ltt_event_cpu_id(e); + if ((ltt_time_compare(event_time,event_data->time_window.start_time) == TRUE) && + (ltt_time_compare(event_data->time_window.end_time,event_time) == TRUE)) + { + calcul_duration( event_time, cpu_id, event_data); + + } + return FALSE; +} + +/** + * This function calculates the duration of an interrupt. + * + */ +static void calcul_duration(LttTime time_exit, guint cpu_id,InterruptEventData *event_data){ + + gint i, irq_id; + irq_entry *element; + LttTime duration; + GArray *IrqExit = event_data->IrqExit; + GArray *IrqEntry = event_data->IrqEntry; + for(i = 0; i < IrqEntry->len; i++){ + element = &g_array_index(IrqEntry,irq_entry,i); + if(element->cpu_id == cpu_id) + { + sum_interrupt_data(element,time_exit, IrqExit); + g_array_remove_index(IrqEntry, i); + break; + } + } +} +/** + * This function calculates the total duration of an interrupt. + * + */ +static void sum_interrupt_data(irq_entry *e, LttTime time_exit, GArray *IrqExit) +{ + Irq irq; + Irq *element; + guint i; + LttTime duration; + gboolean notFound = FALSE; + memset ((void*)&irq, 0,sizeof(Irq)); + + + if(IrqExit->len == NO_ITEMS) + { + irq.cpu_id = e->cpu_id; + irq.id = e->id; + irq.TotalNumberOfInterrupts++; + irq.total_duration = ltt_time_sub(time_exit, e->event_time); + g_array_append_val (IrqExit, irq); + } + else{ + for(i = 0; i < IrqExit->len; i++) + { + element = &g_array_index(IrqExit, Irq, i); + if(element->id == e->id){ + notFound = TRUE; + duration = ltt_time_sub(time_exit, e->event_time); + element->total_duration = ltt_time_add(element->total_duration, duration); + element->TotalNumberOfInterrupts++; + } + } + + if(!notFound) + { + irq.cpu_id = e->cpu_id; + irq.id = e->id; + irq.TotalNumberOfInterrupts++; + irq.total_duration = ltt_time_sub(time_exit, e->event_time); + g_array_append_val (IrqExit, irq); + } + } +} + +/** + * This function displays the result on the viewer + * + */ +static gboolean interrupt_display(void *hook_data, void *call_data) +{ + + gint i; + Irq element; + LttTime average_duration; + GtkTreeIter iter; + guint64 real_data; + int FrequencyHZ = 0; + + InterruptEventData *event_data = (InterruptEventData *)hook_data; + GArray *IrqExit = event_data->IrqExit; + gtk_list_store_clear(event_data->ListStore); + for(i = 0; i < IrqExit->len; i++) + { + + element = g_array_index(IrqExit,Irq,i); + real_data = element.total_duration.tv_sec; + real_data *= NANOSECONDS_PER_SECOND; + real_data += element.total_duration.tv_nsec; + + FrequencyHZ = FrequencyInHZ(element.TotalNumberOfInterrupts,event_data->time_window); + + gtk_list_store_append (event_data->ListStore, &iter); + + gtk_list_store_set (event_data->ListStore, &iter, + CPUID_COLUMN, element.cpu_id, + IRQ_ID_COLUMN, element.id, + FREQUENCY_COLUMN, FrequencyHZ, + DURATION_COLUMN, real_data, + -1); + + } + + if(event_data->IrqExit->len) + g_array_remove_range (event_data->IrqExit,0,event_data->IrqExit->len); + + if(event_data->IrqEntry->len) + g_array_remove_range (event_data->IrqEntry,0,event_data->IrqEntry->len); + return FALSE; +} + +/** + * This function converts the number of interrupts over a time window to + * frequency in HZ + */ +static int FrequencyInHZ(gint NumerofInterruptions, TimeWindow time_window) +{ + guint64 frequencyHz = 0; + double timeSec; // time in second + double result; + result = ltt_time_to_double(time_window.time_width); + timeSec = (result/NANOSECONDS_PER_SECOND); //time in second + frequencyHz = NumerofInterruptions / timeSec; + return frequencyHz; +} + + +/* + * This function is called by the main window + * when the time interval needs to be updated. + **/ +gboolean interrupt_update_time_window(void * hook_data, void * call_data) +{ + InterruptEventData *event_data = (InterruptEventData *) hook_data; + const TimeWindowNotifyData *time_window_nofify_data = ((const TimeWindowNotifyData *)call_data); + event_data->time_window = *time_window_nofify_data->new_time_window; + g_info("interrupts: interrupt_update_time_window()\n"); + Tab *tab = event_data->tab; + lttvwindow_events_request_remove_all(tab, event_data); + request_event(event_data ); + return FALSE; +} + + +gboolean trace_header(void *hook_data, void *call_data) +{ + + InterruptEventData *event_data = (InterruptEventData *)hook_data; + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + LttEvent *e; + LttTime event_time; + return FALSE; +} + +void interrupt_destroy_walk(gpointer data, gpointer user_data) +{ + g_info("interrupt_destroy_walk"); + interrupt_destructor((InterruptEventData*)data); + +} + +void interrupt_destructor(InterruptEventData *event_viewer_data) +{ + /* May already been done by GTK window closing */ + g_info("enter interrupt_destructor \n"); + if(GTK_IS_WIDGET(event_viewer_data->Hbox)) + { + gtk_widget_destroy(event_viewer_data->Hbox); + } +} + +/** + This function is called when the viewer is destroyed to free hooks and memory +*/ +static void InterruptFree(InterruptEventData *event_viewer_data) +{ + Tab *tab = event_viewer_data->tab; + if(tab != NULL) + { + g_array_free(event_viewer_data->IrqExit, TRUE); + + g_array_free(event_viewer_data->IrqEntry, TRUE); + + lttvwindow_unregister_time_window_notify(tab, interrupt_update_time_window, event_viewer_data); + + lttvwindow_events_request_remove_all(event_viewer_data->tab, + event_viewer_data); + + interrupt_data_list = g_slist_remove(interrupt_data_list, event_viewer_data); + + } + +} + +/** + * plugin's destroy function + * + * This function releases the memory reserved by the module and unregisters + * everything that has been registered in the gtkTraceSet API. + */ +static void destroy() { + g_info("Destroy interrupts"); + g_slist_foreach(interrupt_data_list, interrupt_destroy_walk, NULL ); + g_slist_free(interrupt_data_list); + lttvwindow_unregister_constructor(interrupts); + +} + +LTTV_MODULE("tutorial", "interrupts info view", \ + "Graphical module to display interrupts performance", \ + init, destroy, "lttvwindow") diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/text/Makefile.am b/tags/lttv-0.11.3-23102008/lttv/modules/text/Makefile.am new file mode 100644 index 00000000..9971ef80 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/text/Makefile.am @@ -0,0 +1,17 @@ +AM_CFLAGS = $(GLIB_CFLAGS) +LIBS += $(GLIB_LIBS) -lgobject-2.0 -L${top_builddir}/ltt -llttvtraceread + +libdir = ${lttvplugindir} + +lib_LTLIBRARIES = libtextDump.la libbatchAnalysis.la libtextFilter.la libprecomputeState.la +libtextDump_la_LDFLAGS = -module -avoid-version +libtextDump_la_SOURCES = textDump.c +libbatchAnalysis_la_LDFLAGS = -module -avoid-version +libbatchAnalysis_la_SOURCES = batchAnalysis.c +libtextFilter_la_LDFLAGS = -module -avoid-version +libtextFilter_la_SOURCES = textFilter.c +libprecomputeState_la_LDFLAGS = -module -avoid-version +libprecomputeState_la_SOURCES = precomputeState.c + +noinst_HEADERS = \ + batchanalysis.h diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/text/batchAnalysis.c b/tags/lttv-0.11.3-23102008/lttv/modules/text/batchAnalysis.c new file mode 100644 index 00000000..12dea8d5 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/text/batchAnalysis.c @@ -0,0 +1,246 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +/* This module inserts a hook in the program main loop. This hook processes + all the events in the main tracefile. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static LttvTraceset *traceset; + +static LttvHooks + *before_traceset, + *after_traceset, + *before_trace, + *after_trace, + *before_tracefile, + *after_tracefile, + *event_hook, + *main_hooks; + +static char *a_trace; + +static gboolean a_stats; + +void lttv_trace_option(void *hook_data) +{ + LttTrace *trace; + + trace = ltt_trace_open(a_trace); + if(trace == NULL) g_critical("cannot open trace %s", a_trace); + lttv_traceset_add(traceset, lttv_trace_new(trace)); +} + + +static gboolean process_traceset(void *hook_data, void *call_data) +{ + LttvAttributeValue value_expression, value_filter; + + LttvIAttribute *attributes = LTTV_IATTRIBUTE(lttv_global_attributes()); + + LttvTracesetStats *tscs; + + LttvTracesetContext *tc; + + LttTime start, end; + + g_info("BatchAnalysis begin process traceset"); + + tscs = g_object_new(LTTV_TRACESET_STATS_TYPE, NULL); + tc = &tscs->parent.parent; + + g_info("BatchAnalysis initialize context"); + + lttv_context_init(tc, traceset); + lttv_state_add_event_hooks(&tscs->parent); + if(a_stats) lttv_stats_add_event_hooks(tscs); + + g_assert(lttv_iattribute_find_by_path(attributes, "filter/expression", + LTTV_POINTER, &value_expression)); + + g_assert(lttv_iattribute_find_by_path(attributes, "filter/lttv_filter", + LTTV_POINTER, &value_filter)); + + *(value_filter.v_pointer) = lttv_filter_new(); + //g_debug("Filter string: %s",((GString*)*(value_expression.v_pointer))->str); + + lttv_filter_append_expression(*(value_filter.v_pointer),((GString*)*(value_expression.v_pointer))->str); + + //lttv_traceset_context_add_hooks(tc, + //before_traceset, after_traceset, NULL, before_trace, after_trace, + //NULL, before_tracefile, after_tracefile, NULL, before_event, after_event); + lttv_process_traceset_begin(tc, + before_traceset, + before_trace, + before_tracefile, + event_hook, + NULL); + + start.tv_sec = 0; + start.tv_nsec = 0; + end.tv_sec = G_MAXULONG; + end.tv_nsec = G_MAXULONG; + + g_info("BatchAnalysis process traceset"); + + lttv_process_traceset_seek_time(tc, start); + lttv_process_traceset_middle(tc, + end, + G_MAXULONG, + NULL); + + + //lttv_traceset_context_remove_hooks(tc, + //before_traceset, after_traceset, NULL, before_trace, after_trace, + //NULL, before_tracefile, after_tracefile, NULL, before_event, after_event); + lttv_process_traceset_end(tc, + after_traceset, + after_trace, + after_tracefile, + event_hook, + NULL); + + g_info("BatchAnalysis destroy context"); + + lttv_filter_destroy(*(value_filter.v_pointer)); + lttv_state_remove_event_hooks(&tscs->parent); + if(a_stats) lttv_stats_remove_event_hooks(tscs); + lttv_context_fini(tc); + g_object_unref(tscs); + + g_info("BatchAnalysis end process traceset"); + return FALSE; +} + + +static void init() +{ + LttvAttributeValue value; + + LttvIAttribute *attributes = LTTV_IATTRIBUTE(lttv_global_attributes()); + + g_info("Init batchAnalysis.c"); + + lttv_option_add("trace", 't', + "add a trace to the trace set to analyse", + "pathname of the directory containing the trace", + LTTV_OPT_STRING, &a_trace, lttv_trace_option, NULL); + + a_stats = FALSE; + lttv_option_add("stats", 's', + "write the traceset and trace statistics", + "", + LTTV_OPT_NONE, &a_stats, NULL, NULL); + + + traceset = lttv_traceset_new(); + + before_traceset = lttv_hooks_new(); + after_traceset = lttv_hooks_new(); + before_trace = lttv_hooks_new(); + after_trace = lttv_hooks_new(); + before_tracefile = lttv_hooks_new(); + after_tracefile = lttv_hooks_new(); + //before_event = lttv_hooks_new(); + //after_event = lttv_hooks_new(); + event_hook = lttv_hooks_new(); + + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/traceset/before", + LTTV_POINTER, &value)); + *(value.v_pointer) = before_traceset; + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/traceset/after", + LTTV_POINTER, &value)); + *(value.v_pointer) = after_traceset; + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/trace/before", + LTTV_POINTER, &value)); + *(value.v_pointer) = before_trace; + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/trace/after", + LTTV_POINTER, &value)); + *(value.v_pointer) = after_trace; + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/tracefile/before", + LTTV_POINTER, &value)); + *(value.v_pointer) = before_tracefile; + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/tracefile/after", + LTTV_POINTER, &value)); + *(value.v_pointer) = after_tracefile; + //g_assert(lttv_iattribute_find_by_path(attributes, "hooks/event/before", + // LTTV_POINTER, &value)); + //*(value.v_pointer) = before_event; + //g_assert(lttv_iattribute_find_by_path(attributes, "hooks/event/after", + // LTTV_POINTER, &value)); + //*(value.v_pointer) = after_event; + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/event", + LTTV_POINTER, &value)); + *(value.v_pointer) = event_hook; + + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/main/before", + LTTV_POINTER, &value)); + g_assert((main_hooks = *(value.v_pointer)) != NULL); + lttv_hooks_add(main_hooks, process_traceset, NULL, LTTV_PRIO_DEFAULT); +} + +static void destroy() +{ + guint i, nb; + + LttvTrace *trace; + + g_info("Destroy batchAnalysis.c"); + + lttv_option_remove("trace"); + lttv_option_remove("stats"); + + lttv_hooks_destroy(before_traceset); + lttv_hooks_destroy(after_traceset); + lttv_hooks_destroy(before_trace); + lttv_hooks_destroy(after_trace); + lttv_hooks_destroy(before_tracefile); + lttv_hooks_destroy(after_tracefile); + //lttv_hooks_destroy(before_event); + //lttv_hooks_destroy(after_event); + lttv_hooks_destroy(event_hook); + lttv_hooks_remove_data(main_hooks, process_traceset, NULL); + + nb = lttv_traceset_number(traceset); + for(i = 0 ; i < nb ; i++) { + trace = lttv_traceset_get(traceset, i); + ltt_trace_close(lttv_trace(trace)); + /* This will be done by lttv_traceset_destroy */ + //lttv_trace_destroy(trace); + } + + lttv_traceset_destroy(traceset); +} + +LTTV_MODULE("batchAnalysis", "Batch processing of a trace", \ + "Run through a trace calling all the registered hooks", \ + init, destroy, "state", "stats", "option","textFilter") diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/text/batchanalysis.h b/tags/lttv-0.11.3-23102008/lttv/modules/text/batchanalysis.h new file mode 100644 index 00000000..19fda358 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/text/batchanalysis.h @@ -0,0 +1,62 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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 BATCH_ANALYSIS_H +#define BATCH_ANALYSIS_H + +/* The batch analysis module defines a main traceset and command line options + to specify the traces in the main traceset. It then processes that + traceset calling hooks lists at various stages of the analysis. + + The hooks lists are defined in the global attributes for these various + stages of the analysis. Loaded modules may add hooks to these lists. + Thus, by requesting that a certain module be loaded, the analysis may + produce additional information as the module adds hooks and these hooks + are called during the analysis. + + The hooks lists defined are as follows. These may be split in more + specific lists eventually to select the hooks applicable to state update, + incremental or batch processing... + + /hooks/traceset/before + Before analyzing a traceset. + + /hooks/traceset/after + After analyzing a traceset. + + /hooks/trace/before + Before each trace. + + /hooks/trace/after + After each trace. + + /hooks/tracefile/before + Before each tracefile. + + /hooks/tracefile/after + After each tracefile. + + /hooks/event/before + Before each event. + + /hooks/event/after + After each event. + +*/ + +#endif // BATCH_ANALYSIS_H diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/text/precomputeState.c b/tags/lttv-0.11.3-23102008/lttv/modules/text/precomputeState.c new file mode 100644 index 00000000..14b207c0 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/text/precomputeState.c @@ -0,0 +1,280 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * 2005 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +/* The text dump facility needs to print headers before the trace set and + before each trace, to print each event, and to print statistics + after each trace. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static gboolean + a_field_names, + a_state, + a_cpu_stats, + a_process_stats, + a_raw; + +static char + *a_file_name = NULL, + *a_quark_file_name = NULL; + +static LttvHooks + *before_traceset, + *after_traceset, + *before_trace, + *after_trace, + *event_hook; + +static guint a_event_count = 0; + +/* Insert the hooks before and after each trace and tracefile, and for each + event. Print a global header. */ + +static FILE *a_file; + +static GString *a_string; + +static gboolean write_traceset_header(void *hook_data, void *call_data) +{ + LttvTracesetContext *tc = (LttvTracesetContext *)call_data; + + if(a_file_name == NULL) a_file = stdout; + else a_file = fopen(a_file_name, "w"); + + if(a_file == NULL) g_error("cannot open file %s", a_file_name); + + /* Print the trace set header */ + if(a_raw) { + /* TODO : Write a header that will check for ILP size and endianness */ + //fputc(HDR_TRACESET, a_file); + g_assert(lttv_traceset_number(tc->ts) == 1); /* Only one trace in traceset */ + } else { + fprintf(a_file,"\n", + lttv_traceset_number(tc->ts)); + } + + return FALSE; +} + + +static gboolean write_traceset_footer(void *hook_data, void *call_data) +{ + LttvTracesetContext *tc = (LttvTracesetContext *)call_data; + GQuark q; + gchar *string; + + if(a_raw) { + + } else { + fprintf(a_file,"\n"); + } + + if(a_file_name != NULL) fclose(a_file); + + if(a_raw) { + if(a_quark_file_name == NULL) { + if(a_file_name == NULL) a_file = stdout; + else a_file = fopen(a_file_name, "a"); + } else { + if(a_quark_file_name == NULL) a_file = stdout; + else a_file = fopen(a_quark_file_name, "w"); + } + + if(a_file == NULL) g_error("cannot open file %s", a_quark_file_name); + + fputc(HDR_QUARKS, a_file); + q = 1; + do { + string = g_quark_to_string(q); + if(string == NULL) break; + fputc(HDR_QUARK, a_file); + // increment. fwrite(&q, sizeof(GQuark), 1, a_file); + fwrite(string, sizeof(char), strlen(string)+1, a_file); + q++; + } while(1); + + if(a_quark_file_name != NULL || a_file_name != NULL) fclose(a_file); + + } + + return FALSE; +} + + +static gboolean write_trace_header(void *hook_data, void *call_data) +{ + LttvTraceContext *tc = (LttvTraceContext *)call_data; + + if(a_raw) { + fputc(HDR_TRACE, a_file); + } else { + fprintf(a_file,"\n", + tc->index); + } + + return FALSE; +} + +static gboolean write_trace_footer(void *hook_data, void *call_data) +{ + LttvTraceContext *tc = (LttvTraceContext *)call_data; + + if(a_raw) { + + } else { + fprintf(a_file,"\n"); + } + + return FALSE; +} + + +static int for_each_event(void *hook_data, void *call_data) +{ + guint *event_count = (guint*)hook_data; + + LttvIAttribute *attributes = LTTV_IATTRIBUTE(lttv_global_attributes()); + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttEvent *e; + + LttvAttributeValue value_filter; + + /* Only save at LTTV_STATE_SAVE_INTERVAL */ + if(likely((*event_count)++ < LTTV_STATE_SAVE_INTERVAL)) + return FALSE; + else + *event_count = 0; + + guint cpu = tfs->cpu; + LttvTraceState *ts = (LttvTraceState*)tfc->t_context; + LttvProcessState *process = ts->running_process[cpu]; + + e = ltt_tracefile_get_event(tfc->tf); + + if(a_raw) { + lttv_state_write_raw(ts, tfs->parent.timestamp, a_file); + } else { + lttv_state_write(ts, tfs->parent.timestamp, a_file); + } + + return FALSE; +} + + +static void init() +{ + LttvAttributeValue value; + + LttvIAttribute *attributes = LTTV_IATTRIBUTE(lttv_global_attributes()); + + g_info("Init precomputeState.c"); + + a_string = g_string_new(""); + + a_file_name = NULL; + lttv_option_add("output", 'o', + "output file where the saved states are to be written", + "file name", + LTTV_OPT_STRING, &a_file_name, NULL, NULL); + + a_quark_file_name = NULL; + lttv_option_add("qoutput", 'q', + "output file where the quarks (tuples integer, string) are to be written", + "file name", + LTTV_OPT_STRING, &a_quark_file_name, NULL, NULL); + + lttv_option_add("raw", 'r', + "Output in raw binary", + "Raw binary", + LTTV_OPT_NONE, &a_raw, NULL, NULL); + + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/event", + LTTV_POINTER, &value)); + g_assert((event_hook = *(value.v_pointer)) != NULL); + lttv_hooks_add(event_hook, for_each_event, &a_event_count, LTTV_PRIO_DEFAULT); + + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/trace/before", + LTTV_POINTER, &value)); + g_assert((before_trace = *(value.v_pointer)) != NULL); + lttv_hooks_add(before_trace, write_trace_header, NULL, LTTV_PRIO_DEFAULT); + + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/trace/after", + LTTV_POINTER, &value)); + g_assert((after_trace = *(value.v_pointer)) != NULL); + lttv_hooks_add(after_trace, write_trace_footer, NULL, LTTV_PRIO_DEFAULT); + + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/traceset/before", + LTTV_POINTER, &value)); + g_assert((before_traceset = *(value.v_pointer)) != NULL); + lttv_hooks_add(before_traceset, write_traceset_header, NULL, + LTTV_PRIO_DEFAULT); + + g_assert(lttv_iattribute_find_by_path(attributes, "hooks/traceset/after", + LTTV_POINTER, &value)); + g_assert((after_traceset = *(value.v_pointer)) != NULL); + lttv_hooks_add(after_traceset, write_traceset_footer, NULL, + LTTV_PRIO_DEFAULT); +} + +static void destroy() +{ + g_info("Destroy precomputeState"); + + lttv_option_remove("output"); + + lttv_option_remove("qoutput"); + + lttv_option_remove("raw"); + + g_string_free(a_string, TRUE); + + lttv_hooks_remove_data(event_hook, for_each_event, NULL); + + lttv_hooks_remove_data(before_trace, write_trace_header, NULL); + + lttv_hooks_remove_data(before_trace, write_traceset_header, NULL); + + lttv_hooks_remove_data(before_trace, write_traceset_footer, NULL); +} + + +LTTV_MODULE("precomputeState", "Precompute states", \ + "Precompute states in a trace, XML or binary output.", \ + init, destroy, "stats", "batchAnalysis", "option", "print") + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/text/textDump.c b/tags/lttv-0.11.3-23102008/lttv/modules/text/textDump.c new file mode 100644 index 00000000..4fcd5b5c --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/text/textDump.c @@ -0,0 +1,463 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Michel Dagenais + * 2005 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +/* The text dump facility needs to print headers before the trace set and + before each trace, to print each event, and to print statistics + after each trace. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static gboolean + a_no_field_names, + a_state, + a_cpu_stats, + a_process_stats, + a_path_output; + +static char + *a_file_name = NULL; + +static LttvHooks + *before_traceset, + *after_traceset, + *before_trace, + *event_hook; + + +static void +print_path_tree(FILE *fp, GString *indent, LttvAttribute *tree) +{ + int i, nb, saved_length; + + LttvAttribute *subtree; + + LttvAttributeName name; + + LttvAttributeValue value; + + LttvAttributeType type; + + gboolean is_named; + + saved_length = indent->len; + nb = lttv_attribute_get_number(tree); + for(i = 0 ; i < nb ; i++) { + type = lttv_attribute_get(tree, i, &name, &value, &is_named); + if(is_named) { + g_string_sprintfa(indent, "/%s", g_quark_to_string(name)); + } else { + g_string_sprintfa(indent, "/%s", name); + } + + switch(type) { + case LTTV_INT: + fprintf(fp, "%s: %d\n", indent->str, *value.v_int); + break; + case LTTV_UINT: + fprintf(fp, "%s: %d\n", indent->str, *value.v_int); + break; + case LTTV_LONG: + fprintf(fp, "%s: %ld\n", indent->str, *value.v_ulong); + break; + case LTTV_ULONG: + fprintf(fp, "%s: %lu\n", indent->str, *value.v_ulong); + break; + case LTTV_FLOAT: + fprintf(fp, "%s: %f\n", indent->str, (double) *value.v_float); + break; + case LTTV_DOUBLE: + fprintf(fp, "%s: %f\n", indent->str, *value.v_double); + break; + case LTTV_TIME: + fprintf(fp, "%s: %lu.%09lu\n", indent->str, value.v_time->tv_sec, value.v_time->tv_nsec); + break; + case LTTV_POINTER: + fprintf(fp, "%s: POINTER\n", indent->str); + break; + case LTTV_STRING: + fprintf(fp, "%s: %s\n", indent->str, *value.v_string); + break; + case LTTV_GOBJECT: + if(LTTV_IS_ATTRIBUTE(*(value.v_gobject))) { + subtree = (LttvAttribute*) *(value.v_gobject); + print_path_tree(fp, indent, subtree); + } else { + fprintf(fp, "%s: GOBJECT\n", indent->str); + } + break; + case LTTV_NONE: + break; + } + g_string_truncate(indent, saved_length); + } +} + +static void +print_tree(FILE *fp, GString *indent, LttvAttribute *tree) +{ + int i, nb, saved_length; + + LttvAttribute *subtree; + + LttvAttributeName name; + + LttvAttributeValue value; + + LttvAttributeType type; + + gboolean is_named; + + nb = lttv_attribute_get_number(tree); + for(i = 0 ; i < nb ; i++) { + type = lttv_attribute_get(tree, i, &name, &value, &is_named); + if(is_named) + fprintf(fp, "%s%s: ", indent->str, g_quark_to_string(name)); + else + fprintf(fp, "%s%lu: ", indent->str, name); + + switch(type) { + case LTTV_INT: + fprintf(fp, "%d\n", *value.v_int); + break; + case LTTV_UINT: + fprintf(fp, "%u\n", *value.v_uint); + break; + case LTTV_LONG: + fprintf(fp, "%ld\n", *value.v_long); + break; + case LTTV_ULONG: + fprintf(fp, "%lu\n", *value.v_ulong); + break; + case LTTV_FLOAT: + fprintf(fp, "%f\n", (double)*value.v_float); + break; + case LTTV_DOUBLE: + fprintf(fp, "%f\n", *value.v_double); + break; + case LTTV_TIME: + fprintf(fp, "%10lu.%09lu\n", value.v_time->tv_sec, + value.v_time->tv_nsec); + break; + case LTTV_POINTER: + fprintf(fp, "POINTER\n"); + break; + case LTTV_STRING: + fprintf(fp, "%s\n", *value.v_string); + break; + case LTTV_GOBJECT: + if(LTTV_IS_ATTRIBUTE(*(value.v_gobject))) { + fprintf(fp, "\n"); + subtree = (LttvAttribute *)*(value.v_gobject); + saved_length = indent->len; + indent = g_string_append(indent, " "); + print_tree(fp, indent, subtree); + g_string_truncate(indent, saved_length); + } + else fprintf(fp, "GOBJECT\n"); + break; + case LTTV_NONE: + break; + } + } +} + +static void +print_stats(FILE *fp, LttvTracesetStats *tscs) +{ + int i, nb, saved_length; + + LttvTraceset *ts; + + LttvTraceStats *tcs; + + GString *indent; + + LttSystemDescription *desc; + + if(tscs->stats == NULL) return; + indent = g_string_new(""); + fprintf(fp, "Traceset statistics:\n\n"); + if(a_path_output) { + print_path_tree(fp, indent, tscs->stats); + } else { + print_tree(fp, indent, tscs->stats); + } + + ts = tscs->parent.parent.ts; + nb = lttv_traceset_number(ts); + + for(i = 0 ; i < nb ; i++) { + tcs = (LttvTraceStats *)(LTTV_TRACESET_CONTEXT(tscs)->traces[i]); +#if 0 //FIXME + desc = ltt_trace_system_description(tcs->parent.parent.t); + LttTime start_time = ltt_trace_system_description_trace_start_time(desc); + fprintf(fp, "Trace on system %s at time %lu.%09lu :\n", + ltt_trace_system_description_node_name(desc), + start_time.tv_sec, + start_time.tv_nsec); +#endif //FIXME + saved_length = indent->len; + if(a_path_output) { + g_string_sprintfa(indent, "/trace%i", i); + print_path_tree(fp, indent, tcs->stats); + } else { + g_string_append(indent, " "); + print_tree(fp, indent, tcs->stats); + } + g_string_truncate(indent, saved_length); + } + g_string_free(indent, TRUE); +} + +/* Insert the hooks before and after each trace and tracefile, and for each + event. Print a global header. */ + +static FILE *a_file; + +static GString *a_string; + +static gboolean write_traceset_header(void *hook_data, void *call_data) +{ + LttvTracesetContext *tc = (LttvTracesetContext *)call_data; + + g_info("TextDump traceset header"); + + if(a_file_name == NULL) a_file = stdout; + else a_file = fopen(a_file_name, "w"); + + if(a_file == NULL) g_error("cannot open file %s", a_file_name); + + /* Print the trace set header */ + fprintf(a_file,"Trace set contains %d traces\n\n", + lttv_traceset_number(tc->ts)); + + return FALSE; +} + + +static gboolean write_traceset_footer(void *hook_data, void *call_data) +{ + LttvTracesetContext *tc = (LttvTracesetContext *)call_data; + + g_info("TextDump traceset footer"); + + fprintf(a_file,"End trace set\n\n"); + + if(LTTV_IS_TRACESET_STATS(tc)) { + lttv_stats_sum_traceset((LttvTracesetStats *)tc, ltt_time_infinite); + print_stats(a_file, (LttvTracesetStats *)tc); + } + + if(a_file_name != NULL) fclose(a_file); + + return FALSE; +} + + +static gboolean write_trace_header(void *hook_data, void *call_data) +{ + LttvTraceContext *tc = (LttvTraceContext *)call_data; +#if 0 //FIXME + LttSystemDescription *system = ltt_trace_system_description(tc->t); + + fprintf(a_file," Trace from %s in %s\n%s\n\n", + ltt_trace_system_description_node_name(system), + ltt_trace_system_description_domain_name(system), + ltt_trace_system_description_description(system)); +#endif //0 + return FALSE; +} + + +static int write_event_content(void *hook_data, void *call_data) +{ + gboolean result; + + LttvIAttribute *attributes = LTTV_IATTRIBUTE(lttv_global_attributes()); + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttEvent *e; + + LttvAttributeValue value_filter; + + LttvFilter *filter; + + guint cpu = tfs->cpu; + LttvTraceState *ts = (LttvTraceState*)tfc->t_context; + LttvProcessState *process = ts->running_process[cpu]; + + e = ltt_tracefile_get_event(tfc->tf); + + result = lttv_iattribute_find_by_path(attributes, "filter/lttv_filter", + LTTV_POINTER, &value_filter); + g_assert(result); + filter = (LttvFilter*)*(value_filter.v_pointer); + + /* + * call to the filter if available + */ + if(filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + lttv_event_to_string(e, a_string, TRUE, !a_no_field_names, tfs); + + if(a_state) { + g_string_append_printf(a_string, " %s ", + g_quark_to_string(process->state->s)); + } + + g_string_append_printf(a_string,"\n"); + + fputs(a_string->str, a_file); + return FALSE; +} + + +static void init() +{ + gboolean result; + + LttvAttributeValue value; + + LttvIAttribute *attributes = LTTV_IATTRIBUTE(lttv_global_attributes()); + + g_info("Init textDump.c"); + + a_string = g_string_new(""); + + a_file_name = NULL; + lttv_option_add("output", 'o', + "output file where the text is written", + "file name", + LTTV_OPT_STRING, &a_file_name, NULL, NULL); + + a_no_field_names = FALSE; + lttv_option_add("field_names", 's', + "do not write the field names for each event", + "", + LTTV_OPT_NONE, &a_no_field_names, NULL, NULL); + + a_state = FALSE; + lttv_option_add("process_state", 'r', + "write the pid and state for each event", + "", + LTTV_OPT_NONE, &a_state, NULL, NULL); + + a_cpu_stats = FALSE; + lttv_option_add("cpu_stats", 'c', + "write the per cpu statistics", + "", + LTTV_OPT_NONE, &a_cpu_stats, NULL, NULL); + + a_process_stats = FALSE; + lttv_option_add("process_stats", 'p', + "write the per process statistics", + "", + LTTV_OPT_NONE, &a_process_stats, NULL, NULL); + + a_path_output = FALSE; + lttv_option_add("path_output", 'a', + "print the process stats in path format, for easy grepping", + "", + LTTV_OPT_NONE, &a_path_output, NULL, NULL); + + result = lttv_iattribute_find_by_path(attributes, "hooks/event", + LTTV_POINTER, &value); + g_assert(result); + event_hook = *(value.v_pointer); + g_assert(event_hook); + lttv_hooks_add(event_hook, write_event_content, NULL, LTTV_PRIO_DEFAULT); + + result = lttv_iattribute_find_by_path(attributes, "hooks/trace/before", + LTTV_POINTER, &value); + g_assert(result); + before_trace = *(value.v_pointer); + g_assert(before_trace); + lttv_hooks_add(before_trace, write_trace_header, NULL, LTTV_PRIO_DEFAULT); + + result = lttv_iattribute_find_by_path(attributes, "hooks/traceset/before", + LTTV_POINTER, &value); + g_assert(result); + before_traceset = *(value.v_pointer); + g_assert(before_traceset); + lttv_hooks_add(before_traceset, write_traceset_header, NULL, + LTTV_PRIO_DEFAULT); + + result = lttv_iattribute_find_by_path(attributes, "hooks/traceset/after", + LTTV_POINTER, &value); + g_assert(result); + after_traceset = *(value.v_pointer); + g_assert(after_traceset); + lttv_hooks_add(after_traceset, write_traceset_footer, NULL, + LTTV_PRIO_DEFAULT); +} + +static void destroy() +{ + g_info("Destroy textDump"); + + lttv_option_remove("output"); + + lttv_option_remove("field_names"); + + lttv_option_remove("process_state"); + + lttv_option_remove("cpu_stats"); + + lttv_option_remove("process_stats"); + + lttv_option_remove("path_output"); + + g_string_free(a_string, TRUE); + + lttv_hooks_remove_data(event_hook, write_event_content, NULL); + + lttv_hooks_remove_data(before_trace, write_trace_header, NULL); + + lttv_hooks_remove_data(before_traceset, write_traceset_header, NULL); + + lttv_hooks_remove_data(after_traceset, write_traceset_footer, NULL); +} + + +LTTV_MODULE("textDump", "Print events in a file", \ + "Produce a detailed text printout of a trace", \ + init, destroy, "stats", "batchAnalysis", "option", "print") + diff --git a/tags/lttv-0.11.3-23102008/lttv/modules/text/textFilter.c b/tags/lttv-0.11.3-23102008/lttv/modules/text/textFilter.c new file mode 100644 index 00000000..ca613ac6 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/lttv/modules/text/textFilter.c @@ -0,0 +1,226 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2005 Simon Bouvier-Zappa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation; + * + * 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. + */ + +/*! \file lttv/modules/text/textFilter.c + * \brief Textual prompt for user filtering expression. + * + * The text filter facility will prompt for user filter option + * and transmit them to the lttv core. User can either specify + * a filtering string with the command line or/and specify a + * file containing filtering expressions. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Insert the hooks before and after each trace and tracefile, and for each + event. Print a global header. */ + +/* + * TODO + * - specify wich hook function will be used to call the core filter + */ + +static char + *a_file_name = NULL, + *a_string = NULL; + +static LttvHooks + *before_traceset, + *event_hook; + +/** + * filters the file input from user + * @param hook_data the hook data, unused + */ +void filter_analyze_file(void *hook_data) { + + LttvAttributeValue value; + + LttvIAttribute *attributes = LTTV_IATTRIBUTE(lttv_global_attributes()); + + /* + * User may specify filtering options through static file + * and/or command line string. From these sources, an + * option string is rebuilded and sent to the filter core + */ + if(!g_file_test(a_file_name,G_FILE_TEST_EXISTS)) { + g_warning("file %s does not exist", a_file_name); + return; + } + + char* a_file_content = NULL; + + g_file_get_contents(a_file_name,&a_file_content,NULL,NULL); + + g_assert(lttv_iattribute_find_by_path(attributes, "filter/expression", + LTTV_POINTER, &value)); + + if(((GString*)*(value.v_pointer))->len != 0) + g_string_append_c((GString*)*(value.v_pointer),'&'); + g_string_append_c((GString*)*(value.v_pointer),'('); + g_string_append((GString*)*(value.v_pointer),a_file_content); + g_string_append_c((GString*)*(value.v_pointer),')'); + +} + +/** + * filters the string input from user + * @param hook_data the hook data, unused + */ +void filter_analyze_string(void *hook_data) { + + LttvAttributeValue value; + + LttvIAttribute *attributes = LTTV_IATTRIBUTE(lttv_global_attributes()); + + /* + * User may specify filtering options through static file + * and/or command line string. From these sources, an + * option string is rebuilded and sent to the filter core + */ + g_assert(lttv_iattribute_find_by_path(attributes, "filter/expression", + LTTV_POINTER, &value)); + + if(((GString*)*(value.v_pointer))->len != 0) + g_string_append_c((GString*)*(value.v_pointer),'&'); + g_string_append_c((GString*)*(value.v_pointer),'('); + g_string_append((GString*)*(value.v_pointer),a_string); + g_string_append_c((GString*)*(value.v_pointer),')'); + +} + +/** + * Output all filter commands on console + * @param hook_data the hook data + */ +void filter_list_commands(void *hook_data) { + + g_print("[field] [op] [value]\n\n"); + + g_print("*** Possible fields ***\n"); + g_print("event.name (string)\n"); + g_print("event.facility (string)\n"); + g_print("event.category (string)\n"); + g_print("event.time (double)\n"); + g_print("event.tsc (integer)\n"); + g_print("event.target_pid (integer)\n"); + g_print("event.field.facility_name.event_name.field_name.subfield_name (field_type)\n"); + g_print("tracefile.name (string)\n"); + g_print("trace.name (string)\n"); + g_print("state.pid (integer)\n"); + g_print("state.ppid (integer)\n"); + g_print("state.creation_time (double)\n"); + g_print("state.insertion_time (double)\n"); + g_print("state.process_name (string)\n"); + g_print("state.thread_brand (string)\n"); + g_print("state.execution_mode (string)\n"); + g_print("state.execution_submode (string)\n"); + g_print("state.process_status (string)\n"); + g_print("state.cpu (string)\n\n"); + + g_print("*** Possible operators ***\n"); + g_print("equal '='\n"); + g_print("not equal '!='\n"); + g_print("greater '>'\n"); + g_print("greater or equal '>='\n"); + g_print("lower '<'\n"); + g_print("lower or equal '<='\n"); + + g_print("*** Possible values ***\n"); + g_print("string, integer, double"); +} + +/** + * initialize the new module + */ +static void init() { + + LttvAttributeValue value; + + LttvIAttribute *attributes = LTTV_IATTRIBUTE(lttv_global_attributes()); + + g_assert(lttv_iattribute_find_by_path(attributes, "filter/expression", + LTTV_POINTER, &value)); + + *(value.v_pointer) = g_string_new(""); + + g_info("Init textFilter.c"); + + a_string = NULL; + lttv_option_add("expression", 'e', + "filters a string issued by the user on the command line", + "string", + LTTV_OPT_STRING, &a_string, filter_analyze_string, NULL); + // add function to call for option + + a_file_name = NULL; + lttv_option_add("filename", 'f', + "browse the filter options contained in specified file", + "file name", + LTTV_OPT_STRING, &a_file_name, filter_analyze_file, NULL); + + lttv_option_add("list", 'l', + "list all possible filter commands for module", + "list commands", + LTTV_OPT_NONE, NULL, filter_list_commands, NULL); + +} + +/** + * Destroy the current module + */ +static void destroy() { + g_info("Destroy textFilter"); + + lttv_option_remove("expression"); + + lttv_option_remove("filename"); + + lttv_option_remove("list"); + + LttvAttributeValue value; + + LttvIAttribute *attributes = LTTV_IATTRIBUTE(lttv_global_attributes()); + + g_assert(lttv_iattribute_find_by_path(attributes, "filter/expression", + LTTV_POINTER, &value)); + + g_string_free((GString*)*(value.v_pointer),TRUE); + +} + + +LTTV_MODULE("textFilter", "Filters traces", \ + "Filter the trace following commands issued by user input", \ + init, destroy, "option") + diff --git a/tags/lttv-0.11.3-23102008/profile.sh b/tags/lttv-0.11.3-23102008/profile.sh new file mode 100755 index 00000000..0c0a3fc0 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/profile.sh @@ -0,0 +1,41 @@ +#! /bin/sh + +# Script to build three versions of ltt/lttv (optimized, optimized and profiled +# and not optimized) and compare their performance for processing a trace. +# The profiled version produces detailed per function timings. +# +# The script expects 2 arguments: the temporary directory where to install +# the three versions and the directory containing the trace to process. + +v1=$1/ltt1 +v2=$1/ltt2 +v3=$1/ltt3 +tracedir=$2 + +if [ -z "$1" -o -z "$tracedir" ]; then + echo "Usage: $0 tmpdir trace" + exit 1 +fi + +BuildTest () { + (make clean; ./autogen.sh --prefix=$1 --enable-lttvstatic CFLAGS="$2" LDFLAGS="$3"; make; make install) >& build.`basename $1` +} + +RunTest () { + echo RunTest $1 $2 + rm gmon.out + for version in $v1 $v2 $v3; do + /usr/bin/time $version/bin/lttv -m batchtest -t $tracedir $1 >& test.`basename $version`.$2 + done + gprof $v2/bin/lttv >& test.profile.$2 +} + +BuildTest $v1 "-O2 -g" "-g" +BuildTest $v2 "-pg -g -O2" "-pg -g" +BuildTest $v3 "-g" "-g" + +RunTest --test1 countevents +RunTest --test2 computestate +RunTest --test4 computestats +RunTest --test6 savestate +RunTest "--test3 --test6 --test7 --seek-number 200" seekevents diff --git a/tags/lttv-0.11.3-23102008/runlttv b/tags/lttv-0.11.3-23102008/runlttv new file mode 100755 index 00000000..60fe83b5 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/runlttv @@ -0,0 +1,41 @@ +#!/bin/sh +# Released under the GPL +# pmf - 2008/07/31 + +# This script runs LTTV in place in the compile directory without need for +# installing it with make install. +# +# The .runlttvrc file can be used to control its behavior. +# - by setting the TRACEFILE variable, a trace can be automatically loaded +# ex: TRACEFILE="-t /tmp/traces/dijkstra-20071212" +# - by setting the ARGS variable, a different set of plugins can be loaded +# for an example see the ARGS= line below +# +# In order for icons to display correctly, it might be necessary to create a +# symlink: +# $ ln -s ./lttv/modules/gui/lttvwindow/pixmaps +# while in the same directory as this script. + +RCFILE=".runlttvrc" + +ARGS="-L lttv/modules/gui/controlflow/.libs -m guicontrolflow -L lttv/modules/gui/lttvwindow/lttvwindow/.libs -m lttvwindow -L lttv/modules/gui/detailedevents/.libs -m guievents -L lttv/modules/gui/tracecontrol/.libs -m guitracecontrol -L lttv/modules/gui/statistics/.libs -m guistatistics -L lttv/modules/gui/resourceview/.libs -m resourceview -L lttv/modules/gui/filter/.libs -m guifilter -L lttv/modules/gui/interrupts/.libs -m interrupts -L lttv/modules/gui/histogram/.libs -m guihistogram" +LTTV_EXEC="lttv/lttv/.libs/lttv.real" + +if [ ! -e "$LTTV_EXEC" ]; then + echo "error: LTTV should be compiled before running this script." >/dev/stderr + exit 1 +fi + +if [ -e "$RCFILE" ]; then + source "$RCFILE"; +fi + +if [ "$1" = "dbg" ]; then + LD_LIBRARY_PATH=ltt/.libs gdb --args $LTTV_EXEC $ARGS $TRACEFILE +elif [ "$1" = "valgrind" ]; then + LD_LIBRARY_PATH=ltt/.libs valgrind $LTTV_EXEC $ARGS $TRACEFILE +elif [ "$1" = "strace" ]; then + LD_LIBRARY_PATH=ltt/.libs strace $LTTV_EXEC $ARGS $TRACEFILE +else + LD_LIBRARY_PATH=ltt/.libs $LTTV_EXEC $ARGS $TRACEFILE +fi diff --git a/tags/lttv-0.11.3-23102008/specs/lttv.spec b/tags/lttv-0.11.3-23102008/specs/lttv.spec new file mode 100644 index 00000000..31852770 --- /dev/null +++ b/tags/lttv-0.11.3-23102008/specs/lttv.spec @@ -0,0 +1,161 @@ +# +# Spec file for LTTV +# +Summary: Linux Trace Toolkit Viewer +Name: lttv +Version: 0.8.48 +Release: 21062006 +License: GPL +Group: Applications/Development +Source: http://ltt.polymtl.ca/packages/LinuxTraceToolkitViewer-%{version}-%{release}.tar.gz +URL: http://ltt.polymtl.ca +Packager: Mathieu Desnoyers +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) + +# Where do we install the libs +%ifarch x86_64 ppc64 ppc64iseries ia64 +%define libdir /usr/lib64 +%else +%define libdir /usr/lib +%endif + + +%description +LTTV is a modular trace viewer. It can perform analysis on traces of a Linux + kernel instrumented with LTTng. + +%prep +%setup -q -n LinuxTraceToolkitViewer-%{version}-%{release} + +%build +# These two commands were added to fix compilation on x86_64 machines +aclocal +automake + +./configure --prefix=/usr --libdir=%{libdir} +make + +%install +rm -rf $RPM_BUILD_ROOT +make DESTDIR=$RPM_BUILD_ROOT install + +%post +echo "Running ldconfig (might take a while)" +ldconfig + +%postun +echo "Running ldconfig (might take a while)" +ldconfig + +%files +%{libdir}/liblttvtraceread.so.0.0.0 +%{libdir}/liblttvtraceread.so.0 +%{libdir}/liblttvtraceread.so +%{libdir}/liblttvtraceread.la +%{libdir}/liblttvtraceread.a +%{libdir}/lttv +%{libdir}/lttv/plugins +%{libdir}/lttv/plugins/libtextDump.so.0.0.0 +%{libdir}/lttv/plugins/libtextDump.so.0 +%{libdir}/lttv/plugins/libtextDump.so +%{libdir}/lttv/plugins/libtextDump.la +%{libdir}/lttv/plugins/libtextDump.a +%{libdir}/lttv/plugins/libbatchAnalysis.so.0.0.0 +%{libdir}/lttv/plugins/libbatchAnalysis.so.0 +%{libdir}/lttv/plugins/libbatchAnalysis.so +%{libdir}/lttv/plugins/libbatchAnalysis.la +%{libdir}/lttv/plugins/libbatchAnalysis.a +%{libdir}/lttv/plugins/libtextFilter.so.0.0.0 +%{libdir}/lttv/plugins/libtextFilter.so.0 +%{libdir}/lttv/plugins/libtextFilter.so +%{libdir}/lttv/plugins/libtextFilter.la +%{libdir}/lttv/plugins/libtextFilter.a +%{libdir}/lttv/plugins/libguicontrolflow.so.0.0.0 +%{libdir}/lttv/plugins/libguicontrolflow.so.0 +%{libdir}/lttv/plugins/libguicontrolflow.so +%{libdir}/lttv/plugins/libguicontrolflow.la +%{libdir}/lttv/plugins/libguicontrolflow.a +%{libdir}/lttv/plugins/libguievents.so.0.0.0 +%{libdir}/lttv/plugins/libguievents.so.0 +%{libdir}/lttv/plugins/libguievents.so +%{libdir}/lttv/plugins/libguievents.la +%{libdir}/lttv/plugins/libguievents.a +%{libdir}/lttv/plugins/libguistatistics.so.0.0.0 +%{libdir}/lttv/plugins/libguistatistics.so.0 +%{libdir}/lttv/plugins/libguistatistics.so +%{libdir}/lttv/plugins/libguistatistics.la +%{libdir}/lttv/plugins/libguistatistics.a +%{libdir}/lttv/plugins/libguifilter.so.0.0.0 +%{libdir}/lttv/plugins/libguifilter.so.0 +%{libdir}/lttv/plugins/libguifilter.so +%{libdir}/lttv/plugins/libguifilter.la +%{libdir}/lttv/plugins/libguifilter.a +%{libdir}/lttv/plugins/libguitracecontrol.so.0.0.0 +%{libdir}/lttv/plugins/libguitracecontrol.so.0 +%{libdir}/lttv/plugins/libguitracecontrol.so +%{libdir}/lttv/plugins/libguitracecontrol.la +%{libdir}/lttv/plugins/libguitracecontrol.a +%{libdir}/liblttvwindow.so.0.0.0 +%{libdir}/liblttvwindow.so.0 +%{libdir}/liblttvwindow.so +%{libdir}/liblttvwindow.la +%{libdir}/liblttvwindow.a +/usr/include/ltt +/usr/include/ltt/compiler.h +/usr/include/ltt/event.h +/usr/include/ltt/facility.h +/usr/include/ltt/ltt.h +/usr/include/ltt/time.h +/usr/include/ltt/trace.h +/usr/include/ltt/type.h +/usr/include/ltt/ltt-types.h +/usr/include/lttv +/usr/include/lttv/attribute.h +/usr/include/lttv/hook.h +/usr/include/lttv/iattribute.h +/usr/include/lttv/lttv.h +/usr/include/lttv/module.h +/usr/include/lttv/option.h +/usr/include/lttv/state.h +/usr/include/lttv/stats.h +/usr/include/lttv/tracecontext.h +/usr/include/lttv/traceset.h +/usr/include/lttv/filter.h +/usr/include/lttv/print.h +/usr/include/lttvwindow +/usr/include/lttvwindow/lttvwindow.h +/usr/include/lttvwindow/lttvwindowtraces.h +/usr/include/lttvwindow/mainwindow.h +/usr/include/lttvwindow/menu.h +/usr/include/lttvwindow/toolbar.h +/usr/bin/lttv.real +/usr/bin/lttv +/usr/bin/lttv-gui +/usr/share/LinuxTraceToolkitViewer +/usr/share/LinuxTraceToolkitViewer/pixmaps +/usr/share/LinuxTraceToolkitViewer/pixmaps/1downarrow.png +/usr/share/LinuxTraceToolkitViewer/pixmaps/1uparrow.png +/usr/share/LinuxTraceToolkitViewer/pixmaps/Makefile +/usr/share/LinuxTraceToolkitViewer/pixmaps/Makefile.am +/usr/share/LinuxTraceToolkitViewer/pixmaps/Makefile.in +/usr/share/LinuxTraceToolkitViewer/pixmaps/close.png +/usr/share/LinuxTraceToolkitViewer/pixmaps/edit_add_22.png +/usr/share/LinuxTraceToolkitViewer/pixmaps/edit_remove_22.png +/usr/share/LinuxTraceToolkitViewer/pixmaps/filenew.png +/usr/share/LinuxTraceToolkitViewer/pixmaps/fileopen.png +/usr/share/LinuxTraceToolkitViewer/pixmaps/filesave.png +/usr/share/LinuxTraceToolkitViewer/pixmaps/filesaveas.png +/usr/share/LinuxTraceToolkitViewer/pixmaps/gtk-add.png +/usr/share/LinuxTraceToolkitViewer/pixmaps/gtk-jump-to.png +/usr/share/LinuxTraceToolkitViewer/pixmaps/lttv-color-list.png +/usr/share/LinuxTraceToolkitViewer/pixmaps/mini-display.xpm +/usr/share/LinuxTraceToolkitViewer/pixmaps/move_message.xpm +/usr/share/LinuxTraceToolkitViewer/pixmaps/remove.png +/usr/share/LinuxTraceToolkitViewer/pixmaps/remove1.png +/usr/share/LinuxTraceToolkitViewer/pixmaps/stock_jump_to_24.png +/usr/share/LinuxTraceToolkitViewer/pixmaps/stock_redo_24.png +/usr/share/LinuxTraceToolkitViewer/pixmaps/stock_refresh_24.png +/usr/share/LinuxTraceToolkitViewer/pixmaps/stock_stop_24.png +/usr/share/LinuxTraceToolkitViewer/pixmaps/stock_zoom_fit_24.png +/usr/share/LinuxTraceToolkitViewer/pixmaps/stock_zoom_in_24.png +/usr/share/LinuxTraceToolkitViewer/pixmaps/stock_zoom_out_24.png diff --git a/trunk/tests/kernel/Makefile b/trunk/tests/kernel/Makefile index af125b7e..9555094a 100644 --- a/trunk/tests/kernel/Makefile +++ b/trunk/tests/kernel/Makefile @@ -15,6 +15,7 @@ endif #obj-m += test-sys_call.o # obj-m += test-bug.o obj-m += test-nop-speed.o + obj-m += test-hpet.o # obj-m += test-prefix-speed-32.o obj-m += test-prefix-speed.o obj-m += test-psrwlock.o diff --git a/trunk/tests/kernel/test-hpet.c b/trunk/tests/kernel/test-hpet.c index bf0dda5c..41cebce1 100644 --- a/trunk/tests/kernel/test-hpet.c +++ b/trunk/tests/kernel/test-hpet.c @@ -9,21 +9,41 @@ #include #include #include +#include +#include +#include + +//#define NR_LOOPS 10000 +#define NR_LOOPS 10 + +extern cycle_t read_hpet(void); static int __init test_init(void) { int i; cycles_t time1, time2; volatile unsigned long myval; + int sync_save; /* racy */ + sync_save = ltt_tsc_is_sync; + ltt_tsc_is_sync = 0; + //ltt_tsc_is_sync = 1; + return -EPERM; //TEST ! + myval = ltt_get_timestamp64(); time1 = get_cycles(); - for (i=0; i<1; i++) { + for (i=0; i < NR_LOOPS; i++) { //printk("time %llu\n", ltt_tsc_read()); - //myval = ltt_tsc_read(); - myval = hpet_readl(HPET_COUNTER); + //get_cycles_barrier(); + //myval = get_cycles(); + //get_cycles_barrier(); + myval = read_hpet(); + //clflush(<t_last_tsc); + //myval = ltt_get_timestamp64(); + printk("val : %llu\n", (unsigned long long)myval); } time2 = get_cycles(); - printk("timediff %llu\n", time2-time1); + printk("timediff %llu\n", (time2-time1)/NR_LOOPS); + ltt_tsc_is_sync = sync_save; return -EPERM; }