--- /dev/null
+*.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
--- /dev/null
+2012-02-16 LTTngTop 0.2
+ * Remove internal Babeltrace code, use the API
--- /dev/null
+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.
--- /dev/null
+AM_CFLAGS = $(PACKAGE_CFLAGS)
+
+ACLOCAL_AMFLAGS = -I m4
+
+SUBDIRS = lttngtop
--- /dev/null
+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.
--- /dev/null
+#! /bin/sh
+
+set -x
+if [ ! -e config ]; then
+ mkdir config
+fi
+aclocal
+libtoolize --force --copy
+autoheader
+automake --add-missing --copy
+autoconf
+
--- /dev/null
+# -*- 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
--- /dev/null
+ 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.
--- /dev/null
+AM_CFLAGS = $(PACKAGE_CFLAGS)
+
+bin_PROGRAMS = lttngtop
+
+lttngtop_SOURCES = \
+ lttngtop.c \
+ common.c \
+ cursesdisplay.c \
+ cputop.c \
+ iostreamtop.c
+
+lttngtop_LDADD = -lbabeltrace -lctf
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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(<tngtop, 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(<tngtop, tid, comm, timestamp);
+
+ return BT_CB_OK;
+
+error:
+ return BT_CB_ERROR_STOP;
+
+}
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
+
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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(<tngtop, 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(<tngtop, 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(<tngtop, tid, comm, timestamp);
+ tmp->iostream->syscall_info = syscall_info;
+
+ return BT_CB_OK;
+
+error:
+ return BT_CB_ERROR_STOP;
+}
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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(<tngtop, tid, comm);
+ if (!child)
+ child = add_proc(<tngtop, tid, comm, timestamp);
+ update_proc(child, pid, tid, ppid, comm);
+
+ if (pid != tid) {
+ /* find or create the parent */
+ parent = find_process_tid(<tngtop, pid, comm);
+ if (!parent) {
+ parent = add_proc(<tngtop, 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;
+}
--- /dev/null
+/*
+ * 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 */