Initial import of code.
authorJulien Desfossez <julien.desfossez@efficios.com>
Tue, 21 Feb 2012 21:59:26 +0000 (16:59 -0500)
committerJulien Desfossez <julien.desfossez@efficios.com>
Tue, 21 Feb 2012 21:59:26 +0000 (16:59 -0500)
This is the first code base which uses the babeltrace API instead of
being built inside the babeltrace tree.

Signed-off-by: Julien Desfossez <julien.desfossez@efficios.com>
19 files changed:
.gitignore [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
LICENSE [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
README [new file with mode: 0644]
bootstrap [new file with mode: 0755]
configure.ac [new file with mode: 0644]
gpl-2.0.txt [new file with mode: 0644]
lttngtop/Makefile.am [new file with mode: 0644]
lttngtop/common.c [new file with mode: 0644]
lttngtop/common.h [new file with mode: 0644]
lttngtop/cputop.c [new file with mode: 0644]
lttngtop/cputop.h [new file with mode: 0644]
lttngtop/cursesdisplay.c [new file with mode: 0644]
lttngtop/cursesdisplay.h [new file with mode: 0644]
lttngtop/iostreamtop.c [new file with mode: 0644]
lttngtop/iostreamtop.h [new file with mode: 0644]
lttngtop/lttngtop.c [new file with mode: 0644]
lttngtop/lttngtoptypes.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..4f465a4
--- /dev/null
@@ -0,0 +1,21 @@
+*.o
+*.a
+*.la
+*.lo
+.libs
+.deps
+*.bkp
+/config.h
+/config.h.in
+/config.status
+*.log
+*.m4
+*.patch
+libtool
+/configure
+Makefile
+Makefile.in
+autom4te.cache/
+config/
+core
+stamp-h1
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..013e59d
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,2 @@
+2012-02-16 LTTngTop 0.2
+       * Remove internal Babeltrace code, use the API
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..f1f4a4c
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,8 @@
+LTTngTop
+Julien Desfossez
+February 16, 2012
+
+* GPLv2
+
+All source code is distributed under the GPLv2 license, as specified in the
+per-file license. See gpl-2.0.txt for details.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..009425c
--- /dev/null
@@ -0,0 +1,5 @@
+AM_CFLAGS = $(PACKAGE_CFLAGS)
+
+ACLOCAL_AMFLAGS = -I m4
+
+SUBDIRS = lttngtop
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..592e109
--- /dev/null
+++ b/README
@@ -0,0 +1,93 @@
+lttngtop
+Julien Desfossez <julien.desfossez@polymtl.ca>
+August 2011
+
+Lttngtop is an ncurses interface for reading and browsing traces recorded by
+the LTTng tracer and displaying various statistics.
+As of now, the cpu usage and perf counters are displayed. This version currently
+only supports offline traces, but a live version is in alpha and will be
+available for testing soon.
+
+USAGE
+-----
+
+Record a trace with LTTng 2.0 with at least the sched_switch event, and the pid,
+procname, tid, ppid contexts. To have the perftop view working you can enable
+any number of perf contexts (depending on your hardware).
+For the iotop view, you need to have some syscall events : sys_read, sys_write
+and exit_syscall.
+
+Once your trace is recorded, you can use lttngtop this way :
+$ lttngtop /path/to/your/trace
+
+Make sure you have read permissions on the whole directory.
+
+NOTE
+----
+
+Since this is an early release of lttngtop, it is still inside the babeltrace
+source tree. As soon as all babeltrace dependencies are  available from the
+library and header files, lttngtop will be in its own repository. Until then,
+changes in the babeltrace source tree will be merged in this tree to avoid
+conflicts. This repository doesn't install the babeltrace binary, or any of its
+libraries. This way you can safely install it in the default locations.
+
+EXAMPLE
+-------
+
+# lttng create lttngtop
+# lttng enable-event -k sched_switch
+# lttng add-context -k -t pid -t procname -t tid -t ppid -t perf:cache-misses \
+       -t perf:major-faults -t perf:branch-load-misses
+# lttng start
+# ...do stuff...
+# lttng stop
+# lttng destroy
+
+$ lttngtop $HOME/lttng-traces/lttngtop-...
+
+BUILDING
+--------
+
+       ./bootstrap (skip if using tarball)
+       ./configure
+       make
+       make install
+
+DEPENDENCIES
+------------
+
+To compile Babeltrace and lttngtop, you will need:
+
+       gcc 3.2 or better
+       libc6 development librairies
+         (Debian : libc6, libc6-dev)
+         (Fedora : glibc, glibc)
+       glib 2.22 or better development libraries
+         (Debian : libglib2.0-0, libglib2.0-dev)
+         (Fedora : glib2, glib2-devel)
+       uuid development libraries
+         (Debian : uuid-dev)
+         (Fedora : uuid-devel)
+       libpopt >= 1.13 development libraries
+         (Debian : libpopt-dev)
+         (Fedora : popt)
+       ncurses development libraries
+         (Debian : libncurses5-dev)
+
+For developers using the git tree:
+
+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 git repository tree :
+
+- GNU autotools (automake >=1.10, autoconf >=2.50, autoheader >=2.50)
+  (make sure your system wide "automake" points to a recent version!)
+- GNU Libtool >=2.2
+  (for more information, go to http://www.gnu.org/software/autoconf/)
+- Flex >=2.5.35.
+- Bison >=2.4.
+
+If you get the tree from the repository, you will need to use the "bootstrap"
+script in the root of the tree. It calls all the GNU tools needed to prepare the
+tree configuration.
diff --git a/bootstrap b/bootstrap
new file mode 100755 (executable)
index 0000000..c507425
--- /dev/null
+++ b/bootstrap
@@ -0,0 +1,12 @@
+#! /bin/sh
+
+set -x
+if [ ! -e config ]; then
+       mkdir config
+fi
+aclocal
+libtoolize --force --copy
+autoheader
+automake --add-missing --copy
+autoconf
+
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..d38cefb
--- /dev/null
@@ -0,0 +1,71 @@
+#                                               -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_INIT([lttngtop],[0.2],[julien dot desfossez at efficios dot com])
+AC_CONFIG_AUX_DIR([config])
+AC_CANONICAL_TARGET
+AC_CANONICAL_HOST
+AM_INIT_AUTOMAKE([foreign dist-bzip2 no-dist-gzip])
+AM_SILENT_RULES([yes])
+
+AC_CONFIG_MACRO_DIR([m4])
+
+AC_CONFIG_HEADERS([config.h])
+
+# Checks for programs.
+AC_PROG_CC
+AC_PROG_MAKE_SET
+LT_INIT
+AC_PROG_YACC
+AC_PROG_LEX
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_C_INLINE
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+
+# Checks for library functions.
+AC_FUNC_MALLOC
+AC_FUNC_MMAP
+AC_CHECK_FUNCS([bzero gettimeofday munmap strtoul])
+
+# Check for libuuid
+AC_CHECK_LIB([uuid], [uuid_generate], [],
+       [AC_MSG_ERROR([Cannot find the libuuid library.])]
+)
+
+# Check for libpopt
+AC_CHECK_LIB([popt], [poptGetContext], [],
+        [AC_MSG_ERROR([Cannot find the popt library.])]
+)
+
+# Check for libncurses
+AC_CHECK_LIB([ncurses], [initscr], [],
+       [AC_MSG_ERROR([Cannot find the the ncurses library.])]
+)
+
+# Check for libpanel, also part of libncurses
+# (but this check will add the required -lpanel flag)
+AC_CHECK_LIB([panel], [update_panels], [],
+       [AC_MSG_ERROR([Cannot find the the ncurses library.])]
+)
+
+# Check for Glib. It needs to be installed anyway or this macro will not be defined.
+AM_PATH_GLIB_2_0([2.22.0], [],
+       [AC_MSG_ERROR([Glib 2.22 is required in order to compile LTTngTop.
+Please install the Glib development files.])], [gmodule]
+)
+
+pkg_modules="gmodule-2.0 >= 2.0.0"
+PKG_CHECK_MODULES(GMODULE, [$pkg_modules])
+AC_SUBST(PACKAGE_LIBS)
+LIBS="$LIBS $GMODULE_LIBS"
+
+PACKAGE_CFLAGS="$GMODULE_CFLAGS -Wall -Werror=format-security"
+AC_SUBST(PACKAGE_CFLAGS)
+
+AC_CONFIG_FILES([
+       Makefile
+       lttngtop/Makefile
+])
+AC_OUTPUT
diff --git a/gpl-2.0.txt b/gpl-2.0.txt
new file mode 100644 (file)
index 0000000..d511905
--- /dev/null
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/lttngtop/Makefile.am b/lttngtop/Makefile.am
new file mode 100644 (file)
index 0000000..5d6ca3e
--- /dev/null
@@ -0,0 +1,12 @@
+AM_CFLAGS = $(PACKAGE_CFLAGS)
+
+bin_PROGRAMS = lttngtop
+
+lttngtop_SOURCES = \
+       lttngtop.c \
+       common.c \
+       cursesdisplay.c \
+       cputop.c \
+       iostreamtop.c
+
+lttngtop_LDADD = -lbabeltrace -lctf
diff --git a/lttngtop/common.c b/lttngtop/common.c
new file mode 100644 (file)
index 0000000..2a0f1f4
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2011 Julien Desfossez
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include "common.h"
+
+struct processtop *find_process_tid(struct lttngtop *ctx, int tid, char *comm)
+{
+       gint i;
+       struct processtop *tmp;
+
+       for (i = 0; i < ctx->process_table->len; i++) {
+               tmp = g_ptr_array_index(ctx->process_table, i);
+               if (tmp && tmp->tid == tid)
+                       return tmp;
+       }
+       return NULL;
+}
+
+struct processtop* add_proc(struct lttngtop *ctx, int tid, char *comm,
+               unsigned long timestamp)
+{
+       struct processtop *newproc;
+
+       /* if the PID already exists, we just rename the process */
+       /* FIXME : need to integrate with clone/fork/exit to be accurate */
+       newproc = find_process_tid(ctx, tid, comm);
+       if (!newproc) {
+               newproc = malloc(sizeof(struct processtop));
+               memset(newproc, 0, sizeof(struct processtop));
+               newproc->tid = tid;
+               newproc->birth = timestamp;
+               newproc->process_files_table = g_ptr_array_new();
+               newproc->threads = g_ptr_array_new();
+               newproc->perf = g_hash_table_new(g_direct_hash, g_direct_equal);
+               newproc->iostream = malloc(sizeof(struct iostream));
+               newproc->iostream->ret_read = 0;
+               newproc->iostream->ret_write = 0;
+               newproc->iostream->ret_total = 0;
+               newproc->iostream->syscall_info = NULL;
+               g_ptr_array_add(ctx->process_table, newproc);
+       }
+       newproc->comm = strdup(comm);
+
+       return newproc;
+}
+
+struct processtop* update_proc(struct processtop* proc, int pid, int tid,
+               int ppid, char *comm)
+{
+       if (proc) {
+               proc->pid = pid;
+               proc->tid = tid;
+               proc->ppid = ppid;
+               if (strcmp(proc->comm, comm) != 0) {
+                       free(proc->comm);
+                       proc->comm = strdup(comm);
+               }
+       }
+       return proc;
+}
+
+/*
+ * This function just sets the time of death of a process.
+ * When we rotate the cputime we remove it from the process list.
+ */
+void death_proc(struct lttngtop *ctx, int tid, char *comm,
+               unsigned long timestamp)
+{
+       struct processtop *tmp;
+       tmp = find_process_tid(ctx, tid, comm);
+       if (tmp && strcmp(tmp->comm, comm) == 0)
+               tmp->death = timestamp;
+}
+
+struct processtop* get_proc(struct lttngtop *ctx, int tid, char *comm,
+               unsigned long timestamp)
+{
+       struct processtop *tmp;
+       tmp = find_process_tid(ctx, tid, comm);
+       if (tmp && strcmp(tmp->comm, comm) == 0)
+               return tmp;
+       return add_proc(ctx, tid, comm, timestamp);
+}
+
+void add_thread(struct processtop *parent, struct processtop *thread)
+{
+       gint i;
+       struct processtop *tmp;
+
+       for (i = 0; i < parent->threads->len; i++) {
+               tmp = g_ptr_array_index(parent->threads, i);
+               if (tmp == thread)
+                       return;
+       }
+       g_ptr_array_add(parent->threads, thread);
+}
+
+struct cputime* add_cpu(int cpu)
+{
+       struct cputime *newcpu;
+
+       newcpu = malloc(sizeof(struct cputime));
+       newcpu->id = cpu;
+       newcpu->current_task = NULL;
+       newcpu->perf = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+       g_ptr_array_add(lttngtop.cpu_table, newcpu);
+
+       return newcpu;
+}
+struct cputime* get_cpu(int cpu)
+{
+       gint i;
+       struct cputime *tmp;
+
+       for (i = 0; i < lttngtop.cpu_table->len; i++) {
+               tmp = g_ptr_array_index(lttngtop.cpu_table, i);
+               if (tmp->id == cpu)
+                       return tmp;
+       }
+
+       return add_cpu(cpu);
+}
+
+/*
+ * At the end of a sampling period, we need to display the cpu time for each
+ * process and to reset it to zero for the next period
+ */
+void rotate_cputime(unsigned long end)
+{
+       gint i;
+       struct cputime *tmp;
+       unsigned long elapsed;
+
+       for (i = 0; i < lttngtop.cpu_table->len; i++) {
+               tmp = g_ptr_array_index(lttngtop.cpu_table, i);
+               elapsed = end - tmp->task_start;
+               if (tmp->current_task) {
+                       tmp->current_task->totalcpunsec += elapsed;
+                       tmp->current_task->threadstotalcpunsec += elapsed;
+                       if (tmp->current_task->pid != tmp->current_task->tid &&
+                                       tmp->current_task->threadparent) {
+                               tmp->current_task->threadparent->threadstotalcpunsec += elapsed;
+                       }
+               }
+               tmp->task_start = end;
+       }
+}
+
+void reset_perf_counter(gpointer key, gpointer value, gpointer user_data)
+{
+       ((struct perfcounter*) value)->count = 0;
+}
+
+void copy_perf_counter(gpointer key, gpointer value, gpointer new_table)
+{
+       struct perfcounter *newperf;
+       
+       newperf = malloc(sizeof(struct perfcounter));
+       newperf->count = ((struct perfcounter *) value)->count;
+       newperf->visible = ((struct perfcounter *) value)->visible;
+       newperf->sort = ((struct perfcounter *) value)->sort;
+       g_hash_table_insert((GHashTable *) new_table, key, newperf);
+}
+
+void rotate_perfcounter() {
+       int i;
+       struct processtop *tmp;
+       for (i = 0; i < lttngtop.process_table->len; i++) {
+               tmp = g_ptr_array_index(lttngtop.process_table, i);
+               g_hash_table_foreach(tmp->perf, reset_perf_counter, NULL);
+       }
+}
+
+void cleanup_processtop()
+{
+       gint i;
+       struct processtop *tmp;
+
+       for (i = 0; i < lttngtop.process_table->len; i++) {
+               tmp = g_ptr_array_index(lttngtop.process_table, i);
+               tmp->totalcpunsec = 0;
+               tmp->threadstotalcpunsec = 0;
+               tmp->iostream->ret_read = 0;
+               tmp->iostream->ret_write = 0;
+       }
+}
+
+struct lttngtop* get_copy_lttngtop(unsigned long start, unsigned long end)
+{
+       gint i, j;
+       unsigned long time;
+       struct lttngtop *dst;
+       struct processtop *tmp, *tmp2, *new;
+       struct cputime *tmpcpu, *newcpu;
+       struct files *tmpfile, *newfile;
+
+       dst = malloc(sizeof(struct lttngtop));
+       dst = memset(dst, 0, sizeof(struct lttngtop));
+       dst->start = start;
+       dst->end = end;
+       dst->process_table = g_ptr_array_new();
+       dst->files_table = g_ptr_array_new();
+       dst->cpu_table = g_ptr_array_new();
+       dst->perf_list = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+       rotate_cputime(end);
+
+       g_hash_table_foreach(lttngtop.perf_list, copy_perf_counter, dst->perf_list);
+       for (i = 0; i < lttngtop.process_table->len; i++) {
+               tmp = g_ptr_array_index(lttngtop.process_table, i);
+               new = malloc(sizeof(struct processtop));
+
+               memcpy(new, tmp, sizeof(struct processtop));
+               new->threads = g_ptr_array_new();
+               new->comm = strdup(tmp->comm);
+               new->process_files_table = g_ptr_array_new();
+               new->perf = g_hash_table_new(g_direct_hash, g_direct_equal);
+               g_hash_table_foreach(tmp->perf, copy_perf_counter, new->perf);
+
+               new->iostream = malloc(sizeof(struct iostream));
+               memcpy(new->iostream, tmp->iostream, sizeof(struct iostream));
+               /* compute the stream speed */
+               if (end - start != 0)
+               {
+                       time = (end - start)/NSEC_PER_SEC;
+                       new->iostream->ret_read = new->iostream->ret_read/(time);
+                       new->iostream->ret_write = new->iostream->ret_write/(time);
+               }
+
+               for (j = 0; j < tmp->process_files_table->len; j++) {
+                       tmpfile = g_ptr_array_index(tmp->process_files_table, j);
+                       newfile = malloc(sizeof(struct files));
+
+                       memcpy(newfile, tmpfile, sizeof(struct files));
+
+                       newfile->name = strdup(tmpfile->name);
+                       newfile->ref = new;
+
+                       g_ptr_array_add(new->process_files_table, newfile);
+                       g_ptr_array_add(dst->files_table, newfile);
+
+                       /*
+                        * if the process died during the last period, we remove all
+                        * files associated with if after the copy
+                        */
+                       if (tmp->death > 0 && tmp->death < end) {
+                               g_ptr_array_remove(tmp->process_files_table, tmpfile);
+                               free(tmpfile);
+                       }
+               }
+               g_ptr_array_add(dst->process_table, new);
+
+               /*
+                * if the process died during the last period, we remove it from
+                * the current process list after the copy
+                */
+               if (tmp->death > 0 && tmp->death < end) {
+                       g_ptr_array_remove(lttngtop.process_table, tmp);
+                       g_ptr_array_free(tmp->threads, TRUE);
+                       free(tmp->comm);
+                       g_ptr_array_free(tmp->process_files_table, TRUE);
+                       g_hash_table_destroy(tmp->perf);
+                       free(tmp);
+               }
+       }
+       rotate_perfcounter();
+
+       for (i = 0; i < lttngtop.cpu_table->len; i++) {
+               tmpcpu = g_ptr_array_index(lttngtop.cpu_table, i);
+               newcpu = malloc(sizeof(struct cputime));
+               memcpy(newcpu, tmpcpu, sizeof(struct cputime));
+               newcpu->perf = g_hash_table_new(g_direct_hash, g_direct_equal);
+               g_hash_table_foreach(tmpcpu->perf, copy_perf_counter, newcpu->perf);
+               /*
+                * note : we don't care about the current process pointer in the copy
+                * so the reference is invalid after the memcpy
+                */
+               g_ptr_array_add(dst->cpu_table, newcpu);
+       }
+       /* create the threads index if required */
+       for (i = 0; i < dst->process_table->len; i++) {
+               tmp = g_ptr_array_index(dst->process_table, i);
+               if (tmp->pid == tmp->tid) {
+                       for (j = 0; j < dst->process_table->len; j++) {
+                               tmp2 = g_ptr_array_index(dst->process_table, j);
+                               if (tmp2->pid == tmp->pid) {
+                                       tmp2->threadparent = tmp;
+                                       g_ptr_array_add(tmp->threads, tmp2);
+                               }
+                       }
+               }
+       }
+
+       //  update_global_stats(dst);
+       cleanup_processtop();
+
+       return dst;
+}
+
diff --git a/lttngtop/common.h b/lttngtop/common.h
new file mode 100644 (file)
index 0000000..e81256c
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 Julien Desfossez
+ *
+ * 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 _COMMON_H
+#define _COMMON_H
+
+#include <semaphore.h>
+#include <babeltrace/ctf/events.h>
+#include "lttngtoptypes.h"
+#include "cputop.h"
+
+#define NSEC_PER_USEC 1000
+#define NSEC_PER_SEC 1000000000L
+
+sem_t goodtodisplay, goodtoupdate, timer, pause_sem, end_trace_sem, bootstrap;
+
+GPtrArray *copies; /* struct lttngtop */
+pthread_mutex_t perf_list_mutex;
+
+struct lttngtop *data;
+
+struct processtop *find_process_tid(struct lttngtop *ctx, int pid, char *comm);
+struct processtop* add_proc(struct lttngtop *ctx, int pid, char *comm,
+               unsigned long timestamp);
+struct processtop* update_proc(struct processtop* proc, int pid, int tid,
+               int ppid, char *comm);
+void add_thread(struct processtop *parent, struct processtop *thread);
+struct processtop* get_proc(struct lttngtop *ctx, int tid, char *comm,
+               unsigned long timestamp);
+void death_proc(struct lttngtop *ctx, int tid, char *comm,
+               unsigned long timestamp);
+struct cputime* add_cpu(int cpu);
+struct cputime* get_cpu(int cpu);
+struct lttngtop* get_copy_lttngtop(unsigned long start, unsigned long end);
+struct perfcounter *add_perf_counter(GPtrArray *perf, GQuark quark,
+               unsigned long count);
+struct perfcounter *get_perf_counter(const char *name, struct processtop *proc,
+               struct cputime *cpu);
+
+#endif /* _COMMON_H */
diff --git a/lttngtop/cputop.c b/lttngtop/cputop.c
new file mode 100644 (file)
index 0000000..2ade0db
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2011 Julien Desfossez
+ *
+ * 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 <babeltrace/babeltrace.h>
+
+#include "lttngtoptypes.h"
+#include "common.h"
+#include "cputop.h"
+
+void update_cputop_data(unsigned long timestamp, int64_t cpu, int prev_pid,
+               int next_pid, char *prev_comm, char *next_comm)
+{
+       struct cputime *tmpcpu;
+       unsigned long elapsed;
+
+       tmpcpu = get_cpu(cpu);
+
+       if (tmpcpu->current_task && tmpcpu->current_task->pid == prev_pid) {
+               elapsed = timestamp - tmpcpu->task_start;
+               tmpcpu->current_task->totalcpunsec += elapsed;
+               tmpcpu->current_task->threadstotalcpunsec += elapsed;
+               if (tmpcpu->current_task->pid != tmpcpu->current_task->tid)
+                       tmpcpu->current_task->threadparent->threadstotalcpunsec += elapsed;
+       }
+
+       if (next_pid != 0)
+               tmpcpu->current_task = get_proc(&lttngtop, next_pid, next_comm, timestamp);
+       else
+               tmpcpu->current_task = NULL;
+
+       tmpcpu->task_start = timestamp;
+}
+
+enum bt_cb_ret handle_sched_switch(struct bt_ctf_event *call_data,
+               void *private_data)
+{
+       struct definition *scope;
+       unsigned long timestamp;
+       uint64_t cpu_id;
+       char *prev_comm, *next_comm;
+       int prev_tid, next_tid;
+
+       timestamp = bt_ctf_get_timestamp(call_data);
+       if (timestamp == -1ULL)
+               goto error;
+
+       scope = bt_ctf_get_top_level_scope(call_data,
+                       BT_EVENT_FIELDS);
+       prev_comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
+                               scope, "_prev_comm"));
+       if (bt_ctf_field_get_error()) {
+               fprintf(stderr, "Missing prev_comm context info\n");
+               goto error;
+       }
+
+       next_comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
+                               scope, "_next_comm"));
+       if (bt_ctf_field_get_error()) {
+               fprintf(stderr, "Missing next_comm context info\n");
+               goto error;
+       }
+
+       prev_tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
+                               scope, "_prev_tid"));
+       if (bt_ctf_field_get_error()) {
+               fprintf(stderr, "Missing prev_tid context info\n");
+               goto error;
+       }
+
+       next_tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
+                               scope, "_next_tid"));
+       if (bt_ctf_field_get_error()) {
+               fprintf(stderr, "Missing next_tid context info\n");
+               goto error;
+       }
+
+       scope = bt_ctf_get_top_level_scope(call_data,
+                       BT_STREAM_PACKET_CONTEXT);
+       cpu_id = bt_ctf_get_uint64(bt_ctf_get_field(call_data,
+                               scope, "cpu_id"));
+       if (bt_ctf_field_get_error()) {
+               fprintf(stderr, "Missing cpu_id context info\n");
+               goto error;
+       }
+
+       update_cputop_data(timestamp, cpu_id, prev_tid, next_tid,
+                       prev_comm, next_comm);
+
+       return BT_CB_OK;
+
+error:
+       return BT_CB_ERROR_STOP;
+}
+
+enum bt_cb_ret handle_sched_process_free(struct bt_ctf_event *call_data,
+               void *private_data)
+{
+       struct definition *scope;
+       unsigned long timestamp;
+       char *comm;
+       int tid;
+
+       timestamp = bt_ctf_get_timestamp(call_data);
+       if (timestamp == -1ULL)
+               goto error;
+
+       scope = bt_ctf_get_top_level_scope(call_data,
+                       BT_EVENT_FIELDS);
+       comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
+                               scope, "_comm"));
+       if (bt_ctf_field_get_error()) {
+               fprintf(stderr, "Missing procname context info\n");
+               goto error;
+       }
+
+       tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
+                               scope, "_tid"));
+       if (bt_ctf_field_get_error()) {
+               fprintf(stderr, "Missing tid context info\n");
+               goto error;
+       }
+
+       death_proc(&lttngtop, tid, comm, timestamp);
+
+       return BT_CB_OK;
+
+error:
+       return BT_CB_ERROR_STOP;
+
+}
+
diff --git a/lttngtop/cputop.h b/lttngtop/cputop.h
new file mode 100644 (file)
index 0000000..8b57823
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2011 Julien Desfossez
+ *
+ * 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 _LTTNGTOP_H
+#define _LTTNGTOP_H
+
+#include <babeltrace/babeltrace.h>
+#include <babeltrace/ctf/callbacks.h>
+#include <inttypes.h>
+#include <glib.h>
+
+enum bt_cb_ret handle_sched_switch(struct bt_ctf_event *hook_data,
+               void *call_data);
+
+enum bt_cb_ret handle_sched_process_free(struct bt_ctf_event *call_data,
+               void *private_data);
+
+#endif /* _LTTNGTOP_H */
diff --git a/lttngtop/cursesdisplay.c b/lttngtop/cursesdisplay.c
new file mode 100644 (file)
index 0000000..0f79485
--- /dev/null
@@ -0,0 +1,1003 @@
+/*
+ * Copyright (C) 2011 Julien Desfossez
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <ncurses.h>
+#include <panel.h>
+#include <pthread.h>
+#include <semaphore.h>
+
+#include "cursesdisplay.h"
+#include "lttngtoptypes.h"
+#include "common.h"
+
+#define DEFAULT_DELAY 15
+#define MAX_LINE_LENGTH 50
+#define MAX_LOG_LINES 4
+
+/* to prevent concurrent updates of the different windows */
+sem_t update_display_sem;
+
+char *termtype;
+WINDOW *footer, *header, *center, *status;
+WINDOW *perf_panel_window = NULL;
+PANEL *perf_panel, *main_panel;
+
+int perf_panel_visible = 0;
+int perf_line_selected = 0;
+
+int last_display_index, currently_displayed_index;
+
+struct processtop *selected_process = NULL;
+int selected_tid;
+char *selected_comm;
+int selected_ret;
+
+int selected_line = 0; /* select bar position */
+int selected_in_list = 0; /* selection relative to the whole list */
+int list_offset = 0; /* first index in the list to display (scroll) */
+int nb_log_lines = 0;
+char log_lines[MAX_LINE_LENGTH * MAX_LOG_LINES + MAX_LOG_LINES];
+
+int max_elements = 80;
+
+int toggle_threads = -1;
+int toggle_pause = -1;
+int toggle_tree = -1;
+
+int max_center_lines;
+
+pthread_t keyboard_thread;
+
+void reset_ncurses()
+{
+       curs_set(1);
+       endwin();
+       exit(0);
+}
+
+static void handle_sigterm(int signal)
+{
+       reset_ncurses();
+}
+
+void init_screen()
+{
+       initscr();
+       noecho();
+       halfdelay(DEFAULT_DELAY);
+       nonl();
+       intrflush(stdscr, false);
+       keypad(stdscr, true);
+       curs_set(0);
+
+       if (has_colors()) {
+               start_color();
+               init_pair(1, COLOR_RED, COLOR_BLACK); /* - */
+               init_pair(2, COLOR_GREEN, COLOR_BLACK); /* + */
+               init_pair(3, COLOR_BLACK, COLOR_WHITE); /* keys */
+               init_pair(4, COLOR_WHITE, COLOR_GREEN); /* keys activated */
+               init_pair(5, COLOR_WHITE, COLOR_BLUE); /* select line */
+       }
+       termtype = getenv("TERM");
+       if (!strcmp(termtype, "xterm") ||  !strcmp(termtype, "xterm-color") ||
+                       !strcmp(termtype, "vt220")) {
+               define_key("\033[H", KEY_HOME);
+               define_key("\033[F", KEY_END);
+               define_key("\033OP", KEY_F(1));
+               define_key("\033OQ", KEY_F(2));
+               define_key("\033OR", KEY_F(3));
+               define_key("\033OS", KEY_F(4));
+               define_key("\0330U", KEY_F(6));
+               define_key("\033[11~", KEY_F(1));
+               define_key("\033[12~", KEY_F(2));
+               define_key("\033[13~", KEY_F(3));
+               define_key("\033[14~", KEY_F(4));
+               define_key("\033[16~", KEY_F(6));
+               define_key("\033[17;2~", KEY_F(18));
+       }
+       signal(SIGTERM, handle_sigterm);
+       mousemask(BUTTON1_CLICKED, NULL);
+       refresh();
+}
+
+WINDOW *create_window(int height, int width, int startx, int starty)
+{
+       WINDOW *win;
+       win = newwin(height, width, startx, starty);
+       box(win, 0 , 0);
+       wrefresh(win);
+       return win;
+}
+
+WINDOW *create_window_no_border(int height, int width, int startx, int starty)
+{
+       WINDOW *win;
+       win = newwin(height, width, startx, starty);
+       wrefresh(win);
+       return win;
+}
+
+void print_digit(WINDOW *win, int digit)
+{
+       if (digit < 0) {
+               wattron(win, COLOR_PAIR(1));
+               wprintw(win, "%d", digit);
+               wattroff(win, COLOR_PAIR(1));
+       } else if (digit > 0) {
+               wattron(win, COLOR_PAIR(2));
+               wprintw(win, "+%d", digit);
+               wattroff(win, COLOR_PAIR(2));
+       } else {
+               wprintw(win, "0");
+       }
+}
+
+void print_digits(WINDOW *win, int first, int second)
+{
+       wprintw(win, "(");
+       print_digit(win, first);
+       wprintw(win, ", ");
+       print_digit(win, second);
+       wprintw(win, ")");
+}
+
+void print_headers(int line, char *desc, int value, int first, int second)
+{
+       wattron(header, A_BOLD);
+       mvwprintw(header, line, 4, "%s", desc);
+       wattroff(header, A_BOLD);
+       mvwprintw(header, line, 16, "N/A", value);
+       wmove(header, line, 24);
+       print_digits(header, first, second);
+       wmove(header, line, 40);
+}
+
+void set_window_title(WINDOW *win, char *title)
+{
+       wattron(win, A_BOLD);
+       mvwprintw(win, 0, 1, title);
+       wattroff(win, A_BOLD);
+}
+
+void print_log(char *str)
+{
+       int i;
+       int current_line = 1;
+       int current_char = 1;
+       char *tmp, *tmp2;
+       /* rotate the line buffer */
+       if (nb_log_lines >= MAX_LOG_LINES) {
+               tmp = strndup(log_lines, MAX_LINE_LENGTH * MAX_LOG_LINES + MAX_LOG_LINES);
+               tmp2 = strchr(tmp, '\n');
+               memset(log_lines, '\0', strlen(log_lines));
+               strncat(log_lines, tmp2 + 1, strlen(tmp2) - 1);
+               log_lines[strlen(log_lines)] = '\n';
+               log_lines[strlen(log_lines)] = '\0';
+               free(tmp);
+       }
+       nb_log_lines++;
+
+       strncat(log_lines, str, MAX_LINE_LENGTH - 1);
+
+       if (nb_log_lines < MAX_LOG_LINES)
+               log_lines[strlen(log_lines)] = '\n';
+       log_lines[strlen(log_lines)] = '\0';
+
+       werase(status);
+       box(status, 0 , 0);
+       set_window_title(status, "Status");
+       for (i = 0; i < strlen(log_lines); i++) {
+               if (log_lines[i] == '\n') {
+                       wmove(status, ++current_line, 1);
+                       current_char = 1;
+               } else {
+                       mvwprintw(status, current_line, current_char++, "%c", log_lines[i]);
+               }
+       }
+       wrefresh(status);
+}
+
+void print_key(WINDOW *win, char *key, char *desc, int toggle)
+{
+       int pair;
+       if (toggle > 0)
+               pair = 4;
+       else
+               pair = 3;
+       wattron(win, COLOR_PAIR(pair));
+       wprintw(footer, "%s", key);
+       wattroff(win, COLOR_PAIR(pair));
+       wprintw(footer, ":%s", desc);
+}
+
+void update_footer()
+{
+       sem_wait(&update_display_sem);
+       werase(footer);
+       wmove(footer, 1, 1);
+       print_key(footer, "F2", "CPUtop  ", current_view == cpu);
+       print_key(footer, "F3", "PerfTop  ", current_view == perf);
+       print_key(footer, "F6", "IOTop  ", current_view == iostream);
+       print_key(footer, "Enter", "Details  ", current_view == process_details);
+       print_key(footer, "q", "Quit | ", 0);
+       print_key(footer, "P", "Perf Pref  ", 0);
+       print_key(footer, "p", "Pause  ", toggle_pause);
+
+       wrefresh(footer);
+       sem_post(&update_display_sem);
+}
+
+void basic_header()
+{
+       werase(header);
+       box(header, 0 , 0);
+       set_window_title(header, "Statistics for interval [gathering data...[");
+       wattron(header, A_BOLD);
+       mvwprintw(header, 1, 4, "CPUs");
+       mvwprintw(header, 2, 4, "Processes");
+       mvwprintw(header, 3, 4, "Threads");
+       mvwprintw(header, 4, 4, "Files");
+       mvwprintw(header, 5, 4, "Network");
+       mvwprintw(header, 6, 4, "IO");
+       wattroff(header, A_BOLD);
+       wrefresh(header);
+}
+
+void update_header()
+{
+       werase(header);
+       box(header, 0 , 0);
+       set_window_title(header, "Statistics for interval ");
+       wattron(header, A_BOLD);
+       /*
+       wprintw(header, "[%lu.%lu, %lu.%lu[",
+                       data->start.tv_sec, data->start.tv_nsec,
+                       data->end.tv_sec, data->end.tv_nsec);
+                       */
+       wprintw(header, "[%lu, %lu[",
+            data->start,
+            data->end);
+       mvwprintw(header, 1, 4, "CPUs");
+       wattroff(header, A_BOLD);
+       wprintw(header, "\t%d\t(max/cpu : %0.2f%)", data->cpu_table->len,
+                       100.0/data->cpu_table->len);
+       print_headers(2, "Processes", data->nbproc, data->nbnewproc,
+                       -1*(data->nbdeadproc));
+       print_headers(3, "Threads", data->nbthreads, data->nbnewthreads,
+                       -1*(data->nbdeadthreads));
+       print_headers(4, "Files", data->nbfiles, data->nbnewfiles,
+                       -1*(data->nbclosedfiles));
+       mvwprintw(header, 4, 43, "N/A kbytes/sec");
+       print_headers(5, "Network", 114, 0, 0);
+       mvwprintw(header, 5, 43, "N/A Mbytes/sec");
+       wrefresh(header);
+}
+
+gint sort_by_cpu_desc(gconstpointer p1, gconstpointer p2)
+{
+       struct processtop *n1 = *(struct processtop **)p1;
+       struct processtop *n2 = *(struct processtop **)p2;
+       unsigned long totaln1 = n1->totalcpunsec;
+       unsigned long totaln2 = n2->totalcpunsec;
+
+       if (totaln1 < totaln2)
+               return 1;
+       if (totaln1 == totaln2)
+               return 0;
+       return -1;
+}
+
+gint sort_by_cpu_group_by_threads_desc(gconstpointer p1, gconstpointer p2)
+{
+       struct processtop *n1 = *(struct processtop **)p1;
+       struct processtop *n2 = *(struct processtop **)p2;
+       unsigned long totaln1 = n1->threadstotalcpunsec;
+       unsigned long totaln2 = n2->threadstotalcpunsec;
+
+       if (totaln1 < totaln2)
+               return 1;
+       if (totaln1 == totaln2)
+               return 0;
+       return -1;
+}
+
+void update_cputop_display()
+{
+       int i;
+       int header_offset = 2;
+       struct processtop *tmp;
+       unsigned long elapsed;
+       double maxcputime;
+       int nblinedisplayed = 0;
+       int current_line = 0;
+
+       elapsed = data->end - data->start;
+       maxcputime = elapsed * data->cpu_table->len / 100.0;
+
+       g_ptr_array_sort(data->process_table, sort_by_cpu_desc);
+
+       set_window_title(center, "CPU Top");
+       wattron(center, A_BOLD);
+       mvwprintw(center, 1, 1, "CPU(%)");
+       mvwprintw(center, 1, 12, "TGID");
+       mvwprintw(center, 1, 22, "PID");
+       mvwprintw(center, 1, 32, "NAME");
+       wattroff(center, A_BOLD);
+
+       max_center_lines = LINES - 7 - 7 - 1 - header_offset;
+
+       /* iterate the process (thread) list */
+       for (i = list_offset; i < data->process_table->len &&
+                       nblinedisplayed < max_center_lines; i++) {
+               tmp = g_ptr_array_index(data->process_table, i);
+
+               if (current_line == selected_line) {
+                       selected_process = tmp;
+                       selected_tid = tmp->tid;
+                       selected_comm = tmp->comm;
+                       wattron(center, COLOR_PAIR(5));
+                       mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
+               }
+               /* CPU(%) */
+               mvwprintw(center, current_line + header_offset, 1, "%1.2f",
+                               tmp->totalcpunsec / maxcputime);
+               /* TGID */
+               mvwprintw(center, current_line + header_offset, 12, "%d", tmp->pid);
+               /* PID */
+               mvwprintw(center, current_line + header_offset, 22, "%d", tmp->tid);
+               /* NAME */
+               mvwprintw(center, current_line + header_offset, 32, "%s", tmp->comm);
+               wattroff(center, COLOR_PAIR(5));
+               nblinedisplayed++;
+               current_line++;
+       }
+}
+
+gint sort_perf(gconstpointer p1, gconstpointer p2, gpointer key)
+{
+       struct processtop *n1 = *(struct processtop **) p1;
+       struct processtop *n2 = *(struct processtop **) p2;
+
+       struct perfcounter *tmp1, *tmp2;
+       unsigned long totaln2 = 0;
+       unsigned long totaln1 = 0;
+
+       if (!key)
+               return 0;
+
+       tmp1 = g_hash_table_lookup(n1->perf, key);
+       if (!tmp1)
+               totaln1 = 0;
+       else
+               totaln1 = tmp1->count;
+
+       tmp2 = g_hash_table_lookup(n2->perf, key);
+       if (!tmp2)
+               totaln2 = 0;
+       else
+               totaln2 = tmp2->count;
+
+       if (totaln1 < totaln2)
+               return 1;
+       if (totaln1 == totaln2) {
+               totaln1 = n1->tid;
+               totaln2 = n2->tid;
+               if (totaln1 < totaln2)
+                       return 1;
+               return -1;
+       }
+       return -1;
+}
+
+void print_key_title(char *key, int line)
+{
+       wattron(center, A_BOLD);
+       mvwprintw(center, line, 1, "%s\t", key);
+       wattroff(center, A_BOLD);
+}
+
+void update_process_details()
+{
+       unsigned long elapsed;
+       double maxcputime;
+       struct processtop *tmp = find_process_tid(data, selected_tid, selected_comm);
+
+       set_window_title(center, "Process details");
+
+
+       elapsed = data->end - data->start;
+       maxcputime = elapsed * data->cpu_table->len / 100.0;
+
+       print_key_title("Name", 1);
+       wprintw(center, "%s", selected_comm);
+       print_key_title("TID", 2);
+       wprintw(center, "%d", selected_tid);
+       if (!tmp) {
+               print_key_title("Does not exit at this time", 3);
+               return;
+       }
+
+       print_key_title("PID", 3);
+       wprintw(center, "%d", tmp->pid);
+       print_key_title("PPID", 4);
+       wprintw(center, "%d", tmp->ppid);
+       print_key_title("CPU", 5);
+       wprintw(center, "%1.2f %%", tmp->totalcpunsec/maxcputime);
+}
+
+void update_perf()
+{
+       int i, j;
+       int nblinedisplayed = 0;
+       int current_line = 0;
+       struct processtop *tmp;
+       int header_offset = 2;
+       int perf_row = 40;
+       struct perfcounter *perfn1, *perfn2;
+       GList *perflist;
+       char *perf_key = NULL;
+       int value;
+
+       set_window_title(center, "Perf Top");
+       wattron(center, A_BOLD);
+       mvwprintw(center, 1, 1, "PID");
+       mvwprintw(center, 1, 11, "TID");
+       mvwprintw(center, 1, 22, "NAME");
+
+       perf_row = 40;
+       perflist = g_list_first(g_hash_table_get_keys(data->perf_list));
+       while (perflist) {
+               perfn1 = g_hash_table_lookup(data->perf_list, perflist->data);
+               /* + 6 to strip the "_perf_" prefix */
+               if (perfn1->visible) {
+                       mvwprintw(center, 1, perf_row, "%s",
+                                       (char *) perflist->data + 6);
+                       perf_row += 20;
+               }
+               if (perfn1->sort) {
+                       perf_key = (char *) perflist->data;
+               }
+               perflist = g_list_next(perflist);
+       }
+       wattroff(center, A_BOLD);
+
+       g_ptr_array_sort_with_data(data->process_table, sort_perf, perf_key);
+       for (i = 0; i < data->process_table->len && 
+                       nblinedisplayed < max_center_lines; i++) {
+               GList *perf_keys;
+               tmp = g_ptr_array_index(data->process_table, i);
+
+               if (current_line == selected_line) {
+                       selected_process = tmp;
+                       wattron(center, COLOR_PAIR(5));
+                       mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
+               }
+
+               mvwprintw(center, current_line + header_offset, 1, "%d", tmp->pid);
+               mvwprintw(center, current_line + header_offset, 11, "%d", tmp->tid);
+               mvwprintw(center, current_line + header_offset, 22, "%s", tmp->comm);
+
+               /* FIXME : sometimes there is a segfault here, I have no idea why :-( */
+               perf_keys = g_hash_table_get_keys(data->perf_list);
+               if (perf_keys)
+                       perflist = g_list_first(perf_keys);
+               else
+                       perflist = NULL;
+
+               perf_row = 40;
+               j = 0;
+               while (perflist) {
+                       j++;
+                       perfn1 = g_hash_table_lookup(data->perf_list, perflist->data);
+                       if (!perfn1) {
+                               perflist = g_list_next(perflist);
+                               continue;
+                       }
+                       if (perfn1->visible) {
+                               perfn2 = g_hash_table_lookup(tmp->perf, perflist->data);
+                               if (perfn2)
+                                       value = perfn2->count;
+                               else
+                                       value = 0;
+                               mvwprintw(center, current_line + header_offset, perf_row, "%d", value);
+                               perf_row += 20;
+                       }
+                       perflist = g_list_next(perflist);
+               }
+
+               wattroff(center, COLOR_PAIR(5));
+               nblinedisplayed++;
+               current_line++;
+       }
+}
+
+void update_fileio()
+{
+       int i;
+       int offset;
+
+       set_window_title(center, "IO Top");
+       wattron(center, A_BOLD);
+       mvwprintw(center, 1, 10, "READ");
+       mvwprintw(center, 2, 1, "bytes");
+       mvwprintw(center, 2, 15, "bytes/sec");
+
+       mvwprintw(center, 1, 39, "WRITE");
+       mvwprintw(center, 2, 33, "bytes");
+       mvwprintw(center, 2, 45, "bytes/sec");
+
+       if (toggle_threads > 0) {
+               mvwprintw(center, 1, 60, "TGID");
+               mvwprintw(center, 1, 70, "PID");
+               offset = 8;
+       } else {
+               mvwprintw(center, 1, 60, "PID(TGID)");
+               offset = 0;
+       }
+       mvwprintw(center, 1, 72 + offset, "NAME");
+       wattroff(center, A_BOLD);
+
+       for (i = 3; i < LINES - 3 - 8 - 1; i++) {
+               mvwprintw(center, i, 1, "%d", i*1000);
+               mvwprintw(center, i, 15, "%dk", i);
+               mvwprintw(center, i, 28, "|    %d", i*2000);
+               mvwprintw(center, i, 45, "%dk", i*2);
+               if (toggle_threads > 0) {
+                       mvwprintw(center, i, 57, "|  %d", i);
+                       mvwprintw(center, i, 70, "%d", i);
+               } else {
+                       mvwprintw(center, i, 57, "|  %d", i);
+               }
+               mvwprintw(center, i, 72 + offset, "process_%d", i);
+       }
+}
+
+gint sort_by_ret_desc(gconstpointer p1, gconstpointer p2)
+{
+       struct processtop *n1 = *(struct processtop **)p1;
+       struct processtop *n2 = *(struct processtop **)p2;
+       unsigned long totaln1 = n1->iostream->ret_total;
+       unsigned long totaln2 = n2->iostream->ret_total;
+
+       if (totaln1 < totaln2)
+               return 1;
+       if (totaln1 == totaln2)
+               return 0;
+       return -1;
+}
+
+void update_iostream()
+{
+       int i;
+       int header_offset = 2;
+       struct processtop *tmp;
+       int nblinedisplayed = 0;
+       int current_line = 0;
+
+       set_window_title(center, "IO Top");
+       wattron(center, A_BOLD);
+       mvwprintw(center, 1, 1, "READ (B/s)");
+       mvwprintw(center, 1, 20, "WRITE (B/s)");
+
+       mvwprintw(center, 1, 40, "TOTAL STREAM");
+
+       mvwprintw(center, 1, 60, "TGID");
+       mvwprintw(center, 1, 80, "PID");
+
+       mvwprintw(center, 1, 92, "NAME");
+       wattroff(center, A_BOLD);
+
+       g_ptr_array_sort(data->process_table, sort_by_ret_desc);
+
+       for (i = list_offset; i < data->process_table->len &&
+                       nblinedisplayed < max_center_lines; i++) {
+               tmp = g_ptr_array_index(data->process_table, i);
+
+               if (current_line == selected_line) {
+                       selected_process = tmp;
+                       selected_tid = tmp->tid;
+                       selected_comm = tmp->comm;
+                       wattron(center, COLOR_PAIR(5));
+                       mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
+               }
+               /* READ (bytes/sec) */
+               mvwprintw(center, current_line + header_offset, 1, "%lu",
+                               tmp->iostream->ret_read);
+
+               /* WRITE (bytes/sec) */
+               mvwprintw(center, current_line + header_offset, 20, "%lu",
+                               tmp->iostream->ret_write);
+
+               /* TOTAL STREAM */
+               if(tmp->iostream->ret_total >= 1000000)
+                       mvwprintw(center, current_line + header_offset, 40, "%lu MB",
+                                       tmp->iostream->ret_total/1000000);
+               else if(tmp->iostream->ret_total >=1000)
+                       mvwprintw(center, current_line + header_offset, 40, "%lu KB",
+                                       tmp->iostream->ret_total/1000);
+               else
+                       mvwprintw(center, current_line + header_offset, 40, "%lu B",
+                                       tmp->iostream->ret_total);
+               /* TGID */
+               mvwprintw(center, current_line + header_offset, 60, "%d", tmp->pid);
+               /* PID */
+               mvwprintw(center, current_line + header_offset, 80, "%d", tmp->tid);
+               /* NAME */
+               mvwprintw(center, current_line + header_offset, 92, "%s", tmp->comm);
+               wattroff(center, COLOR_PAIR(5));
+               nblinedisplayed++;
+               current_line++;
+       }
+}
+
+void update_current_view()
+{
+       sem_wait(&update_display_sem);
+       if (!data)
+               return;
+       update_header();
+
+       werase(center);
+       box(center, 0, 0);
+       switch (current_view) {
+       case cpu:
+               update_cputop_display();
+               break;
+       case perf:
+               update_perf();
+               break;
+       case process_details:
+               update_process_details();
+               break;
+       case fileio:
+               update_fileio();
+               break;
+       case iostream:
+               update_iostream();
+               break;
+       case tree:
+               update_cputop_display();
+               break;
+       default:
+               break;
+       }
+       update_panels();
+       doupdate();
+       sem_post(&update_display_sem);
+}
+
+void setup_perf_panel()
+{
+       int size;
+       if (!data)
+               return;
+       if (perf_panel_window) {
+               del_panel(perf_panel);
+               delwin(perf_panel_window);
+       }
+       size = g_hash_table_size(data->perf_list);
+       perf_panel_window = create_window(size + 2, 30, 10, 10);
+       perf_panel = new_panel(perf_panel_window);
+       perf_panel_visible = 0;
+       hide_panel(perf_panel);
+}
+
+void update_perf_panel(int line_selected, int toggle_view, int toggle_sort)
+{
+       int i;
+       struct perfcounter *perf;
+       GList *perflist;
+
+       if (!data)
+               return;
+
+       werase(perf_panel_window);
+       box(perf_panel_window, 0 , 0);
+       set_window_title(perf_panel_window, "Perf Preferences ");
+       wattron(perf_panel_window, A_BOLD);
+       mvwprintw(perf_panel_window, g_hash_table_size(data->perf_list) + 1, 1, " 's' to sort");
+       wattroff(perf_panel_window, A_BOLD);
+
+       if (toggle_sort == 1) {
+               i = 0;
+               perflist = g_list_first(g_hash_table_get_keys(data->perf_list));
+               while (perflist) {
+                       perf = g_hash_table_lookup(data->perf_list, perflist->data);
+                       if (i != line_selected)
+                               perf->sort = 0;
+                       else
+                               perf->sort = 1;
+                       i++;
+                       perflist = g_list_next(perflist);
+               }
+               update_current_view();
+       }
+
+       i = 0;
+       perflist = g_list_first(g_hash_table_get_keys(data->perf_list));
+       while (perflist) {
+               perf = g_hash_table_lookup(data->perf_list, perflist->data);
+               if (i == line_selected && toggle_view == 1) {
+                       perf->visible = perf->visible == 1 ? 0:1;
+                       update_current_view();
+               }
+               if (i == line_selected) {
+                       wattron(perf_panel_window, COLOR_PAIR(5));
+                       mvwhline(perf_panel_window, i + 1, 1, ' ', 30 - 2);
+               }
+               if (perf->sort == 1)
+                       wattron(perf_panel_window, A_BOLD);
+               mvwprintw(perf_panel_window, i + 1, 1, "[%c] %s",
+                               perf->visible == 1 ? 'x' : ' ',
+                               (char *) perflist->data + 6);
+               wattroff(perf_panel_window, A_BOLD);
+               wattroff(perf_panel_window, COLOR_PAIR(5));
+               i++;
+               perflist = g_list_next(perflist);
+       }
+       update_panels();
+       doupdate();
+}
+
+
+void toggle_perf_panel(void)
+{
+       if (perf_panel_visible) {
+               hide_panel(perf_panel);
+               perf_panel_visible = 0;
+       } else {
+               setup_perf_panel();
+               update_perf_panel(perf_line_selected, 0, 0);
+               show_panel(perf_panel);
+               perf_panel_visible = 1;
+       }
+       update_panels();
+       doupdate();
+}
+
+void display(unsigned int index)
+{
+       last_display_index = index;
+       currently_displayed_index = index;
+       data = g_ptr_array_index(copies, index);
+       if (!data)
+               return;
+       max_elements = data->process_table->len;
+       update_current_view();
+       update_footer();
+       update_panels();
+       doupdate();
+}
+
+void pause_display()
+{
+       toggle_pause = 1;
+       print_log("Pause");
+       sem_wait(&pause_sem);
+}
+
+void resume_display()
+{
+       toggle_pause = -1;
+       print_log("Resume");
+       sem_post(&pause_sem);
+}
+
+void *handle_keyboard(void *p)
+{
+       int ch;
+       while((ch = getch())) {
+               switch(ch) {
+               /* Move the cursor and scroll */
+               case KEY_DOWN:
+                       if (perf_panel_visible) {
+                               if (perf_line_selected < g_hash_table_size(data->perf_list) - 1)
+                                       perf_line_selected++;
+                               update_perf_panel(perf_line_selected, 0, 0);
+                       } else {
+                               if (selected_line < (max_center_lines - 1) &&
+                                               selected_line < max_elements - 1) {
+                                       selected_line++;
+                                       selected_in_list++;
+                               } else if (selected_in_list < (max_elements - 1)
+                                               && (list_offset < (max_elements - max_center_lines))) {
+                                       selected_in_list++;
+                                       list_offset++;
+                               }
+                               update_current_view();
+                       }
+                       break;
+               case KEY_NPAGE:
+                       if ((selected_line + 10 < max_center_lines - 1) &&
+                                       ((selected_line + 10) < max_elements - 1)) {
+                               selected_line += 10;
+                               selected_in_list += 10;
+                       } else if (max_elements > max_center_lines) {
+                               selected_line = max_center_lines - 1;
+                               if (selected_in_list + 10 < max_elements - 1) {
+                                       selected_in_list += 10;
+                                       list_offset += (selected_in_list - max_center_lines + 1);
+                               }
+                       } else if (selected_line + 10 > max_elements) {
+                               selected_line = max_elements - 1;
+                       }
+                       update_current_view();
+                       break;
+               case KEY_UP:
+                       if (perf_panel_visible) {
+                               if (perf_line_selected > 0)
+                                       perf_line_selected--;
+                               update_perf_panel(perf_line_selected, 0, 0);
+                       } else {
+                               if (selected_line > 0) {
+                                       selected_line--;
+                                       selected_in_list--;
+                               } else if (selected_in_list > 0 && list_offset > 0) {
+                                       selected_in_list--;
+                                       list_offset--;
+                               }
+                               update_current_view();
+                       }
+                       break;
+               case KEY_PPAGE:
+                       if (selected_line - 10 > 0)
+                               selected_line -= 10;
+                       else
+                               selected_line = 0;
+                       update_current_view();
+                       break;
+
+               /* Navigate the history with arrows */
+               case KEY_LEFT:
+                       if (currently_displayed_index > 0) {
+                               currently_displayed_index--;
+                               print_log("Going back in time");
+                       } else {
+                               print_log("Cannot rewind, last data is already displayed");
+                       }
+                       data = g_ptr_array_index(copies, currently_displayed_index);
+                       max_elements = data->process_table->len;
+
+                       /* we force to pause the display when moving in time */
+                       if (toggle_pause < 0)
+                               pause_display();
+
+                       update_current_view();
+                       update_footer();
+                       break;
+               case KEY_RIGHT:
+                       if (currently_displayed_index < last_display_index) {
+                               currently_displayed_index++;
+                               print_log("Going forward in time");
+                               data = g_ptr_array_index(copies, currently_displayed_index);
+                               max_elements = data->process_table->len;
+                               update_current_view();
+                               update_footer();
+                       } else {
+                               print_log("Manually moving forward");
+                               sem_post(&timer);
+                               /* we force to resume the refresh when moving forward */
+                               if (toggle_pause > 0)
+                                       resume_display();
+                       }
+
+                       break;
+               case ' ':
+                       if (perf_panel_visible)
+                               update_perf_panel(perf_line_selected, 1, 0);
+                       break;
+               case 's':
+                       if (perf_panel_visible)
+                               update_perf_panel(perf_line_selected, 0, 1);
+                       break;
+
+               case 13: /* FIXME : KEY_ENTER ?? */
+                       if (current_view == cpu) {
+                               current_view = process_details;
+                       }
+                       update_current_view();
+                       break;
+
+               case KEY_F(1):
+                       toggle_tree *= -1;
+                       current_view = cpu;
+                       update_current_view();
+                       break;
+               case KEY_F(2):
+                       current_view = cpu;
+                       update_current_view();
+                       break;
+               case KEY_F(3):
+                       current_view = perf;
+                       toggle_tree = -1;
+                       update_current_view();
+                       break;
+               case KEY_F(4):
+                       current_view = fileio;
+                       toggle_tree = -1;
+                       update_current_view();
+                       break;
+               case KEY_F(5):
+                       current_view = netio;
+                       toggle_tree = -1;
+                       update_current_view();
+                       break;
+               case KEY_F(6):
+                       current_view = iostream;
+                       toggle_tree = -1;
+                       update_current_view();
+                       break;
+               case KEY_F(10):
+               case 'q':
+                       reset_ncurses();
+                       break;
+               case 't':
+                       toggle_threads *= -1;
+                       update_current_view();
+                       break;
+               case 'p':
+                       if (toggle_pause < 0) {
+                               pause_display();
+                       } else {
+                               resume_display();
+                       }
+                       break;
+               case 'P':
+                       toggle_perf_panel();
+                       break;
+               default:
+                       /*
+                        * commented because it makes the list refresh in different order
+                        * if we sort and there are equal values
+                        if (data)
+                        update_current_view();
+                        */
+                       break;
+               }
+               update_footer();
+       }
+       return NULL;
+}
+
+void init_ncurses()
+{
+       sem_init(&update_display_sem, 0, 1);
+       init_screen();
+
+       header = create_window(7, COLS - 1, 0, 0);
+       center = create_window(LINES - 7 - 7, COLS - 1, 7, 0);
+       status = create_window(MAX_LOG_LINES + 2, COLS - 1, LINES - 7, 0);
+       footer = create_window(1, COLS - 1, LINES - 1, 0);
+
+       print_log("Starting display");
+
+       main_panel = new_panel(center);
+       setup_perf_panel();
+
+       current_view = cpu;
+
+       basic_header();
+       update_footer();
+
+       pthread_create(&keyboard_thread, NULL, handle_keyboard, (void *)NULL);
+}
+
diff --git a/lttngtop/cursesdisplay.h b/lttngtop/cursesdisplay.h
new file mode 100644 (file)
index 0000000..6a54252
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2011 Julien Desfossez
+ *
+ * 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 CURSESDISPLAY_H
+#define CURSESDISPLAY_H
+
+#include <glib.h>
+#include <ncurses.h>
+#include "common.h"
+
+enum current_view_list
+{
+       cpu = 1,
+       perf,
+       process_details,
+       fileio,
+       netio,
+       iostream,
+       tree,
+} current_view;
+
+void display(unsigned int);
+void init_ncurses();
+void reset_ncurses();
+
+#endif // CURSESDISPLAY_H
diff --git a/lttngtop/iostreamtop.c b/lttngtop/iostreamtop.c
new file mode 100644 (file)
index 0000000..1594df7
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2011 Mathieu Bain <mathieu.bain@polymtl.ca>
+ *
+ * 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 <babeltrace/babeltrace.h>
+
+#include "lttngtoptypes.h"
+#include "common.h"
+#include "iostreamtop.h"
+
+#include <stdlib.h>
+
+int update_iostream_ret(struct lttngtop *ctx, int tid, char *comm,
+               unsigned long timestamp, int cpu_id, int ret)
+{
+       struct processtop *tmp;
+       int err = 0;
+
+       tmp = get_proc(ctx, tid, comm, timestamp);
+       if ((tmp->iostream->syscall_info != NULL) && (tmp->iostream->syscall_info->cpu_id == cpu_id)) {
+               if (tmp->iostream->syscall_info->type == __NR_read && ret > 0) {
+                       tmp->iostream->ret_read += ret;
+                       tmp->iostream->ret_total += ret;
+               } else if(tmp->iostream->syscall_info->type == __NR_write && ret > 0) {
+                       tmp->iostream->ret_write += ret;
+                       tmp->iostream->ret_total += ret;
+               } else{
+                       err = -1;
+               }
+               free(tmp->iostream->syscall_info);
+               tmp->iostream->syscall_info = NULL;
+       }
+
+       return err;
+}
+
+enum bt_cb_ret handle_exit_syscall(struct bt_ctf_event *call_data,
+               void *private_data)
+{
+       struct definition *scope;
+       unsigned long timestamp;
+       char *comm;
+       uint64_t ret, tid;
+       int64_t cpu_id;
+
+       timestamp = bt_ctf_get_timestamp(call_data);
+       if (timestamp == -1ULL)
+               goto error;
+
+       scope = bt_ctf_get_top_level_scope(call_data,
+                       BT_STREAM_EVENT_CONTEXT);
+       comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
+                               scope, "_procname"));
+       if (bt_ctf_field_get_error()) {
+               fprintf(stderr, "Missing procname context info\n");
+               goto error;
+       }
+
+       tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
+                               scope, "_tid"));
+       if (bt_ctf_field_get_error()) {
+               fprintf(stderr, "Missing tid context info\n");
+               goto error;
+       }
+
+       scope = bt_ctf_get_top_level_scope(call_data,
+                       BT_EVENT_FIELDS);
+       ret = bt_ctf_get_int64(bt_ctf_get_field(call_data,
+                               scope, "_ret"));
+       if (bt_ctf_field_get_error()) {
+               fprintf(stderr, "Missing ret context info\n");
+               goto error;
+       }
+
+       scope = bt_ctf_get_top_level_scope(call_data,
+                       BT_STREAM_PACKET_CONTEXT);
+       cpu_id = bt_ctf_get_uint64(bt_ctf_get_field(call_data,
+                               scope, "cpu_id"));
+       if (bt_ctf_field_get_error()) {
+               fprintf(stderr, "Missing cpu_id context info\n");
+               goto error;
+       }
+
+       /*
+        * if we encounter an exit_syscall and it is not for a syscall read or write
+        * we just abort the execution of this callback
+        */
+       if ((update_iostream_ret(&lttngtop, tid, comm, timestamp, cpu_id, ret)) < 0)
+               return BT_CB_ERROR_CONTINUE;
+
+       return BT_CB_OK;
+
+error:
+       return BT_CB_ERROR_STOP;
+}
+
+
+enum bt_cb_ret handle_sys_write(struct bt_ctf_event *call_data,
+               void *private_data)
+{
+       struct definition *scope;
+       struct processtop *tmp;
+       struct syscalls *syscall_info;
+       unsigned long timestamp;
+       uint64_t cpu_id;
+       char *comm;
+       int64_t tid;
+
+       timestamp = bt_ctf_get_timestamp(call_data);
+       if (timestamp == -1ULL)
+               goto error;
+
+       scope = bt_ctf_get_top_level_scope(call_data,
+                       BT_STREAM_EVENT_CONTEXT);
+       comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
+                               scope, "_procname"));
+       if (bt_ctf_field_get_error()) {
+               fprintf(stderr, "Missing procname context info\n");
+               goto error;
+       }
+
+       tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
+                               scope, "_tid"));
+       if (bt_ctf_field_get_error()) {
+               fprintf(stderr, "Missing tid context info\n");
+               goto error;
+       }
+
+       scope = bt_ctf_get_top_level_scope(call_data,
+                       BT_STREAM_PACKET_CONTEXT);
+       cpu_id = bt_ctf_get_uint64(bt_ctf_get_field(call_data,
+                               scope, "cpu_id"));
+       if (bt_ctf_field_get_error()) {
+               fprintf(stderr, "Missing cpu_id context info\n");
+               goto error;
+       }
+
+       syscall_info = malloc(sizeof(struct syscalls));
+       syscall_info->cpu_id = cpu_id;
+       syscall_info->type = __NR_write;
+       syscall_info->tid =  tid;
+       tmp = get_proc(&lttngtop, tid, comm, timestamp);
+       tmp->iostream->syscall_info = syscall_info;
+
+       return BT_CB_OK;
+
+error:
+       return BT_CB_ERROR_STOP;
+}
+
+enum bt_cb_ret handle_sys_read(struct bt_ctf_event *call_data,
+               void *private_data)
+{
+       struct processtop *tmp;
+       struct definition *scope;
+       struct syscalls * syscall_info;
+       unsigned long timestamp;
+       uint64_t cpu_id;
+       char *comm;
+       int64_t tid;
+
+       timestamp = bt_ctf_get_timestamp(call_data);
+       if (timestamp == -1ULL)
+               goto error;
+
+       scope = bt_ctf_get_top_level_scope(call_data,
+                       BT_STREAM_EVENT_CONTEXT);
+       comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
+                               scope, "_procname"));
+       if (bt_ctf_field_get_error()) {
+               fprintf(stderr, "Missing procname context info\n");
+               goto error;
+       }
+
+       tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
+                               scope, "_tid"));
+       if (bt_ctf_field_get_error()) {
+               fprintf(stderr, "Missing tid context info\n");
+               goto error;
+       }
+
+       scope = bt_ctf_get_top_level_scope(call_data,
+                       BT_STREAM_PACKET_CONTEXT);
+       cpu_id = bt_ctf_get_uint64(bt_ctf_get_field(call_data,
+                               scope, "cpu_id"));
+       if (bt_ctf_field_get_error()) {
+               fprintf(stderr, "Missing cpu_id context info\n");
+               goto error;
+       }
+
+       syscall_info = malloc(sizeof(struct syscalls));
+       syscall_info->cpu_id = cpu_id;
+       syscall_info->type = __NR_read;
+       syscall_info->tid =  tid;
+       tmp = get_proc(&lttngtop, tid, comm, timestamp);
+       tmp->iostream->syscall_info = syscall_info;
+
+       return BT_CB_OK;
+
+error:
+       return BT_CB_ERROR_STOP;
+}
+
diff --git a/lttngtop/iostreamtop.h b/lttngtop/iostreamtop.h
new file mode 100644 (file)
index 0000000..bde4dbf
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2011 Mathieu Bain <mathieu.bain@polymtl.ca>
+ *
+ * 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 _IOSTREANTOP_H
+#define _IOSTREAMTOP_H
+
+#include <babeltrace/babeltrace.h>
+#include <inttypes.h>
+#include <glib.h>
+#include <asm/unistd.h>
+
+/*
+#define SYS_READ  1
+#define SYS_WRITE 2
+*/
+
+enum bt_cb_ret handle_exit_syscall(struct bt_ctf_event *call_data,
+               void *private_data);
+
+enum bt_cb_ret handle_sys_write(struct bt_ctf_event *call_data,
+               void *private_data);
+
+enum bt_cb_ret handle_sys_read(struct bt_ctf_event *call_data,
+               void *private_data);
+
+#endif /* _IOSTREAMTOP_H */
diff --git a/lttngtop/lttngtop.c b/lttngtop/lttngtop.c
new file mode 100644 (file)
index 0000000..5d526f3
--- /dev/null
@@ -0,0 +1,572 @@
+/*
+ * Copyright (C) 2011 Julien Desfossez
+ *
+ * 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
+#include <config.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <babeltrace/babeltrace.h>
+#include <babeltrace/ctf/events.h>
+#include <babeltrace/ctf/callbacks.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <popt.h>
+#include <stdlib.h>
+#include <ftw.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <fts.h>
+
+#include "lttngtoptypes.h"
+#include "cputop.h"
+#include "iostreamtop.h"
+#include "cursesdisplay.h"
+#include "common.h"
+
+#define DEFAULT_FILE_ARRAY_SIZE 1
+
+const char *opt_input_path;
+
+struct lttngtop *copy;
+pthread_t display_thread;
+pthread_t timer_thread;
+
+unsigned long refresh_display = 1 * NSEC_PER_SEC;
+unsigned long last_display_update = 0;
+int quit = 0;
+
+enum {
+       OPT_NONE = 0,
+       OPT_HELP,
+       OPT_LIST,
+       OPT_VERBOSE,
+       OPT_DEBUG,
+       OPT_NAMES,
+};
+
+static struct poptOption long_options[] = {
+       /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
+       { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL },
+       { NULL, 0, 0, NULL, 0, NULL, NULL },
+};
+
+void *refresh_thread(void *p)
+{
+       while (1) {
+               sem_wait(&pause_sem);
+               sem_post(&pause_sem);
+               sem_post(&timer);
+               sleep(refresh_display/NSEC_PER_SEC);
+       }
+}
+
+void *ncurses_display(void *p)
+{
+       unsigned int current_display_index = 0;
+
+       sem_wait(&bootstrap);
+       init_ncurses();
+
+       while (1) {
+               sem_wait(&timer);
+               sem_wait(&goodtodisplay);
+               sem_wait(&pause_sem);
+
+               copy = g_ptr_array_index(copies, current_display_index);
+               if (copy)
+                       display(current_display_index++);
+
+               sem_post(&goodtoupdate);
+               sem_post(&pause_sem);
+
+               if (quit) {
+                       reset_ncurses();
+                       pthread_exit(0);
+               }
+       }
+}
+
+/*
+ * hook on each event to check the timestamp and refresh the display if
+ * necessary
+ */
+enum bt_cb_ret check_timestamp(struct bt_ctf_event *call_data, void *private_data)
+{
+       unsigned long timestamp;
+
+       timestamp = bt_ctf_get_timestamp(call_data);
+       if (timestamp == -1ULL)
+               goto error;
+
+       if (last_display_update == 0)
+               last_display_update = timestamp;
+
+       if (timestamp - last_display_update >= refresh_display) {
+               sem_wait(&goodtoupdate);
+               g_ptr_array_add(copies, get_copy_lttngtop(last_display_update,
+                                       timestamp));
+               sem_post(&goodtodisplay);
+               sem_post(&bootstrap);
+               last_display_update = timestamp;
+       }
+       return BT_CB_OK;
+
+error:
+       fprintf(stderr, "check_timestamp callback error\n");
+       return BT_CB_ERROR_STOP;
+}
+
+/*
+ * get_perf_counter : get or create and return a perf_counter struct for
+ * either a process or a cpu (only one of the 2 parameters mandatory)
+ */
+struct perfcounter *get_perf_counter(const char *name, struct processtop *proc,
+               struct cputime *cpu)
+{
+       struct perfcounter *ret, *global;
+       GHashTable *table;
+
+       if (proc)
+               table = proc->perf;
+       else if (cpu)
+               table = cpu->perf;
+       else
+               goto error;
+
+       ret = g_hash_table_lookup(table, (gpointer) name);
+       if (ret)
+               goto end;
+
+       ret = malloc(sizeof(struct perfcounter));
+       memset(ret, 0, sizeof(struct perfcounter));
+       /* by default, make it visible in the UI */
+       ret->visible = 1;
+       g_hash_table_insert(table, (gpointer) name, ret);
+
+       global = g_hash_table_lookup(lttngtop.perf_list, (gpointer) name);
+       if (!global) {
+               global = malloc(sizeof(struct perfcounter));
+               memset(global, 0, sizeof(struct perfcounter));
+               memcpy(global, ret, sizeof(struct perfcounter));
+               /* by default, sort on the first perf context */
+               if (g_hash_table_size(lttngtop.perf_list) == 0)
+                       global->sort = 1;
+               g_hash_table_insert(lttngtop.perf_list, (gpointer) name, global);
+       }
+
+end:
+       return ret;
+
+error:
+       return NULL;
+}
+
+void update_perf_value(struct processtop *proc, struct cputime *cpu,
+               const char *name, int value)
+{
+       struct perfcounter *cpu_perf, *process_perf;
+
+       cpu_perf = get_perf_counter(name, NULL, cpu);
+       if (cpu_perf->count < value) {
+               process_perf = get_perf_counter(name, proc, NULL);
+               process_perf->count += value - cpu_perf->count;
+               cpu_perf->count = value;
+       }
+}
+
+void extract_perf_counter_scope(struct bt_ctf_event *event,
+               struct definition *scope,
+               struct processtop *proc,
+               struct cputime *cpu)
+{
+       struct definition const * const *list = NULL;
+       unsigned int count;
+       int i, ret;
+
+       if (!scope)
+               goto end;
+
+       ret = bt_ctf_get_field_list(event, scope, &list, &count);
+       if (ret < 0)
+               goto end;
+
+       for (i = 0; i < count; i++) {
+               const char *name = bt_ctf_field_name(list[i]);
+               if (strncmp(name, "_perf_", 6) == 0) {
+                       int value = bt_ctf_get_uint64(list[i]);
+                       if (bt_ctf_field_get_error())
+                               continue;
+                       update_perf_value(proc, cpu, name, value);
+               }
+       }
+
+end:
+       return;
+}
+
+void update_perf_counter(struct processtop *proc, struct bt_ctf_event *event)
+{
+       struct definition *scope;
+       uint64_t cpu_id;
+       struct cputime *cpu;
+
+       scope = bt_ctf_get_top_level_scope(event, BT_STREAM_PACKET_CONTEXT);
+       cpu_id = bt_ctf_get_uint64(bt_ctf_get_field(event, scope, "cpu_id"));
+       if (bt_ctf_field_get_error()) {
+               fprintf(stderr, "[error] get cpu_id\n");
+               goto end;
+       }
+       cpu = get_cpu(cpu_id);
+
+       scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT);
+       extract_perf_counter_scope(event, scope, proc, cpu);
+
+       scope = bt_ctf_get_top_level_scope(event, BT_STREAM_PACKET_CONTEXT);
+       extract_perf_counter_scope(event, scope, proc, cpu);
+
+       scope = bt_ctf_get_top_level_scope(event, BT_EVENT_CONTEXT);
+       extract_perf_counter_scope(event, scope, proc, cpu);
+
+end:
+       return;
+}
+
+enum bt_cb_ret fix_process_table(struct bt_ctf_event *call_data,
+               void *private_data)
+{
+       int pid, tid, ppid;
+       char *comm;
+       struct processtop *parent, *child;
+       struct definition *scope;
+       unsigned long timestamp;
+
+       /* FIXME : check context pid, tid, ppid and comm */
+
+       timestamp = bt_ctf_get_timestamp(call_data);
+       if (timestamp == -1ULL)
+               goto error;
+
+       scope = bt_ctf_get_top_level_scope(call_data, BT_STREAM_EVENT_CONTEXT);
+
+       pid = bt_ctf_get_int64(bt_ctf_get_field(call_data, scope, "_pid"));
+       if (bt_ctf_field_get_error()) {
+//             fprintf(stderr, "Missing pid context info\n");
+               goto error;
+       }
+       tid = bt_ctf_get_int64(bt_ctf_get_field(call_data, scope, "_tid"));
+       if (bt_ctf_field_get_error()) {
+//             fprintf(stderr, "Missing tid context info\n");
+               goto error;
+       }
+       ppid = bt_ctf_get_int64(bt_ctf_get_field(call_data, scope, "_ppid"));
+       if (bt_ctf_field_get_error()) {
+//             fprintf(stderr, "Missing ppid context info\n");
+               goto error;
+       }
+       comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data, scope, "_procname"));
+       if (bt_ctf_field_get_error()) {
+//             fprintf(stderr, "Missing procname context info\n");
+               goto error;
+       }
+
+       /* find or create the current process */
+       child = find_process_tid(&lttngtop, tid, comm);
+       if (!child)
+               child = add_proc(&lttngtop, tid, comm, timestamp);
+       update_proc(child, pid, tid, ppid, comm);
+
+       if (pid != tid) {
+               /* find or create the parent */
+               parent = find_process_tid(&lttngtop, pid, comm);
+               if (!parent) {
+                       parent = add_proc(&lttngtop, pid, comm, timestamp);
+                       parent->pid = pid;
+               }
+
+               /* attach the parent to the current process */
+               child->threadparent = parent;
+               add_thread(parent, child);
+       }
+
+       update_perf_counter(child, call_data);
+
+       return BT_CB_OK;
+
+error:
+       return BT_CB_ERROR_STOP;
+}
+
+void init_lttngtop()
+{
+       copies = g_ptr_array_new();
+       lttngtop.perf_list = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+       sem_init(&goodtodisplay, 0, 0);
+       sem_init(&goodtoupdate, 0, 1);
+       sem_init(&timer, 0, 1);
+       sem_init(&bootstrap, 0, 0);
+       sem_init(&pause_sem, 0, 1);
+       sem_init(&end_trace_sem, 0, 0);
+
+       lttngtop.process_table = g_ptr_array_new();
+       lttngtop.files_table = g_ptr_array_new();
+       lttngtop.cpu_table = g_ptr_array_new();
+}
+
+void usage(FILE *fd)
+{
+
+}
+
+/*
+ * Return 0 if caller should continue, < 0 if caller should return
+ * error, > 0 if caller should exit without reporting error.
+ */
+static int parse_options(int argc, char **argv)
+{
+       poptContext pc;
+       int opt, ret = 0;
+
+       if (argc == 1) {
+               usage(stdout);
+               return 1;   /* exit cleanly */
+       }
+
+       pc = poptGetContext(NULL, argc, (const char **) argv, long_options, 0);
+       poptReadDefaultConfig(pc, 0);
+
+       while ((opt = poptGetNextOpt(pc)) != -1) {
+               switch (opt) {
+                       case OPT_HELP:
+                               usage(stdout);
+                               ret = 1;    /* exit cleanly */
+                               goto end;
+                       case OPT_LIST:
+                       //      list_formats(stdout);
+                               ret = 1;
+                               goto end;
+                       case OPT_VERBOSE:
+//                             babeltrace_verbose = 1;
+                               break;
+                       case OPT_DEBUG:
+//                             babeltrace_debug = 1;
+                               break;
+                       case OPT_NAMES:
+//                             opt_field_names = 1;
+                               break;
+                       default:
+                               ret = -EINVAL;
+                               goto end;
+               }
+       }
+
+       opt_input_path = poptGetArg(pc);
+       if (!opt_input_path) {
+               ret = -EINVAL;
+               goto end;
+       }
+end:
+       if (pc) {
+               poptFreeContext(pc);
+       }
+       return ret;
+}
+
+void iter_trace(struct bt_context *bt_ctx)
+{
+       struct bt_ctf_iter *iter;
+       struct bt_iter_pos begin_pos;
+       struct bt_ctf_event *event;
+       int ret = 0;
+
+       begin_pos.type = BT_SEEK_BEGIN;
+       iter = bt_ctf_iter_create(bt_ctx, &begin_pos, NULL);
+
+       /* at each event check if we need to refresh */
+       bt_ctf_iter_add_callback(iter, 0, NULL, 0,
+                       check_timestamp,
+                       NULL, NULL, NULL);
+       /* at each event, verify the status of the process table */
+       bt_ctf_iter_add_callback(iter, 0, NULL, 0,
+                       fix_process_table,
+                       NULL, NULL, NULL);
+       /* to handle the scheduling events */
+       bt_ctf_iter_add_callback(iter,
+                       g_quark_from_static_string("sched_switch"),
+                       NULL, 0, handle_sched_switch, NULL, NULL, NULL);
+       /* to clean up the process table */
+       bt_ctf_iter_add_callback(iter,
+                       g_quark_from_static_string("sched_process_free"),
+                       NULL, 0, handle_sched_process_free, NULL, NULL, NULL);
+
+       /* for IO top */
+       bt_ctf_iter_add_callback(iter,
+                       g_quark_from_static_string("exit_syscall"),
+                       NULL, 0, handle_exit_syscall, NULL, NULL, NULL);
+       bt_ctf_iter_add_callback(iter,
+                       g_quark_from_static_string("sys_write"),
+                       NULL, 0, handle_sys_write, NULL, NULL, NULL);
+       bt_ctf_iter_add_callback(iter,
+                       g_quark_from_static_string("sys_read"),
+                       NULL, 0, handle_sys_read, NULL, NULL, NULL);
+       while ((event = bt_ctf_iter_read_event(iter)) != NULL) {
+               ret = bt_iter_next(bt_ctf_get_iter(iter));
+               if (ret < 0)
+                       goto end_iter;
+       }
+
+       /* block until quit, we reached the end of the trace */
+       sem_wait(&end_trace_sem);
+
+end_iter:
+       bt_iter_destroy(bt_ctf_get_iter(iter));
+}
+
+/*
+ * bt_context_add_traces_recursive: Open a trace recursively
+ * (copied from BSD code in converter/babeltrace.c)
+ *
+ * Find each trace present in the subdirectory starting from the given
+ * path, and add them to the context. The packet_seek parameter can be
+ * NULL: this specify to use the default format packet_seek.
+ *
+ * Return: 0 on success, nonzero on failure.
+ * Unable to open toplevel: failure.
+ * Unable to open some subdirectory or file: warn and continue;
+ */
+int bt_context_add_traces_recursive(struct bt_context *ctx, const char *path,
+               const char *format_str,
+               void (*packet_seek)(struct stream_pos *pos,
+                       size_t offset, int whence))
+{
+       FTS *tree;
+       FTSENT *node;
+       GArray *trace_ids;
+       char lpath[PATH_MAX];
+       char * const paths[2] = { lpath, NULL };
+       int ret;
+
+       /*
+        * Need to copy path, because fts_open can change it.
+        * It is the pointer array, not the strings, that are constant.
+        */
+       strncpy(lpath, path, PATH_MAX);
+       lpath[PATH_MAX - 1] = '\0';
+
+       tree = fts_open(paths, FTS_NOCHDIR | FTS_LOGICAL, 0);
+       if (tree == NULL) {
+               fprintf(stderr, "[error] [Context] Cannot traverse \"%s\" for reading.\n",
+                               path);
+               return -EINVAL;
+       }
+
+       trace_ids = g_array_new(FALSE, TRUE, sizeof(int));
+
+       while ((node = fts_read(tree))) {
+               int dirfd, metafd;
+
+               if (!(node->fts_info & FTS_D))
+                       continue;
+
+               dirfd = open(node->fts_accpath, 0);
+               if (dirfd < 0) {
+                       fprintf(stderr, "[error] [Context] Unable to open trace "
+                               "directory file descriptor.\n");
+                       ret = dirfd;
+                       goto error;
+               }
+               metafd = openat(dirfd, "metadata", O_RDONLY);
+               if (metafd < 0) {
+                       ret = close(dirfd);
+                       if (ret < 0) {
+                               perror("close");
+                               goto error;
+                       }
+               } else {
+                       int trace_id;
+
+                       ret = close(metafd);
+                       if (ret < 0) {
+                               perror("close");
+                               goto error;
+                       }
+                       ret = close(dirfd);
+                       if (ret < 0) {
+                               perror("close");
+                               goto error;
+                       }
+
+                       trace_id = bt_context_add_trace(ctx,
+                               node->fts_accpath, format_str,
+                               packet_seek, NULL, NULL);
+                       if (trace_id < 0) {
+                               fprintf(stderr, "[error] [Context] opening trace \"%s\" from %s "
+                                       "for reading.\n", node->fts_accpath, path);
+                               ret = trace_id;
+                               goto error;
+                       }
+                       g_array_append_val(trace_ids, trace_id);
+               }
+       }
+
+       g_array_free(trace_ids, TRUE);
+       return 0;
+
+error:
+       return ret;
+}
+
+int main(int argc, char **argv)
+{
+       int ret;
+       struct bt_context *bt_ctx = NULL;
+
+       ret = parse_options(argc, argv);
+       if (ret < 0) {
+               fprintf(stdout, "Error parsing options.\n\n");
+               usage(stdout);
+               exit(EXIT_FAILURE);
+       } else if (ret > 0) {
+               exit(EXIT_SUCCESS);
+       }
+
+       init_lttngtop();
+
+       bt_ctx = bt_context_create();
+       ret = bt_context_add_traces_recursive(bt_ctx, opt_input_path, "ctf", NULL);
+       if (ret < 0) {
+               printf("[error] Opening the trace\n");
+               goto end;
+       }
+
+       pthread_create(&display_thread, NULL, ncurses_display, (void *) NULL);
+       pthread_create(&timer_thread, NULL, refresh_thread, (void *) NULL);
+
+       iter_trace(bt_ctx);
+
+       quit = 1;
+       pthread_join(display_thread, NULL);
+
+end:
+       return 0;
+}
diff --git a/lttngtop/lttngtoptypes.h b/lttngtop/lttngtoptypes.h
new file mode 100644 (file)
index 0000000..f10a684
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2011 Julien Desfossez
+ *
+ * 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 LTTNGTOPTYPES_H
+#define LTTNGTOPTYPES_H
+
+#include <glib.h>
+
+struct lttngtop {
+       GPtrArray *process_table;       /* struct processtop */
+       GPtrArray *files_table;         /* struct files */
+       GPtrArray *cpu_table;           /* struct cputime */
+       GHashTable *perf_list;          /* struct perfcounter */
+       unsigned long start;
+       unsigned long end;
+       unsigned int nbproc;
+       unsigned int nbnewproc;
+       unsigned int nbdeadproc;
+       unsigned int nbthreads;
+       unsigned int nbnewthreads;
+       unsigned int nbdeadthreads;
+       unsigned int nbfiles;
+       unsigned int nbnewfiles;
+       unsigned int nbclosedfiles;
+} lttngtop;
+
+struct processtop {
+       unsigned int puuid;
+       int pid;
+       char *comm;
+       int tid;
+       int ppid;
+       int oldpid;
+       int oldtid;
+       int oldppid;
+       unsigned long birth;
+       unsigned long death;
+       unsigned long lastactivity;
+       GPtrArray *process_files_table;
+       GPtrArray *threads;
+       GHashTable *perf;
+       struct processtop *threadparent;
+       unsigned long totalfileread;
+       unsigned long totalfilewrite;
+       unsigned long totalcpunsec;
+       unsigned long threadstotalcpunsec;
+       /* IO speed for this process */
+       struct iostream *iostream;
+};
+
+struct perfcounter
+{
+       unsigned long count;
+       int visible;
+       int sort;
+};
+
+struct cputime {
+       guint id;
+       struct processtop *current_task;
+       unsigned long task_start;
+       GHashTable *perf;
+};
+
+/*
+ * used for "relative seeks" (with fd, for example fs.lseek)
+ * and for "absolute seeks" (events occuring on a device without
+ * any link to a particular process)
+ */
+struct seeks {
+       unsigned long offset;
+       unsigned long count;
+};
+
+struct ioctls {
+       unsigned int command;
+       unsigned long count;
+};
+
+struct files {
+       struct processtop *ref;
+       unsigned int fuuid;
+       int fd;
+       char *name;
+       int oldfd;
+       int device;
+       int openmode;
+       unsigned long openedat;
+       unsigned long closedat;
+       unsigned long lastaccess;
+       unsigned long read;
+       unsigned long write;
+       unsigned long nbpoll;
+       unsigned long nbselect;
+       unsigned long nbopen;
+       unsigned long nbclose;
+       //struct *seeks; /* relative seeks inside the file */
+       //struct *ioctls;
+       /* XXX : average wait time */
+};
+
+struct sockets {
+       int fd;
+       int parent_fd;  /* on accept a new fd is created from the bound socket */
+       int family;
+       int type;
+       int protocol;
+       int sock_address;
+       unsigned long openedat;
+       unsigned long closedat;
+       unsigned long bind_address;
+       unsigned long remote_address;
+       //struct *sock_options;
+};
+
+struct sock_options {
+       int name;
+       int value;
+};
+
+struct vmas {
+       unsigned long start;
+       unsigned long end;
+       unsigned long flags;
+       unsigned long prot;
+       char *description; /* filename or description if possible (stack, heap) */
+       unsigned long page_faults;
+};
+
+struct syscalls {
+       unsigned int id;
+       unsigned long count;
+        unsigned int cpu_id;
+        unsigned int type;
+        unsigned int tid;
+};
+
+struct signals {
+       int dest_pid;
+       int id;
+       unsigned long count;
+};
+
+struct iostream {
+        struct syscalls *syscall_info; /* NULL if there is no waiting for an exit_syscall */
+        unsigned long ret_read;        /* value returned by an I/O syscall_exit for a sys_read*/
+        unsigned long ret_write;       /* value returned by an I/O syscall_exit for a sys_write*/
+        unsigned long ret_total;
+};
+
+#endif /* LTTNGTOPTYPES_H */
This page took 0.054482 seconds and 4 git commands to generate.