auto import from //depot/cupcake/@135843
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..582ddc9
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,3 @@
+ifeq ($(TARGET_ARCH),arm)
+include $(call all-subdir-makefiles)
+endif
diff --git a/MODULE_LICENSE_GPL b/MODULE_LICENSE_GPL
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_GPL
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..d60c31a
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..c220da5
--- /dev/null
+++ b/config.h
@@ -0,0 +1,101 @@
+/* config.h.  Generated by configure.  */
+/* config.h.in.  Generated from configure.in by autoheader.  */
+
+/* whether popt prototype takes a const char ** */
+#define CONST_POPT 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `bfd' library (-lbfd). */
+#define HAVE_LIBBFD 1
+
+/* Define to 1 if you have the `iberty' library (-liberty). */
+#define HAVE_LIBIBERTY 1
+
+/* Define to 1 if you have the <libiberty.h> header file. */
+/* #undef HAVE_LIBIBERTY_H */
+
+/* Define to 1 if you have the `popt' library (-lpopt). */
+#define HAVE_LIBPOPT 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `perfmonctl' function. */
+/* #undef HAVE_PERFMONCTL */
+
+/* Define to 1 if you have the `sched_setaffinity' function. */
+/* #undef HAVE_SCHED_SETAFFINITY */
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `xcalloc' function. */
+#undef HAVE_XCALLOC
+
+/* Define to 1 if you have the `xmemdup' function. */
+#undef HAVE_XMEMDUP
+
+/* whether malloc attribute is understood */
+#define MALLOC_ATTRIBUTE_OK 1
+
+/* whether to build ABI tools */
+#define OPROF_ABI 1
+
+/* package binary directory */
+#define OP_BINDIR "/usr/local/bin/"
+
+/* package data directory */
+#define OP_DATADIR "/usr/local/share/oprofile/"
+
+/* Name of package */
+#define PACKAGE "oprofile"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME ""
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING ""
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION ""
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Synthesize special symbols when needed */
+/* #undef SYNTHESIZE_SYMBOLS */
+
+/* whether bfd.h defines bool values */
+/* #undef TRUE_FALSE_ALREADY_DEFINED */
+
+/* Version number of package */
+#define VERSION "0.9.1"
+
+/* Define to 1 if the X Window System is missing or not being used. */
+/* #undef X_DISPLAY_MISSING */
diff --git a/daemon/Android.mk b/daemon/Android.mk
new file mode 100644
index 0000000..abee74c
--- /dev/null
+++ b/daemon/Android.mk
@@ -0,0 +1,33 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	init.c \
+	opd_anon.c \
+	opd_cookie.c \
+	opd_events.c \
+	opd_kernel.c \
+	opd_mangling.c \
+	opd_perfmon.c \
+	opd_pipe.c \
+	opd_sfile.c \
+	opd_spu.c \
+	opd_stats.c \
+	opd_trans.c \
+	oprofiled.c
+
+LOCAL_STATIC_LIBRARIES := \
+	libpopt libutil libdb libabi libop
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/.. \
+	$(LOCAL_PATH)/../libdb \
+	$(LOCAL_PATH)/../libutil \
+	$(LOCAL_PATH)/../libop \
+	$(LOCAL_PATH)/../libabi
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE:= oprofiled
+
+include $(BUILD_EXECUTABLE)
diff --git a/daemon/init.c b/daemon/init.c
new file mode 100644
index 0000000..be0b9da
--- /dev/null
+++ b/daemon/init.c
@@ -0,0 +1,373 @@
+/**
+ * @file daemon/init.c
+ * Daemon set up and main loop for 2.6
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ * @Modifications Daniel Hansel
+ * Modified by Aravind Menon for Xen
+ * These modifications are:
+ * Copyright (C) 2005 Hewlett-Packard Co.
+ */
+
+#include "config.h"
+ 
+#include "oprofiled.h"
+#include "opd_stats.h"
+#include "opd_sfile.h"
+#include "opd_pipe.h"
+#include "opd_kernel.h"
+#include "opd_trans.h"
+#include "opd_anon.h"
+#include "opd_perfmon.h"
+#include "opd_printf.h"
+
+#include "op_version.h"
+#include "op_config.h"
+#include "op_deviceio.h"
+#include "op_get_time.h"
+#include "op_libiberty.h"
+#include "op_fileio.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#if ANDROID
+#include <sys/wait.h>
+#else
+#include <wait.h>
+#endif
+#include <string.h>
+
+size_t kernel_pointer_size;
+
+static fd_t devfd;
+static char * sbuf;
+static size_t s_buf_bytesize;
+extern char * session_dir;
+static char start_time_str[32];
+static int jit_conversion_running;
+
+static void opd_sighup(void);
+static void opd_alarm(void);
+static void opd_sigterm(void);
+static void opd_sigchild(void);
+static void opd_do_jitdumps(void);
+
+/**
+ * opd_open_files - open necessary files
+ *
+ * Open the device files and the log file,
+ * and mmap() the hash map.
+ */
+static void opd_open_files(void)
+{
+	devfd = op_open_device("/dev/oprofile/buffer");
+	if (devfd == -1) {
+		if (errno == EINVAL)
+			fprintf(stderr, "Failed to open device. Possibly you have passed incorrect\n"
+				"parameters. Check /var/log/messages.");
+		else
+			perror("Failed to open profile device");
+		exit(EXIT_FAILURE);
+	}
+
+	/* give output before re-opening stdout as the logfile */
+	printf("Using log file %s\n", op_log_file);
+
+	/* set up logfile */
+	close(0);
+	close(1);
+
+	if (open("/dev/null", O_RDONLY) == -1) {
+		perror("oprofiled: couldn't re-open stdin as /dev/null: ");
+		exit(EXIT_FAILURE);
+	}
+
+	opd_open_logfile();
+	opd_create_pipe();
+
+	printf("oprofiled started %s", op_get_time());
+	printf("kernel pointer size: %lu\n",
+		(unsigned long)kernel_pointer_size);
+	fflush(stdout);
+}
+ 
+
+/** Done writing out the samples, indicate with complete_dump file */
+static void complete_dump(void)
+{
+	FILE * status_file;
+
+retry:
+	status_file = fopen(op_dump_status, "w");
+
+	if (!status_file && errno == EMFILE) {
+		if (sfile_lru_clear()) {
+			printf("LRU cleared but file open fails for %s.\n",
+			       op_dump_status);
+			abort();
+		}
+		goto retry;
+	}
+
+	if (!status_file) {
+		perror("warning: couldn't set complete_dump: ");
+		return;
+	}
+
+	fprintf(status_file, "1\n");
+	fclose(status_file);
+}
+
+ 
+/**
+ * opd_do_samples - process a sample buffer
+ * @param opd_buf  buffer to process
+ *
+ * Process a buffer of samples.
+ *
+ * If the sample could be processed correctly, it is written
+ * to the relevant sample file.
+ */
+static void opd_do_samples(char const * opd_buf, ssize_t count)
+{
+	size_t num = count / kernel_pointer_size;
+ 
+	opd_stats[OPD_DUMP_COUNT]++;
+
+	verbprintf(vmisc, "Read buffer of %d entries.\n", (unsigned int)num);
+ 
+	opd_process_samples(opd_buf, num);
+
+	complete_dump();
+}
+ 
+static void opd_do_jitdumps(void)
+{ 
+	pid_t childpid;
+	int arg_num;
+	unsigned long long end_time = 0ULL;
+	struct timeval tv;
+	char end_time_str[32];
+	char opjitconv_path[PATH_MAX + 1];
+	char * exec_args[6];
+
+	if (jit_conversion_running)
+		return;
+	jit_conversion_running = 1;
+
+	childpid = fork();
+	switch (childpid) {
+		case -1:
+			perror("Error forking JIT dump process!");
+			break;
+		case 0:
+			gettimeofday(&tv, NULL);
+			end_time = tv.tv_sec;
+			sprintf(end_time_str, "%llu", end_time);
+			sprintf(opjitconv_path, "%s/%s", OP_BINDIR, "opjitconv");
+			arg_num = 0;
+			exec_args[arg_num++] = opjitconv_path;
+			if (vmisc)
+				exec_args[arg_num++] = "-d";
+			exec_args[arg_num++] = session_dir;
+			exec_args[arg_num++] = start_time_str;
+			exec_args[arg_num++] = end_time_str;
+			exec_args[arg_num] = (char *) NULL;
+			execvp("opjitconv", exec_args);
+			fprintf(stderr, "Failed to exec %s: %s\n",
+			        exec_args[0], strerror(errno));
+			/* We don't want any cleanup in the child */
+			_exit(EXIT_FAILURE);
+		default:
+			break;
+	} 
+
+} 
+
+/**
+ * opd_do_read - enter processing loop
+ * @param buf  buffer to read into
+ * @param size  size of buffer
+ *
+ * Read some of a buffer from the device and process
+ * the contents.
+ */
+static void opd_do_read(char * buf, size_t size)
+{
+	opd_open_pipe();
+
+	while (1) {
+		ssize_t count = -1;
+
+		/* loop to handle EINTR */
+		while (count < 0) {
+			count = op_read_device(devfd, buf, size);
+
+			/* we can lose an alarm or a hup but
+			 * we don't care.
+			 */
+			if (signal_alarm) {
+				signal_alarm = 0;
+				opd_alarm();
+			}
+
+			if (signal_hup) {
+				signal_hup = 0;
+				opd_sighup();
+			}
+
+			if (signal_term)
+				opd_sigterm();
+
+			if (signal_child)
+				opd_sigchild();
+
+			if (signal_usr1) {
+				signal_usr1 = 0;
+				perfmon_start();
+			}
+
+			if (signal_usr2) {
+				signal_usr2 = 0;
+				perfmon_stop();
+			}
+
+			if (is_jitconv_requested()) {
+				verbprintf(vmisc, "Start opjitconv was triggered\n");
+				opd_do_jitdumps();
+			}
+		}
+
+		opd_do_samples(buf, count);
+	}
+	
+	opd_close_pipe();
+}
+
+
+/** opd_alarm - sync files and report stats */
+static void opd_alarm(void)
+{
+	sfile_sync_files();
+	opd_print_stats();
+	alarm(60 * 10);
+}
+ 
+
+/** re-open files for logrotate/opcontrol --reset */
+static void opd_sighup(void)
+{
+	printf("Received SIGHUP.\n");
+	/* We just close them, and re-open them lazily as usual. */
+	sfile_close_files();
+	close(1);
+	close(2);
+	opd_open_logfile();
+}
+
+
+static void clean_exit(void)
+{
+	perfmon_exit();
+	unlink(op_lock_file);
+}
+
+
+static void opd_sigterm(void)
+{
+	opd_do_jitdumps();
+	opd_print_stats();
+	printf("oprofiled stopped %s", op_get_time());
+	exit(EXIT_FAILURE);
+}
+
+/* SIGCHLD received from JIT dump child process. */
+static void opd_sigchild(void)
+{
+	int child_status;
+	wait(&child_status);
+	jit_conversion_running = 0;
+	if (WIFEXITED(child_status) && (!WEXITSTATUS(child_status))) {
+		verbprintf(vmisc, "JIT dump processing complete.\n");
+	} else {
+		printf("JIT dump processing exited abnormally: %d\n",
+		       WEXITSTATUS(child_status));
+	}
+
+}
+ 
+static void opd_26_init(void)
+{
+	size_t i;
+	size_t opd_buf_size;
+	unsigned long long start_time = 0ULL;
+	struct timeval tv;
+
+	opd_create_vmlinux(vmlinux, kernel_range);
+	opd_create_xen(xenimage, xen_range);
+
+	opd_buf_size = opd_read_fs_int("/dev/oprofile/", "buffer_size", 1);
+	kernel_pointer_size = opd_read_fs_int("/dev/oprofile/", "pointer_size", 1);
+
+	s_buf_bytesize = opd_buf_size * kernel_pointer_size;
+
+	sbuf = xmalloc(s_buf_bytesize);
+
+	opd_reread_module_info();
+
+	for (i = 0; i < OPD_MAX_STATS; i++)
+		opd_stats[i] = 0;
+
+	perfmon_init();
+
+	cookie_init();
+	sfile_init();
+	anon_init();
+
+	/* must be /after/ perfmon_init() at least */
+	if (atexit(clean_exit)) {
+		perfmon_exit();
+		perror("oprofiled: couldn't set exit cleanup: ");
+		exit(EXIT_FAILURE);
+	}
+
+	/* trigger kernel module setup before returning control to opcontrol */
+	opd_open_files();
+	gettimeofday(&tv, NULL);
+	start_time = 0ULL;
+	start_time = tv.tv_sec;
+	sprintf(start_time_str, "%llu", start_time);
+		  
+}
+
+
+static void opd_26_start(void)
+{
+	/* simple sleep-then-process loop */
+	opd_do_read(sbuf, s_buf_bytesize);
+}
+
+
+static void opd_26_exit(void)
+{
+	opd_print_stats();
+	printf("oprofiled stopped %s", op_get_time());
+
+	free(sbuf);
+	free(vmlinux);
+	/* FIXME: free kernel images, sfiles etc. */
+}
+
+struct oprofiled_ops opd_26_ops = {
+	.init = opd_26_init,
+	.start = opd_26_start,
+	.exit = opd_26_exit,
+};
diff --git a/daemon/opd_anon.c b/daemon/opd_anon.c
new file mode 100644
index 0000000..9caea3d
--- /dev/null
+++ b/daemon/opd_anon.c
@@ -0,0 +1,228 @@
+/**
+ * @file opd_anon.c
+ * Anonymous region handling.
+ *
+ * Our caching of maps has some problems: if we get tgid reuse,
+ * and it's the same application, we might end up with wrong
+ * maps. The same happens in an unmap-remap case. There's not much
+ * we can do about this, we just hope it's not too common...
+ *
+ * What is relatively common is expanding anon maps, which leaves us
+ * with lots of separate sample files.
+ *
+ * @remark Copyright 2005 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @Modifications Gisle Dankel
+ */
+
+#include "opd_anon.h"
+#include "opd_trans.h"
+#include "opd_sfile.h"
+#include "opd_printf.h"
+#include "op_libiberty.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define HASH_SIZE 1024
+#define HASH_BITS (HASH_SIZE - 1)
+
+/*
+ * Note that this value is tempered by the fact that when we miss in the
+ * anon cache, we'll tear down all the mappings for that tgid. Thus, LRU
+ * of a mapping can potentially clear out a much larger number of
+ * mappings.
+ */
+#define LRU_SIZE 8192
+#define LRU_AMOUNT (LRU_SIZE/8)
+
+static struct list_head hashes[HASH_SIZE];
+static struct list_head lru;
+static size_t nr_lru;
+
+static void do_lru(struct transient * trans)
+{
+	size_t nr_to_kill = LRU_AMOUNT;
+	struct list_head * pos;
+	struct list_head * pos2;
+	struct anon_mapping * entry;
+
+	list_for_each_safe(pos, pos2, &lru) {
+		entry = list_entry(pos, struct anon_mapping, lru_list);
+		if (trans->anon == entry)
+			clear_trans_current(trans);
+		if (trans->last_anon == entry)
+			clear_trans_last(trans);
+		sfile_clear_anon(entry);
+		list_del(&entry->list);
+		list_del(&entry->lru_list);
+		--nr_lru;
+		free(entry);
+		if (nr_to_kill-- == 0)
+			break;
+	}
+}
+
+
+static unsigned long hash_anon(pid_t tgid, cookie_t app)
+{
+	return ((app >> DCOOKIE_SHIFT) ^ (tgid >> 2)) & (HASH_SIZE - 1);
+}
+ 
+
+static void clear_anon_maps(struct transient * trans)
+{
+	unsigned long hash = hash_anon(trans->tgid, trans->app_cookie);
+	pid_t tgid = trans->tgid;
+	cookie_t app = trans->app_cookie;
+	struct list_head * pos;
+	struct list_head * pos2;
+	struct anon_mapping * entry;
+
+	clear_trans_current(trans);
+
+	list_for_each_safe(pos, pos2, &hashes[hash]) {
+		entry = list_entry(pos, struct anon_mapping, list);
+		if (entry->tgid == tgid && entry->app_cookie == app) {
+			if (trans->last_anon == entry)
+				clear_trans_last(trans);
+			sfile_clear_anon(entry);
+			list_del(&entry->list);
+			list_del(&entry->lru_list);
+			--nr_lru;
+			free(entry);
+		}
+	}
+
+	if (vmisc) {
+		char const * name = verbose_cookie(app);
+		printf("Cleared anon maps for tgid %u (%s).\n", tgid, name);
+	}
+}
+
+
+static void
+add_anon_mapping(struct transient * trans, vma_t start, vma_t end, char * name)
+{
+	unsigned long hash = hash_anon(trans->tgid, trans->app_cookie);
+	struct anon_mapping * m = xmalloc(sizeof(struct anon_mapping));
+	m->tgid = trans->tgid;
+	m->app_cookie = trans->app_cookie;
+	m->start = start;
+	m->end = end;
+	strncpy(m->name, name, MAX_IMAGE_NAME_SIZE + 1);
+	list_add_tail(&m->list, &hashes[hash]);
+	list_add_tail(&m->lru_list, &lru);
+	if (++nr_lru == LRU_SIZE)
+		do_lru(trans);
+	if (vmisc) {
+		char const * name = verbose_cookie(m->app_cookie);
+		printf("Added anon map 0x%llx-0x%llx for tgid %u (%s).\n",
+		       start, end, m->tgid, name);
+	}
+}
+
+
+/* 42000000-4212f000 r-xp 00000000 16:03 424334 /lib/tls/libc-2.3.2.so */
+static void get_anon_maps(struct transient * trans)
+{
+	FILE * fp = NULL;
+	char buf[PATH_MAX];
+	vma_t start, end;
+	int ret;
+
+	snprintf(buf, PATH_MAX, "/proc/%d/maps", trans->tgid);
+	fp = fopen(buf, "r");
+	if (!fp)
+		return;
+
+	while (fgets(buf, PATH_MAX, fp) != NULL) {
+		char tmp[MAX_IMAGE_NAME_SIZE + 1];
+		char name[MAX_IMAGE_NAME_SIZE + 1];
+		/* Some anon maps have labels like
+		 * [heap], [stack], [vdso], [vsyscall] ...
+		 * Keep track of these labels. If a map has no name, call it "anon".
+		 * Ignore all mappings starting with "/" (file or shared memory object)
+		 */
+		strcpy(name, "anon");
+		ret = sscanf(buf, "%llx-%llx %20s %20s %20s %20s %20s",
+		             &start, &end, tmp, tmp, tmp, tmp, name);
+		if (ret < 6 || name[0] == '/')
+			continue;
+
+		add_anon_mapping(trans, start, end, name);
+	}
+
+	fclose(fp);
+}
+
+
+static int
+anon_match(struct transient const * trans, struct anon_mapping const * anon)
+{
+	if (!anon)
+		return 0;
+	if (trans->tgid != anon->tgid)
+		return 0;
+	if (trans->app_cookie != anon->app_cookie)
+		return 0;
+	if (trans->pc < anon->start)
+		return 0;
+	return (trans->pc < anon->end);
+}
+
+
+struct anon_mapping * find_anon_mapping(struct transient * trans)
+{
+	unsigned long hash = hash_anon(trans->tgid, trans->app_cookie);
+	struct list_head * pos;
+	struct anon_mapping * entry;
+	int tried = 0;
+
+	if (anon_match(trans, trans->anon))
+		return (trans->anon);
+
+retry:
+	list_for_each(pos, &hashes[hash]) {
+		entry = list_entry(pos, struct anon_mapping, list);
+		if (anon_match(trans, entry))
+			goto success;
+	}
+
+	if (!tried) {
+		clear_anon_maps(trans);
+		get_anon_maps(trans);
+		tried = 1;
+		goto retry;
+	}
+
+	return NULL;
+
+success:
+	/*
+	 * Typically, there's one big mapping that matches. Let's go
+	 * faster.
+	 */
+	list_del(&entry->list);
+	list_add(&entry->list, &hashes[hash]);
+
+	verbprintf(vmisc, "Found range 0x%llx-0x%llx for tgid %u, pc %llx.\n",
+	           entry->start, entry->end, (unsigned int)entry->tgid,
+		   trans->pc);
+	return entry;
+}
+
+
+void anon_init(void)
+{
+	size_t i;
+
+	for (i = 0; i < HASH_SIZE; ++i)
+		list_init(&hashes[i]);
+
+	list_init(&lru);
+}
diff --git a/daemon/opd_anon.h b/daemon/opd_anon.h
new file mode 100644
index 0000000..3f66b55
--- /dev/null
+++ b/daemon/opd_anon.h
@@ -0,0 +1,54 @@
+/**
+ * @file opd_anon.h
+ * Anonymous region handling.
+ *
+ * @remark Copyright 2005 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ */
+
+#ifndef OPD_ANON_H
+#define OPD_ANON_H
+
+#include "op_types.h"
+#include "op_list.h"
+
+#include "opd_cookie.h"
+
+#include <sys/types.h>
+
+struct transient;
+
+/**
+ * Shift useful bits into play for VMA hashing.
+ */
+#define VMA_SHIFT 13 
+
+/* Maximum size of the image name considered */
+#define MAX_IMAGE_NAME_SIZE 20
+
+struct anon_mapping {
+	/** start of the mapping */
+	vma_t start;
+	/** end of the mapping */
+	vma_t end;
+	/** tgid of the app */
+	pid_t tgid;
+	/** cookie of the app */
+	cookie_t app_cookie;
+	/** hash list */
+	struct list_head list;
+	/** lru list */
+	struct list_head lru_list;
+	char name[MAX_IMAGE_NAME_SIZE+1];
+};
+
+/**
+ * Try to find an anonymous mapping for the given pc/tgid pair.
+ */
+struct anon_mapping * find_anon_mapping(struct transient *);
+
+void anon_init(void);
+
+#endif /* OPD_ANON_H */
diff --git a/daemon/opd_cookie.c b/daemon/opd_cookie.c
new file mode 100644
index 0000000..3578e48
--- /dev/null
+++ b/daemon/opd_cookie.c
@@ -0,0 +1,209 @@
+/**
+ * @file opd_cookie.c
+ * cookie -> name cache
+ *
+ * @remark Copyright 2002, 2005 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ */
+
+#include "opd_cookie.h"
+#include "oprofiled.h"
+#include "op_list.h"
+#include "op_libiberty.h"
+
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#ifndef __NR_lookup_dcookie
+#if defined(__i386__)
+#define __NR_lookup_dcookie 253
+#elif defined(__x86_64__)
+#define __NR_lookup_dcookie 212
+#elif defined(__powerpc__)
+#define __NR_lookup_dcookie 235
+#elif defined(__alpha__)
+#define __NR_lookup_dcookie 406
+#elif defined(__hppa__)
+#define __NR_lookup_dcookie 223
+#elif defined(__ia64__)
+#define __NR_lookup_dcookie 1237
+#elif defined(__sparc__)
+/* untested */
+#define __NR_lookup_dcookie 208
+#elif defined(__s390__) || defined (__s390x__)
+#define __NR_lookup_dcookie 110
+#elif defined(__arm__)
+#define __NR_lookup_dcookie (__NR_SYSCALL_BASE+249)
+#elif defined(__mips__)
+#include <sgidefs.h>
+/* O32 */
+#if _MIPS_SIM == _MIPS_SIM_ABI32
+#define __NR_lookup_dcookie 4247
+/* N64 */
+#elif _MIPS_SIM == _MIPS_SIM_ABI64
+#define __NR_lookup_dcookie 5206
+/* N32 */
+#elif _MIPS_SIM == _MIPS_SIM_NABI32
+#define __NR_lookup_dcookie 6206
+#else
+#error Unknown MIPS ABI: Dunno __NR_lookup_dcookie
+#endif
+#else
+#error Please define __NR_lookup_dcookie for your architecture
+#endif
+#endif /* __NR_lookup_dcookie */
+
+#if (defined(__powerpc__) && !defined(__powerpc64__)) || defined(__hppa__)\
+	|| (defined(__s390__) && !defined(__s390x__)) \
+	|| (defined(__mips__) && (_MIPS_SIM == _MIPS_SIM_ABI32) \
+	    && defined(__MIPSEB__)) \
+        || (defined(__arm__) && defined(__ARM_EABI__) \
+            && defined(__ARMEB__))
+static inline int lookup_dcookie(cookie_t cookie, char * buf, size_t size)
+{
+	return syscall(__NR_lookup_dcookie, (unsigned long)(cookie >> 32),
+		       (unsigned long)(cookie & 0xffffffff), buf, size);
+}
+#elif (defined(__mips__) && (_MIPS_SIM == _MIPS_SIM_ABI32)) \
+	|| (defined(__arm__) && defined(__ARM_EABI__))
+static inline int lookup_dcookie(cookie_t cookie, char * buf, size_t size)
+{
+	return syscall(__NR_lookup_dcookie,
+		       (unsigned long)(cookie & 0xffffffff),
+		       (unsigned long)(cookie >> 32), buf, size);
+}
+#else
+static inline int lookup_dcookie(cookie_t cookie, char * buf, size_t size)
+{
+	return syscall(__NR_lookup_dcookie, cookie, buf, size);
+}
+#endif
+
+
+struct cookie_entry {
+	cookie_t value;
+	char * name;
+	int ignored;
+	struct list_head list;
+};
+
+
+#define HASH_SIZE 512
+#define HASH_BITS (HASH_SIZE - 1)
+
+static struct list_head hashes[HASH_SIZE];
+
+static struct cookie_entry * create_cookie(cookie_t cookie)
+{
+	int err;
+	struct cookie_entry * entry = xmalloc(sizeof(struct cookie_entry));
+
+	entry->value = cookie;
+	entry->name = xmalloc(PATH_MAX + 1);
+
+	err = lookup_dcookie(cookie, entry->name, PATH_MAX);
+
+	if (err < 0) {
+		fprintf(stderr, "Lookup of cookie %llx failed, errno=%d\n",
+		       cookie, errno); 
+		free(entry->name);
+		entry->name = NULL;
+		entry->ignored = 0;
+	} else {
+		entry->ignored = is_image_ignored(entry->name);
+	}
+
+	return entry;
+}
+
+
+/* Cookie monster want cookie! */
+static unsigned long hash_cookie(cookie_t cookie)
+{
+	return (cookie >> DCOOKIE_SHIFT) & (HASH_SIZE - 1);
+}
+ 
+
+char const * find_cookie(cookie_t cookie)
+{
+	unsigned long hash = hash_cookie(cookie);
+	struct list_head * pos;
+	struct cookie_entry * entry;
+
+	if (cookie == INVALID_COOKIE || cookie == NO_COOKIE)
+		return NULL;
+
+	list_for_each(pos, &hashes[hash]) {
+		entry = list_entry(pos, struct cookie_entry, list);
+		if (entry->value == cookie)
+			goto out;
+	}
+
+	/* not sure this can ever happen due to is_cookie_ignored */
+	entry = create_cookie(cookie);
+	list_add(&entry->list, &hashes[hash]);
+out:
+	return entry->name;
+}
+
+
+int is_cookie_ignored(cookie_t cookie)
+{
+	unsigned long hash = hash_cookie(cookie);
+	struct list_head * pos;
+	struct cookie_entry * entry;
+
+	if (cookie == INVALID_COOKIE || cookie == NO_COOKIE)
+		return 1;
+
+	list_for_each(pos, &hashes[hash]) {
+		entry = list_entry(pos, struct cookie_entry, list);
+		if (entry->value == cookie)
+			goto out;
+	}
+
+	entry = create_cookie(cookie);
+	list_add(&entry->list, &hashes[hash]);
+out:
+	return entry->ignored;
+}
+
+
+char const * verbose_cookie(cookie_t cookie)
+{
+	unsigned long hash = hash_cookie(cookie);
+	struct list_head * pos;
+	struct cookie_entry * entry;
+
+	if (cookie == INVALID_COOKIE)
+		return "invalid";
+
+	if (cookie == NO_COOKIE)
+		return "anonymous";
+
+	list_for_each(pos, &hashes[hash]) {
+		entry = list_entry(pos, struct cookie_entry, list);
+		if (entry->value == cookie) {
+			if (!entry->name)
+				return "failed lookup";
+			return entry->name;
+		}
+	}
+
+	return "not hashed";
+}
+
+
+void cookie_init(void)
+{
+	size_t i;
+
+	for (i = 0; i < HASH_SIZE; ++i)
+		list_init(&hashes[i]);
+}
diff --git a/daemon/opd_cookie.h b/daemon/opd_cookie.h
new file mode 100644
index 0000000..a9f13b1
--- /dev/null
+++ b/daemon/opd_cookie.h
@@ -0,0 +1,39 @@
+/**
+ * @file opd_cookie.h
+ * cookie -> name cache
+ *
+ * @remark Copyright 2002, 2005 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ */
+
+#ifndef OPD_COOKIE_H
+#define OPD_COOKIE_H
+
+typedef unsigned long long cookie_t;
+
+#define INVALID_COOKIE ~0LLU
+#define NO_COOKIE 0LLU
+
+/**
+ * Shift value to remove trailing zero on a dcookie value, 7 is sufficient
+ * for most architecture
+ */
+#define DCOOKIE_SHIFT 7
+
+/**
+ * Return the name of the given dcookie. May return
+ * NULL on failure.
+ */
+char const * find_cookie(cookie_t cookie);
+
+/** return true if this cookie should be ignored */
+int is_cookie_ignored(cookie_t cookie);
+
+/** give a textual description of the cookie */
+char const * verbose_cookie(cookie_t cookie);
+
+void cookie_init(void);
+
+#endif /* OPD_COOKIE_H */
diff --git a/daemon/opd_events.c b/daemon/opd_events.c
new file mode 100644
index 0000000..81a87d2
--- /dev/null
+++ b/daemon/opd_events.c
@@ -0,0 +1,165 @@
+/**
+ * @file daemon/opd_events.c
+ * Event details for each counter
+ *
+ * @remark Copyright 2002, 2003 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include "config.h"
+ 
+#include "opd_events.h"
+#include "opd_printf.h"
+#include "oprofiled.h"
+
+#include "op_string.h"
+#include "op_config.h"
+#include "op_cpufreq.h"
+#include "op_cpu_type.h"
+#include "op_libiberty.h"
+#include "op_hw_config.h"
+#include "op_sample_file.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+extern op_cpu cpu_type;
+
+struct opd_event opd_events[OP_MAX_COUNTERS];
+
+static double cpu_speed;
+
+static void malformed_events(void)
+{
+	fprintf(stderr, "oprofiled: malformed events passed "
+	        "on the command line\n");
+	exit(EXIT_FAILURE);
+}
+
+
+static char * copy_token(char ** c, char delim)
+{
+	char * tmp = *c;
+	char * tmp2 = *c;
+	char * str;
+
+	if (!**c)
+		return NULL;
+
+	while (*tmp2 && *tmp2 != delim)
+		++tmp2;
+
+	if (tmp2 == tmp)
+		return NULL;
+
+	str = op_xstrndup(tmp, tmp2 - tmp);
+	*c = tmp2;
+	if (**c)
+		++*c;
+	return str;
+}
+
+
+static unsigned long copy_ulong(char ** c, char delim)
+{
+	unsigned long val = 0;
+	char * str = copy_token(c, delim);
+	if (!str)
+		malformed_events();
+	val = strtoul(str, NULL, 0);
+	free(str);
+	return val;
+}
+
+
+void opd_parse_events(char const * events)
+{
+	char * ev = xstrdup(events);
+	char * c;
+	size_t cur = 0;
+
+	if (cpu_type == CPU_TIMER_INT) {
+		struct opd_event * event = &opd_events[0];
+		event->name = xstrdup("TIMER");
+		event->value = event->counter
+			= event->count = event->um = 0;
+		event->kernel = 1;
+		event->user = 1;
+		return;
+	}
+
+	if (!ev || !strlen(ev)) {
+		fprintf(stderr, "oprofiled: no events passed.\n");
+		exit(EXIT_FAILURE);
+	}
+
+	verbprintf(vmisc, "Events: %s\n", ev);
+
+	c = ev;
+
+	while (*c && cur < op_nr_counters) {
+		struct opd_event * event = &opd_events[cur];
+
+		if (!(event->name = copy_token(&c, ':')))
+			malformed_events();
+		event->value = copy_ulong(&c, ':');
+		event->counter = copy_ulong(&c, ':');
+		event->count = copy_ulong(&c, ':');
+		event->um = copy_ulong(&c, ':');
+		event->kernel = copy_ulong(&c, ':');
+		event->user = copy_ulong(&c, ',');
+		++cur;
+	}
+
+	if (*c) {
+		fprintf(stderr, "oprofiled: too many events passed.\n");
+		exit(EXIT_FAILURE);
+	}
+
+	free(ev);
+
+	cpu_speed = op_cpu_frequency();
+}
+
+
+struct opd_event * find_counter_event(unsigned long counter)
+{
+	size_t i;
+
+	for (i = 0; i < op_nr_counters && opd_events[i].name; ++i) {
+		if (counter == opd_events[i].counter)
+			return &opd_events[i];
+	}
+
+	fprintf(stderr, "Unknown event for counter %lu\n", counter);
+	abort();
+	return NULL;
+}
+
+
+void fill_header(struct opd_header * header, unsigned long counter,
+                 vma_t anon_start, vma_t cg_to_anon_start,
+                 int is_kernel, int cg_to_is_kernel,
+                 int spu_samples, uint64_t embed_offset, time_t mtime)
+{
+	struct opd_event * event = find_counter_event(counter);
+
+	memset(header, '\0', sizeof(struct opd_header));
+	header->version = OPD_VERSION;
+	memcpy(header->magic, OPD_MAGIC, sizeof(header->magic));
+	header->cpu_type = cpu_type;
+	header->ctr_event = event->value;
+	header->ctr_count = event->count;
+	header->ctr_um = event->um;
+	header->is_kernel = is_kernel;
+	header->cg_to_is_kernel = cg_to_is_kernel;
+	header->cpu_speed = cpu_speed;
+	header->mtime = mtime;
+	header->anon_start = anon_start;
+	header->spu_profile = spu_samples;
+	header->embedded_offset = embed_offset;
+	header->cg_to_anon_start = cg_to_anon_start;
+}
diff --git a/daemon/opd_events.h b/daemon/opd_events.h
new file mode 100644
index 0000000..3bd0106
--- /dev/null
+++ b/daemon/opd_events.h
@@ -0,0 +1,47 @@
+/**
+ * @file daemon/opd_events.h
+ * Event details for each counter
+ *
+ * @remark Copyright 2002, 2003 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OPD_EVENTS_H
+
+#include "op_types.h"
+
+#include <stdint.h>
+#include <time.h>
+
+/** event description for setup (perfmon) and mangling */
+struct opd_event {
+	char * name;
+	unsigned long value;
+	unsigned long counter;
+	unsigned long count;
+	unsigned long um;
+	unsigned long kernel;
+	unsigned long user;
+};
+
+/* needed for opd_perfmon.c */
+extern struct opd_event opd_events[];
+
+/** parse the events into the opd_events array */
+void opd_parse_events(char const * events);
+
+/** Find the event for the given counter */
+struct opd_event * find_counter_event(unsigned long counter);
+
+struct opd_header;
+
+/** fill the sample file header with event info etc. */
+void fill_header(struct opd_header * header, unsigned long counter,
+                 vma_t anon_start, vma_t anon_end,
+                 int is_kernel, int cg_to_is_kernel,
+                 int spu_samples, uint64_t embed_offset, time_t mtime);
+
+#endif /* OPD_EVENTS_H */
diff --git a/daemon/opd_interface.h b/daemon/opd_interface.h
new file mode 100644
index 0000000..c876830
--- /dev/null
+++ b/daemon/opd_interface.h
@@ -0,0 +1,45 @@
+/**
+ * @file opd_interface.h
+ *
+ * Module / user space interface for 2.6 kernels and above
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ * Modified by Aravind Menon for Xen
+ * These modifications are:
+ * Copyright (C) 2005 Hewlett-Packard Co.
+ */
+
+#ifndef OPD_INTERFACE_H
+#define OPD_INTERFACE_H
+
+#define CTX_SWITCH_CODE			1
+#define CPU_SWITCH_CODE			2
+#define COOKIE_SWITCH_CODE		3
+#define KERNEL_ENTER_SWITCH_CODE	4
+#define USER_ENTER_SWITCH_CODE		5
+#define MODULE_LOADED_CODE		6
+#define CTX_TGID_CODE			7
+#define TRACE_BEGIN_CODE		8
+/* Code 9 used to be TRACE_END_CODE which is not used anymore  */
+/* Code 9 is now considered an unknown escape code             */
+#define XEN_ENTER_SWITCH_CODE		10
+/*
+ * Ugly work-around for the unfortunate collision between Xenoprof's
+ * DOMAIN_SWITCH_CODE (in use on x86) and Cell's SPU_PROFILING_CODE
+ * (in use with Power):
+ */
+#if defined(__powerpc__)
+#define SPU_PROFILING_CODE		11
+#define SPU_CTX_SWITCH_CODE		12
+#define DOMAIN_SWITCH_CODE		13
+#define LAST_CODE			14
+#else
+#define DOMAIN_SWITCH_CODE		11
+#define LAST_CODE			12
+#endif
+ 
+#endif /* OPD_INTERFACE_H */
diff --git a/daemon/opd_kernel.c b/daemon/opd_kernel.c
new file mode 100644
index 0000000..5ebc210
--- /dev/null
+++ b/daemon/opd_kernel.c
@@ -0,0 +1,229 @@
+/**
+ * @file daemon/opd_kernel.c
+ * Dealing with the kernel and kernel module samples
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ * Modified by Aravind Menon for Xen
+ * These modifications are:
+ * Copyright (C) 2005 Hewlett-Packard Co.
+ */
+
+#include "opd_kernel.h"
+#include "opd_sfile.h"
+#include "opd_trans.h"
+#include "opd_printf.h"
+#include "opd_stats.h"
+#include "oprofiled.h"
+
+#include "op_fileio.h"
+#include "op_config.h"
+#include "op_libiberty.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+
+static LIST_HEAD(modules);
+
+static struct kernel_image vmlinux_image;
+
+static struct kernel_image xen_image;
+
+void opd_create_vmlinux(char const * name, char const * arg)
+{
+	/* vmlinux is *not* on the list of modules */
+	list_init(&vmlinux_image.list);
+
+	/* for no vmlinux */
+	if (no_vmlinux) {
+		vmlinux_image.name = "no-vmlinux";
+		return;
+	}
+	
+	vmlinux_image.name = xstrdup(name);
+
+	sscanf(arg, "%llx,%llx", &vmlinux_image.start, &vmlinux_image.end);
+
+	verbprintf(vmisc, "kernel_start = %llx, kernel_end = %llx\n",
+	           vmlinux_image.start, vmlinux_image.end);
+
+	if (!vmlinux_image.start && !vmlinux_image.end) {
+		fprintf(stderr, "error: mis-parsed kernel range: %llx-%llx\n",
+		        vmlinux_image.start, vmlinux_image.end);
+		exit(EXIT_FAILURE);
+	}
+}
+
+void opd_create_xen(char const * name, char const * arg)
+{
+	/* xen is *not* on the list of modules */
+	list_init(&xen_image.list);
+
+	/* for no xen */
+	if (no_xen) {
+		xen_image.name = "no-xen";
+		return;
+	}
+
+	xen_image.name = xstrdup(name);
+
+	sscanf(arg, "%llx,%llx", &xen_image.start, &xen_image.end);
+
+	verbprintf(vmisc, "xen_start = %llx, xen_end = %llx\n",
+	           xen_image.start, xen_image.end);
+
+	if (!xen_image.start && !xen_image.end) {
+		fprintf(stderr, "error: mis-parsed xen range: %llx-%llx\n",
+		        xen_image.start, xen_image.end);
+		exit(EXIT_FAILURE);
+	}
+}
+
+
+/**
+ * Allocate and initialise a kernel image description
+ * @param name image name
+ * @param start start address
+ * @param end end address
+ */
+static struct kernel_image *
+opd_create_module(char const * name, vma_t start, vma_t end)
+{
+	struct kernel_image * image = xmalloc(sizeof(struct kernel_image));
+
+	image->name = xstrdup(name);
+	image->start = start;
+	image->end = end;
+	list_add(&image->list, &modules);
+
+	return image;
+}
+
+
+/**
+ * Clear and free all kernel image information and reset
+ * values.
+ */
+static void opd_clear_modules(void)
+{
+	struct list_head * pos;
+	struct list_head * pos2;
+	struct kernel_image * image;
+
+	list_for_each_safe(pos, pos2, &modules) {
+		image = list_entry(pos, struct kernel_image, list);
+		if (image->name)
+			free(image->name);
+		free(image);
+	}
+
+	list_init(&modules);
+
+	/* clear out lingering references */
+	sfile_clear_kernel();
+}
+
+
+/*
+ * each line is in the format:
+ *
+ * module_name 16480 1 dependencies Live 0xe091e000
+ *
+ * without any blank space in each field
+ */
+void opd_reread_module_info(void)
+{
+	FILE * fp;
+	char * line;
+	struct kernel_image * image;
+	int module_size;
+	char ref_count[32+1];
+	int ret;
+	char module_name[256+1];
+	char live_info[32+1];
+	char dependencies[4096+1];
+	unsigned long long start_address;
+
+	if (no_vmlinux)
+		return;
+
+	opd_clear_modules();
+
+	printf("Reading module info.\n");
+
+	fp = op_try_open_file("/proc/modules", "r");
+
+	if (!fp) {
+		printf("oprofiled: /proc/modules not readable, "
+			"can't process module samples.\n");
+		return;
+	}
+
+	while (1) {
+		line = op_get_line(fp);
+
+		if (!line)
+			break;
+
+		if (line[0] == '\0') {
+			free(line);
+			continue;
+		}
+
+		ret = sscanf(line, "%256s %u %32s %4096s %32s %llx",
+			     module_name, &module_size, ref_count,
+			     dependencies, live_info, &start_address);
+		if (ret != 6) {
+			printf("bad /proc/modules entry: %s\n", line);
+			free(line);
+			continue;
+		}
+
+		image = opd_create_module(module_name, start_address,
+		                          start_address + module_size);
+
+		verbprintf(vmodule, "module %s start %llx end %llx\n",
+			   image->name, image->start, image->end);
+
+		free(line);
+	}
+
+	op_close_file(fp);
+}
+
+
+/**
+ * find a kernel image by PC value
+ * @param trans holds PC value to look up
+ *
+ * find the kernel image which contains this PC.
+ *
+ * Return %NULL if not found.
+ */
+struct kernel_image * find_kernel_image(struct transient const * trans)
+{
+	struct list_head * pos;
+	struct kernel_image * image = &vmlinux_image;
+
+	if (no_vmlinux)
+		return image;
+
+	if (image->start <= trans->pc && image->end > trans->pc)
+		return image;
+
+	list_for_each(pos, &modules) {
+		image = list_entry(pos, struct kernel_image, list);
+		if (image->start <= trans->pc && image->end > trans->pc)
+			return image;
+	}
+
+	if (xen_image.start <= trans->pc && xen_image.end > trans->pc)
+		return &xen_image;
+
+	return NULL;
+}
diff --git a/daemon/opd_kernel.h b/daemon/opd_kernel.h
new file mode 100644
index 0000000..cb71a30
--- /dev/null
+++ b/daemon/opd_kernel.h
@@ -0,0 +1,43 @@
+/**
+ * @file daemon/opd_kernel.h
+ * Dealing with the kernel and kernel module images
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ * Modified by Aravind Menon for Xen
+ * These modifications are:
+ * Copyright (C) 2005 Hewlett-Packard Co.
+ */
+
+#ifndef OPD_KERNEL_H
+#define OPD_KERNEL_H
+
+#include "op_types.h"
+#include "op_list.h"
+
+struct transient;
+
+/** create the kernel image */
+void opd_create_vmlinux(char const * name, char const * arg);
+
+void opd_create_xen(char const * name, char const * arg);
+
+/** opd_reread_module_info - parse /proc/modules for kernel modules */
+void opd_reread_module_info(void);
+
+/** Describes a kernel module or vmlinux itself */
+struct kernel_image {
+	char * name;
+	vma_t start;
+	vma_t end;
+	struct list_head list;
+};
+
+/** Find a kernel_image based upon the given parameters in trans. */
+struct kernel_image *
+find_kernel_image(struct transient const * trans);
+
+#endif /* OPD_KERNEL_H */
diff --git a/daemon/opd_mangling.c b/daemon/opd_mangling.c
new file mode 100644
index 0000000..08a6079
--- /dev/null
+++ b/daemon/opd_mangling.c
@@ -0,0 +1,205 @@
+/**
+ * @file daemon/opd_mangling.c
+ * Mangling and opening of sample files
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include <sys/types.h>
+ 
+#include "opd_mangling.h"
+#include "opd_kernel.h"
+#include "opd_cookie.h"
+#include "opd_sfile.h"
+#include "opd_anon.h"
+#include "opd_printf.h"
+#include "opd_events.h"
+#include "oprofiled.h"
+
+#include "op_file.h"
+#include "op_sample_file.h"
+#include "op_config.h"
+#include "op_mangle.h"
+#include "op_events.h"
+#include "op_libiberty.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+
+static char const * get_dep_name(struct sfile const * sf)
+{
+	if (sf->anon)
+		return find_cookie(sf->app_cookie);
+
+	/* avoid to call find_cookie(), caller can recover using image_name */
+	if (sf->cookie == sf->app_cookie)
+		return NULL;
+
+	if (!separate_kernel && !(separate_lib && !sf->kernel))
+		return NULL;
+
+	/* this will fail if e.g. kernel thread */
+	if (sf->app_cookie == 0)
+		return NULL;
+
+	return find_cookie(sf->app_cookie);
+}
+
+
+static char * mangle_anon(struct anon_mapping const * anon)
+{
+	char * name = xmalloc(PATH_MAX);
+
+	snprintf(name, 1024, "%u.0x%llx.0x%llx", (unsigned int)anon->tgid,
+	       anon->start, anon->end);
+
+	return name;
+}
+
+
+static char *
+mangle_filename(struct sfile * last, struct sfile const * sf, int counter, int cg)
+{
+	char * mangled;
+	struct mangle_values values;
+	struct opd_event * event = find_counter_event(counter);
+
+	values.flags = 0;
+
+	if (sf->kernel) {
+		values.image_name = sf->kernel->name;
+		values.flags |= MANGLE_KERNEL;
+	} else if (sf->anon) {
+		values.flags |= MANGLE_ANON;
+		values.image_name = mangle_anon(sf->anon);
+		values.anon_name = sf->anon->name;
+	} else {
+		values.image_name = find_cookie(sf->cookie);
+	}
+
+	values.dep_name = get_dep_name(sf);
+	if (!values.dep_name)
+		values.dep_name = values.image_name;
+ 
+	/* FIXME: log */
+	if (!values.image_name || !values.dep_name)
+		return NULL;
+
+	if (separate_thread) {
+		values.flags |= MANGLE_TGID | MANGLE_TID;
+		values.tid = sf->tid;
+		values.tgid = sf->tgid;
+	}
+ 
+	if (separate_cpu) {
+		values.flags |= MANGLE_CPU;
+		values.cpu = sf->cpu;
+	}
+
+	if (cg) {
+		values.flags |= MANGLE_CALLGRAPH;
+		if (last->kernel) {
+			values.cg_image_name = last->kernel->name;
+		} else if (last->anon) {
+			values.flags |= MANGLE_CG_ANON;
+			values.cg_image_name = mangle_anon(last->anon);
+			values.anon_name = last->anon->name;
+		} else {
+			values.cg_image_name = find_cookie(last->cookie);
+		}
+
+		/* FIXME: log */
+		if (!values.cg_image_name) {
+			if (values.flags & MANGLE_ANON)
+				free((char *)values.image_name);
+			return NULL;
+		}
+	}
+
+	values.event_name = event->name;
+	values.count = event->count;
+	values.unit_mask = event->um;
+
+	mangled = op_mangle_filename(&values);
+
+	if (values.flags & MANGLE_ANON)
+		free((char *)values.image_name);
+	if (values.flags & MANGLE_CG_ANON)
+		free((char *)values.cg_image_name);
+	return mangled;
+}
+
+
+int opd_open_sample_file(odb_t * file, struct sfile * last,
+                         struct sfile * sf, int counter, int cg)
+{
+	char * mangled;
+	char const * binary;
+	int spu_profile = 0;
+	vma_t last_start = 0;
+	int err;
+
+	mangled = mangle_filename(last, sf, counter, cg);
+
+	if (!mangled)
+		return EINVAL;
+
+	verbprintf(vsfile, "Opening \"%s\"\n", mangled);
+
+	create_path(mangled);
+
+	/* locking sf will lock associated cg files too */
+	sfile_get(sf);
+	if (sf != last)
+		sfile_get(last);
+
+retry:
+	err = odb_open(file, mangled, ODB_RDWR, sizeof(struct opd_header));
+
+	/* This can naturally happen when racing against opcontrol --reset. */
+	if (err) {
+		if (err == EMFILE) {
+			if (sfile_lru_clear()) {
+				printf("LRU cleared but odb_open() fails for %s.\n", mangled);
+				abort();
+			}
+			goto retry;
+		}
+
+		fprintf(stderr, "oprofiled: open of %s failed: %s\n",
+		        mangled, strerror(err));
+		goto out;
+	}
+
+	if (!sf->kernel)
+		binary = find_cookie(sf->cookie);
+	else
+		binary = sf->kernel->name;
+
+	if (last && last->anon)
+		last_start = last->anon->start;
+
+	if (sf->embedded_offset != UNUSED_EMBEDDED_OFFSET)
+		spu_profile = 1;
+
+	fill_header(odb_get_data(file), counter,
+		    sf->anon ? sf->anon->start : 0, last_start,
+		    !!sf->kernel, last ? !!last->kernel : 0,
+		    spu_profile, sf->embedded_offset,
+		    binary ? op_get_mtime(binary) : 0);
+
+out:
+	sfile_put(sf);
+	if (sf != last)
+		sfile_put(last);
+	free(mangled);
+	return err;
+}
diff --git a/daemon/opd_mangling.h b/daemon/opd_mangling.h
new file mode 100644
index 0000000..0e46ec4
--- /dev/null
+++ b/daemon/opd_mangling.h
@@ -0,0 +1,33 @@
+/**
+ * @file daemon/opd_mangling.h
+ * Mangling and opening of sample files
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OPD_MANGLING_H
+#define OPD_MANGLING_H
+
+#include "odb.h"
+
+struct sfile;
+
+/*
+ * opd_open_sample_file - open a sample file
+ * @param sf  sfile to open sample file for
+ * @param counter  counter number
+ * @param cg if this is a callgraph file
+ *
+ * Open image sample file for the sfile, counter
+ * counter and set up memory mappings for it.
+ *
+ * Returns 0 on success.
+ */
+int opd_open_sample_file(odb_t * file, struct sfile * last,
+                         struct sfile * sf, int counter, int cg);
+
+#endif /* OPD_MANGLING_H */
diff --git a/daemon/opd_perfmon.c b/daemon/opd_perfmon.c
new file mode 100644
index 0000000..a1b158a
--- /dev/null
+++ b/daemon/opd_perfmon.c
@@ -0,0 +1,496 @@
+/**
+ * @file opd_perfmon.c
+ * perfmonctl() handling
+ *
+ * @remark Copyright 2003 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ */
+
+#ifdef __ia64__
+
+/* need this for sched_setaffinity() in <sched.h> */
+#define _GNU_SOURCE
+
+#include "oprofiled.h"
+#include "opd_perfmon.h"
+#include "opd_events.h"
+
+#include "op_cpu_type.h"
+#include "op_libiberty.h"
+#include "op_hw_config.h"
+
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#ifdef HAVE_SCHED_SETAFFINITY
+#include <sched.h>
+#endif
+
+extern op_cpu cpu_type;
+
+#ifndef HAVE_SCHED_SETAFFINITY
+
+/* many glibc's are not yet up to date */
+#ifndef __NR_sched_setaffinity
+#define __NR_sched_setaffinity 1231
+#endif
+
+/* Copied from glibc's <sched.h> and <bits/sched.h> and munged */
+#define CPU_SETSIZE	1024
+#define __NCPUBITS	(8 * sizeof (unsigned long))
+typedef struct
+{
+	unsigned long __bits[CPU_SETSIZE / __NCPUBITS];
+} cpu_set_t;
+
+#define CPU_SET(cpu, cpusetp) \
+	((cpusetp)->__bits[(cpu)/__NCPUBITS] |= (1UL << ((cpu) % __NCPUBITS)))
+#define CPU_ZERO(cpusetp) \
+	memset((cpusetp), 0, sizeof(cpu_set_t))
+
+static int
+sched_setaffinity(pid_t pid, size_t len, cpu_set_t const * cpusetp)
+{
+	return syscall(__NR_sched_setaffinity, pid, len, cpusetp);
+}
+#endif
+
+
+#ifndef HAVE_PERFMONCTL
+#ifndef __NR_perfmonctl
+#define __NR_perfmonctl 1175
+#endif
+
+static int perfmonctl(int fd, int cmd, void * arg, int narg)
+{
+	return syscall(__NR_perfmonctl, fd, cmd, arg, narg);
+}
+#endif
+
+
+static unsigned char uuid[16] = {
+	0x77, 0x7a, 0x6e, 0x61, 0x20, 0x65, 0x73, 0x69,
+	0x74, 0x6e, 0x72, 0x20, 0x61, 0x65, 0x0a, 0x6c
+};
+
+
+static size_t nr_cpus;
+
+struct child {
+	pid_t pid;
+	int up_pipe[2];
+	int ctx_fd;
+	sig_atomic_t sigusr1;
+	sig_atomic_t sigusr2;
+	sig_atomic_t sigterm;
+};
+
+static struct child * children;
+
+static void perfmon_start_child(int ctx_fd)
+{
+	if (perfmonctl(ctx_fd, PFM_START, 0, 0) == -1) {
+		perror("Couldn't start perfmon: ");
+		exit(EXIT_FAILURE);
+	}
+}
+
+
+static void perfmon_stop_child(int ctx_fd)
+{
+	if (perfmonctl(ctx_fd, PFM_STOP, 0, 0) == -1) {
+		perror("Couldn't stop perfmon: ");
+		exit(EXIT_FAILURE);
+	}
+}
+
+
+static void child_sigusr1(int val __attribute__((unused)))
+{
+	size_t i;
+
+	for (i = 0; i < nr_cpus; ++i) {
+		if (children[i].pid == getpid()) {
+			children[i].sigusr1 = 1;
+			return;
+		}
+	}
+}
+
+
+static void child_sigusr2(int val __attribute__((unused)))
+{
+	size_t i;
+
+	for (i = 0; i < nr_cpus; ++i) {
+		if (children[i].pid == getpid()) {
+			children[i].sigusr2 = 1;
+			return;
+		}
+	}
+}
+
+
+static void child_sigterm(int val __attribute__((unused)))
+{
+	printf("Child received SIGTERM, killing parent.\n");
+	kill(getppid(), SIGTERM);
+}
+
+
+static void set_affinity(size_t cpu)
+{
+	cpu_set_t set;
+
+	CPU_ZERO(&set);
+	CPU_SET(cpu, &set);
+
+	int err = sched_setaffinity(getpid(), sizeof(set), &set);
+
+	if (err == -1) {
+		fprintf(stderr, "Failed to set affinity: %s\n",
+			    strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+}
+
+
+static void setup_signals(void)
+{
+	struct sigaction act;
+	sigset_t mask;
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGUSR1);
+	sigaddset(&mask, SIGUSR2);
+	sigprocmask(SIG_BLOCK, &mask, NULL);
+
+	act.sa_handler = child_sigusr1;
+	act.sa_flags = 0;
+	sigemptyset(&act.sa_mask);
+
+	if (sigaction(SIGUSR1, &act, NULL)) {
+		perror("oprofiled: install of SIGUSR1 handler failed: ");
+		exit(EXIT_FAILURE);
+	}
+
+	act.sa_handler = child_sigusr2;
+	act.sa_flags = 0;
+	sigemptyset(&act.sa_mask);
+
+	if (sigaction(SIGUSR2, &act, NULL)) {
+		perror("oprofiled: install of SIGUSR2 handler failed: ");
+		exit(EXIT_FAILURE);
+	}
+
+	act.sa_handler = child_sigterm;
+	act.sa_flags = 0;
+	sigemptyset(&act.sa_mask);
+
+	if (sigaction(SIGTERM, &act, NULL)) {
+		perror("oprofiled: install of SIGTERM handler failed: ");
+		exit(EXIT_FAILURE);
+	}
+}
+
+
+/** create the per-cpu context */
+static void create_context(struct child * self)
+{
+	pfarg_context_t ctx;
+	int err;
+
+	memset(&ctx, 0, sizeof(pfarg_context_t));
+	memcpy(&ctx.ctx_smpl_buf_id, &uuid, 16);
+	ctx.ctx_flags = PFM_FL_SYSTEM_WIDE;
+
+	err = perfmonctl(0, PFM_CREATE_CONTEXT, &ctx, 1);
+	if (err == -1) {
+		fprintf(stderr, "CREATE_CONTEXT failed: %s\n",
+		        strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	self->ctx_fd = ctx.ctx_fd;
+}
+
+
+/** program the perfmon counters */
+static void write_pmu(struct child * self)
+{
+	pfarg_reg_t pc[OP_MAX_COUNTERS];
+	pfarg_reg_t pd[OP_MAX_COUNTERS];
+	int err;
+	size_t i;
+
+	memset(pc, 0, sizeof(pc));
+	memset(pd, 0, sizeof(pd));
+
+#define PMC_GEN_INTERRUPT (1UL << 5)
+#define PMC_PRIV_MONITOR (1UL << 6)
+/* McKinley requires pmc4 to have bit 23 set (enable PMU).
+ * It is supposedly ignored in other pmc registers.
+ */
+#define PMC_MANDATORY (1UL << 23)
+#define PMC_USER (1UL << 3)
+#define PMC_KERNEL (1UL << 0)
+	for (i = 0; i < op_nr_counters && opd_events[i].name; ++i) {
+		struct opd_event * event = &opd_events[i];
+		pc[i].reg_num = event->counter + 4;
+		pc[i].reg_value = PMC_GEN_INTERRUPT;
+		pc[i].reg_value |= PMC_PRIV_MONITOR;
+		pc[i].reg_value |= PMC_MANDATORY;
+		(event->user) ? (pc[i].reg_value |= PMC_USER)
+		              : (pc[i].reg_value &= ~PMC_USER);
+		(event->kernel) ? (pc[i].reg_value |= PMC_KERNEL)
+		                : (pc[i].reg_value &= ~PMC_KERNEL);
+		pc[i].reg_value &= ~(0xff << 8);
+		pc[i].reg_value |= ((event->value & 0xff) << 8);
+		pc[i].reg_value &= ~(0xf << 16);
+		pc[i].reg_value |= ((event->um & 0xf) << 16);
+		pc[i].reg_smpl_eventid = event->counter;
+	}
+
+	for (i = 0; i < op_nr_counters && opd_events[i].name; ++i) {
+		struct opd_event * event = &opd_events[i];
+		pd[i].reg_value = ~0UL - event->count + 1;
+		pd[i].reg_short_reset = ~0UL - event->count + 1;
+		pd[i].reg_num = event->counter + 4;
+	}
+
+	err = perfmonctl(self->ctx_fd, PFM_WRITE_PMCS, pc, i);
+	if (err == -1) {
+		perror("Couldn't write PMCs: ");
+		exit(EXIT_FAILURE);
+	}
+
+	err = perfmonctl(self->ctx_fd, PFM_WRITE_PMDS, pd, i);
+	if (err == -1) {
+		perror("Couldn't write PMDs: ");
+		exit(EXIT_FAILURE);
+	}
+}
+
+
+static void load_context(struct child * self)
+{
+	pfarg_load_t load_args;
+	int err;
+
+	memset(&load_args, 0, sizeof(load_args));
+	load_args.load_pid = self->pid;
+
+	err = perfmonctl(self->ctx_fd, PFM_LOAD_CONTEXT, &load_args, 1);
+	if (err == -1) {
+		perror("Couldn't load context: ");
+		exit(EXIT_FAILURE);
+	}
+}
+
+
+static void notify_parent(struct child * self, size_t cpu)
+{
+	for (;;) {
+		ssize_t ret;
+		ret = write(self->up_pipe[1], &cpu, sizeof(size_t));
+		if (ret == sizeof(size_t))
+			break;
+		if (ret < 0 && errno != EINTR) {
+			fprintf(stderr, "Failed to write child pipe with %s\n",
+			        strerror(errno));
+			exit(EXIT_FAILURE);
+		}
+	}
+}
+
+
+static void run_child(size_t cpu)
+{
+	struct child * self = &children[cpu];
+
+	self->pid = getpid();
+	self->sigusr1 = 0;
+	self->sigusr2 = 0;
+	self->sigterm = 0;
+
+	setup_signals();
+
+	set_affinity(cpu);
+
+	create_context(self);
+
+	write_pmu(self);
+
+	load_context(self);
+
+	notify_parent(self, cpu);
+
+	for (;;) {
+		sigset_t sigmask;
+		sigfillset(&sigmask);
+		sigdelset(&sigmask, SIGUSR1);
+		sigdelset(&sigmask, SIGUSR2);
+		sigdelset(&sigmask, SIGTERM);
+
+		if (self->sigusr1) {
+			printf("PFM_START on CPU%d\n", (int)cpu);
+			fflush(stdout);
+			perfmon_start_child(self->ctx_fd);
+			self->sigusr1 = 0;
+		}
+
+		if (self->sigusr2) {
+			printf("PFM_STOP on CPU%d\n", (int)cpu);
+			fflush(stdout);
+			perfmon_stop_child(self->ctx_fd);
+			self->sigusr2 = 0;
+		}
+
+		sigsuspend(&sigmask);
+	}
+}
+
+
+static void wait_for_child(struct child * child)
+{
+	size_t tmp;
+	for (;;) {
+		ssize_t ret;
+		ret = read(child->up_pipe[0], &tmp, sizeof(size_t));
+		if (ret == sizeof(size_t))
+			break;
+		if (ret < 0 && errno != EINTR) {
+			fprintf(stderr, "Failed to read child pipe with %s\n",
+			        strerror(errno));
+			exit(EXIT_FAILURE);
+		}
+	}
+	printf("Perfmon child up on CPU%d\n", (int)tmp);
+	fflush(stdout);
+
+	close(child->up_pipe[0]);
+	close(child->up_pipe[1]);
+}
+
+static struct child* xen_ctx;
+
+void perfmon_init(void)
+{
+	size_t i;
+	long nr;
+
+	if (cpu_type == CPU_TIMER_INT)
+		return;
+
+	if (!no_xen) {
+		xen_ctx = xmalloc(sizeof(struct child));
+		xen_ctx->pid = getpid();
+		xen_ctx->up_pipe[0] = -1;
+		xen_ctx->up_pipe[1] = -1;
+		xen_ctx->sigusr1 = 0;
+		xen_ctx->sigusr2 = 0;
+		xen_ctx->sigterm = 0;
+
+		create_context(xen_ctx);
+
+		write_pmu(xen_ctx);
+		
+		load_context(xen_ctx);
+		return;
+	}
+	
+
+	nr = sysconf(_SC_NPROCESSORS_ONLN);
+	if (nr == -1) {
+		fprintf(stderr, "Couldn't determine number of CPUs.\n");
+		exit(EXIT_FAILURE);
+	}
+
+	nr_cpus = nr;
+
+	children = xmalloc(sizeof(struct child) * nr_cpus);
+
+	for (i = 0; i < nr_cpus; ++i) {
+		int ret;
+
+		if (pipe(children[i].up_pipe)) {
+			perror("Couldn't create child pipe.\n");
+			exit(EXIT_FAILURE);
+		}
+
+		ret = fork();
+		if (ret == -1) {
+			fprintf(stderr, "Couldn't fork perfmon child.\n");
+			exit(EXIT_FAILURE);
+		} else if (ret == 0) {
+			printf("Running perfmon child on CPU%d.\n", (int)i);
+			fflush(stdout);
+			run_child(i);
+		} else {
+			children[i].pid = ret;
+			printf("Waiting on CPU%d\n", (int)i);
+			wait_for_child(&children[i]);
+		}
+	}
+}
+
+
+void perfmon_exit(void)
+{
+	size_t i;
+
+	if (cpu_type == CPU_TIMER_INT)
+		return;
+
+	if (!no_xen)
+		return;
+
+	for (i = 0; i < nr_cpus; ++i) {
+		kill(children[i].pid, SIGKILL);
+		waitpid(children[i].pid, NULL, 0);
+	}
+}
+
+
+void perfmon_start(void)
+{
+	size_t i;
+
+	if (cpu_type == CPU_TIMER_INT)
+		return;
+
+	if (!no_xen) {
+		perfmon_start_child(xen_ctx->ctx_fd);
+		return;
+	}
+
+	for (i = 0; i < nr_cpus; ++i)
+		kill(children[i].pid, SIGUSR1);
+}
+
+
+void perfmon_stop(void)
+{
+	size_t i;
+
+	if (cpu_type == CPU_TIMER_INT)
+		return;
+
+	if (!no_xen) {
+		perfmon_stop_child(xen_ctx->ctx_fd);
+		return;
+	}
+	
+	for (i = 0; i < nr_cpus; ++i)
+		kill(children[i].pid, SIGUSR2);
+}
+
+#endif /* __ia64__ */
diff --git a/daemon/opd_perfmon.h b/daemon/opd_perfmon.h
new file mode 100644
index 0000000..9b4267f
--- /dev/null
+++ b/daemon/opd_perfmon.h
@@ -0,0 +1,106 @@
+/**
+ * @file opd_perfmon.h
+ * perfmonctl() handling
+ *
+ * @remark Copyright 2003 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ */
+
+#ifndef OPD_PERFMON_H
+#define OPD_PERFMON_H
+
+#ifdef __ia64__
+
+#include <stdlib.h>
+
+void perfmon_init(void);
+void perfmon_exit(void);
+void perfmon_start(void);
+void perfmon_stop(void);
+
+/* The following is from asm/perfmon.h. When it's installed on
+ * enough boxes, we can remove this and include the platform
+ * perfmon.h
+ */
+
+typedef unsigned char pfm_uuid_t[16];	/* custom sampling buffer identifier type */
+
+/*
+ * Request structure used to define a context
+ */
+typedef struct {
+	pfm_uuid_t     ctx_smpl_buf_id;	 /* which buffer format to use (if needed) */
+	unsigned long  ctx_flags;	 /* noblock/block */
+	unsigned short ctx_nextra_sets;	 /* number of extra event sets (you always get 1) */
+	unsigned short ctx_reserved1;	 /* for future use */
+	int	       ctx_fd;		 /* return arg: unique identification for context */
+	void	       *ctx_smpl_vaddr;	 /* return arg: virtual address of sampling buffer, is used */
+	unsigned long  ctx_reserved2[11];/* for future use */
+} pfarg_context_t;
+
+/*
+ * Request structure used to write/read a PMC or PMD
+ */
+typedef struct {
+	unsigned int	reg_num;	   /* which register */
+	unsigned short	reg_set;	   /* event set for this register */
+	unsigned short	reg_reserved1;	   /* for future use */
+
+	unsigned long	reg_value;	   /* initial pmc/pmd value */
+	unsigned long	reg_flags;	   /* input: pmc/pmd flags, return: reg error */
+
+	unsigned long	reg_long_reset;	   /* reset after buffer overflow notification */
+	unsigned long	reg_short_reset;   /* reset after counter overflow */
+
+	unsigned long	reg_reset_pmds[4]; /* which other counters to reset on overflow */
+	unsigned long	reg_random_seed;   /* seed value when randomization is used */
+	unsigned long	reg_random_mask;   /* bitmask used to limit random value */
+	unsigned long   reg_last_reset_val;/* return: PMD last reset value */
+
+	unsigned long	reg_smpl_pmds[4];  /* which pmds are accessed when PMC overflows */
+	unsigned long	reg_smpl_eventid;  /* opaque sampling event identifier */
+
+	unsigned long   reg_reserved2[3];   /* for future use */
+} pfarg_reg_t;
+
+typedef struct {
+	pid_t		load_pid;	   /* process to load the context into */
+	unsigned short	load_set;	   /* first event set to load */
+	unsigned short	load_reserved1;	   /* for future use */
+	unsigned long	load_reserved2[3]; /* for future use */
+} pfarg_load_t;
+
+#define PFM_WRITE_PMCS      0x01
+#define PFM_WRITE_PMDS      0x02
+#define PFM_STOP            0x04
+#define PFM_START           0x05
+#define PFM_CREATE_CONTEXT  0x08
+#define PFM_LOAD_CONTEXT    0x10
+#define PFM_FL_SYSTEM_WIDE  0x02
+
+#else
+
+void perfmon_init(void)
+{
+}
+
+
+void perfmon_exit(void)
+{
+}
+
+
+void perfmon_start(void)
+{
+}
+
+
+void perfmon_stop(void)
+{
+}
+
+#endif /* __ia64__ */
+
+#endif /* OPD_PERFMON_H */
diff --git a/daemon/opd_pipe.c b/daemon/opd_pipe.c
new file mode 100644
index 0000000..a5c334a
--- /dev/null
+++ b/daemon/opd_pipe.c
@@ -0,0 +1,96 @@
+/**
+ * @file daemon/opd_pipe.c
+ * Functions handling the $SESSIONDIR/opd_pipe FIFO special file.
+ * NOTE: This code is dealing with potentially insecure input.
+ *
+ * @remark Copyright 2008 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Daniel Hansel
+ */
+
+#include "opd_pipe.h"
+#include "opd_printf.h"
+#include "op_config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+static int fifo;
+
+void opd_create_pipe(void)
+{
+	mode_t orig_umask = umask(0111);
+	if (mkfifo(op_pipe_file, 0666) == -1) {
+		if (errno != EEXIST) {
+			perror("oprofiled: couldn't create pipe: ");
+			exit(EXIT_FAILURE);
+		}
+	}
+	umask(orig_umask);
+}
+
+
+void opd_open_pipe(void)
+{
+	fifo = open(op_pipe_file, O_RDONLY | O_NONBLOCK);
+	if (fifo == -1) {
+		perror("oprofiled: couldn't open pipe: ");
+		exit(EXIT_FAILURE);
+	}
+}
+
+
+void opd_close_pipe(void)
+{
+	close(fifo);
+}
+
+
+int is_jitconv_requested(void)
+{
+	/* number of dropped (unknown) requests */
+	static long nr_drops = 0;
+	/* modulus to output only a few warnings to avoid flooding oprofiled.log */
+	static int mod_cnt_drops = 1;
+	FILE * fd;
+	char line[256];
+	int i, ret = 0;
+
+	/* get a file descriptor to the pipe */
+	fd = fdopen(fifo, "r");
+
+	if (fd == NULL) {
+		perror("oprofiled: couldn't create file descriptor: ");
+		exit(EXIT_FAILURE);
+	}
+
+	/* read up to 99 lines to check for 'do_jitconv' */
+	for (i = 0; i < 99; i++) {
+		/* just break if no new line is found */
+		if (fgets(line, 256, fd) == NULL)
+			break;
+		line[strlen(line) - 1] = '\0';
+
+		if (strstr(line, "do_jitconv") != NULL) {
+			ret = 1;
+		} else {
+			nr_drops++;
+
+			if (nr_drops % mod_cnt_drops == 0) {
+				printf(
+				       "Warning: invalid pipe request received (dropped request(s): %ld)\n",
+				       nr_drops);
+				/* increase modulus to avoid flooding log file */
+				mod_cnt_drops *= 5;
+			}
+		}
+	}
+
+	return ret;
+}
diff --git a/daemon/opd_pipe.h b/daemon/opd_pipe.h
new file mode 100644
index 0000000..7f96b07
--- /dev/null
+++ b/daemon/opd_pipe.h
@@ -0,0 +1,45 @@
+/**
+ * @file daemon/opd_pipe.h
+ * Functions handling the $SESSIONDIR/opd_pipe FIFO special file.
+ * NOTE: This code is dealing with potencially insecure input.
+ *
+ * @remark Copyright 2008 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Daniel Hansel
+ */
+
+#ifndef OPD_PIPE_H_
+#define OPD_PIPE_H_
+ 
+/**
+ * opd_create_pipe - creates the oprofiled fifo file
+ *
+ * Creates the Oprofile daemon fifo pipe to enable communication between
+ * the daemon and the 'opcontrol --dump' command. Failure to create the pipe
+ * is a fatal error.
+ */
+void opd_create_pipe(void);
+
+/**
+ * opd_open_pipe - opens the oprofiled fifo file
+ */
+void opd_open_pipe(void);
+
+/**
+ * opd_close_pipe - closes the oprofiled fifo file
+ *
+ * Closes the Oprofile daemon fifo pipe.
+ */
+void opd_close_pipe(void);
+
+/**
+ * is_jitconv_requested - check for request to jit conversion
+ *
+ * Checks the Oprofile daemon fifo pipe for do_jitconv request.
+ * If jit conversion is requested ('do_jitconv' is sent) the check returns 1.
+ * Otherwise it returns 0.
+ */
+int is_jitconv_requested(void);
+
+#endif /*OPD_PIPE_H_*/
diff --git a/daemon/opd_printf.h b/daemon/opd_printf.h
new file mode 100644
index 0000000..e1f8476
--- /dev/null
+++ b/daemon/opd_printf.h
@@ -0,0 +1,35 @@
+/**
+ * @file daemon/opd_printf.h
+ * Output routines
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OPD_PRINTF_H
+#define OPD_PRINTF_H
+
+/// log all sample file name manipulation; sample files open, close,
+/// sfile LRU etc. voluminous. FIXME need to be splitted (filename manip, files
+/// handling) ?
+extern int vsfile;
+/// log samples, voluminous.
+extern int vsamples;
+/// log arc, very voluminous.
+extern int varcs;
+/// kernel module handling
+extern int vmodule;
+/// all others not fitting in above category, not voluminous.
+extern int vmisc;
+
+#define verbprintf(x, args...) \
+	do { \
+		/* look like fragile but we must catch verbrintf("%s", "") */ \
+		if (x == 1) \
+			printf(args); \
+	} while (0)
+
+#endif /* OPD_PRINTF_H */
diff --git a/daemon/opd_sfile.c b/daemon/opd_sfile.c
new file mode 100644
index 0000000..03ebf55
--- /dev/null
+++ b/daemon/opd_sfile.c
@@ -0,0 +1,619 @@
+/**
+ * @file daemon/opd_sfile.c
+ * Management of sample files
+ *
+ * @remark Copyright 2002, 2005 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include "opd_sfile.h"
+
+#include "opd_trans.h"
+#include "opd_kernel.h"
+#include "opd_mangling.h"
+#include "opd_anon.h"
+#include "opd_printf.h"
+#include "opd_stats.h"
+#include "oprofiled.h"
+
+#include "op_libiberty.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define HASH_SIZE 2048
+#define HASH_BITS (HASH_SIZE - 1)
+
+/** All sfiles are hashed into these lists */
+static struct list_head hashes[HASH_SIZE];
+
+/** All sfiles are on this list. */
+static LIST_HEAD(lru_list);
+
+
+/* FIXME: can undoubtedly improve this hashing */
+/** Hash the transient parameters for lookup. */
+static unsigned long
+sfile_hash(struct transient const * trans, struct kernel_image * ki)
+{
+	unsigned long val = 0;
+	
+	if (separate_thread) {
+		val ^= trans->tid << 2;
+		val ^= trans->tgid << 2;
+	}
+
+	if (separate_kernel || ((trans->anon || separate_lib) && !ki))
+		val ^= trans->app_cookie >> (DCOOKIE_SHIFT + 3);
+
+	if (separate_cpu)
+		val ^= trans->cpu;
+
+	/* cookie meaningless for kernel, shouldn't hash */
+	if (trans->in_kernel) {
+		val ^= ki->start >> 14;
+		val ^= ki->end >> 7;
+		return val & HASH_BITS;
+	}
+
+	if (trans->cookie != NO_COOKIE) {
+		val ^= trans->cookie >> DCOOKIE_SHIFT;
+		return val & HASH_BITS;
+	}
+
+	if (!separate_thread)
+		val ^= trans->tgid << 2;
+
+	if (trans->anon) {
+		val ^= trans->anon->start >> VMA_SHIFT;
+		val ^= trans->anon->end >> (VMA_SHIFT + 1);
+	}
+
+	return val & HASH_BITS;
+}
+
+
+static int
+do_match(struct sfile const * sf, cookie_t cookie, cookie_t app_cookie,
+         struct kernel_image const * ki, struct anon_mapping const * anon,
+         pid_t tgid, pid_t tid, unsigned int cpu)
+{
+	/* this is a simplified check for "is a kernel image" AND
+	 * "is the right kernel image". Also handles no-vmlinux
+	 * correctly.
+	 */
+	if (sf->kernel != ki)
+		return 0;
+
+	if (separate_thread) {
+		if (sf->tid != tid || sf->tgid != tgid)
+			return 0;
+	}
+
+	if (separate_cpu) {
+		if (sf->cpu != cpu)
+			return 0;
+	}
+
+	if (separate_kernel || ((anon || separate_lib) && !ki)) {
+		if (sf->app_cookie != app_cookie)
+			return 0;
+	}
+
+	/* ignore the cached trans->cookie for kernel images,
+	 * it's meaningless and we checked all others already
+	 */
+	if (ki)
+		return 1;
+
+	if (sf->anon != anon)
+		return 0;
+
+	return sf->cookie == cookie;
+}
+
+
+static int
+trans_match(struct transient const * trans, struct sfile const * sfile,
+            struct kernel_image const * ki)
+{
+	return do_match(sfile, trans->cookie, trans->app_cookie, ki,
+	                trans->anon, trans->tgid, trans->tid, trans->cpu);
+}
+
+
+static int
+sfile_equal(struct sfile const * sf, struct sfile const * sf2)
+{
+	return do_match(sf, sf2->cookie, sf2->app_cookie, sf2->kernel,
+	                sf2->anon, sf2->tgid, sf2->tid, sf2->cpu);
+}
+
+
+static int
+is_sf_ignored(struct sfile const * sf)
+{
+	if (sf->kernel) {
+		if (!is_image_ignored(sf->kernel->name))
+			return 0;
+
+		/* Let a dependent kernel image redeem the sf if we're
+		 * executing on behalf of an application.
+		 */
+		return is_cookie_ignored(sf->app_cookie);
+	}
+
+	/* Anon regions are always dependent on the application.
+ 	 * Otherwise, let a dependent image redeem the sf.
+	 */
+	if (sf->anon || is_cookie_ignored(sf->cookie))
+		return is_cookie_ignored(sf->app_cookie);
+
+	return 0;
+}
+
+
+/** create a new sfile matching the current transient parameters */
+static struct sfile *
+create_sfile(unsigned long hash, struct transient const * trans,
+             struct kernel_image * ki)
+{
+	size_t i;
+	struct sfile * sf;
+
+	sf = xmalloc(sizeof(struct sfile));
+
+	sf->hashval = hash;
+
+	/* The logic here: if we're in the kernel, the cached cookie is
+	 * meaningless (though not the app_cookie if separate_kernel)
+	 */
+	sf->cookie = trans->in_kernel ? INVALID_COOKIE : trans->cookie;
+	sf->app_cookie = INVALID_COOKIE;
+	sf->tid = (pid_t)-1;
+	sf->tgid = (pid_t)-1;
+	sf->cpu = 0;
+	sf->kernel = ki;
+	sf->anon = trans->anon;
+
+	for (i = 0 ; i < op_nr_counters ; ++i)
+		odb_init(&sf->files[i]);
+
+	for (i = 0; i < CG_HASH_SIZE; ++i)
+		list_init(&sf->cg_hash[i]);
+
+	if (separate_thread)
+		sf->tid = trans->tid;
+	if (separate_thread || trans->cookie == NO_COOKIE)
+		sf->tgid = trans->tgid;
+
+	if (separate_cpu)
+		sf->cpu = trans->cpu;
+
+	if (separate_kernel || ((trans->anon || separate_lib) && !ki))
+		sf->app_cookie = trans->app_cookie;
+
+	sf->ignored = is_sf_ignored(sf);
+
+	sf->embedded_offset = trans->embedded_offset;
+
+	/* If embedded_offset is a valid value, it means we're
+	 * processing a Cell BE SPU profile; in which case, we
+	 * want sf->app_cookie to hold trans->app_cookie.
+	 */
+	if (trans->embedded_offset != UNUSED_EMBEDDED_OFFSET)
+		sf->app_cookie = trans->app_cookie;
+	return sf;
+}
+
+
+struct sfile * sfile_find(struct transient const * trans)
+{
+	struct sfile * sf;
+	struct list_head * pos;
+	struct kernel_image * ki = NULL;
+	unsigned long hash;
+
+	if (trans->tracing != TRACING_ON) {
+		opd_stats[OPD_SAMPLES]++;
+		opd_stats[trans->in_kernel == 1 ? OPD_KERNEL : OPD_PROCESS]++;
+	}
+
+	/* There is a small race where this *can* happen, see
+	 * caller of cpu_buffer_reset() in the kernel
+	 */
+	if (trans->in_kernel == -1) {
+		verbprintf(vsamples, "Losing sample at 0x%llx of unknown provenance.\n",
+		           trans->pc);
+		opd_stats[OPD_NO_CTX]++;
+		return NULL;
+	}
+
+	/* we might need a kernel image start/end to hash on */
+	if (trans->in_kernel) {
+		ki = find_kernel_image(trans);
+		if (!ki) {
+			verbprintf(vsamples, "Lost kernel sample %llx\n", trans->pc);
+			opd_stats[OPD_LOST_KERNEL]++;
+			return NULL;
+		}
+	} else if (trans->cookie == NO_COOKIE && !trans->anon) {
+		if (vsamples) {
+			char const * app = verbose_cookie(trans->app_cookie);
+			printf("No anon map for pc %llx, app %s.\n",
+			       trans->pc, app);
+		}
+		opd_stats[OPD_LOST_NO_MAPPING]++;
+		return NULL;
+	}
+
+	hash = sfile_hash(trans, ki);
+	list_for_each(pos, &hashes[hash]) {
+		sf = list_entry(pos, struct sfile, hash);
+		if (trans_match(trans, sf, ki)) {
+			sfile_get(sf);
+			goto lru;
+		}
+	}
+
+	sf = create_sfile(hash, trans, ki);
+	list_add(&sf->hash, &hashes[hash]);
+
+lru:
+	sfile_put(sf);
+	return sf;
+}
+
+
+static void sfile_dup(struct sfile * to, struct sfile * from)
+{
+	size_t i;
+
+	memcpy(to, from, sizeof (struct sfile));
+
+	for (i = 0 ; i < op_nr_counters ; ++i)
+		odb_init(&to->files[i]);
+
+	for (i = 0; i < CG_HASH_SIZE; ++i)
+		list_init(&to->cg_hash[i]);
+
+	list_init(&to->hash);
+	list_init(&to->lru);
+}
+
+
+static odb_t * get_file(struct transient const * trans, int is_cg)
+{
+	struct sfile * sf = trans->current;
+	struct sfile * last = trans->last;
+	struct cg_entry * cg;
+	struct list_head * pos;
+	unsigned long hash;
+	odb_t * file;
+
+	if (trans->event >= op_nr_counters) {
+		fprintf(stderr, "%s: Invalid counter %lu\n", __FUNCTION__,
+			trans->event);
+		abort();
+	}
+
+	file = &sf->files[trans->event];
+
+	if (!is_cg)
+		goto open;
+
+	hash = last->hashval & (CG_HASH_SIZE - 1);
+
+	/* Need to look for the right 'to'. Since we're looking for
+	 * 'last', we use its hash.
+	 */
+	list_for_each(pos, &sf->cg_hash[hash]) {
+		cg = list_entry(pos, struct cg_entry, hash);
+		if (sfile_equal(last, &cg->to)) {
+			file = &cg->to.files[trans->event];
+			goto open;
+		}
+	}
+
+	cg = xmalloc(sizeof(struct cg_entry));
+	sfile_dup(&cg->to, last);
+	list_add(&cg->hash, &sf->cg_hash[hash]);
+	file = &cg->to.files[trans->event];
+
+open:
+	if (!odb_open_count(file))
+		opd_open_sample_file(file, last, sf, trans->event, is_cg);
+
+	/* Error is logged by opd_open_sample_file */
+	if (!odb_open_count(file))
+		return NULL;
+
+	return file;
+}
+
+
+static void verbose_print_sample(struct sfile * sf, vma_t pc, uint counter)
+{
+	char const * app = verbose_cookie(sf->app_cookie);
+	printf("0x%llx(%u): ", pc, counter);
+	if (sf->anon) {
+		printf("anon (tgid %u, 0x%llx-0x%llx), ",
+		       (unsigned int)sf->anon->tgid,
+		       sf->anon->start, sf->anon->end);
+	} else if (sf->kernel) {
+		printf("kern (name %s, 0x%llx-0x%llx), ", sf->kernel->name,
+		       sf->kernel->start, sf->kernel->end);
+	} else {
+		printf("%s(%llx), ", verbose_cookie(sf->cookie),  sf->cookie);
+	}
+	printf("app %s(%llx)", app, sf->app_cookie);
+}
+
+
+static void verbose_sample(struct transient const * trans, vma_t pc)
+{
+	printf("Sample ");
+	verbose_print_sample(trans->current, pc, trans->event);
+	printf("\n");
+}
+
+
+static void
+verbose_arc(struct transient const * trans, vma_t from, vma_t to)
+{
+	printf("Arc ");
+	verbose_print_sample(trans->current, from, trans->event);
+	printf(" -> 0x%llx", to);
+	printf("\n");
+}
+
+
+static void sfile_log_arc(struct transient const * trans)
+{
+	int err;
+	vma_t from = trans->pc;
+	vma_t to = trans->last_pc;
+	uint64_t key;
+	odb_t * file;
+
+	file = get_file(trans, 1);
+
+	/* absolute value -> offset */
+	if (trans->current->kernel)
+		from -= trans->current->kernel->start;
+
+	if (trans->last->kernel)
+		to -= trans->last->kernel->start;
+
+	if (trans->current->anon)
+		from -= trans->current->anon->start;
+
+	if (trans->last->anon)
+		to -= trans->last->anon->start;
+
+	if (varcs)
+		verbose_arc(trans, from, to);
+
+	if (!file) {
+		opd_stats[OPD_LOST_SAMPLEFILE]++;
+		return;
+	}
+
+	/* Possible narrowings to 32-bit value only. */
+	key = to & (0xffffffff);
+	key |= ((uint64_t)from) << 32;
+
+	err = odb_update_node(file, key);
+	if (err) {
+		fprintf(stderr, "%s: %s\n", __FUNCTION__, strerror(err));
+		abort();
+	}
+}
+
+
+void sfile_log_sample(struct transient const * trans)
+{
+	int err;
+	vma_t pc = trans->pc;
+	odb_t * file;
+
+	if (trans->tracing == TRACING_ON) {
+		/* can happen if kernel sample falls through the cracks,
+		 * see opd_put_sample() */
+		if (trans->last)
+			sfile_log_arc(trans);
+		return;
+	}
+
+	file = get_file(trans, 0);
+
+	/* absolute value -> offset */
+	if (trans->current->kernel)
+		pc -= trans->current->kernel->start;
+
+	if (trans->current->anon)
+		pc -= trans->current->anon->start;
+ 
+	if (vsamples)
+		verbose_sample(trans, pc);
+
+	if (!file) {
+		opd_stats[OPD_LOST_SAMPLEFILE]++;
+		return;
+	}
+
+	err = odb_update_node(file, (uint64_t)pc);
+	if (err) {
+		fprintf(stderr, "%s: %s\n", __FUNCTION__, strerror(err));
+		abort();
+	}
+}
+
+
+static int close_sfile(struct sfile * sf, void * data __attribute__((unused)))
+{
+	size_t i;
+
+	/* it's OK to close a non-open odb file */
+	for (i = 0; i < op_nr_counters; ++i)
+		odb_close(&sf->files[i]);
+
+	return 0;
+}
+
+
+static void kill_sfile(struct sfile * sf)
+{
+	close_sfile(sf, NULL);
+	list_del(&sf->hash);
+	list_del(&sf->lru);
+}
+
+
+static int sync_sfile(struct sfile * sf, void * data __attribute__((unused)))
+{
+	size_t i;
+
+	for (i = 0; i < op_nr_counters; ++i)
+		odb_sync(&sf->files[i]);
+
+	return 0;
+}
+
+
+static int is_sfile_kernel(struct sfile * sf, void * data __attribute__((unused)))
+{
+	return !!sf->kernel;
+}
+
+
+static int is_sfile_anon(struct sfile * sf, void * data)
+{
+	return sf->anon == data;
+}
+
+
+typedef int (*sfile_func)(struct sfile *, void *);
+
+static void
+for_one_sfile(struct sfile * sf, sfile_func func, void * data)
+{
+	size_t i;
+	int free_sf = func(sf, data);
+
+	for (i = 0; i < CG_HASH_SIZE; ++i) {
+		struct list_head * pos;
+		struct list_head * pos2;
+		list_for_each_safe(pos, pos2, &sf->cg_hash[i]) {
+			struct cg_entry * cg =
+				list_entry(pos, struct cg_entry, hash);
+			if (free_sf || func(&cg->to, data)) {
+				kill_sfile(&cg->to);
+				list_del(&cg->hash);
+				free(cg);
+			}
+		}
+	}
+
+	if (free_sf) {
+		kill_sfile(sf);
+		free(sf);
+	}
+}
+
+
+static void for_each_sfile(sfile_func func, void * data)
+{
+	struct list_head * pos;
+	struct list_head * pos2;
+
+	list_for_each_safe(pos, pos2, &lru_list) {
+		struct sfile * sf = list_entry(pos, struct sfile, lru);
+		for_one_sfile(sf, func, data);
+	}
+}
+
+
+void sfile_clear_kernel(void)
+{
+	for_each_sfile(is_sfile_kernel, NULL);
+}
+
+
+void sfile_clear_anon(struct anon_mapping * anon)
+{
+	for_each_sfile(is_sfile_anon, anon);
+}
+
+
+void sfile_sync_files(void)
+{
+	for_each_sfile(sync_sfile, NULL);
+}
+
+
+void sfile_close_files(void)
+{
+	for_each_sfile(close_sfile, NULL);
+}
+
+
+static int always_true(void)
+{
+	return 1;
+}
+
+
+#define LRU_AMOUNT 256
+
+/*
+ * Clear out older sfiles. Note the current sfiles we're using
+ * will not be present in this list, due to sfile_get/put() pairs
+ * around the caller of this.
+ */
+int sfile_lru_clear(void)
+{
+	struct list_head * pos;
+	struct list_head * pos2;
+	int amount = LRU_AMOUNT;
+
+	if (list_empty(&lru_list))
+		return 1;
+
+	list_for_each_safe(pos, pos2, &lru_list) {
+		struct sfile * sf;
+		if (!--amount)
+			break;
+		sf = list_entry(pos, struct sfile, lru);
+		for_one_sfile(sf, (sfile_func)always_true, NULL);
+	}
+
+	return 0;
+}
+
+
+void sfile_get(struct sfile * sf)
+{
+	if (sf)
+		list_del(&sf->lru);
+}
+
+
+void sfile_put(struct sfile * sf)
+{
+	if (sf)
+		list_add_tail(&sf->lru, &lru_list);
+}
+
+
+void sfile_init(void)
+{
+	size_t i = 0;
+
+	for (; i < HASH_SIZE; ++i)
+		list_init(&hashes[i]);
+}
diff --git a/daemon/opd_sfile.h b/daemon/opd_sfile.h
new file mode 100644
index 0000000..86d5025
--- /dev/null
+++ b/daemon/opd_sfile.h
@@ -0,0 +1,113 @@
+/**
+ * @file daemon/opd_sfile.h
+ * Management of sample files
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OPD_SFILE_H
+#define OPD_SFILE_H
+
+#include "opd_cookie.h"
+
+#include "odb.h"
+#include "op_hw_config.h"
+#include "op_types.h"
+#include "op_list.h"
+
+#include <sys/types.h>
+
+struct kernel_image;
+struct transient;
+
+#define CG_HASH_SIZE 16
+#define UNUSED_EMBEDDED_OFFSET ~0LLU
+
+/**
+ * Each set of sample files (where a set is over the physical counter
+ * types) will have one of these for it. We match against the
+ * descriptions here to find which sample DB file we need to modify.
+ *
+ * cg files are stored in the hash.
+ */
+struct sfile {
+	/** hash value for this sfile */
+	unsigned long hashval;
+	/** cookie value for the binary profiled */
+	cookie_t cookie;
+	/** cookie value for the application owner, INVALID_COOKIE if not set */
+	cookie_t app_cookie;
+	/** thread ID, -1 if not set */
+	pid_t tid;
+	/** thread group ID, -1 if not set */
+	pid_t tgid;
+	/** CPU number */
+	unsigned int cpu;
+	/** kernel image if applicable */
+	struct kernel_image * kernel;
+	/** anonymous mapping */
+	struct anon_mapping * anon;
+	/** embedded offset for Cell BE SPU */
+	uint64_t embedded_offset;
+
+	/** hash table link */
+	struct list_head hash;
+	/** lru list */
+	struct list_head lru;
+	/** true if this file should be ignored in profiles */
+	int ignored;
+	/** opened sample files */
+	odb_t files[OP_MAX_COUNTERS];
+	/** hash table of opened cg sample files */
+	struct list_head cg_hash[CG_HASH_SIZE];
+};
+
+/** a call-graph entry */
+struct cg_entry {
+	/** where arc is to */
+	struct sfile to;
+	/** next in the hash slot */
+	struct list_head hash;
+};
+
+/** clear any sfiles that are for the kernel */
+void sfile_clear_kernel(void);
+
+struct anon_mapping;
+
+/** clear any sfiles for the given anon mapping */
+void sfile_clear_anon(struct anon_mapping *);
+
+/** sync sample files */
+void sfile_sync_files(void);
+
+/** close sample files */
+void sfile_close_files(void);
+
+/** clear out a certain amount of LRU entries
+ * return non-zero if the lru is already empty */
+int sfile_lru_clear(void);
+
+/** remove a sfile from the lru list, protecting it from sfile_lru_clear() */
+void sfile_get(struct sfile * sf);
+
+/** add this sfile to lru list */
+void sfile_put(struct sfile * sf);
+
+/**
+ * Find the sfile for the current parameters. Note that is required
+ * that the PC value be set appropriately (needed for kernel images)
+ */
+struct sfile * sfile_find(struct transient const * trans);
+
+/** Log the sample in a previously located sfile. */
+void sfile_log_sample(struct transient const * trans);
+
+/** initialise hashes */
+void sfile_init(void);
+
+#endif /* OPD_SFILE_H */
diff --git a/daemon/opd_spu.c b/daemon/opd_spu.c
new file mode 100644
index 0000000..62a2c2b
--- /dev/null
+++ b/daemon/opd_spu.c
@@ -0,0 +1,176 @@
+/**
+ * @file daemon/opd_spu.c
+ * Processing the sample buffer for Cell BE SPU profile
+ *
+ * @remark Copyright 2007 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Maynard Johnson
+ * (C) Copyright IBM Corporation 2007
+ */
+
+#include "opd_interface.h"
+#include "opd_printf.h"
+#include "opd_sfile.h"
+#include "opd_stats.h"
+#include "opd_trans.h"
+#include "op_libiberty.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+struct spu_context_info {
+	pid_t tid;
+	pid_t tgid;
+	cookie_t app_cookie;
+	uint64_t embedded_offset;
+	cookie_t spu_cookie;
+};
+
+static struct spu_context_info * spu_context_cache;
+
+/* Forward declaration */
+static void process_spu_samples(struct transient * trans);
+
+void (*special_processor)(struct transient *);
+
+/*
+ * This function is called when the first value found in the
+ * buffer (after the beginning ESCAPE_CODE) is SPU_PROFILING_CODE.
+ * Once we get here, the rest of the processing of the buffer is
+ * Cell-SPU-specific, so we do not need to return until the
+ * trans.buffer is empty.
+ */
+void code_spu_profiling(struct transient * trans)
+{
+	/* Next value in buffer is the number of SPUs. */
+	unsigned long long num_spus = pop_buffer_value(trans);
+	/* Free the cache from previous run */
+	free(spu_context_cache);
+	spu_context_cache = xmalloc(sizeof(struct spu_context_info) * num_spus);
+	special_processor = process_spu_samples;
+	process_spu_samples(trans);
+}
+
+void code_spu_ctx_switch(struct transient * trans)
+{
+	clear_trans_current(trans);
+
+	if (!enough_remaining(trans, 6)) {
+		trans->remaining = 0;
+		return;
+	}
+
+	/* First value in the buffer for an SPU context switch is
+	 * the SPU number.  For SPU profiling, 'cpu' = 'spu'.
+	 */
+	trans->cpu = pop_buffer_value(trans);
+	trans->tid = pop_buffer_value(trans);
+	trans->tgid = pop_buffer_value(trans);
+	trans->app_cookie = pop_buffer_value(trans);
+
+	if (vmisc) {
+		char const * app = find_cookie(trans->app_cookie);
+		printf("SPU_CTX_SWITCH to tid %lu, tgid %lu, cookie %llx(%s)\n",
+		       (unsigned long)trans->tid, (unsigned long)trans->tgid,
+		       trans->app_cookie, app ? app : "none");
+	}
+
+	/* The trans->cookie will point to the binary file where the SPU ELF
+	 * can be found.  If the SPU ELF is embedded, it may be embedded in
+	 * either the executable application binary or a shared lib.  If shared
+	 * library, then trans->cookie will differ from the previously obtained
+	 * trans->app_cookie.  For the non-embedded case, trans->cookie always
+	 * points to a separate binary file.
+	 */
+	trans->cookie = pop_buffer_value(trans);
+	trans->embedded_offset = pop_buffer_value(trans);
+}
+
+
+static void cache_spu_context_info(struct transient * trans)
+{
+	int i = trans->cpu;
+	spu_context_cache[i].tid = trans->tid;
+	spu_context_cache[i].tgid = trans->tgid;
+	spu_context_cache[i].app_cookie = trans->app_cookie;
+	spu_context_cache[i].embedded_offset = trans->embedded_offset;
+	spu_context_cache[i].spu_cookie = trans->cookie;
+}
+
+static void update_trans_for_spu(struct transient * trans)
+{
+	int i = trans->cpu;
+	trans->tid = spu_context_cache[i].tid;
+	trans->tgid = spu_context_cache[i].tgid;
+	trans->app_cookie = spu_context_cache[i].app_cookie;
+	trans->embedded_offset = spu_context_cache[i].embedded_offset;
+	trans->cookie = spu_context_cache[i].spu_cookie;
+}
+#define SPU_NUM_MASK 0xFFFFFFFF00000000ULL
+#define SPU_CYCLES_COUNTER 0
+
+static void opd_put_spu_sample
+(struct transient * trans, unsigned long long pc)
+{
+	unsigned long spu_number = (pc & SPU_NUM_MASK) >> 32;
+	if (trans->cpu != spu_number) {
+		trans->cpu = spu_number;
+	        clear_trans_current(trans);
+		update_trans_for_spu(trans);
+	}
+	/* get the current sfile if needed */
+	if (!trans->current)
+		trans->current = sfile_find(trans);
+
+	if (trans->tracing != TRACING_ON)
+		trans->event = SPU_CYCLES_COUNTER;
+
+	trans->pc = (pc & ~SPU_NUM_MASK);
+	/* log the sample or arc */
+	sfile_log_sample(trans);
+
+	/* switch to trace mode */
+	if (trans->tracing == TRACING_START)
+		trans->tracing = TRACING_ON;
+
+	update_trans_last(trans);
+}
+
+/*
+ * This function processes SPU context switches and
+ * SPU program counter samples.  After processing a
+ * context switch (via handlers[code)), we cache the
+ * SPU context information that has been temporarily
+ * stored in trans.
+ */
+static void process_spu_samples(struct transient * trans)
+{
+	unsigned long long code;
+	trans->in_kernel = 0;
+	while (trans->remaining) {
+		code = pop_buffer_value(trans);
+
+		if (!is_escape_code(code)) {
+			opd_put_spu_sample(trans, code);
+			continue;
+		}
+
+		if (!trans->remaining) {
+			verbprintf(vmisc, "Dangling ESCAPE_CODE.\n");
+			opd_stats[OPD_DANGLING_CODE]++;
+			break;
+		}
+
+		/* started with ESCAPE_CODE, next is type */
+		code = pop_buffer_value(trans);
+
+		if (code >= LAST_CODE) {
+			fprintf(stderr, "Unknown code %llu\n", code);
+			abort();
+		}
+
+		handlers[code](trans);
+		cache_spu_context_info(trans);
+	}
+}
diff --git a/daemon/opd_stats.c b/daemon/opd_stats.c
new file mode 100644
index 0000000..ddb1940
--- /dev/null
+++ b/daemon/opd_stats.c
@@ -0,0 +1,85 @@
+/**
+ * @file daemon/opd_stats.c
+ * Management of daemon statistics
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include "opd_stats.h"
+#include "oprofiled.h"
+
+#include "op_get_time.h"
+
+#include <dirent.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+unsigned long opd_stats[OPD_MAX_STATS];
+
+/**
+ * print_if - print an integer value read from file filename,
+ * do nothing if the value read == -1 except if force is non-zero
+ */
+static void print_if(char const * fmt, char const * path, char const * filename, int force)
+{
+	int value = opd_read_fs_int(path, filename, 0);
+	if (value != -1 || force)
+		printf(fmt, value);
+}
+
+/**
+ * opd_print_stats - print out latest statistics
+ */
+void opd_print_stats(void)
+{
+	DIR * dir;
+	struct dirent * dirent;
+
+	printf("\n%s\n", op_get_time());
+	printf("Nr. sample dumps: %lu\n", opd_stats[OPD_DUMP_COUNT]);
+	printf("Nr. non-backtrace samples: %lu\n", opd_stats[OPD_SAMPLES]);
+	printf("Nr. kernel samples: %lu\n", opd_stats[OPD_KERNEL]);
+	printf("Nr. lost samples (no kernel/user): %lu\n", opd_stats[OPD_NO_CTX]);
+	printf("Nr. lost kernel samples: %lu\n", opd_stats[OPD_LOST_KERNEL]);
+	printf("Nr. incomplete code structs: %lu\n", opd_stats[OPD_DANGLING_CODE]);
+	printf("Nr. samples lost due to sample file open failure: %lu\n",
+		opd_stats[OPD_LOST_SAMPLEFILE]);
+	printf("Nr. samples lost due to no permanent mapping: %lu\n",
+		opd_stats[OPD_LOST_NO_MAPPING]);
+	print_if("Nr. event lost due to buffer overflow: %u\n",
+	       "/dev/oprofile/stats", "event_lost_overflow", 1);
+	print_if("Nr. samples lost due to no mapping: %u\n",
+	       "/dev/oprofile/stats", "sample_lost_no_mapping", 1);
+	print_if("Nr. backtraces skipped due to no file mapping: %u\n",
+	       "/dev/oprofile/stats", "bt_lost_no_mapping", 0);
+	print_if("Nr. samples lost due to no mm: %u\n",
+	       "/dev/oprofile/stats", "sample_lost_no_mm", 1);
+
+	if (!(dir = opendir("/dev/oprofile/stats/")))
+		goto out;
+	while ((dirent = readdir(dir))) {
+		int cpu_nr;
+		char path[256];
+		if (sscanf(dirent->d_name, "cpu%d", &cpu_nr) != 1)
+			continue;
+		snprintf(path, 256, "/dev/oprofile/stats/%s", dirent->d_name);
+
+		print_if("Nr. samples lost cpu buffer overflow: %u\n",
+		     path, "sample_lost_overflow", 1);
+		print_if("Nr. samples lost task exit: %u\n",
+		     path, "sample_lost_task_exit", 0);
+		print_if("Nr. samples received: %u\n",
+		     path, "sample_received", 1);
+		print_if("Nr. backtrace aborted: %u\n", 
+		     path, "backtrace_aborted", 0);
+		print_if("Nr. samples lost invalid pc: %u\n", 
+		     path, "sample_invalid_eip", 0);
+	}
+	closedir(dir);
+out:
+	fflush(stdout);
+}
diff --git a/daemon/opd_stats.h b/daemon/opd_stats.h
new file mode 100644
index 0000000..177b86a
--- /dev/null
+++ b/daemon/opd_stats.h
@@ -0,0 +1,31 @@
+/**
+ * @file daemon/opd_stats.h
+ * Management of daemon statistics
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OPD_STATS_H
+#define OPD_STATS_H
+
+extern unsigned long opd_stats[];
+
+enum {	OPD_SAMPLES, /**< nr. samples */
+	OPD_KERNEL, /**< nr. kernel samples */
+	OPD_PROCESS, /**< nr. userspace samples */
+	OPD_NO_CTX, /**< nr. samples lost due to not knowing if in the kernel or not */
+	OPD_LOST_KERNEL,  /**< nr. kernel samples lost */
+	OPD_LOST_SAMPLEFILE, /**< nr samples for which sample file can't be opened */
+	OPD_LOST_NO_MAPPING, /**< nr samples lost due to no mapping */
+	OPD_DUMP_COUNT, /**< nr. of times buffer is read */
+	OPD_DANGLING_CODE, /**< nr. partial code notifications (buffer overflow */
+	OPD_MAX_STATS /**< end of stats */
+};
+
+void opd_print_stats(void);
+
+#endif /* OPD_STATS_H */
diff --git a/daemon/opd_trans.c b/daemon/opd_trans.c
new file mode 100644
index 0000000..871e6e6
--- /dev/null
+++ b/daemon/opd_trans.c
@@ -0,0 +1,349 @@
+/**
+ * @file daemon/opd_trans.c
+ * Processing the sample buffer
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ * Modified by Aravind Menon for Xen
+ * These modifications are:
+ * Copyright (C) 2005 Hewlett-Packard Co.
+ *
+ * Modified by Maynard Johnson <maynardj@us.ibm.com>
+ * These modifications are:
+ * (C) Copyright IBM Corporation 2007
+ */
+
+#include "opd_trans.h"
+#include "opd_kernel.h"
+#include "opd_sfile.h"
+#include "opd_anon.h"
+#include "opd_stats.h"
+#include "opd_printf.h"
+#include "opd_interface.h"
+ 
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <errno.h>
+
+extern size_t kernel_pointer_size;
+
+
+void clear_trans_last(struct transient * trans)
+{
+	trans->last = NULL;
+	trans->last_anon = NULL;
+}
+
+
+void clear_trans_current(struct transient * trans)
+{
+	trans->current = NULL;
+	trans->anon = NULL;
+}
+
+
+uint64_t pop_buffer_value(struct transient * trans)
+{
+	uint64_t val;
+
+	if (!trans->remaining) {
+		fprintf(stderr, "BUG: popping empty buffer !\n");
+		abort();
+	}
+
+	if (kernel_pointer_size == 4) {
+		uint32_t const * lbuf = (void const *)trans->buffer;
+		val = *lbuf;
+	} else {
+		uint64_t const * lbuf = (void const *)trans->buffer;
+		val = *lbuf;
+	}
+
+	trans->remaining--;
+	trans->buffer += kernel_pointer_size;
+	return val;
+}
+
+
+int enough_remaining(struct transient * trans, size_t size)
+{
+	if (trans->remaining >= size)
+		return 1;
+
+	verbprintf(vmisc, "Dangling ESCAPE_CODE.\n");
+	opd_stats[OPD_DANGLING_CODE]++;
+	return 0;
+}
+
+
+static void opd_put_sample(struct transient * trans, unsigned long long pc)
+{
+	unsigned long long event;
+
+	if (!enough_remaining(trans, 1)) {
+		trans->remaining = 0;
+		return;
+	}
+
+	event = pop_buffer_value(trans);
+
+	if (trans->tracing != TRACING_ON)
+		trans->event = event;
+
+	trans->pc = pc;
+
+	/* sfile can change at each sample for kernel */
+	if (trans->in_kernel != 0)
+		clear_trans_current(trans);
+
+	if (!trans->in_kernel && trans->cookie == NO_COOKIE)
+		trans->anon = find_anon_mapping(trans);
+
+	/* get the current sfile if needed */
+	if (!trans->current)
+		trans->current = sfile_find(trans);
+
+	/*
+	 * can happen if kernel sample falls through the cracks, or if
+	 * it's a sample from an anon region we couldn't find
+	 */
+	if (!trans->current)
+		goto out;
+
+	/* FIXME: this logic is perhaps too harsh? */
+	if (trans->current->ignored || (trans->last && trans->last->ignored))
+		goto out;
+
+	/* log the sample or arc */
+	sfile_log_sample(trans);
+
+out:
+	/* switch to trace mode */
+	if (trans->tracing == TRACING_START)
+		trans->tracing = TRACING_ON;
+
+	update_trans_last(trans);
+}
+
+
+static void code_unknown(struct transient * trans __attribute__((unused)))
+{
+	fprintf(stderr, "Unknown code !\n");
+	abort();
+}
+
+
+static void code_ctx_switch(struct transient * trans)
+{
+	clear_trans_current(trans);
+
+	if (!enough_remaining(trans, 5)) {
+		trans->remaining = 0;
+		return;
+	}
+
+	trans->tid = pop_buffer_value(trans);
+	trans->app_cookie = pop_buffer_value(trans);
+	/* must be ESCAPE_CODE, CTX_TGID_CODE, tgid. Like this
+	 * because tgid was added later in a compatible manner.
+	 */
+	pop_buffer_value(trans);
+	pop_buffer_value(trans);
+	trans->tgid = pop_buffer_value(trans);
+
+	if (vmisc) {
+		char const * app = find_cookie(trans->app_cookie);
+		printf("CTX_SWITCH to tid %lu, tgid %lu, cookie %llx(%s)\n",
+		       (unsigned long)trans->tid, (unsigned long)trans->tgid,
+		       trans->app_cookie, app ? app : "none");
+	}
+}
+
+
+static void code_cpu_switch(struct transient * trans)
+{
+	clear_trans_current(trans);
+
+	if (!enough_remaining(trans, 1)) {
+		trans->remaining = 0;
+		return;
+	}
+
+	trans->cpu = pop_buffer_value(trans);
+	verbprintf(vmisc, "CPU_SWITCH to %lu\n", trans->cpu);
+}
+
+
+static void code_cookie_switch(struct transient * trans)
+{
+	clear_trans_current(trans);
+
+	if (!enough_remaining(trans, 1)) {
+		trans->remaining = 0;
+		return;
+	}
+
+	trans->cookie = pop_buffer_value(trans);
+
+	if (vmisc) {
+		char const * name = verbose_cookie(trans->cookie);
+		verbprintf(vmisc, "COOKIE_SWITCH to cookie %s(%llx)\n",
+		           name, trans->cookie);
+	}
+}
+
+
+static void code_kernel_enter(struct transient * trans)
+{
+	verbprintf(vmisc, "KERNEL_ENTER_SWITCH to kernel\n");
+	trans->in_kernel = 1;
+	clear_trans_current(trans);
+	/* subtlety: we must keep trans->cookie cached,
+	 * even though it's meaningless for the kernel -
+	 * we won't necessarily get a cookie switch on
+	 * kernel exit. See comments in opd_sfile.c
+	 */
+}
+
+
+static void code_user_enter(struct transient * trans)
+{
+	verbprintf(vmisc, "USER_ENTER_SWITCH to user-space\n");
+	trans->in_kernel = 0;
+	clear_trans_current(trans);
+	clear_trans_last(trans);
+}
+
+
+static void code_module_loaded(struct transient * trans __attribute__((unused)))
+{
+	verbprintf(vmodule, "MODULE_LOADED_CODE\n");
+	opd_reread_module_info();
+	clear_trans_current(trans);
+	clear_trans_last(trans);
+}
+
+
+/*
+ * This also implicitly signals the end of the previous
+ * trace, so we never explicitly set TRACING_OFF when
+ * processing a buffer.
+ */
+static void code_trace_begin(struct transient * trans)
+{
+	verbprintf(varcs, "TRACE_BEGIN\n");
+	trans->tracing = TRACING_START;
+}
+
+static void code_xen_enter(struct transient * trans)
+{
+	verbprintf(vmisc, "XEN_ENTER_SWITCH to xen\n");
+	trans->in_kernel = 1;
+	trans->current = NULL;
+	/* subtlety: we must keep trans->cookie cached, even though it's 
+	 * meaningless for Xen - we won't necessarily get a cookie switch 
+	 * on Xen exit. See comments in opd_sfile.c. It seems that we can 
+	 * get away with in_kernel = 1 as long as we supply the correct 
+	 * Xen image, and its address range in startup find_kernel_image 
+	 * is modified to look in the Xen image also
+	 */
+}
+
+extern void code_spu_profiling(struct transient * trans);
+extern void code_spu_ctx_switch(struct transient * trans);
+
+handler_t handlers[LAST_CODE + 1] = {
+	&code_unknown,
+	&code_ctx_switch,
+	&code_cpu_switch,
+	&code_cookie_switch,
+	&code_kernel_enter,
+ 	&code_user_enter,
+	&code_module_loaded,
+	/* tgid handled differently */
+	&code_unknown,
+	&code_trace_begin,
+	&code_unknown,
+ 	&code_xen_enter,
+#if defined(__powerpc__)
+	&code_spu_profiling,
+	&code_spu_ctx_switch,
+#endif
+	&code_unknown,
+};
+
+extern void (*special_processor)(struct transient *);
+
+void opd_process_samples(char const * buffer, size_t count)
+{
+	struct transient trans = {
+		.buffer = buffer,
+		.remaining = count,
+		.tracing = TRACING_OFF,
+		.current = NULL,
+		.last = NULL,
+		.cookie = INVALID_COOKIE,
+		.app_cookie = INVALID_COOKIE,
+		.anon = NULL,
+		.last_anon = NULL,
+		.pc = 0,
+		.last_pc = 0,
+		.event = 0,
+		.in_kernel = -1,
+		.cpu = -1,
+		.tid = -1,
+		.embedded_offset = UNUSED_EMBEDDED_OFFSET,
+		.tgid = -1
+	};
+
+	/* FIXME: was uint64_t but it can't compile on alpha where uint64_t
+	 * is an unsigned long and below the printf("..." %llu\n", code)
+	 * generate a warning, this look like a stopper to use c98 types :/
+	 */
+	unsigned long long code;
+
+	if (special_processor) {
+		special_processor(&trans);
+		return;
+	}
+
+    int i;
+
+    for (i = 0; i < count && i < 200; i++) {
+        verbprintf(vmisc, "buffer[%d] is %x\n", i, buffer[i]);
+    }
+
+	while (trans.remaining) {
+		code = pop_buffer_value(&trans);
+
+        verbprintf(vmisc, "In opd_process_samples (code is %lld)\n", code);
+
+		if (!is_escape_code(code)) {
+			opd_put_sample(&trans, code);
+			continue;
+		}
+
+		if (!trans.remaining) {
+			verbprintf(vmisc, "Dangling ESCAPE_CODE.\n");
+			opd_stats[OPD_DANGLING_CODE]++;
+			break;
+		}
+
+		// started with ESCAPE_CODE, next is type
+		code = pop_buffer_value(&trans);
+	
+        verbprintf(vmisc, "next code is %lld\n", code);
+		if (code >= LAST_CODE) {
+			fprintf(stderr, "Unknown code %llu\n", code);
+			abort();
+		}
+
+		handlers[code](&trans);
+	}
+}
diff --git a/daemon/opd_trans.h b/daemon/opd_trans.h
new file mode 100644
index 0000000..ab4e816
--- /dev/null
+++ b/daemon/opd_trans.h
@@ -0,0 +1,85 @@
+/**
+ * @file daemon/opd_trans.h
+ * Processing the sample buffer
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ *
+ * Modified by Maynard Johnson <maynardj@us.ibm.com>
+ * These modifications are:
+ * (C) Copyright IBM Corporation 2007
+ */
+
+#ifndef OPD_TRANS_H
+#define OPD_TRANS_H
+
+#include "opd_cookie.h"
+#include "op_types.h"
+
+#include <stdint.h>
+
+struct sfile;
+struct anon_mapping;
+
+enum tracing_type {
+	TRACING_OFF,
+	TRACING_START,
+	TRACING_ON
+};
+
+/**
+ * Transient values used for parsing the event buffer.
+ * Note that these are reset for each buffer read, but
+ * that should be ok as in the kernel, cpu_buffer_reset()
+ * ensures that a correct context starts off the buffer.
+ */
+struct transient {
+	char const * buffer;
+	size_t remaining;
+	enum tracing_type tracing;
+	struct sfile * current;
+	struct sfile * last;
+	struct anon_mapping * anon;
+	struct anon_mapping * last_anon;
+	cookie_t cookie;
+	cookie_t app_cookie;
+	vma_t pc;
+	vma_t last_pc;
+	unsigned long event;
+	int in_kernel;
+	unsigned long cpu;
+	pid_t tid;
+	pid_t tgid;
+	uint64_t embedded_offset;
+};
+
+typedef void (*handler_t)(struct transient *);
+extern handler_t handlers[];
+
+uint64_t pop_buffer_value(struct transient * trans);
+int enough_remaining(struct transient * trans, size_t size);
+static inline void update_trans_last(struct transient * trans)
+{
+	trans->last = trans->current;
+	trans->last_anon = trans->anon;
+	trans->last_pc = trans->pc;
+}
+
+extern size_t kernel_pointer_size;
+static inline int is_escape_code(uint64_t code)
+{
+	return kernel_pointer_size == 4 ? code == ~0LU : code == ~0LLU;
+}
+
+void opd_process_samples(char const * buffer, size_t count);
+
+/** used when we need to clear data that's been freed */
+void clear_trans_last(struct transient * trans);
+
+/** used when we need to clear data that's been freed */
+void clear_trans_current(struct transient * trans);
+
+#endif /* OPD_TRANS_H */
diff --git a/daemon/oprofiled.c b/daemon/oprofiled.c
new file mode 100644
index 0000000..ec2ea1b
--- /dev/null
+++ b/daemon/oprofiled.c
@@ -0,0 +1,522 @@
+/**
+ * @file daemon/oprofiled.c
+ * Initialisation and setup
+ *
+ * @remark Copyright 2002, 2003 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ * Modified by Aravind Menon for Xen
+ * These modifications are:
+ * Copyright (C) 2005 Hewlett-Packard Co.
+ */
+
+#include "config.h"
+ 
+#include "oprofiled.h"
+#include "opd_printf.h"
+#include "opd_events.h"
+
+#include "op_config.h"
+#include "op_version.h"
+#include "op_hw_config.h"
+#include "op_libiberty.h"
+#include "op_file.h"
+#include "op_abi.h"
+#include "op_string.h"
+#include "op_cpu_type.h"
+#include "op_popt.h"
+#include "op_lockfile.h"
+#include "op_list.h"
+#include "op_fileio.h"
+
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+#include <dirent.h>
+#include <limits.h>
+
+sig_atomic_t signal_alarm;
+sig_atomic_t signal_hup;
+sig_atomic_t signal_term;
+sig_atomic_t signal_child;
+sig_atomic_t signal_usr1;
+sig_atomic_t signal_usr2;
+
+uint op_nr_counters;
+op_cpu cpu_type;
+int vsfile;
+int vsamples;
+int varcs;
+int vmodule;
+int vmisc;
+int separate_lib;
+int separate_kernel;
+int separate_thread;
+int separate_cpu;
+int no_vmlinux;
+char * vmlinux;
+char * kernel_range;
+char * session_dir;
+int no_xen;
+char * xenimage;
+char * xen_range;
+static char * verbose;
+static char * binary_name_filter;
+static char * events;
+static int showvers;
+static struct oprofiled_ops * opd_ops;
+extern struct oprofiled_ops opd_24_ops;
+extern struct oprofiled_ops opd_26_ops;
+
+#define OPD_IMAGE_FILTER_HASH_SIZE 32
+static struct list_head images_filter[OPD_IMAGE_FILTER_HASH_SIZE];
+
+static struct poptOption options[] = {
+	{ "session-dir", 0, POPT_ARG_STRING, &session_dir, 0, "place sample database in dir instead of default location", "/var/lib/oprofile", },
+	{ "kernel-range", 'r', POPT_ARG_STRING, &kernel_range, 0, "Kernel VMA range", "start-end", },
+	{ "vmlinux", 'k', POPT_ARG_STRING, &vmlinux, 0, "vmlinux kernel image", "file", },
+	{ "no-vmlinux", 0, POPT_ARG_NONE, &no_vmlinux, 0, "vmlinux kernel image file not available", NULL, },
+	{ "xen-range", 0, POPT_ARG_STRING, &xen_range, 0, "Xen VMA range", "start-end", },
+	{ "xen-image", 0, POPT_ARG_STRING, &xenimage, 0, "Xen image", "file", },
+	{ "image", 0, POPT_ARG_STRING, &binary_name_filter, 0, "image name filter", "profile these comma separated image" },
+	{ "separate-lib", 0, POPT_ARG_INT, &separate_lib, 0, "separate library samples for each distinct application", "[0|1]", },
+	{ "separate-kernel", 0, POPT_ARG_INT, &separate_kernel, 0, "separate kernel samples for each distinct application", "[0|1]", },
+	{ "separate-thread", 0, POPT_ARG_INT, &separate_thread, 0, "thread-profiling mode", "[0|1]" },
+	{ "separate-cpu", 0, POPT_ARG_INT, &separate_cpu, 0, "separate samples for each CPU", "[0|1]" },
+	{ "events", 'e', POPT_ARG_STRING, &events, 0, "events list", "[events]" },
+	{ "version", 'v', POPT_ARG_NONE, &showvers, 0, "show version", NULL, },
+	{ "verbose", 'V', POPT_ARG_STRING, &verbose, 0, "be verbose in log file", "all,sfile,arcs,samples,module,misc", },
+	POPT_AUTOHELP
+	{ NULL, 0, 0, NULL, 0, NULL, NULL, },
+};
+ 
+
+void opd_open_logfile(void)
+{
+	if (open(op_log_file, O_WRONLY|O_CREAT|O_NOCTTY|O_APPEND, 0644) == -1) {
+		perror("oprofiled: couldn't re-open stdout: ");
+		exit(EXIT_FAILURE);
+	}
+
+	if (dup2(1, 2) == -1) {
+		perror("oprofiled: couldn't dup stdout to stderr: ");
+		exit(EXIT_FAILURE);
+	}
+}
+ 
+
+/**
+ * opd_fork - fork and return as child
+ *
+ * fork() and exit the parent with _exit().
+ * Failure is fatal.
+ */
+static void opd_fork(void)
+{
+	switch (fork()) {
+		case -1:
+			perror("oprofiled: fork() failed: ");
+			exit(EXIT_FAILURE);
+			break;
+		case 0:
+			break;
+		default:
+			/* parent */
+			_exit(EXIT_SUCCESS);
+			break;
+	}
+}
+
+ 
+static void opd_go_daemon(void)
+{
+	opd_fork();
+
+	if (chdir(op_session_dir)) {
+		fprintf(stderr, "oprofiled: opd_go_daemon: couldn't chdir to %s: %s",
+			op_session_dir, strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	if (setsid() < 0) {
+		perror("oprofiled: opd_go_daemon: couldn't setsid: ");
+		exit(EXIT_FAILURE);
+	}
+
+	opd_fork();
+}
+
+
+static void opd_write_abi(void)
+{
+	char * cbuf;
+ 
+	cbuf = xmalloc(strlen(op_session_dir) + 5);
+	strcpy(cbuf, op_session_dir);
+	strcat(cbuf, "/abi");
+	op_write_abi_to_file(cbuf);
+	free(cbuf);
+}
+
+
+/**
+ * opd_alarm - sync files and report stats
+ */
+static void opd_alarm(int val __attribute__((unused)))
+{
+	signal_alarm = 1;
+}
+ 
+
+/* re-open logfile for logrotate */
+static void opd_sighup(int val __attribute__((unused)))
+{
+	signal_hup = 1;
+}
+
+
+static void opd_sigterm(int val __attribute__((unused)))
+{
+	signal_term = 1;
+}
+
+static void opd_sigchild(int val __attribute__((unused)))
+{
+	signal_child = 1;
+}
+ 
+
+static void opd_sigusr1(int val __attribute__((unused)))
+{
+	signal_usr1 = 1;
+}
+
+ 
+static void opd_sigusr2(int val __attribute__((unused)))
+{
+	signal_usr2 = 1;
+}
+
+
+static void opd_setup_signals(void)
+{
+	struct sigaction act;
+ 
+	act.sa_handler = opd_alarm;
+	act.sa_flags = 0;
+	sigemptyset(&act.sa_mask);
+
+	if (sigaction(SIGALRM, &act, NULL)) {
+		perror("oprofiled: install of SIGALRM handler failed: ");
+		exit(EXIT_FAILURE);
+	}
+
+	act.sa_handler = opd_sighup;
+	act.sa_flags = 0;
+	sigemptyset(&act.sa_mask);
+	sigaddset(&act.sa_mask, SIGALRM);
+
+	if (sigaction(SIGHUP, &act, NULL)) {
+		perror("oprofiled: install of SIGHUP handler failed: ");
+		exit(EXIT_FAILURE);
+	}
+
+	act.sa_handler = opd_sigterm;
+	act.sa_flags = 0;
+	sigemptyset(&act.sa_mask);
+	sigaddset(&act.sa_mask, SIGTERM);
+
+	if (sigaction(SIGTERM, &act, NULL)) {
+		perror("oprofiled: install of SIGTERM handler failed: ");
+		exit(EXIT_FAILURE);
+	}
+
+	act.sa_handler = opd_sigchild;
+	act.sa_flags = 0;
+	sigemptyset(&act.sa_mask);
+	sigaddset(&act.sa_mask, SIGCHLD);
+
+	if (sigaction(SIGCHLD, &act, NULL)) {
+		perror("oprofiled: install of SIGCHLD handler failed: ");
+		exit(EXIT_FAILURE);
+	}
+
+	act.sa_handler = opd_sigusr1;
+	act.sa_flags = 0;
+	sigemptyset(&act.sa_mask);
+	sigaddset(&act.sa_mask, SIGTERM);
+
+	if (sigaction(SIGUSR1, &act, NULL)) {
+		perror("oprofiled: install of SIGUSR1 handler failed: ");
+		exit(EXIT_FAILURE);
+	}
+
+	act.sa_handler = opd_sigusr2;
+	act.sa_flags = 0;
+	sigemptyset(&act.sa_mask);
+	sigaddset(&act.sa_mask, SIGTERM);
+
+	if (sigaction(SIGUSR2, &act, NULL)) {
+		perror("oprofiled: install of SIGUSR2 handler failed: ");
+		exit(EXIT_FAILURE);
+	}
+}
+
+
+struct opd_hashed_name {
+	char * name;
+	struct list_head next;
+};
+
+
+static void add_image_filter(char const * name)
+{
+	size_t hash;
+	struct opd_hashed_name * elt = xmalloc(sizeof(struct opd_hashed_name));
+	elt->name = xmalloc(PATH_MAX);
+	if (!realpath(name, elt->name)) {
+		free(elt->name);
+		free(elt);
+		return;
+	}
+	hash = op_hash_string(elt->name);
+	verbprintf(vmisc, "Adding to image filter: \"%s\"\n", elt->name);
+	list_add(&elt->next, &images_filter[hash % OPD_IMAGE_FILTER_HASH_SIZE]);
+}
+
+
+static void opd_parse_image_filter(void)
+{
+	size_t i;
+	char const * last = binary_name_filter;
+	char const * cur = binary_name_filter;
+
+	if (!binary_name_filter)
+		return;
+
+	for (i = 0; i < OPD_IMAGE_FILTER_HASH_SIZE; ++i)
+		list_init(&images_filter[i]);
+
+	while ((cur = strchr(last, ',')) != NULL) {
+		char * tmp = op_xstrndup(last, cur - last);
+		add_image_filter(tmp);
+		free(tmp);
+		last = cur + 1;
+	}
+	add_image_filter(last);
+}
+
+
+int is_image_ignored(char const * name)
+{
+	size_t hash;
+	struct list_head * pos;
+
+	if (!binary_name_filter)
+		return 0;
+	
+	hash = op_hash_string(name);
+
+	list_for_each(pos, &images_filter[hash % OPD_IMAGE_FILTER_HASH_SIZE]) {
+		struct opd_hashed_name * hashed_name =
+			list_entry(pos, struct opd_hashed_name, next);
+		if (!strcmp(hashed_name->name, name))
+			return 0;
+	}
+
+	return 1;
+}
+
+
+/** return the int in the given oprofilefs file */
+int opd_read_fs_int(char const * path, char const * name, int fatal)
+{
+	char filename[PATH_MAX + 1];
+	snprintf(filename, PATH_MAX, "%s/%s", path, name);
+	return op_read_int_from_file(filename, fatal);
+}
+
+
+static void opd_handle_verbose_option(char const * name)
+{
+	if (!strcmp(name, "all")) {
+		vsfile = 1;
+		vsamples = 1;
+		varcs = 1;
+		vmodule = 1;
+		vmisc = 1;
+	} else if (!strcmp(name, "sfile")) {
+		vsfile = 1;
+	} else if (!strcmp(name, "arcs")) {
+		varcs = 1;
+	} else if (!strcmp(name, "samples")) {
+		vsamples = 1;
+	} else if (!strcmp(name, "module")) {
+		vmodule = 1;
+	} else if (!strcmp(name, "misc")) {
+		vmisc = 1;
+	} else {
+		fprintf(stderr, "unknown verbose options\n");
+		exit(EXIT_FAILURE);
+	}
+}
+
+static void opd_parse_verbose(void)
+{
+	char const * last = verbose;
+	char const * cur = verbose;
+
+	if (!verbose)
+		return;
+
+	while ((cur = strchr(last, ',')) != NULL) {
+		char * tmp = op_xstrndup(last, cur - last);
+		opd_handle_verbose_option(tmp);
+		free(tmp);
+		last = cur + 1;
+	}
+	opd_handle_verbose_option(last);
+}
+
+
+static void opd_options(int argc, char const * argv[])
+{
+	poptContext optcon;
+	char * tmp;
+
+	optcon = op_poptGetContext(NULL, argc, argv, options, 0);
+
+	if (showvers)
+		show_version(argv[0]);
+
+	opd_parse_verbose();
+
+	if (separate_kernel)
+		separate_lib = 1;
+
+	cpu_type = op_get_cpu_type();
+	op_nr_counters = op_get_nr_counters(cpu_type);
+
+	if (!no_vmlinux) {
+		if (!vmlinux || !strcmp("", vmlinux)) {
+			fprintf(stderr, "oprofiled: no vmlinux specified.\n");
+			poptPrintHelp(optcon, stderr, 0);
+			exit(EXIT_FAILURE);
+		}
+
+		/* canonicalise vmlinux filename. fix #637805 */
+		tmp = xmalloc(PATH_MAX);
+		if (realpath(vmlinux, tmp))
+			vmlinux = tmp;
+		else
+			free(tmp);
+
+		if (!kernel_range || !strcmp("", kernel_range)) {
+			fprintf(stderr, "oprofiled: no kernel VMA range specified.\n");
+			poptPrintHelp(optcon, stderr, 0);
+			exit(EXIT_FAILURE);
+		}
+	}
+
+	if (events == NULL) {
+		fprintf(stderr, "oprofiled: no events specified.\n");
+		poptPrintHelp(optcon, stderr, 0);
+		exit(EXIT_FAILURE);
+	}
+
+	if (!xenimage || !strcmp("", xenimage)) {
+		no_xen = 1;
+	} else {
+		no_xen = 0;
+
+		/* canonicalise xen image filename. */
+		tmp = xmalloc(PATH_MAX);
+		if (realpath(xenimage, tmp))
+			xenimage = tmp;
+		else
+			free(tmp);
+
+		if (!xen_range || !strcmp("", xen_range)) {
+			fprintf(stderr, "oprofiled: no Xen VMA range specified.\n");
+			poptPrintHelp(optcon, stderr, 0);
+			exit(EXIT_FAILURE);
+		}
+	}
+
+	opd_parse_events(events);
+
+	opd_parse_image_filter();
+
+	poptFreeContext(optcon);
+}
+
+
+/* determine what kernel we're running and which daemon
+ * to use
+ */
+static struct oprofiled_ops * get_ops(void)
+{
+	switch (op_get_interface()) {
+#ifndef ANDROID
+		case OP_INTERFACE_24:
+			printf("Using 2.4 OProfile kernel interface.\n");
+			return &opd_24_ops;
+#endif
+		case OP_INTERFACE_26:
+			printf("Using 2.6+ OProfile kernel interface.\n");
+			return &opd_26_ops;
+		default:
+			break;
+	}
+
+	fprintf(stderr, "Couldn't determine kernel version.\n");
+	exit(EXIT_FAILURE);
+	return NULL;
+}
+
+
+int main(int argc, char const * argv[])
+{
+	int err;
+	struct rlimit rlim = { 2048, 2048 };
+
+	opd_options(argc, argv);
+	init_op_config_dirs(session_dir);
+
+	opd_setup_signals();
+
+	err = setrlimit(RLIMIT_NOFILE, &rlim);
+	if (err)
+		perror("warning: could not set RLIMIT_NOFILE to 2048: ");
+
+	opd_write_abi();
+
+	opd_ops = get_ops();
+
+	opd_ops->init();
+
+	opd_go_daemon();
+
+	/* clean up every 10 minutes */
+	alarm(60 * 10);
+
+	if (op_write_lock_file(op_lock_file)) {
+		fprintf(stderr, "oprofiled: could not create lock file %s\n",
+			op_lock_file);
+		exit(EXIT_FAILURE);
+	}
+
+	opd_ops->start();
+
+	opd_ops->exit();
+
+	return 0;
+}
diff --git a/daemon/oprofiled.h b/daemon/oprofiled.h
new file mode 100644
index 0000000..b319df1
--- /dev/null
+++ b/daemon/oprofiled.h
@@ -0,0 +1,69 @@
+/**
+ * @file daemon/oprofiled.h
+ * Initialisation and setup
+ *
+ * @remark Copyright 2002, 2003 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ * Modified by Aravind Menon for Xen
+ * These modifications are:
+ * Copyright (C) 2005 Hewlett-Packard Co.
+ */
+
+#ifndef OPROFILED_H
+
+#include <signal.h>
+
+struct oprofiled_ops {
+	void (*init)(void);
+	void (*start)(void);
+	void (*exit)(void);
+};
+
+
+/**
+ * opd_open_logfile - open the log file
+ *
+ * Open the logfile on stdout and stderr. This function
+ * assumes that 1 and 2 are the lowest close()d file
+ * descriptors. Failure to open on either descriptor is
+ * a fatal error.
+ */
+void opd_open_logfile(void);
+
+ 
+/**
+ * is_image_ignored - check if we must ignore this image
+ * @param name the name to check
+ *
+ * Return true if the image should not be profiled
+ */
+int is_image_ignored(char const * name);
+
+/** return the int in the given oprofilefs file, error is fatal if !is_fatal */
+int opd_read_fs_int(char const * path, char const * name, int is_fatal);
+
+
+/** global variable positioned by signal handler */
+extern sig_atomic_t signal_alarm;
+extern sig_atomic_t signal_hup;
+extern sig_atomic_t signal_term;
+extern sig_atomic_t signal_child;
+extern sig_atomic_t signal_usr1;
+extern sig_atomic_t signal_usr2;
+
+extern unsigned int op_nr_counters;
+extern int separate_lib;
+extern int separate_kernel;
+extern int separate_thread;
+extern int separate_cpu;
+extern int no_vmlinux;
+extern char * vmlinux;
+extern char * kernel_range;
+extern int no_xen;
+extern char * xenimage;
+extern char * xen_range;
+
+#endif /* OPROFILED_H */
diff --git a/libabi/Android.mk b/libabi/Android.mk
new file mode 100644
index 0000000..3213c1e
--- /dev/null
+++ b/libabi/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= op_abi.c
+
+LOCAL_MODULE := libabi
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/.. \
+	$(LOCAL_PATH)/../libdb \
+	$(LOCAL_PATH)/../libutil \
+	$(LOCAL_PATH)/../libop
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/libabi/abi.cpp b/libabi/abi.cpp
new file mode 100644
index 0000000..04a6b2d
--- /dev/null
+++ b/libabi/abi.cpp
@@ -0,0 +1,75 @@
+/**
+ * @file abi.cpp
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Graydon Hoare
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include "abi.h"
+#include "op_abi.h"
+#include "odb.h"
+#include "op_sample_file.h"
+
+#include <iostream>
+#include <cassert>
+
+using namespace std;
+
+typedef map<string, int> abi_map;
+typedef abi_map::const_iterator abi_iter;
+
+abi_exception::abi_exception(string const d) : desc(d) {}
+
+
+abi::abi()
+{
+	op_abi_entry const * entry = get_abi();
+	for ( ; entry->name != 0; ++entry)
+		slots[entry->name] = entry->offset;
+
+	slots["little_endian"] = op_little_endian();
+}
+
+
+int abi::need(string const key) const throw (abi_exception)
+{
+	if (slots.find(key) != slots.end())
+		return slots.find(key)->second;
+	else
+		throw abi_exception(string("missing ABI key: ") + key);
+}
+
+
+bool abi::operator==(abi const & other) const
+{
+	return slots == other.slots;
+}
+
+
+ostream & operator<<(ostream & o, abi const & abi)
+{
+	abi_iter i = abi.slots.begin();
+	abi_iter e = abi.slots.end();
+
+	for (; i != e; ++i)
+		o << i->first << " " << i->second << endl;
+
+	return o;
+}
+
+
+istream & operator>>(istream & i, abi & abi)
+{
+	string key;
+	int val;
+	abi.slots.clear();
+
+	while(i >> key >> val)
+		abi.slots[key] = val;
+
+	return i;
+}
diff --git a/libabi/abi.h b/libabi/abi.h
new file mode 100644
index 0000000..8b63e7f
--- /dev/null
+++ b/libabi/abi.h
@@ -0,0 +1,42 @@
+/**
+ * @file abi.h
+ *
+ * Contains internal ABI management class
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Graydon Hoare
+ */
+
+#ifndef OPROF_ABI_H
+#define OPROF_ABI_H
+ 
+#include <string>
+#include <map>
+#include <iosfwd>
+
+struct abi_exception : std::exception {
+	std::string const desc;
+ 
+	explicit abi_exception(std::string const d);
+ 
+	~abi_exception() throw() {}
+};
+
+
+class abi {
+public:
+	abi();
+
+	int need(std::string const key) const throw (abi_exception);
+
+	bool operator==(abi const & other) const;
+	friend std::ostream & operator<<(std::ostream & o, abi const & abi);
+	friend std::istream & operator>>(std::istream & i, abi & abi);
+
+private:
+	std::map<std::string, int> slots;
+};
+
+#endif // OPROF_ABI_H
diff --git a/libabi/op_abi.c b/libabi/op_abi.c
new file mode 100644
index 0000000..283e3ff
--- /dev/null
+++ b/libabi/op_abi.c
@@ -0,0 +1,94 @@
+/**
+ * @file op_abi.c
+ * This file contains a simple C interface to the ABI-describing functionality,
+ * the majority of which is implemented in C++. This is the file which is 
+ * intended for use in files outside the /libabi directory.
+ *
+ * @remark Copyright 2002, 2005 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Graydon Hoare
+ * @author Philippe Elie
+ */
+
+#include "op_abi.h"
+#include "odb.h"
+#include "op_sample_file.h"
+
+#include <stdio.h>
+#include <stddef.h>
+#include <assert.h>
+
+static struct op_abi_entry const abi_entries[] = {
+	{ "sizeof_double", sizeof(double) },
+	{ "sizeof_time_t", sizeof(time_t) },
+	{ "sizeof_u8", sizeof(u8) },
+	{ "sizeof_u32", sizeof(u32) },
+	{ "sizeof_int", sizeof(int) },
+	{ "sizeof_unsigned_int", sizeof(unsigned int) },
+	{ "sizeof_odb_key_t", sizeof(odb_key_t) },
+	{ "sizeof_odb_index_t", sizeof(odb_index_t) },
+	{ "sizeof_odb_value_t", sizeof(odb_value_t) },
+	{ "sizeof_odb_node_nr_t", sizeof(odb_node_nr_t) },
+	{ "sizeof_odb_descr_t", sizeof(odb_descr_t) },
+	{ "sizeof_odb_node_t", sizeof(odb_node_t) },
+	{ "sizeof_struct_opd_header", sizeof(struct opd_header) },
+	
+	{ "offsetof_node_key", offsetof(odb_node_t, key) },
+	{ "offsetof_node_value", offsetof(odb_node_t, value) },
+	{ "offsetof_node_next", offsetof(odb_node_t, next) },
+	
+	{ "offsetof_descr_size", offsetof(odb_descr_t, size) },
+	{ "offsetof_descr_current_size", offsetof(odb_descr_t, current_size) },
+	
+	{ "offsetof_header_magic", offsetof(struct opd_header, magic) },
+	{ "offsetof_header_version", offsetof(struct opd_header, version) },
+	{ "offsetof_header_cpu_type", offsetof(struct opd_header, cpu_type) },
+	{ "offsetof_header_ctr_event", offsetof(struct opd_header, ctr_event) },
+	{ "offsetof_header_ctr_um", offsetof(struct opd_header, ctr_um) },
+	{ "offsetof_header_ctr_count", offsetof(struct opd_header, ctr_count) },
+	{ "offsetof_header_is_kernel", offsetof(struct opd_header, is_kernel) },
+	{ "offsetof_header_cpu_speed", offsetof(struct opd_header, cpu_speed) },
+	{ "offsetof_header_mtime", offsetof(struct opd_header, mtime) },
+	{ "offsetof_header_cg_to_is_kernel", offsetof(struct opd_header, cg_to_is_kernel), },
+	{ "offsetof_header_anon_start", offsetof(struct opd_header, anon_start) },
+	{ "offsetof_header_cg_to_anon_start", offsetof(struct opd_header, cg_to_anon_start) },
+	
+	{ NULL, 0 },
+};
+
+
+struct op_abi_entry const * get_abi(void)
+{
+	return abi_entries;
+}
+
+
+int op_little_endian(void)
+{
+	unsigned int probe = 0xff;
+	size_t sz = sizeof(unsigned int);
+	unsigned char * probe_byte = (unsigned char *)&probe;
+
+	assert(probe_byte[0] == 0xff || probe_byte[sz - 1] == 0xff);
+
+	return probe_byte[0] == 0xff;
+}
+
+
+int op_write_abi_to_file(char const * abi_file)
+{
+	FILE * fp;
+	struct op_abi_entry const * abi_entry;
+
+	if ((fp = fopen(abi_file, "w")) == NULL)
+		return 0;
+
+	for (abi_entry = get_abi() ; abi_entry->name != NULL; ++abi_entry)
+		fprintf(fp, "%s %u\n", abi_entry->name, abi_entry->offset);
+	fprintf(fp, "little_endian %d\n", op_little_endian());
+
+	fclose(fp);
+
+	return 1;
+}
diff --git a/libabi/op_abi.h b/libabi/op_abi.h
new file mode 100644
index 0000000..fbd337b
--- /dev/null
+++ b/libabi/op_abi.h
@@ -0,0 +1,43 @@
+/**
+ * @file op_abi.h
+ * This file contains a simple C interface to the ABI-describing functionality,
+ * the majority of which is implemented in C++. this is the file which is 
+ * intended for use in files outside the /libabi directory.
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Graydon Hoare
+ * @author Philippe Elie
+ */
+
+#ifndef OP_ABI_H
+#define OP_ABI_H
+
+struct op_abi_entry {
+	char const * name;
+	/// offset or size of the named entry
+	int offset;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// return array is terminated by a NULL entry in name field
+struct op_abi_entry const * get_abi(void);
+
+/// return non zero if the abi is little endian
+int op_little_endian(void);
+
+/**
+ * Write current abi to file.
+ * return 1 on success, 0 on failure
+ */
+int op_write_abi_to_file(char const * abi_file);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // OP_ABI_H
diff --git a/libabi/opimport.cpp b/libabi/opimport.cpp
new file mode 100644
index 0000000..57f74a7
--- /dev/null
+++ b/libabi/opimport.cpp
@@ -0,0 +1,225 @@
+/**
+ * @file opimport.cpp
+ * Import sample files from other ABI
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Graydon Hoare
+ */
+
+#include "abi.h"
+#include "odb.h"
+#include "popt_options.h"
+#include "op_sample_file.h"
+
+#include <fstream>
+#include <iostream>
+#include <vector>
+#include <cassert>
+#include <cstring>
+#include <cstdlib>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <cstdlib>
+#include <cstring>
+
+using namespace std;
+
+namespace {
+	string output_filename;
+	string abi_filename;
+	bool verbose;
+	bool force;
+};
+
+
+popt::option options_array[] = {
+	popt::option(verbose, "verbose", 'V', "verbose output"),
+	popt::option(output_filename, "output", 'o', "output to file", "filename"),
+	popt::option(abi_filename, "abi", 'a', "abi description", "filename"),
+	popt::option(force, "force", 'f', "force conversion, even if identical")
+};
+
+
+struct extractor {
+
+	abi const & theabi;
+
+	unsigned char const * begin;
+	unsigned char const * end;
+	bool little_endian;
+
+	explicit
+	extractor(abi const & a, unsigned char const * src, size_t len)
+		: theabi(a), begin(src), end(src + len) {
+		little_endian = theabi.need(string("little_endian")) == 1;
+		if (verbose) {
+			cerr << "source byte order is: "
+			     << string(little_endian ? "little" : "big")
+			     << " endian" << endl;
+		}
+	}
+
+	template <typename T>
+	void extract(T & targ, void const * src_,
+	             char const * sz, char const * off);
+};
+
+
+template <typename T>
+void extractor::extract(T & targ, void const * src_,
+                        char const * sz, char const * off)
+{
+	unsigned char const * src = static_cast<unsigned char const *>(src_)
+		+ theabi.need(off);
+	size_t nbytes = theabi.need(sz);
+	
+	if (nbytes == 0)
+		return;
+	
+	assert(nbytes <= sizeof(T));
+	assert(src >= begin);
+	assert(src + nbytes <= end);
+	
+	if (verbose)
+		cerr << hex << "get " << sz << " = " << nbytes
+		     << " bytes @ " << off << " = " << (src - begin)
+		     << " : ";
+
+	targ = 0;
+	if (little_endian)
+		while(nbytes--)
+			targ = (targ << 8) | src[nbytes];
+	else
+		for(size_t i = 0; i < nbytes; ++i)
+			targ = (targ << 8) | src[i];
+	
+	if (verbose)
+		cerr << " = " << targ << endl;
+}
+
+
+void import_from_abi(abi const & abi, void const * srcv,
+                     size_t len, odb_t * dest) throw (abi_exception)
+{
+	struct opd_header * head =
+		static_cast<opd_header *>(odb_get_data(dest));
+	unsigned char const * src = static_cast<unsigned char const *>(srcv);
+	unsigned char const * const begin = src;
+	extractor ext(abi, src, len);	
+
+	memcpy(head->magic, src + abi.need("offsetof_header_magic"), 4);
+
+	// begin extracting opd header
+	ext.extract(head->version, src, "sizeof_u32", "offsetof_header_version");
+	ext.extract(head->cpu_type, src, "sizeof_u32", "offsetof_header_cpu_type");
+	ext.extract(head->ctr_event, src, "sizeof_u32", "offsetof_header_ctr_event");
+	ext.extract(head->ctr_um, src, "sizeof_u32", "offsetof_header_ctr_um");
+	ext.extract(head->ctr_count, src, "sizeof_u32", "offsetof_header_ctr_count");
+	ext.extract(head->is_kernel, src, "sizeof_u32", "offsetof_header_is_kernel");
+	// "double" extraction is unlikely to work
+	head->cpu_speed = 0.0;
+	ext.extract(head->mtime, src, "sizeof_time_t", "offsetof_header_mtime");
+	ext.extract(head->cg_to_is_kernel, src, "sizeof_u32",
+		"offsetof_header_cg_to_is_kernel");
+	ext.extract(head->anon_start, src, "sizeof_u32",
+		"offsetof_header_anon_start");
+	ext.extract(head->cg_to_anon_start, src, "sizeof_u32",
+		"offsetof_header_cg_to_anon_start");
+	src += abi.need("sizeof_struct_opd_header");
+	// done extracting opd header
+
+	// begin extracting necessary parts of descr
+	odb_node_nr_t node_nr;
+	ext.extract(node_nr, src, "sizeof_odb_node_nr_t", "offsetof_descr_current_size");
+	src += abi.need("sizeof_odb_descr_t");
+	// done extracting descr
+
+	// skip node zero, it is reserved and contains nothing usefull
+	src += abi.need("sizeof_odb_node_t");
+
+	// begin extracting nodes
+	unsigned int step = abi.need("sizeof_odb_node_t");
+	if (verbose)
+		cerr << "extracting " << node_nr << " nodes of " << step << " bytes each " << endl;
+
+	assert(src + (node_nr * step) <= begin + len);
+
+	for (odb_node_nr_t i = 1 ; i < node_nr ; ++i, src += step) {
+		odb_key_t key;
+		odb_value_t val;
+		ext.extract(key, src, "sizeof_odb_key_t", "offsetof_node_key");
+		ext.extract(val, src, "sizeof_odb_value_t", "offsetof_node_value");
+		int rc = odb_add_node(dest, key, val);
+		if (rc != EXIT_SUCCESS) {
+			cerr << strerror(rc) << endl;
+			exit(EXIT_FAILURE);
+		}
+	}
+	// done extracting nodes
+}
+
+
+int main(int argc, char const ** argv)
+{
+
+	vector<string> inputs;
+	popt::parse_options(argc, argv, inputs);
+
+	if (inputs.size() != 1) {
+		cerr << "error: must specify exactly 1 input file" << endl;
+		exit(1);
+	}
+
+	abi current_abi, input_abi;
+
+	{
+		ifstream abi_file(abi_filename.c_str());
+		if (!abi_file) {
+			cerr << "error: cannot open abi file "
+			     << abi_filename << endl;
+			exit(1);
+		}
+		abi_file >> input_abi;
+	}
+
+	if (!force && current_abi == input_abi) {
+		cerr << "input abi is identical to native. "
+		     << "no conversion necessary." << endl;
+		exit(1);
+	}
+
+	int in_fd;
+	struct stat statb;
+	void * in;
+	odb_t dest;
+	int rc;
+
+	assert((in_fd = open(inputs[0].c_str(), O_RDONLY)) > 0);		
+	assert(fstat(in_fd, &statb) == 0);
+	assert((in = mmap(0, statb.st_size, PROT_READ,
+			  MAP_PRIVATE, in_fd, 0)) != (void *)-1);
+
+	rc = odb_open(&dest, output_filename.c_str(), ODB_RDWR,
+		      sizeof(struct opd_header));
+	if (rc) {
+		cerr << "odb_open() fail:\n"
+		     << strerror(rc) << endl;
+		exit(EXIT_FAILURE);
+	}
+
+	try {
+		import_from_abi(input_abi, in, statb.st_size, &dest);
+	} catch (abi_exception & e) {
+		cerr << "caught abi exception: " << e.desc << endl;
+	}
+
+	odb_close(&dest);
+
+	assert(munmap(in, statb.st_size) == 0);
+}
diff --git a/libdb/Android.mk b/libdb/Android.mk
new file mode 100644
index 0000000..1594fe8
--- /dev/null
+++ b/libdb/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	db_debug.c \
+	db_insert.c \
+	db_manage.c \
+	db_stat.c \
+	db_travel.c
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/.. \
+	$(LOCAL_PATH)/../libutil
+
+LOCAL_MODULE := libdb
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/libdb/db_debug.c b/libdb/db_debug.c
new file mode 100644
index 0000000..0575570
--- /dev/null
+++ b/libdb/db_debug.c
@@ -0,0 +1,132 @@
+/**
+ * @file db_debug.c
+ * Debug routines for libdb
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Philippe Elie
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "odb.h"
+
+static int check_circular_list(odb_data_t const * data)
+{
+	odb_node_nr_t pos;
+	int do_abort = 0;
+	unsigned char * bitmap = malloc(data->descr->current_size);
+	memset(bitmap, '\0', data->descr->current_size);
+
+	for (pos = 0 ; pos < data->descr->size * BUCKET_FACTOR ; ++pos) {
+
+		odb_index_t index = data->hash_base[pos];
+		if (index && !do_abort) {
+			while (index) {
+				if (bitmap[index])
+					do_abort = 1;
+
+				bitmap[index] = 1;
+				index = data->node_base[index].next;
+			}
+		}
+
+		if (do_abort) {
+			printf("circular list detected size: %d\n",
+			       data->descr->current_size);
+
+			memset(bitmap, '\0', data->descr->current_size);
+
+			index = data->hash_base[pos];
+			while (index) {
+				printf("%d ", index);
+				if (bitmap[index])
+					exit(1);
+
+				bitmap[index] = 1;
+				index = data->node_base[index].next;
+			}
+		}
+
+		/* purely an optimization: intead of memset the map reset only
+		 * the needed part: not my use to optimize test but here the
+		 * test was so slow it was useless */
+		index = data->hash_base[pos];
+		while (index) {
+			bitmap[index] = 1;
+			index = data->node_base[index].next;
+		}
+	}
+
+	free(bitmap);
+
+	return do_abort;
+}
+
+static int check_redundant_key(odb_data_t const * data, odb_key_t max)
+{
+	odb_node_nr_t pos;
+
+	unsigned char * bitmap = malloc(max + 1);
+	memset(bitmap, '\0', max + 1);
+
+	for (pos = 1 ; pos < data->descr->current_size ; ++pos) {
+		if (bitmap[data->node_base[pos].key]) {
+			printf("redundant key found %lld\n",
+			       (unsigned long long)data->node_base[pos].key);
+			return 1;
+		}
+		bitmap[data->node_base[pos].key] = 1;
+	}
+	free(bitmap);
+
+	return 0;
+}
+
+int odb_check_hash(odb_t const * odb)
+{
+	odb_node_nr_t pos;
+	odb_node_nr_t nr_node = 0;
+	odb_node_nr_t nr_node_out_of_bound = 0;
+	int ret = 0;
+	odb_key_t max = 0;
+	odb_data_t * data = odb->data;
+
+	for (pos = 0 ; pos < data->descr->size * BUCKET_FACTOR ; ++pos) {
+		odb_index_t index = data->hash_base[pos];
+		while (index) {
+			if (index >= data->descr->current_size) {
+				nr_node_out_of_bound++;
+				break;
+			}
+			++nr_node;
+
+			if (data->node_base[index].key > max)
+				max = data->node_base[index].key;
+
+			index = data->node_base[index].next;
+		}
+	}
+
+	if (nr_node != data->descr->current_size - 1) {
+		printf("hash table walk found %d node expect %d node\n",
+		       nr_node, data->descr->current_size - 1);
+		ret = 1;
+	}
+
+	if (nr_node_out_of_bound) {
+		printf("out of bound node index: %d\n", nr_node_out_of_bound);
+		ret = 1;
+	}
+
+	if (ret == 0)
+		ret = check_circular_list(data);
+
+	if (ret == 0)
+		ret = check_redundant_key(data, max);
+
+	return ret;
+}
diff --git a/libdb/db_insert.c b/libdb/db_insert.c
new file mode 100644
index 0000000..018c294
--- /dev/null
+++ b/libdb/db_insert.c
@@ -0,0 +1,102 @@
+/**
+ * @file db_insert.c
+ * Inserting a key-value pair into a DB
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Philippe Elie
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "odb.h"
+
+
+static inline int add_node(odb_data_t * data, odb_key_t key, odb_value_t value)
+{
+	odb_index_t new_node;
+	odb_node_t * node;
+	odb_index_t index;
+
+	/* no locking is necessary: iteration interface retrieve data through
+	 * the node_base array, we doesn't increase current_size now but it's
+	 * done by odb_commit_reservation() so the new slot is visible only
+	 * after the increment
+	 */
+	if (data->descr->current_size >= data->descr->size) {
+		if (odb_grow_hashtable(data))
+			return EINVAL;
+	}
+	new_node = data->descr->current_size;
+
+	node = &data->node_base[new_node];
+	node->value = value;
+	node->key = key;
+
+	index = odb_do_hash(data, key);
+	node->next = data->hash_base[index];
+	data->hash_base[index] = new_node;
+
+	/* FIXME: we need wrmb() here */
+	odb_commit_reservation(data);
+
+	return 0;
+}
+
+int odb_update_node(odb_t * odb, odb_key_t key)
+{
+	odb_index_t index;
+	odb_node_t * node;
+	odb_data_t * data;
+
+	data = odb->data;
+	index = data->hash_base[odb_do_hash(data, key)];
+	while (index) {
+		node = &data->node_base[index];
+		if (node->key == key) {
+			if (node->value + 1 != 0) {
+				node->value += 1;
+			} else {
+				/* post profile tools must handle overflow */
+				/* FIXME: the tricky way will be just to add
+				 * a goto to jump right before the return
+				 * add_node(), in this way we no longer can
+				 * overflow. It'll work because new node are
+				 * linked at the start of the node list for
+				 * this bucket so this loop will see first a
+				 * non overflowed node if one exist. When we
+				 * grow the hashtable the most recently
+				 * allocated node for this key will be setup
+				 * last, so again it'll be linked at start of
+				 * the list. pp tools looke like ok with this
+				 * change.
+				 *
+				 * This change doesn't involve any file format
+				 * change but perhaps it's a bit hacky to do
+				 * this w/o bumping the sample file format
+				 * version. The drawback of this is the added
+				 * node are additive not multiplicative.
+				 * (multiplicative as if we add more bits to
+				 * store a value)
+				 */
+			}
+			return 0;
+		}
+
+		index = node->next;
+	}
+
+	return add_node(data, key, 1);
+}
+
+
+int odb_add_node(odb_t * odb, odb_key_t key, odb_value_t value)
+{
+	return add_node(odb->data, key, value);
+}
diff --git a/libdb/db_manage.c b/libdb/db_manage.c
new file mode 100644
index 0000000..d8a6fcb
--- /dev/null
+++ b/libdb/db_manage.c
@@ -0,0 +1,311 @@
+/**
+ * @file db_manage.c
+ * Management of a DB file
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Philippe Elie
+ */
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#ifndef ANDROID
+#include <sys/fcntl.h>
+#else
+#include <fcntl.h>
+#endif
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "odb.h"
+#include "op_string.h"
+#include "op_libiberty.h"
+
+ 
+static __inline odb_descr_t * odb_to_descr(odb_data_t * data)
+{
+	return (odb_descr_t *)(((char*)data->base_memory) + data->sizeof_header);
+}
+
+ 
+static __inline odb_node_t * odb_to_node_base(odb_data_t * data)
+{
+	return (odb_node_t *)(((char *)data->base_memory) + data->offset_node);
+}
+
+ 
+static __inline odb_index_t * odb_to_hash_base(odb_data_t * data)
+{
+	return (odb_index_t *)(((char *)data->base_memory) + 
+				data->offset_node +
+				(data->descr->size * sizeof(odb_node_t)));
+}
+
+ 
+/**
+ * return the number of bytes used by hash table, node table and header.
+ */
+static unsigned int tables_size(odb_data_t const * data, odb_node_nr_t node_nr)
+{
+	size_t size;
+
+	size = node_nr * (sizeof(odb_index_t) * BUCKET_FACTOR);
+	size += node_nr * sizeof(odb_node_t);
+	size += data->offset_node;
+
+	return size;
+}
+
+
+int odb_grow_hashtable(odb_data_t * data)
+{
+	unsigned int old_file_size;
+	unsigned int new_file_size;
+	unsigned int pos;
+	void * new_map;
+
+	old_file_size = tables_size(data, data->descr->size);
+	new_file_size = tables_size(data, data->descr->size * 2);
+
+	if (ftruncate(data->fd, new_file_size))
+		return 1;
+
+	new_map = mremap(data->base_memory,
+			 old_file_size, new_file_size, MREMAP_MAYMOVE);
+
+	if (new_map == MAP_FAILED)
+		return 1;
+
+	data->base_memory = new_map;
+	data->descr = odb_to_descr(data);
+	data->descr->size *= 2;
+	data->node_base = odb_to_node_base(data);
+	data->hash_base = odb_to_hash_base(data);
+	data->hash_mask = (data->descr->size * BUCKET_FACTOR) - 1;
+
+	/* rebuild the hash table, node zero is never used. This works
+	 * because layout of file is node table then hash table,
+	 * sizeof(node) > sizeof(bucket) and when we grow table we
+	 * double size ==> old hash table and new hash table can't
+	 * overlap so on the new hash table is entirely in the new
+	 * memory area (the grown part) and we know the new hash
+	 * hash table is zeroed. That's why we don't need to zero init
+	 * the new table */
+	/* OK: the above is not exact
+	 * if BUCKET_FACTOR < sizeof(bd_node_t) / sizeof(bd_node_nr_t)
+	 * all things are fine and we don't need to init the hash
+	 * table because in this case the new hash table is completely
+	 * inside the new growed part. Avoiding to touch this memory is
+	 * useful.
+	 */
+#if 0
+	for (pos = 0 ; pos < data->descr->size*BUCKET_FACTOR ; ++pos)
+		data->hash_base[pos] = 0;
+#endif
+
+	for (pos = 1; pos < data->descr->current_size; ++pos) {
+		odb_node_t * node = &data->node_base[pos];
+		size_t index = odb_do_hash(data, node->key);
+		node->next = data->hash_base[index];
+		data->hash_base[index] = pos;
+	}
+
+	return 0;
+}
+
+
+void odb_init(odb_t * odb)
+{
+	odb->data = NULL;
+}
+
+
+/* the default number of page, calculated to fit in 4096 bytes */
+#define DEFAULT_NODE_NR(offset_node)	128
+#define FILES_HASH_SIZE                 512
+
+static struct list_head files_hash[FILES_HASH_SIZE];
+
+
+static void init_hash()
+{
+	size_t i;
+	for (i = 0; i < FILES_HASH_SIZE; ++i)
+		list_init(&files_hash[i]);
+}
+
+
+static odb_data_t *
+find_samples_data(size_t hash, char const * filename)
+{
+	struct list_head * pos;
+
+	/* FIXME: maybe an initial init routine ? */
+	if (files_hash[0].next == NULL) {
+		init_hash();
+		return NULL;
+	}
+
+	list_for_each(pos, &files_hash[hash]) {
+		odb_data_t * entry = list_entry(pos, odb_data_t, list);
+		if (strcmp(entry->filename, filename) == 0)
+			return entry;
+	}
+
+	return NULL;
+}
+
+
+int odb_open(odb_t * odb, char const * filename, enum odb_rw rw,
+	     size_t sizeof_header)
+{
+	struct stat stat_buf;
+	odb_node_nr_t nr_node;
+	odb_data_t * data;
+	size_t hash;
+	int err = 0;
+
+	int flags = (rw == ODB_RDWR) ? (O_CREAT | O_RDWR) : O_RDONLY;
+	int mmflags = (rw == ODB_RDWR) ? (PROT_READ | PROT_WRITE) : PROT_READ;
+
+	hash = op_hash_string(filename) % FILES_HASH_SIZE;
+	data = find_samples_data(hash, filename);
+	if (data) {
+		odb->data = data;
+		data->ref_count++;
+		return 0;
+	}
+
+	data = xmalloc(sizeof(odb_data_t));
+	memset(data, '\0', sizeof(odb_data_t));
+	list_init(&data->list);
+	data->offset_node = sizeof_header + sizeof(odb_descr_t);
+	data->sizeof_header = sizeof_header;
+	data->ref_count = 1;
+	data->filename = xstrdup(filename);
+
+	data->fd = open(filename, flags, 0644);
+	if (data->fd < 0) {
+		err = errno;
+		goto out;
+	}
+
+	if (fstat(data->fd, &stat_buf)) {
+		err = errno;
+		goto fail;
+	}
+
+	if (stat_buf.st_size == 0) {
+		size_t file_size;
+
+		if (rw == ODB_RDONLY) {
+			err = EIO;
+			goto fail;
+		}
+
+		nr_node = DEFAULT_NODE_NR(data->offset_node);
+
+		file_size = tables_size(data, nr_node);
+		if (ftruncate(data->fd, file_size)) {
+			err = errno;
+			goto fail;
+		}
+	} else {
+		/* Calculate nr node allowing a sanity check later */
+		nr_node = (stat_buf.st_size - data->offset_node) /
+			((sizeof(odb_index_t) * BUCKET_FACTOR) + sizeof(odb_node_t));
+	}
+
+	data->base_memory = mmap(0, tables_size(data, nr_node), mmflags,
+				MAP_SHARED, data->fd, 0);
+
+	if (data->base_memory == MAP_FAILED) {
+		err = errno;
+		goto fail;
+	}
+
+	data->descr = odb_to_descr(data);
+
+	if (stat_buf.st_size == 0) {
+		data->descr->size = nr_node;
+		/* page zero is not used */
+		data->descr->current_size = 1;
+	} else {
+		/* file already exist, sanity check nr node */
+		if (nr_node != data->descr->size) {
+			err = EINVAL;
+			goto fail_unmap;
+		}
+	}
+
+	data->hash_base = odb_to_hash_base(data);
+	data->node_base = odb_to_node_base(data);
+	data->hash_mask = (data->descr->size * BUCKET_FACTOR) - 1;
+
+	list_add(&data->list, &files_hash[hash]);
+	odb->data = data;
+out:
+	return err;
+fail_unmap:
+	munmap(data->base_memory, tables_size(data, nr_node));
+fail:
+	close(data->fd);
+	free(data->filename);
+	free(data);
+	odb->data = NULL;
+	goto out;
+}
+
+
+void odb_close(odb_t * odb)
+{
+	odb_data_t * data = odb->data;
+
+	if (data) {
+		data->ref_count--;
+		if (data->ref_count == 0) {
+			size_t size = tables_size(data, data->descr->size);
+			list_del(&data->list);
+			munmap(data->base_memory, size);
+			if (data->fd >= 0)
+				close(data->fd);
+			free(data->filename);
+			free(data);
+			odb->data = NULL;
+		}
+	}
+}
+
+
+int odb_open_count(odb_t const * odb)
+{
+	if (!odb->data)
+		return 0;
+	return odb->data->ref_count;
+}
+
+
+void * odb_get_data(odb_t * odb)
+{
+	return odb->data->base_memory;
+}
+
+
+void odb_sync(odb_t const * odb)
+{
+	odb_data_t * data = odb->data;
+	size_t size;
+
+	if (!data)
+		return;
+
+	size = tables_size(data, data->descr->size);
+	msync(data->base_memory, size, MS_ASYNC);
+}
diff --git a/libdb/db_stat.c b/libdb/db_stat.c
new file mode 100644
index 0000000..6d29e9a
--- /dev/null
+++ b/libdb/db_stat.c
@@ -0,0 +1,88 @@
+/**
+ * @file db_stat.c
+ * Statistics routines for libdb
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Philippe Elie
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "odb.h"
+#include "op_types.h"
+
+/// hold various statistics data for a db file
+struct odb_hash_stat_t {
+	odb_node_nr_t node_nr;			/**< allocated node number */
+	odb_node_nr_t used_node_nr;		/**< in use node number */
+	count_type    total_count;		/**< cumulated samples count */
+	odb_index_t   hash_table_size;		/**< hash table entry number */
+	odb_node_nr_t max_list_length;		/**< worst case   */
+	double       average_list_length;	/**< average case */
+	/* do we need variance ? */
+};
+
+odb_hash_stat_t * odb_hash_stat(odb_t const * odb)
+{
+	size_t max_length = 0;
+	double total_length = 0.0;
+	size_t nr_non_empty_list = 0;
+	size_t pos;
+	odb_data_t * data = odb->data;
+
+	odb_hash_stat_t * result = calloc(1, sizeof(odb_hash_stat_t));
+	if (!result) {
+		fprintf(stderr, "not enough memory\n");
+		exit(EXIT_FAILURE);
+	}
+
+	result->node_nr = data->descr->size;
+	result->used_node_nr = data->descr->current_size;
+	result->hash_table_size = data->descr->size * BUCKET_FACTOR;
+
+	/* FIXME: I'm dubious if this do right statistics for hash table
+	 * efficiency check */
+
+	for (pos = 0 ; pos < result->hash_table_size ; ++pos) {
+		size_t cur_length = 0;
+		size_t index = data->hash_base[pos];
+		while (index) {
+			result->total_count += data->node_base[index].value;
+			index = data->node_base[index].next;
+			++cur_length;
+		}
+
+		if (cur_length > max_length)
+			max_length = cur_length;
+
+		if (cur_length) {
+			total_length += cur_length;
+			++nr_non_empty_list;
+		}
+	}
+
+	result->max_list_length = max_length;
+	result->average_list_length = total_length / nr_non_empty_list;
+
+	return result;
+}
+
+
+void odb_hash_display_stat(odb_hash_stat_t const * stat)
+{
+	printf("total node number:   %d\n", stat->node_nr);
+	printf("total used node:     %d\n", stat->used_node_nr);
+	printf("total count:         %llu\n", stat->total_count);
+	printf("hash table size:     %d\n", stat->hash_table_size);
+	printf("greater list length: %d\n", stat->max_list_length);
+	printf("average non empty list length: %2.4f\n", stat->average_list_length);
+}
+
+
+void odb_hash_free_stat(odb_hash_stat_t * stat)
+{
+	free(stat);
+}
diff --git a/libdb/db_travel.c b/libdb/db_travel.c
new file mode 100644
index 0000000..3ed467b
--- /dev/null
+++ b/libdb/db_travel.c
@@ -0,0 +1,18 @@
+/**
+ * @file db_travel.c
+ * Inspection of a DB
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Philippe Elie
+ */
+
+#include "odb.h"
+
+odb_node_t * odb_get_iterator(odb_t const * odb, odb_node_nr_t * nr)
+{
+	/* node zero is unused */
+	*nr = odb->data->descr->current_size - 1;
+	return odb->data->node_base + 1;
+}
diff --git a/libdb/odb.h b/libdb/odb.h
new file mode 100644
index 0000000..c190b57
--- /dev/null
+++ b/libdb/odb.h
@@ -0,0 +1,223 @@
+/**
+ * @file odb.h
+ * This file contains various definitions and interface for management
+ * of in-memory, through mmaped file, growable hash table, that stores
+ * sample files.
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Philippe Elie
+ */
+
+#ifndef ODB_HASH_H
+#define ODB_HASH_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "op_list.h"
+
+/** the type of key. 64-bit because CG needs 32-bit pair {from,to} */
+typedef uint64_t odb_key_t;
+/** the type of an information in the database */
+typedef unsigned int odb_value_t;
+/** the type of index (node number), list are implemented through index */
+typedef unsigned int odb_index_t;
+/** the type store node number */
+typedef odb_index_t odb_node_nr_t;
+/** store the hash mask, hash table size are always power of two */
+typedef odb_index_t odb_hash_mask_t;
+
+/* there is (bucket factor * nr node) entry in hash table, this can seem
+ * excessive but hash coding eip don't give a good distributions and our
+ * goal is to get a O(1) amortized insert time. bucket factor must be a
+ * power of two. FIXME: see big comment in odb_hash_add_node, you must
+ * re-enable zeroing hash table if BUCKET_FACTOR > 2 (roughly exact, you
+ * want to read the comment in odb_hash_add_node() if you tune this define)
+ */
+#define BUCKET_FACTOR 1
+
+/** a db hash node */
+typedef struct {
+	odb_key_t key;			/**< eip */
+	odb_value_t value;		/**< samples count */
+	odb_index_t next;		/**< next entry for this bucket */
+} odb_node_t;
+
+/** the minimal information which must be stored in the file to reload
+ * properly the data base, following this header is the node array then
+ * the hash table (when growing we avoid to copy node array)
+ */
+typedef struct {
+	odb_node_nr_t size;		/**< in node nr (power of two) */
+	odb_node_nr_t current_size;	/**< nr used node + 1, node 0 unused */
+	int padding[6];			/**< for padding and future use */
+} odb_descr_t;
+
+/** a "database". this is an in memory only description.
+ *
+ * We allow to manage a database inside a mapped file with an "header" of
+ * unknown size so odb_open get a parameter to specify the size of this header.
+ * A typical use is:
+ *
+ * struct header { int etc; ... };
+ * odb_open(&hash, filename, ODB_RW, sizeof(header));
+ * so on this library have no dependency on the header type.
+ *
+ * the internal memory layout from base_memory is:
+ *  the unknown header (sizeof_header)
+ *  odb_descr_t
+ *  the node array: (descr->size * sizeof(odb_node_t) entries
+ *  the hash table: array of odb_index_t indexing the node array 
+ *    (descr->size * BUCKET_FACTOR) entries
+ */
+typedef struct odb_data {
+	odb_node_t * node_base;		/**< base memory area of the page */
+	odb_index_t * hash_base;	/**< base memory of hash table */
+	odb_descr_t * descr;		/**< the current state of database */
+	odb_hash_mask_t hash_mask;	/**< == descr->size - 1 */
+	unsigned int sizeof_header;	/**< from base_memory to odb header */
+	unsigned int offset_node;	/**< from base_memory to node array */
+	void * base_memory;		/**< base memory of the maped memory */
+	int fd;				/**< mmaped memory file descriptor */
+	char * filename;                /**< full path name of sample file */
+	int ref_count;                  /**< reference count */
+	struct list_head list;          /**< hash bucket list */
+} odb_data_t;
+
+typedef struct {
+	odb_data_t * data;
+} odb_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* db_manage.c */
+
+/** how to open the DB file */
+enum odb_rw {
+	ODB_RDONLY = 0,	/**< open for read only */
+	ODB_RDWR = 1	/**< open for read and/or write */
+};
+
+/**
+ * odb_init - initialize a DB file
+ * @param odb the DB file to init
+ */
+void odb_init(odb_t * odb);
+
+/**
+ * odb_open - open a DB file
+ * @param odb the data base object to setup
+ * @param filename the filename where go the maped memory
+ * @param rw \enum ODB_RW if opening for writing, else \enum ODB_RDONLY
+ * @param sizeof_header size of the file header if any
+ *
+ * The sizeof_header parameter allows the data file to have a header
+ * at the start of the file which is skipped.
+ * odb_open() always preallocate a few number of pages.
+ * returns 0 on success, errno on failure
+ */
+int odb_open(odb_t * odb, char const * filename,
+             enum odb_rw rw, size_t sizeof_header);
+
+/** Close the given ODB file */
+void odb_close(odb_t * odb);
+
+/** return the number of times this sample file is open */
+int odb_open_count(odb_t const * odb);
+
+/** return the start of the mapped data */
+void * odb_get_data(odb_t * odb);
+
+/** issue a msync on the used size of the mmaped file */
+void odb_sync(odb_t const * odb);
+
+/**
+ * grow the hashtable in such way current_size is the index of the first free
+ * node. Take care all node pointer can be invalidated by this call.
+ *
+ * Node allocation is done in a two step way 1st) ensure a free node exist
+ * eventually, caller can setup it, 2nd) commit the node allocation with
+ * odb_commit_reservation().
+ * This is done in this way to ensure node setup is visible from another
+ * process like pp tools in an atomic way.
+ *
+ * returns 0 on success, non zero on failure in this case this function do
+ * nothing and errno is set by the first libc call failure allowing to retry
+ * after cleanup some program resource.
+ */
+int odb_grow_hashtable(odb_data_t * data);
+/**
+ * commit a previously successfull node reservation. This can't fail.
+ */
+static __inline void odb_commit_reservation(odb_data_t * data)
+{
+	++data->descr->current_size;
+}
+
+/** "immpossible" node number to indicate an error from odb_hash_add_node() */
+#define ODB_NODE_NR_INVALID ((odb_node_nr_t)-1)
+
+/* db_debug.c */
+/** check that the hash is well built */
+int odb_check_hash(odb_t const * odb);
+
+/* db_stat.c */
+typedef struct odb_hash_stat_t odb_hash_stat_t;
+odb_hash_stat_t * odb_hash_stat(odb_t const * odb);
+void odb_hash_display_stat(odb_hash_stat_t const * stats);
+void odb_hash_free_stat(odb_hash_stat_t * stats);
+
+/* db_insert.c */
+/** update info at key by incrementing its associated value by one, 
+ * if the key does not exist a new node is created and the value associated
+ * is set to one.
+ *
+ * returns EXIT_SUCCESS on success, EXIT_FAILURE on failure
+ */
+int odb_update_node(odb_t * odb, odb_key_t key);
+
+/** Add a new node w/o regarding if a node with the same key already exists
+ *
+ * returns EXIT_SUCCESS on success, EXIT_FAILURE on failure
+ */
+int odb_add_node(odb_t * odb, odb_key_t key, odb_value_t value);
+
+/* db_travel.c */
+/**
+ * return a base pointer to the node array and number of node in this array
+ * caller then will iterate through:
+ *
+ * odb_node_nr_t node_nr, pos;
+ * odb_node_t * node = odb_get_iterator(odb, &node_nr);
+ *	for ( pos = 0 ; pos < node_nr ; ++pos)
+ *		// do something
+ *
+ *  note than caller does not need to filter nil key as it's a valid key,
+ * The returned range is all valid (i.e. should never contain zero value).
+ */
+odb_node_t * odb_get_iterator(odb_t const * odb, odb_node_nr_t * nr);
+
+static __inline unsigned int
+odb_do_hash(odb_data_t const * data, odb_key_t value)
+{
+	/* FIXME: better hash for eip value, needs to instrument code
+	 * and do a lot of tests ... */
+	/* trying to combine high order bits his a no-op: inside a binary image
+	 * high order bits don't vary a lot, hash table start with 7 bits mask
+	 * so this hash coding use bits 0-7, 8-15. Hash table is stored in
+	 * files avoiding to rebuilding them at profiling re-start so
+	 * on changing do_hash() change the file format!
+	 */
+	uint32_t temp = (value >> 32) ^ value;
+	return ((temp << 0) ^ (temp >> 8)) & data->hash_mask;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !ODB_H */
diff --git a/libop/Android.mk b/libop/Android.mk
new file mode 100644
index 0000000..8fbd1e6
--- /dev/null
+++ b/libop/Android.mk
@@ -0,0 +1,19 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	op_alloc_counter.c \
+	op_config.c \
+	op_cpu_type.c \
+	op_events.c \
+	op_get_interface.c \
+	op_mangle.c \
+	op_parse_event.c
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/.. \
+	$(LOCAL_PATH)/../libutil
+
+LOCAL_MODULE := libop
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/libop/op_alloc_counter.c b/libop/op_alloc_counter.c
new file mode 100644
index 0000000..353100a
--- /dev/null
+++ b/libop/op_alloc_counter.c
@@ -0,0 +1,213 @@
+/**
+ * @file op_alloc_counter.c
+ * hardware counter allocation
+ *
+ * You can have silliness here.
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <dirent.h>
+
+#include "op_events.h"
+#include "op_libiberty.h"
+
+
+typedef struct counter_arc_head {
+	/** the head of allowed counter for this event */
+	struct list_head next;
+} counter_arc_head;
+
+
+typedef struct counter_arc {
+	/** counter nr */
+	int counter;
+	/** the next counter allowed for this event */
+	struct list_head next;
+} counter_arc;
+
+
+/**
+ * @param pev  an array of event
+ * @param nr_events  number of entry in pev
+ *
+ * build an array of counter list allowed for each events
+ *  counter_arc_head[i] is the list of allowed counter for pev[i] events
+ * The returned pointer is an array of nr_events entry
+ */
+static counter_arc_head *
+build_counter_arc(struct op_event const * pev[], int nr_events)
+{
+	counter_arc_head * ctr_arc;
+	int i;
+
+	ctr_arc = xmalloc(nr_events * sizeof(*ctr_arc));
+
+	for (i = 0; i < nr_events; ++i) {
+		int j;
+		u32 mask = pev[i]->counter_mask;
+
+		list_init(&ctr_arc[i].next);
+		for (j = 0; mask; ++j) {
+			if (mask & (1 << j)) {
+				counter_arc * arc = 
+					xmalloc(sizeof(counter_arc));
+				arc->counter = j;
+				/* we are looping by increasing counter number,
+				 * allocation use a left to right tree walking
+				 * so we add at end to ensure counter will
+				 * be allocated by increasing number: it's not
+				 * required but a bit less surprising when
+				 * debugging code
+				 */
+				list_add_tail(&arc->next, &ctr_arc[i].next);
+				mask &= ~(1 << j);
+			}
+		}
+	}
+
+	return ctr_arc;
+}
+
+
+/**
+ * @param ctr_arc  the array to deallocate
+ * @param nr_events  number of entry in array
+ *
+ *  deallocate all previously allocated resource by build_counter_arc()
+ */
+static void delete_counter_arc(counter_arc_head * ctr_arc, int nr_events)
+{
+	int i;
+	for (i = 0; i < nr_events; ++i) {
+		struct list_head * pos, * pos2;
+		list_for_each_safe(pos, pos2, &ctr_arc[i].next) {
+			counter_arc * arc = list_entry(pos, counter_arc, next);
+			list_del(&arc->next);
+			free(arc);
+		}
+	}
+	free(ctr_arc);
+}
+
+
+/**
+ * @param ctr_arc  tree description, ctr_arc[i] is the i-th level of tree.
+ * @param max_depth  number of entry in array ctr_arc == depth of tree
+ * @param depth  current level we are exploring
+ * @param allocated_mask  current counter already allocated mask
+ * @param counter_map  array of counter number mapping, returned results go
+ *   here
+ *
+ * return non zero on succees, in this case counter_map is set to the counter
+ * mapping number.
+ *
+ * Solution is searched through a simple backtracking exploring recursively all
+ * possible solution until one is found, prunning is done in O(1) by tracking
+ * a bitmask of already allocated counter. Walking through node is done in
+ * preorder left to right.
+ *
+ * Possible improvment if neccessary: partition counters in class of counter,
+ * two counter belong to the same class if they allow exactly the same set of
+ * event. Now using a variant of the backtrack algo can works on class of
+ * counter rather on counter (this is not an improvment if each counter goes
+ * in it's own class)
+ */
+static int
+allocate_counter(counter_arc_head const * ctr_arc, int max_depth, int depth,
+		 u32 allocated_mask, size_t * counter_map)
+{
+	struct list_head * pos;
+
+	if (depth == max_depth)
+		return 1;
+
+	list_for_each(pos, &ctr_arc[depth].next) {
+		counter_arc const * arc = list_entry(pos, counter_arc, next);
+
+		if (allocated_mask & (1 << arc->counter))
+			continue;
+
+		counter_map[depth] = arc->counter;
+
+		if (allocate_counter(ctr_arc, max_depth, depth + 1,
+		                     allocated_mask | (1 << arc->counter),
+		                     counter_map))
+			return 1;
+	}
+
+	return 0;
+}
+
+/* determine which directories are counter directories
+ */
+static int perfcounterdir(const struct dirent * entry)
+{
+	return (isdigit(entry->d_name[0]));
+}
+
+
+/**
+ * @param mask pointer where to place bit mask of unavailable counters
+ *
+ * return >= 0 number of counters that are available
+ *        < 0  could not determine number of counters
+ *
+ */
+static int op_get_counter_mask(u32 * mask)
+{
+	struct dirent **counterlist;
+	int count, i;
+	/* assume nothing is available */
+	u32 available=0;
+
+	count = scandir("/dev/oprofile", &counterlist, perfcounterdir, alphasort);
+	if (count < 0)
+		/* unable to determine bit mask */
+		return -1;
+	/* convert to bit map (0 where counter exists) */
+	for (i=0; i<count; ++i) {
+		available |= 1 << atoi(counterlist[i]->d_name);
+		free(counterlist[i]);
+	}
+	*mask=~available;
+	free(counterlist);
+	return count;
+}
+
+size_t * map_event_to_counter(struct op_event const * pev[], int nr_events,
+                              op_cpu cpu_type)
+{
+	counter_arc_head * ctr_arc;
+	size_t * counter_map;
+	int nr_counters;
+	u32 unavailable_counters = 0;
+
+	nr_counters = op_get_counter_mask(&unavailable_counters);
+	/* no counters then probably perfmon managing perfmon hw */
+	if (nr_counters <= 0) {
+		nr_counters = op_get_nr_counters(cpu_type);
+		unavailable_counters = (~0) << nr_counters;
+	}
+	if (nr_counters < nr_events)
+		return 0;
+
+	ctr_arc = build_counter_arc(pev, nr_events);
+
+	counter_map = xmalloc(nr_counters * sizeof(size_t));
+
+	if (!allocate_counter(ctr_arc, nr_events, 0, unavailable_counters,
+			      counter_map)) {
+		free(counter_map);
+		counter_map = 0;
+	}
+
+	delete_counter_arc(ctr_arc, nr_events);
+	return counter_map;
+}
diff --git a/libop/op_alloc_counter.h b/libop/op_alloc_counter.h
new file mode 100644
index 0000000..40b4ebf
--- /dev/null
+++ b/libop/op_alloc_counter.h
@@ -0,0 +1,43 @@
+/**
+ * @file op_alloc_counter.h
+ * hardware counter allocation
+ *
+ * You can have silliness here.
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_ALLOC_COUNTER_H
+#define OP_ALLOC_COUNTER_H
+
+#include <stddef.h>
+
+#include "op_cpu_type.h"
+
+struct op_event;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @param pev  array of selected event we want to bind to counter
+ * @param nr_events  size of pev array
+ * @param cpu_type  cpu type
+ *
+ * Try to calculate a binding between passed event in pev and counter number.
+ * The binding is returned in a size_t * where returned ptr[i] is the counter
+ * number bound to pev[i]
+ */
+size_t * map_event_to_counter(struct op_event const * pev[], int nr_events,
+                              op_cpu cpu_type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !OP_ALLOC_COUNTER_H */
diff --git a/libop/op_config.c b/libop/op_config.c
new file mode 100644
index 0000000..837242b
--- /dev/null
+++ b/libop/op_config.c
@@ -0,0 +1,77 @@
+/**
+ * @file op_config.c
+ * Oprofile configuration parameters.
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Nathan Tallent
+ * @Modifications Daniel Hansel
+ */
+
+#include "op_config.h"
+#include "op_config_24.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+/* paths in op_config.h */
+char op_session_dir[PATH_MAX];
+char op_samples_dir[PATH_MAX];
+char op_samples_current_dir[PATH_MAX];
+char op_lock_file[PATH_MAX];
+char op_log_file[PATH_MAX];
+char op_pipe_file[PATH_MAX];
+char op_dump_status[PATH_MAX];
+
+/* paths in op_config_24.h */
+char op_device[PATH_MAX];
+char op_note_device[PATH_MAX];
+char op_hash_device[PATH_MAX];
+
+void
+init_op_config_dirs(char const * session_dir)
+{
+	int session_dir_len;
+
+	assert(session_dir);	
+	session_dir_len = strlen(session_dir);
+
+	if (session_dir_len + strlen("/samples/oprofiled.log") > PATH_MAX) {
+		fprintf(stderr, "Session_dir string \"%s\" is too large.\n", 
+			session_dir);
+		exit(EXIT_FAILURE);
+	}
+	
+	strcpy(op_session_dir, session_dir);
+	
+	strcpy(op_samples_dir, op_session_dir);
+	strcat(op_samples_dir, "/samples/");
+	
+	strcpy(op_samples_current_dir, op_samples_dir);
+	strcat(op_samples_current_dir, "/current/");
+
+	strcpy(op_lock_file, op_session_dir);
+	strcat(op_lock_file, "/lock");
+
+	strcpy(op_pipe_file, op_session_dir);
+	strcat(op_pipe_file, "/opd_pipe");
+
+	strcpy(op_log_file, op_samples_dir);
+	strcat(op_log_file, "oprofiled.log");
+
+	strcpy(op_dump_status, op_session_dir);
+	strcat(op_dump_status, "/complete_dump");
+
+	strcpy(op_device, op_session_dir);
+	strcat(op_device, "/opdev");
+
+	strcpy(op_note_device, op_session_dir);
+	strcat(op_note_device, "/opnotedev");
+
+	strcpy(op_hash_device, op_session_dir);
+	strcat(op_hash_device, "/ophashmapdev");
+}
diff --git a/libop/op_config.h b/libop/op_config.h
new file mode 100644
index 0000000..b384497
--- /dev/null
+++ b/libop/op_config.h
@@ -0,0 +1,58 @@
+/**
+ * @file op_config.h
+ *
+ * Parameters a user may want to change. See
+ * also op_config_24.h
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ * @Modifications Daniel Hansel
+ */
+
+#ifndef OP_CONFIG_H
+#define OP_CONFIG_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+  
+/** 
+ * must be called to initialize the paths below.
+ * @param session_dir  the non-NULL value of the base session directory
+ */
+void init_op_config_dirs(char const * session_dir);
+
+/* 
+ * various paths, corresponding to opcontrol, that should be
+ * initialized by init_op_config_dirs() above. 
+ */
+extern char op_session_dir[];
+extern char op_samples_dir[];
+extern char op_samples_current_dir[];
+extern char op_lock_file[];
+extern char op_log_file[];
+extern char op_pipe_file[];
+extern char op_dump_status[];
+
+#define OP_DRIVER_BASE  "/dev/oprofile"
+#define OP_DATA_DIR     "/data/oprofile"
+
+/* Global directory that stores debug files */
+#ifndef DEBUGDIR
+#define DEBUGDIR "/usr/lib/debug"
+#endif
+
+#define OPD_MAGIC "DAE\n"
+#define OPD_VERSION 0x11
+
+#define OP_MIN_CPU_BUF_SIZE 2048
+#define OP_MAX_CPU_BUF_SIZE 131072
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* OP_CONFIG_H */
diff --git a/libop/op_config_24.h b/libop/op_config_24.h
new file mode 100644
index 0000000..1786fae
--- /dev/null
+++ b/libop/op_config_24.h
@@ -0,0 +1,79 @@
+/**
+ * @file op_config_24.h
+ *
+ * Parameters a user may want to change
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_CONFIG_24_H
+#define OP_CONFIG_24_H
+
+#define OP_MOUNT "/proc/sys/dev/oprofile/"
+
+extern char op_device[];
+extern char op_note_device[];
+extern char op_hash_device[];
+
+/*@{\name module default/min/max settings */
+
+/** 65536 * sizeof(op_sample) */
+#define OP_DEFAULT_BUF_SIZE 65536
+/** 
+ * we don't try to wake-up daemon until it remains more than this free entry
+ * in eviction buffer 
+ */
+#define OP_PRE_WATERMARK(buffer_size)			\
+	(((buffer_size) / 8) < OP_MIN_PRE_WATERMARK	\
+		? OP_MIN_PRE_WATERMARK			\
+		: (buffer_size) / 8)
+/** minimal buffer water mark before we try to wakeup daemon */
+#define OP_MIN_PRE_WATERMARK 8192
+/** maximum number of entry in samples eviction buffer */
+#define OP_MAX_BUF_SIZE	1048576
+/** minimum number of entry in samples eviction buffer */
+#define OP_MIN_BUF_SIZE	(32768 + OP_PRE_WATERMARK(32768))
+
+/** 16384 * sizeof(op_note) = 273680 bytes default */
+#define OP_DEFAULT_NOTE_SIZE 16384
+/** 
+ * we don't try to wake-up daemon until it remains more than this free entry
+ * in note buffer 
+ */
+#define OP_PRE_NOTE_WATERMARK(note_size)		\
+	(((note_size) / 32) < OP_MIN_NOTE_PRE_WATERMARK	\
+		? OP_MIN_NOTE_PRE_WATERMARK		\
+		: (note_size) / 32)
+/** minimal note buffer water mark before we try to wakeup daemon */
+#define OP_MIN_NOTE_PRE_WATERMARK 512
+/** maximum number of entry in note buffer */
+#define OP_MAX_NOTE_TABLE_SIZE	1048576
+/** minimum number of entry in note buffer */
+#define OP_MIN_NOTE_TABLE_SIZE	(1024 + OP_PRE_NOTE_WATERMARK(1024))
+
+/** maximum sampling rate when using RTC */
+#define OP_MAX_RTC_COUNT	4096
+/** minimum sampling rate when using RTC */
+#define OP_MIN_RTC_COUNT	2
+
+/*@}*/
+
+/** 
+ * nr entries in hash map. This is the maximum number of name components
+ * allowed. Must be a prime number 
+ */
+#define OP_HASH_MAP_NR 4093
+
+/** size of string pool in bytes */
+#define POOL_SIZE 65536
+
+#ifndef NR_CPUS
+/** maximum number of cpus present in the box */
+#define NR_CPUS 32
+#endif
+
+#endif /* OP_CONFIG_24_H */
diff --git a/libop/op_cpu_type.c b/libop/op_cpu_type.c
new file mode 100644
index 0000000..b9d13de
--- /dev/null
+++ b/libop/op_cpu_type.c
@@ -0,0 +1,158 @@
+/**
+ * @file op_cpu_type.c
+ * CPU type determination
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "op_cpu_type.h"
+
+struct cpu_descr {
+	char const * pretty;
+	char const * name;
+	op_cpu cpu;
+	unsigned int nr_counters;
+};
+
+static struct cpu_descr const cpu_descrs[MAX_CPU_TYPE] = {
+	{ "Pentium Pro", "i386/ppro", CPU_PPRO, 2 },
+	{ "PII", "i386/pii", CPU_PII, 2 },
+	{ "PIII", "i386/piii", CPU_PIII, 2 },
+	{ "Athlon", "i386/athlon", CPU_ATHLON, 4 },
+	{ "CPU with timer interrupt", "timer", CPU_TIMER_INT, 1 },
+	{ "CPU with RTC device", "rtc", CPU_RTC, 1 },
+	{ "P4 / Xeon", "i386/p4", CPU_P4, 8 },
+	{ "IA64", "ia64/ia64", CPU_IA64, 4 },
+	{ "Itanium", "ia64/itanium", CPU_IA64_1, 4 },
+	{ "Itanium 2", "ia64/itanium2", CPU_IA64_2, 4 },
+	{ "AMD64 processors", "x86-64/hammer", CPU_HAMMER, 4 },
+	{ "P4 / Xeon with 2 hyper-threads", "i386/p4-ht", CPU_P4_HT2, 4 },
+	{ "Alpha EV4", "alpha/ev4", CPU_AXP_EV4, 2 },
+	{ "Alpha EV5", "alpha/ev5", CPU_AXP_EV5, 3 },
+	{ "Alpha PCA56", "alpha/pca56", CPU_AXP_PCA56, 3 },
+	{ "Alpha EV6", "alpha/ev6", CPU_AXP_EV6, 2 },
+	{ "Alpha EV67", "alpha/ev67", CPU_AXP_EV67, 20 },
+	{ "Pentium M (P6 core)", "i386/p6_mobile", CPU_P6_MOBILE, 2 },
+	{ "ARM/XScale PMU1", "arm/xscale1", CPU_ARM_XSCALE1, 3 },
+	{ "ARM/XScale PMU2", "arm/xscale2", CPU_ARM_XSCALE2, 5 },
+	{ "ppc64 POWER4", "ppc64/power4", CPU_PPC64_POWER4, 8 },
+	{ "ppc64 POWER5", "ppc64/power5", CPU_PPC64_POWER5, 6 },
+	{ "ppc64 POWER5+", "ppc64/power5+", CPU_PPC64_POWER5p, 6 },
+	{ "ppc64 970", "ppc64/970", CPU_PPC64_970, 8 },
+	{ "MIPS 20K", "mips/20K", CPU_MIPS_20K, 1},
+	{ "MIPS 24K", "mips/24K", CPU_MIPS_24K, 2},
+	{ "MIPS 25K", "mips/25K", CPU_MIPS_25K, 2},
+	{ "MIPS 34K", "mips/34K", CPU_MIPS_34K, 4},
+	{ "MIPS 5K", "mips/5K", CPU_MIPS_5K, 2},
+	{ "MIPS R10000", "mips/r10000", CPU_MIPS_R10000, 2 },
+	{ "MIPS R12000", "mips/r12000", CPU_MIPS_R12000, 4 },
+	{ "QED RM7000", "mips/rm7000", CPU_MIPS_RM7000, 1 },
+	{ "PMC-Sierra RM9000", "mips/rm9000", CPU_MIPS_RM9000, 2 },
+	{ "Sibyte SB1", "mips/sb1", CPU_MIPS_SB1, 4 },
+	{ "NEC VR5432", "mips/vr5432", CPU_MIPS_VR5432, 2 },
+	{ "NEC VR5500", "mips/vr5500", CPU_MIPS_VR5500, 2 },
+	{ "e500", "ppc/e500", CPU_PPC_E500, 4 },
+	{ "e500v2", "ppc/e500v2", CPU_PPC_E500_2, 4 },
+	{ "Core Solo / Duo", "i386/core", CPU_CORE, 2 },
+	{ "PowerPC G4", "ppc/7450",  CPU_PPC_7450, 6 },
+	{ "Core 2", "i386/core_2", CPU_CORE_2, 2 },
+	{ "ppc64 POWER6", "ppc64/power6", CPU_PPC64_POWER6, 4 },
+	{ "ppc64 970MP", "ppc64/970MP", CPU_PPC64_970MP, 8 },
+	{ "ppc64 Cell Broadband Engine", "ppc64/cell-be", CPU_PPC64_CELL, 8 },
+	{ "AMD64 family10", "x86-64/family10", CPU_FAMILY10, 4 },
+	{ "ppc64 PA6T", "ppc64/pa6t", CPU_PPC64_PA6T, 6 },
+	{ "ARM MPCore", "arm/mpcore", CPU_ARM_MPCORE, 2 },
+	{ "ARM V6 PMU", "arm/armv6", CPU_ARM_V6, 3 },
+	{ "ppc64 POWER5++", "ppc64/power5++", CPU_PPC64_POWER5pp, 6 },
+	{ "e300", "ppc/e300", CPU_PPC_E300, 4 },
+	{ "AVR32", "avr32", CPU_AVR32, 3 },
+};
+ 
+static size_t const nr_cpu_descrs = sizeof(cpu_descrs) / sizeof(struct cpu_descr);
+
+op_cpu op_get_cpu_type(void)
+{
+	int cpu_type = CPU_NO_GOOD;
+	char str[100];
+	FILE * fp;
+
+	fp = fopen("/proc/sys/dev/oprofile/cpu_type", "r");
+	if (!fp) {
+		/* Try 2.6's oprofilefs one instead. */
+		fp = fopen("/dev/oprofile/cpu_type", "r");
+		if (!fp) {
+			fprintf(stderr, "Unable to open cpu_type file for reading\n");
+			fprintf(stderr, "Make sure you have done opcontrol --init\n");
+			return cpu_type;
+		}
+	}
+
+	if (!fgets(str, 99, fp)) {
+		fprintf(stderr, "Could not read cpu type.\n");
+		return CPU_NO_GOOD;
+	}
+
+	cpu_type = op_get_cpu_number(str);
+
+	fclose(fp);
+
+	return cpu_type;
+}
+
+
+op_cpu op_get_cpu_number(char const * cpu_string)
+{
+	int cpu_type = CPU_NO_GOOD;
+	size_t i;
+	
+	for (i = 0; i < nr_cpu_descrs; ++i) {
+		if (!strcmp(cpu_descrs[i].name, cpu_string)) {
+			cpu_type = cpu_descrs[i].cpu;
+			break;
+		}
+	}
+
+	/* Attempt to convert into a number */
+	if (cpu_type == CPU_NO_GOOD)
+		sscanf(cpu_string, "%d\n", &cpu_type);
+	
+	if (cpu_type <= CPU_NO_GOOD || cpu_type >= MAX_CPU_TYPE)
+		cpu_type = CPU_NO_GOOD;
+
+	return cpu_type;
+}
+
+
+char const * op_get_cpu_type_str(op_cpu cpu_type)
+{
+	if (cpu_type <= CPU_NO_GOOD || cpu_type >= MAX_CPU_TYPE)
+		return "invalid cpu type";
+
+	return cpu_descrs[cpu_type].pretty;
+}
+
+
+char const * op_get_cpu_name(op_cpu cpu_type)
+{
+	if (cpu_type <= CPU_NO_GOOD || cpu_type >= MAX_CPU_TYPE)
+		return "invalid cpu type";
+
+	return cpu_descrs[cpu_type].name;
+}
+
+
+int op_get_nr_counters(op_cpu cpu_type)
+{
+	if (cpu_type <= CPU_NO_GOOD || cpu_type >= MAX_CPU_TYPE)
+		return 0;
+
+	return cpu_descrs[cpu_type].nr_counters;
+}
diff --git a/libop/op_cpu_type.h b/libop/op_cpu_type.h
new file mode 100644
index 0000000..be95ae2
--- /dev/null
+++ b/libop/op_cpu_type.h
@@ -0,0 +1,139 @@
+/**
+ * @file op_cpu_type.h
+ * CPU type determination
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_CPU_TYPE_H
+#define OP_CPU_TYPE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Supported cpu type.  Always add new CPU types at the very end.
+ */
+typedef enum {
+	CPU_NO_GOOD = -1, /**< unsupported CPU type */
+	CPU_PPRO, /**< Pentium Pro */
+	CPU_PII, /**< Pentium II series */
+	CPU_PIII, /**< Pentium III series */
+	CPU_ATHLON, /**< AMD P6 series */
+	CPU_TIMER_INT, /**< CPU using the timer interrupt */
+	CPU_RTC, /**< other CPU to use the RTC */
+	CPU_P4,  /**< Pentium 4 / Xeon series */
+	CPU_IA64, /**< Generic IA64 */
+	CPU_IA64_1, /**< IA64 Merced */
+	CPU_IA64_2, /**< IA64 McKinley */
+	CPU_HAMMER, /**< AMD Hammer family */
+	CPU_P4_HT2, /**< Pentium 4 / Xeon series with 2 hyper-threads */
+	CPU_AXP_EV4, /**< Alpha EV4 family */
+	CPU_AXP_EV5, /**< Alpha EV5 family */
+	CPU_AXP_PCA56, /**< Alpha PCA56 family */
+	CPU_AXP_EV6, /**< Alpha EV6 family */
+	CPU_AXP_EV67, /**< Alpha EV67 family */
+	CPU_P6_MOBILE, /**< Pentium M series */
+	CPU_ARM_XSCALE1, /**< ARM XScale 1 */
+	CPU_ARM_XSCALE2, /**< ARM XScale 2 */
+	CPU_PPC64_POWER4, /**< ppc64 POWER4 family */
+	CPU_PPC64_POWER5, /**< ppc64 POWER5 family */
+	CPU_PPC64_POWER5p, /**< ppc64 Power5+ family */
+	CPU_PPC64_970, /**< ppc64 970 family */
+	CPU_MIPS_20K, /**< MIPS 20K */
+	CPU_MIPS_24K, /**< MIPS 24K */
+	CPU_MIPS_25K, /**< MIPS 25K */
+	CPU_MIPS_34K, /**< MIPS 34K */
+	CPU_MIPS_5K, /**< MIPS 5K */
+	CPU_MIPS_R10000, /**< MIPS R10000 */
+	CPU_MIPS_R12000, /**< MIPS R12000 */
+	CPU_MIPS_RM7000, /**< QED  RM7000 */
+	CPU_MIPS_RM9000, /**< PMC-Sierra RM9000 */
+	CPU_MIPS_SB1, /**< Broadcom SB1 */
+	CPU_MIPS_VR5432, /**< NEC VR5432 */
+	CPU_MIPS_VR5500, /**< MIPS VR5500, VR5532 and VR7701 */
+	CPU_PPC_E500,	/**< e500 */
+	CPU_PPC_E500_2,	/**< e500v2 */
+	CPU_CORE, /**< Core Solo / Duo series */
+	CPU_PPC_7450, /**< PowerPC G4 */
+	CPU_CORE_2, /**< Intel Core 2 */
+	CPU_PPC64_POWER6, /**< ppc64 POWER6 family */
+	CPU_PPC64_970MP, /**< ppc64 970MP */
+	CPU_PPC64_CELL, /**< ppc64 Cell Broadband Engine*/
+	CPU_FAMILY10, /**< AMD family 10 */
+ 	CPU_PPC64_PA6T, /**< ppc64 PA6T */
+	CPU_ARM_MPCORE, /**< ARM MPCore */
+	CPU_ARM_V6, /**< ARM V6 */
+	CPU_PPC64_POWER5pp,  /**< ppc64 Power5++ family */
+	CPU_PPC_E300, /**< e300 */
+	CPU_AVR32, /**< AVR32 */
+	MAX_CPU_TYPE
+} op_cpu;
+
+/**
+ * get the CPU type from the kernel
+ *
+ * returns CPU_NO_GOOD if the CPU could not be identified.
+ * This function can not work if the module is not loaded
+ */
+op_cpu op_get_cpu_type(void);
+
+/**
+ * get the cpu number based on string
+ * @param cpu_string with either the cpu type identifier or cpu type number
+ *
+ * The function returns CPU_NO_GOOD if no matching string was found.
+ */
+op_cpu op_get_cpu_number(char const * cpu_string);
+
+/**
+ * get the cpu string.
+ * @param cpu_type the cpu type identifier
+ *
+ * The function always return a valid char const * the core cpu denomination
+ * or "invalid cpu type" if cpu_type is not valid.
+ */
+char const * op_get_cpu_type_str(op_cpu cpu_type);
+
+/**
+ * op_get_cpu_name - get the cpu name
+ * @param cpu_type  the cpu identifier name
+ *
+ * The function always return a valid char const *
+ * Return the OProfile CPU name, e.g. "i386/pii"
+ */
+char const * op_get_cpu_name(op_cpu cpu_type);
+
+/**
+ * compute the number of counters available
+ * @param cpu_type numeric processor type
+ *
+ * returns 0 if the CPU could not be identified
+ */
+int op_get_nr_counters(op_cpu cpu_type);
+
+typedef enum {
+	OP_INTERFACE_NO_GOOD = -1,
+	OP_INTERFACE_24,
+	OP_INTERFACE_26
+} op_interface;
+
+/**
+ * get the INTERFACE used to communicate between daemon and the kernel
+ *
+ * returns OP_INTERFACE_NO_GOOD if the INTERFACE could not be identified.
+ * This function will identify the interface as OP_INTERFACE_NO_GOOD if
+ * the module is not loaded.
+ */
+op_interface op_get_interface(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* OP_CPU_TYPE_H */
diff --git a/libop/op_events.c b/libop/op_events.c
new file mode 100644
index 0000000..b4a10e7
--- /dev/null
+++ b/libop/op_events.c
@@ -0,0 +1,862 @@
+/**
+ * @file op_events.c
+ * Details of PMC profiling events
+ *
+ * You can have silliness here.
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include "op_events.h"
+#include "op_libiberty.h"
+#include "op_fileio.h"
+#include "op_string.h"
+#include "op_cpufreq.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+static LIST_HEAD(events_list);
+static LIST_HEAD(um_list);
+
+static char const * filename;
+static unsigned int line_nr;
+
+static void parse_error(char const * context)
+{
+	fprintf(stderr, "oprofile: parse error in %s, line %u\n",
+		filename, line_nr);
+	fprintf(stderr, "%s\n", context);
+	exit(EXIT_FAILURE);
+}
+
+
+static int parse_int(char const * str)
+{
+	int value;
+	if (sscanf(str, "%d", &value) != 1)
+		parse_error("expected decimal value");
+
+	return value;
+}
+
+
+static int parse_hex(char const * str)
+{
+	int value;
+	/* 0x/0X to force the use of hexa notation for field intended to
+	   be in hexadecimal */
+	if (sscanf(str, "0x%x", &value) != 1 &&
+	    sscanf(str, "0X%x", &value) != 1)
+		parse_error("expected hexadecimal value");
+
+	return value;
+}
+
+
+static u64 parse_long_hex(char const * str)
+{
+	u64 value;
+	if (sscanf(str, "%Lx", &value) != 1)
+		parse_error("expected long hexadecimal value");
+
+	fflush(stderr);
+	return value;
+}
+
+
+/* name:MESI type:bitmask default:0x0f */
+static void parse_um(struct op_unit_mask * um, char const * line)
+{
+	int seen_name = 0;
+	int seen_type = 0;
+       	int seen_default = 0;
+	char const * valueend = line + 1;
+       	char const * tagend = line + 1;
+	char const * start = line;
+
+	while (*valueend) {
+		valueend = skip_nonws(valueend);
+
+		while (*tagend != ':' && *tagend)
+			++tagend;
+
+		if (valueend == tagend)
+			break;
+
+		if (!*tagend)
+			parse_error("parse_um() expected :value");
+
+		++tagend;
+
+		if (strisprefix(start, "name")) {
+			if (seen_name)
+				parse_error("duplicate name: tag");
+			seen_name = 1;
+			um->name = op_xstrndup(tagend, valueend - tagend);
+		} else if (strisprefix(start, "type")) {
+			if (seen_type)
+				parse_error("duplicate type: tag");
+			seen_type = 1;
+			if (strisprefix(tagend, "mandatory")) {
+				um->unit_type_mask = utm_mandatory;
+			} else if (strisprefix(tagend, "bitmask")) {
+				um->unit_type_mask = utm_bitmask;
+			} else if (strisprefix(tagend, "exclusive")) {
+				um->unit_type_mask = utm_exclusive;
+			} else {
+				parse_error("invalid unit mask type");
+			}
+		} else if (strisprefix(start, "default")) {
+			if (seen_default)
+				parse_error("duplicate default: tag");
+			seen_default = 1;
+			um->default_mask = parse_hex(tagend);
+		} else {
+			parse_error("invalid unit mask tag");
+		}
+
+		valueend = skip_ws(valueend);
+		tagend = valueend;
+		start = valueend;
+	}
+}
+
+
+/* \t0x08 (M)odified cache state */
+static void parse_um_entry(struct op_described_um * entry, char const * line)
+{
+	char const * c = line;
+
+	c = skip_ws(c);
+	entry->value = parse_hex(c);
+	c = skip_nonws(c);
+
+	if (!*c)
+		parse_error("invalid unit mask entry");
+
+	c = skip_ws(c);
+
+	if (!*c)
+		parse_error("invalid unit mask entry");
+
+	entry->desc = xstrdup(c);
+}
+
+
+static struct op_unit_mask * new_unit_mask(void)
+{
+	struct op_unit_mask * um = xmalloc(sizeof(struct op_unit_mask));
+	memset(um, '\0', sizeof(struct op_unit_mask));
+	list_add_tail(&um->um_next, &um_list);
+
+	return um;
+}
+
+
+/*
+ * name:zero type:mandatory default:0x0
+ * \t0x0 No unit mask
+ */
+static void read_unit_masks(char const * file)
+{
+	struct op_unit_mask * um = NULL;
+	char * line;
+	FILE * fp = fopen(file, "r");
+
+	if (!fp) {
+		fprintf(stderr,
+			"oprofile: could not open unit mask description file %s\n", file);
+		exit(EXIT_FAILURE);
+	}
+
+	filename = file;
+	line_nr = 1;
+
+	line = op_get_line(fp);
+
+	while (line) {
+		if (empty_line(line) || comment_line(line))
+			goto next;
+
+		if (line[0] != '\t') {
+			um = new_unit_mask();
+			parse_um(um, line);
+		} else {
+			if (!um)
+				parse_error("no unit mask name line");
+			if (um->num >= MAX_UNIT_MASK)
+				parse_error("oprofile: maximum unit mask entries exceeded");
+
+			parse_um_entry(&um->um[um->num], line);
+			++(um->num);
+		}
+
+next:
+		free(line);
+		line = op_get_line(fp);
+		++line_nr;
+	}
+
+	fclose(fp);
+}
+
+
+static u32 parse_counter_mask(char const * str)
+{
+	u32 mask = 0;
+	char const * numstart = str;
+
+	while (*numstart) {
+		mask |= 1 << parse_int(numstart);
+
+		while (*numstart && *numstart != ',')
+			++numstart;
+		/* skip , unless we reach eos */
+		if (*numstart)
+			++numstart;
+
+		numstart = skip_ws(numstart);
+	}
+
+	return mask;
+}
+
+
+static struct op_unit_mask * find_um(char const * value)
+{
+	struct list_head * pos;
+
+	list_for_each(pos, &um_list) {
+		struct op_unit_mask * um = list_entry(pos, struct op_unit_mask, um_next);
+		if (strcmp(value, um->name) == 0)
+			return um;
+	}
+
+	fprintf(stderr, "oprofile: could not find unit mask %s\n", value);
+	exit(EXIT_FAILURE);
+}
+
+
+/* parse either a "tag:value" or a ": trailing description string" */
+static int next_token(char const ** cp, char ** name, char ** value)
+{
+	size_t tag_len;
+	size_t val_len;
+	char const * c = *cp;
+	char const * end;
+	char const * colon;
+
+	c = skip_ws(c);
+	end = colon = c;
+	end = skip_nonws(end);
+
+	colon = strchr(colon, ':');
+
+	if (!colon) {
+		if (*c)
+			parse_error("next_token(): garbage at end of line");
+		return 0;
+	}
+
+	if (colon >= end)
+		parse_error("next_token() expected ':'");
+
+	tag_len = colon - c;
+	val_len = end - (colon + 1);
+
+	if (!tag_len) {
+		/* : trailing description */
+		end = skip_ws(end);
+		*name = xstrdup("desc");
+		*value = xstrdup(end);
+		end += strlen(end);
+	} else {
+		/* tag:value */
+		*name = op_xstrndup(c, tag_len);
+		*value = op_xstrndup(colon + 1, val_len);
+		end = skip_ws(end);
+	}
+
+	*cp = end;
+	return 1;
+}
+
+
+static struct op_event * new_event(void)
+{
+	struct op_event * event = xmalloc(sizeof(struct op_event));
+	memset(event, '\0', sizeof(struct op_event));
+	list_add_tail(&event->event_next, &events_list);
+
+	return event;
+}
+
+
+/* event:0x00 counters:0 um:zero minimum:4096 name:ISSUES : Total issues */
+static void read_events(char const * file)
+{
+	struct op_event * event = NULL;
+	char * line;
+	char * name;
+	char * value;
+	char const * c;
+	int seen_event, seen_counters, seen_um, seen_minimum, seen_name;
+	FILE * fp = fopen(file, "r");
+
+	if (!fp) {
+		fprintf(stderr, "oprofile: could not open event description file %s\n", file);
+		exit(EXIT_FAILURE);
+	}
+
+	filename = file;
+	line_nr = 1;
+
+	line = op_get_line(fp);
+
+	while (line) {
+		if (empty_line(line) || comment_line(line))
+			goto next;
+
+		seen_name = 0;
+		seen_event = 0;
+		seen_counters = 0;
+		seen_um = 0;
+		seen_minimum = 0;
+		event = new_event();
+
+		c = line;
+		while (next_token(&c, &name, &value)) {
+			if (strcmp(name, "name") == 0) {
+				if (seen_name)
+					parse_error("duplicate name: tag");
+				seen_name = 1;
+				if (strchr(value, '/') != NULL)
+					parse_error("invalid event name");
+				if (strchr(value, '.') != NULL)
+					parse_error("invalid event name");
+				event->name = value;
+			} else if (strcmp(name, "event") == 0) {
+				if (seen_event)
+					parse_error("duplicate event: tag");
+				seen_event = 1;
+				event->val = parse_hex(value);
+				free(value);
+			} else if (strcmp(name, "counters") == 0) {
+				if (seen_counters)
+					parse_error("duplicate counters: tag");
+				seen_counters = 1;
+				event->counter_mask = parse_counter_mask(value);
+				free(value);
+			} else if (strcmp(name, "um") == 0) {
+				if (seen_um)
+					parse_error("duplicate um: tag");
+				seen_um = 1;
+				event->unit = find_um(value);
+				event->unit->used = 1;
+				free(value);
+			} else if (strcmp(name, "minimum") == 0) {
+				if (seen_minimum)
+					parse_error("duplicate minimum: tag");
+				seen_minimum = 1;
+				event->min_count = parse_int(value);
+				free(value);
+			} else if (strcmp(name, "desc") == 0) {
+				event->desc = value;
+			} else {
+				parse_error("unknown tag");
+			}
+
+			free(name);
+		}
+next:
+		free(line);
+		line = op_get_line(fp);
+		++line_nr;
+	}
+
+	fclose(fp);
+}
+
+
+/* usefull for make check */
+static void check_unit_mask(struct op_unit_mask const * um,
+	char const * cpu_name)
+{
+	u32 i;
+
+	if (!um->used) {
+		fprintf(stderr, "um %s is not used\n", um->name);
+		exit(EXIT_FAILURE);
+	}
+
+	if (um->unit_type_mask == utm_mandatory && um->num != 1) {
+		fprintf(stderr, "mandatory um %s doesn't contain exactly one "
+			"entry (%s)\n", um->name, cpu_name);
+		exit(EXIT_FAILURE);
+	} else if (um->unit_type_mask == utm_bitmask) {
+		u32 default_mask = um->default_mask;
+		for (i = 0; i < um->num; ++i)
+			default_mask &= ~um->um[i].value;
+
+		if (default_mask) {
+			fprintf(stderr, "um %s default mask is not valid "
+				"(%s)\n", um->name, cpu_name);
+			exit(EXIT_FAILURE);
+		}
+	} else {
+		for (i = 0; i < um->num; ++i) {
+			if (um->default_mask == um->um[i].value)
+				break;
+		}
+
+		if (i == um->num) {
+			fprintf(stderr, "exclusive um %s default value is not "
+				"valid (%s)\n", um->name, cpu_name);
+			exit(EXIT_FAILURE);
+		}
+	}
+}
+
+
+static void load_events(op_cpu cpu_type)
+{
+	char const * cpu_name = op_get_cpu_name(cpu_type);
+	char * event_dir;
+	char * event_file;
+	char * um_file;
+	char * dir;
+	struct list_head * pos;
+
+	if (!list_empty(&events_list))
+		return;
+
+	dir = getenv("OPROFILE_EVENTS_DIR");
+	if (dir == NULL)
+		dir = OP_DATADIR;
+
+	event_dir = xmalloc(strlen(dir) + strlen("/") + strlen(cpu_name) +
+                            strlen("/") + 1);
+	strcpy(event_dir, dir);
+	strcat(event_dir, "/"); 
+
+	strcat(event_dir, cpu_name);
+	strcat(event_dir, "/");
+
+	event_file = xmalloc(strlen(event_dir) + strlen("events") + 1);
+	strcpy(event_file, event_dir);
+	strcat(event_file, "events");
+
+	um_file = xmalloc(strlen(event_dir) + strlen("unit_masks") + 1);
+	strcpy(um_file, event_dir);
+	strcat(um_file, "unit_masks");
+
+	read_unit_masks(um_file);
+	read_events(event_file);
+
+	/* sanity check: all unit mask must be used */
+	list_for_each(pos, &um_list) {
+		struct op_unit_mask * um = list_entry(pos, struct op_unit_mask, um_next);
+
+		check_unit_mask(um, cpu_name);
+	}
+	
+	free(um_file);
+	free(event_file);
+	free(event_dir);
+}
+
+
+struct list_head * op_events(op_cpu cpu_type)
+{
+	load_events(cpu_type);
+	return &events_list;
+}
+
+
+static void delete_unit_mask(struct op_unit_mask * unit)
+{
+	u32 cur;
+	for (cur = 0 ; cur < unit->num ; ++cur) {
+		if (unit->um[cur].desc)
+			free(unit->um[cur].desc);
+	}
+
+	if (unit->name)
+		free(unit->name);
+
+	list_del(&unit->um_next);
+	free(unit);
+}
+
+
+static void delete_event(struct op_event * event)
+{
+	if (event->name)
+		free(event->name);
+	if (event->desc)
+		free(event->desc);
+
+	list_del(&event->event_next);
+	free(event);
+}
+
+
+void op_free_events(void)
+{
+	struct list_head * pos, * pos2;
+	list_for_each_safe(pos, pos2, &events_list) {
+		struct op_event * event = list_entry(pos, struct op_event, event_next);
+		delete_event(event);
+	}
+
+	list_for_each_safe(pos, pos2, &um_list) {
+		struct op_unit_mask * unit = list_entry(pos, struct op_unit_mask, um_next);
+		delete_unit_mask(unit);
+	}
+}
+
+
+static struct op_event * find_event(u32 nr)
+{
+	struct list_head * pos;
+
+	list_for_each(pos, &events_list) {
+		struct op_event * event = list_entry(pos, struct op_event, event_next);
+		if (event->val == nr)
+			return event;
+	}
+
+	return NULL;
+}
+
+
+static FILE * open_event_mapping_file(char const * cpu_name) 
+{
+	char * ev_map_file;
+	char * dir;
+	dir = getenv("OPROFILE_EVENTS_DIR");
+	if (dir == NULL)
+		dir = OP_DATADIR;
+
+	ev_map_file = xmalloc(strlen(dir) + strlen("/") + strlen(cpu_name) +
+	                    strlen("/") + + strlen("event_mappings") + 1);
+	strcpy(ev_map_file, dir);
+	strcat(ev_map_file, "/");
+
+	strcat(ev_map_file, cpu_name);
+	strcat(ev_map_file, "/");
+	strcat(ev_map_file, "event_mappings");
+	filename = ev_map_file;
+	return (fopen(ev_map_file, "r"));
+}
+
+
+/**
+ *  This function is PPC64-specific.
+ */
+static char const * get_mapping(u32 nr, FILE * fp) 
+{
+	char * line;
+	char * name;
+	char * value;
+	char const * c;
+	char * map = NULL;
+	int seen_event = 0, seen_mmcr0 = 0, seen_mmcr1 = 0, seen_mmcra = 0;
+	u32 mmcr0 = 0;
+	u64 mmcr1 = 0;
+	u32 mmcra = 0;
+	int event_found = 0;
+
+	line_nr = 1;
+	line = op_get_line(fp);
+	while (line && !event_found) {
+		if (empty_line(line) || comment_line(line))
+			goto next;
+
+		seen_event = 0;
+		seen_mmcr0 = 0;
+		seen_mmcr1 = 0;
+		seen_mmcra = 0;
+		mmcr0 = 0;
+		mmcr1 = 0;
+		mmcra = 0;
+
+		c = line;
+		while (next_token(&c, &name, &value)) {
+			if (strcmp(name, "event") == 0) {
+				u32 evt;
+				if (seen_event)
+					parse_error("duplicate event tag");
+				seen_event = 1;
+				evt = parse_hex(value);
+				if (evt == nr)
+					event_found = 1;
+				free(value);
+			} else if (strcmp(name, "mmcr0") == 0) {
+				if (seen_mmcr0)
+					parse_error("duplicate mmcr0 tag");
+				seen_mmcr0 = 1;
+				mmcr0 = parse_hex(value);
+				free(value);
+			} else if (strcmp(name, "mmcr1") == 0) {
+				if (seen_mmcr1)
+					parse_error("duplicate mmcr1: tag");
+				seen_mmcr1 = 1;
+				mmcr1 = parse_long_hex(value);
+				free(value);
+			} else if (strcmp(name, "mmcra") == 0) {
+				if (seen_mmcra)
+					parse_error("duplicate mmcra: tag");
+				seen_mmcra = 1;
+				mmcra = parse_hex(value);
+				free(value);
+			} else {
+				parse_error("unknown tag");
+			}
+
+			free(name);
+		}
+next:
+		free(line);
+		line = op_get_line(fp);
+		++line_nr;
+	}
+	if (event_found) {
+		if (!seen_mmcr0 || !seen_mmcr1 || !seen_mmcra) {
+			fprintf(stderr, "Error: Missing information in line %d of event mapping file %s\n", line_nr, filename);
+			exit(EXIT_FAILURE);
+		}
+		map = xmalloc(70);
+		snprintf(map, 70, "mmcr0:%u mmcr1:%Lu mmcra:%u",
+		         mmcr0, mmcr1, mmcra);
+	}
+
+	return map;
+}
+
+
+char const * find_mapping_for_event(u32 nr, op_cpu cpu_type)
+{
+	char const * cpu_name = op_get_cpu_name(cpu_type);
+	FILE * fp = open_event_mapping_file(cpu_name);
+	char const * map = NULL;
+	switch (cpu_type) {
+		case CPU_PPC64_PA6T:
+		case CPU_PPC64_970:
+		case CPU_PPC64_970MP:
+		case CPU_PPC64_POWER4:
+		case CPU_PPC64_POWER5:
+		case CPU_PPC64_POWER5p:
+		case CPU_PPC64_POWER5pp:
+		case CPU_PPC64_POWER6:
+			if (!fp) {
+				fprintf(stderr, "oprofile: could not open event mapping file %s\n", filename);
+				exit(EXIT_FAILURE);
+			} else {
+				map = get_mapping(nr, fp);
+			}
+			break;			
+		default:
+			break;
+	}
+
+	if (fp)
+		fclose(fp);
+
+	return map;
+}
+
+
+struct op_event * find_event_by_name(char const * name)
+{
+	struct list_head * pos;
+
+	list_for_each(pos, &events_list) {
+		struct op_event * event = list_entry(pos, struct op_event, event_next);
+		if (strcmp(event->name, name) == 0)
+			return event;
+	}
+
+	return NULL;
+}
+
+
+struct op_event * op_find_event(op_cpu cpu_type, u32 nr)
+{
+	struct op_event * event;
+
+	load_events(cpu_type);
+
+	event = find_event(nr);
+
+	return event;
+}
+
+
+int op_check_events(int ctr, u32 nr, u32 um, op_cpu cpu_type)
+{
+	int ret = OP_OK_EVENT;
+	struct op_event * event;
+	size_t i;
+	u32 ctr_mask = 1 << ctr;
+
+	load_events(cpu_type);
+
+	event = find_event(nr);
+
+	if (!event) {
+		ret |= OP_INVALID_EVENT;
+		return ret;
+	}
+
+	if ((event->counter_mask & ctr_mask) == 0)
+		ret |= OP_INVALID_COUNTER;
+
+	if (event->unit->unit_type_mask == utm_bitmask) {
+		for (i = 0; i < event->unit->num; ++i)
+			um &= ~(event->unit->um[i].value);			
+		
+		if (um)
+			ret |= OP_INVALID_UM;
+
+	} else {
+		for (i = 0; i < event->unit->num; ++i) {
+			if (event->unit->um[i].value == um)
+				break;
+		}
+
+		if (i == event->unit->num)
+			ret |= OP_INVALID_UM;
+	}
+
+	return ret;
+}
+
+
+void op_default_event(op_cpu cpu_type, struct op_default_event_descr * descr)
+{
+	descr->name = "";
+	descr->um = 0x0;
+	/* A fixed value of CPU cycles; this should ensure good
+	 * granulity even on faster CPUs, though it will generate more
+	 * interrupts.
+	 */
+	descr->count = 100000;
+
+	switch (cpu_type) {
+		case CPU_PPRO:
+		case CPU_PII:
+		case CPU_PIII:
+		case CPU_P6_MOBILE:
+		case CPU_CORE:
+		case CPU_CORE_2:
+		case CPU_ATHLON:
+		case CPU_HAMMER:
+		case CPU_FAMILY10:
+			descr->name = "CPU_CLK_UNHALTED";
+			break;
+
+		case CPU_RTC:
+			descr->name = "RTC_INTERRUPTS";
+			descr->count = 1024;
+			break;
+
+		case CPU_P4:
+		case CPU_P4_HT2:
+			descr->name = "GLOBAL_POWER_EVENTS";
+			descr->um = 0x1;
+			break;
+
+		case CPU_IA64:
+		case CPU_IA64_1:
+		case CPU_IA64_2:
+			descr->count = 1000000;
+			descr->name = "CPU_CYCLES";
+			break;
+
+		case CPU_AXP_EV4:
+		case CPU_AXP_EV5:
+		case CPU_AXP_PCA56:
+		case CPU_AXP_EV6:
+		case CPU_AXP_EV67:
+			descr->name = "CYCLES";
+			break;
+
+		// we could possibly use the CCNT
+		case CPU_ARM_XSCALE1:
+		case CPU_ARM_XSCALE2:
+		case CPU_ARM_MPCORE:
+		case CPU_ARM_V6:
+		case CPU_AVR32:
+			descr->name = "CPU_CYCLES";
+			break;
+
+		case CPU_PPC64_PA6T:
+		case CPU_PPC64_970:
+		case CPU_PPC64_970MP:
+		case CPU_PPC_7450:
+		case CPU_PPC64_POWER4:
+		case CPU_PPC64_POWER5:
+		case CPU_PPC64_POWER6:
+		case CPU_PPC64_POWER5p:
+		case CPU_PPC64_POWER5pp:
+		case CPU_PPC64_CELL:
+			descr->name = "CYCLES";
+			break;
+
+		case CPU_MIPS_20K:
+			descr->name = "CYCLES";
+			break;
+
+		case CPU_MIPS_24K:
+			descr->name = "INSTRUCTIONS";
+			break;
+
+		case CPU_MIPS_34K:
+			descr->name = "INSTRUCTIONS";
+			break;
+
+		case CPU_MIPS_5K:
+		case CPU_MIPS_25K:
+			descr->name = "CYCLES";
+			break;
+
+		case CPU_MIPS_R10000:
+		case CPU_MIPS_R12000:
+			descr->name = "INSTRUCTIONS_GRADUATED";
+			break;
+
+		case CPU_MIPS_RM7000:
+		case CPU_MIPS_RM9000:
+			descr->name = "INSTRUCTIONS_ISSUED";
+			break;
+
+		case CPU_MIPS_SB1:
+			descr->name = "INSN_SURVIVED_STAGE7";
+			break;
+
+		case CPU_MIPS_VR5432:
+		case CPU_MIPS_VR5500:
+			descr->name = "INSTRUCTIONS_EXECUTED";
+			break;
+
+		case CPU_PPC_E500:
+		case CPU_PPC_E500_2:
+		case CPU_PPC_E300:
+			descr->name = "CPU_CLK";
+			break;
+
+		// don't use default, if someone add a cpu he wants a compiler
+		// warning if he forgets to handle it here.
+		case CPU_TIMER_INT:
+		case CPU_NO_GOOD:
+		case MAX_CPU_TYPE:
+			break;
+	}
+}
diff --git a/libop/op_events.h b/libop/op_events.h
new file mode 100644
index 0000000..f6462fc
--- /dev/null
+++ b/libop/op_events.h
@@ -0,0 +1,125 @@
+/**
+ * @file op_events.h
+ * Details of PMC profiling events
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_EVENTS_H
+#define OP_EVENTS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "op_cpu_type.h"
+#include "op_types.h"
+#include "op_list.h"
+
+/** Describe an unit mask type. Events can optionally use a filter called
+ * the unit mask. the mask type can be a bitmask or a discrete value */
+enum unit_mask_type {
+	utm_mandatory,		/**< useless but required by the hardware */
+	utm_exclusive,		/**< only one of the values is allowed */
+	utm_bitmask		/**< bitmask */
+};
+
+/** up to thirty two allowed unit masks */
+#define MAX_UNIT_MASK 32
+
+
+/** Describe an unit mask. */
+struct op_unit_mask {
+	char * name;		/**< name of unit mask type */
+	u32 num;		/**< number of possible unit masks */
+	enum unit_mask_type unit_type_mask;
+	u32 default_mask;	/**< only the gui use it */
+	struct op_described_um {
+		u32 value;
+		char * desc;
+	} um[MAX_UNIT_MASK];
+	struct list_head um_next; /**< next um in list */
+	int used;                 /**< used by events file parser */
+};
+
+
+/** Describe an event. */
+struct op_event {
+	u32 counter_mask;	/**< bitmask of allowed counter  */
+	u32 val;		/**< event number */
+	/** which unit mask if any allowed */
+	struct op_unit_mask * unit;			
+	char * name;		/**< the event name */
+	char * desc;      	/**< the event description */
+	int min_count;		/**< minimum counter value allowed */
+	struct list_head event_next;   /**< next event in list */
+};
+
+/** Return the known events list. Idempotent */
+struct list_head * op_events(op_cpu cpu_type);
+
+/** Find a given event, returns NULL on error */
+struct op_event * op_find_event(op_cpu cpu_type, u32 nr);
+
+/** Find a given event by name */
+struct op_event * find_event_by_name(char const * name);
+
+/**
+ * Find a mapping for a given event ID for architectures requiring additional information
+ * from what is held in the events file. 
+ */
+char const * find_mapping_for_event(u32 val, op_cpu cpu_type);
+
+
+/** op_check_events() return code */
+enum op_event_check {
+	OP_OK_EVENT = 0, /**< event is valid and allowed */
+	OP_INVALID_EVENT = 1, /**< event number is invalid */
+	OP_INVALID_UM = 2, /**< unit mask is invalid */
+	OP_INVALID_COUNTER = 4 /**< event is not allowed for the given counter */
+};
+
+/**
+ * sanity check event values
+ * @param ctr counter number
+ * @param event value for counter
+ * @param um unit mask for counter
+ * @param cpu_type processor type
+ *
+ * Check that the counter event and unit mask values are allowed.
+ *
+ * The function returns bitmask of failure cause 0 otherwise
+ *
+ * \sa op_cpu, OP_EVENTS_OK
+ */
+int op_check_events(int ctr, u32 event, u32 um, op_cpu cpu_type);
+
+/**
+ * free memory used by any call to above function. Need to be called only once
+ */
+void op_free_events(void);
+
+struct op_default_event_descr {
+	char * name;
+	unsigned long count;
+	unsigned long um;
+};
+
+/**
+ * op_default_event - return the details of the default event
+ * @param cpu_type  cpu type
+ * @param descr filled event description
+ *
+ * Fills in the event description if applicable
+ */
+void op_default_event(op_cpu cpu_type, struct op_default_event_descr * descr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* OP_EVENTS_H */
diff --git a/libop/op_get_interface.c b/libop/op_get_interface.c
new file mode 100644
index 0000000..bdf72a5
--- /dev/null
+++ b/libop/op_get_interface.c
@@ -0,0 +1,32 @@
+/**
+ * @file op_get_interface.c
+ * Determine which oprofile kernel interface used
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Will Cohen
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "op_cpu_type.h"
+#include "op_file.h"
+
+op_interface op_get_interface(void)
+{
+	static op_interface current_interface = OP_INTERFACE_NO_GOOD;
+
+	if (current_interface != OP_INTERFACE_NO_GOOD)
+		return current_interface;
+
+	if (op_file_readable("/proc/sys/dev/oprofile/cpu_type")) {
+		current_interface = OP_INTERFACE_24;
+	} else if (op_file_readable("/dev/oprofile/cpu_type")) {
+		current_interface = OP_INTERFACE_26;
+	}
+
+	return current_interface;
+}
diff --git a/libop/op_hw_config.h b/libop/op_hw_config.h
new file mode 100644
index 0000000..169b36b
--- /dev/null
+++ b/libop/op_hw_config.h
@@ -0,0 +1,30 @@
+/**
+ * @file op_hw_config.h
+ * Configuration parameters that are dependent on CPU/architecture
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_HW_CONFIG_H
+#define OP_HW_CONFIG_H
+
+/** maximum number of counters, up to 4 for Athlon (18 for P4). The primary
+ * use of this variable is for static/local array dimension. Never use it in
+ * loop or in array index access/index checking unless you know what you
+ * made. */
+#ifdef __alpha__
+#define OP_MAX_COUNTERS	20
+#else
+#define OP_MAX_COUNTERS	8
+#endif
+
+/** maximum number of events between interrupts. Counters are 40 bits, but
+ * for convenience we only use 32 bits. The top bit is used for overflow
+ * detection, so user can set up to (2^31)-1 */
+#define OP_MAX_PERF_COUNT	2147483647UL
+
+#endif /* OP_HW_CONFIG_H */
diff --git a/libop/op_interface.h b/libop/op_interface.h
new file mode 100644
index 0000000..fa2ecbd
--- /dev/null
+++ b/libop/op_interface.h
@@ -0,0 +1,87 @@
+/**
+ * @file op_interface.h
+ *
+ * Module / user space interface for 2.4
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_INTERFACE_H
+#define OP_INTERFACE_H
+
+#include "op_config.h"
+#include "op_types.h"
+
+/*@{\name notifications types encoded in op_note::type */
+/** fork(),vfork(),clone() */
+#define OP_FORK 1
+/** mapping */
+#define OP_MAP 2
+/** execve() */
+#define OP_EXEC 4
+/** init_module() */
+#define OP_DROP_MODULES 8
+/** exit() */
+#define OP_EXIT 16
+/*@}*/
+
+/** Data type to transfer samples counts from the module to the daemon */
+struct op_sample {
+	unsigned long eip; /**< eip value where occur interrupt */
+	u32 counter; /**< counter nr */
+	u32 pid; /**< 32 bits can hold any pid */
+	u32 tgid; /**< always equal to pid for kernel < 2.4.0 */
+};
+
+/** the current kernel-side profiler state */
+enum oprof_state {
+	STOPPED = 0,
+	STOPPING = 1,
+	RUNNING = 2
+};
+ 
+/**
+ * The head structure of a kernel sample buffer.
+ */
+struct op_buffer_head {
+	int cpu_nr; /**< the CPU number of this buffer */
+	size_t count; /**< number of samples in this buffer */
+	enum oprof_state state; /**< current profiler state */
+	struct op_sample buffer[0]; /**< the sample buffer */
+} __attribute__((__packed__));
+	 
+/**
+ * Data type used by the module to notify daemon of fork/exit/mapping etc.
+ * Meanings of fields depend on the type of notification encoded in the type
+ * field.
+ * \sa OP_FORK, OP_EXEC, OP_MAP, OP_DROP_MODULES and OP_EXIT
+ */
+struct op_note {
+	unsigned long   addr;
+	unsigned long   len;
+	unsigned long   offset;
+	unsigned int    hash;
+	unsigned int	pid;
+	unsigned int    tgid;
+	unsigned short	type;
+};
+
+/**
+ * A path component. Directory name are stored as a stack of path components.
+ * Note than the name index acts also as an unique identifier
+ */
+struct op_hash_index {
+	/** index inside the string pool */
+	u32 name;
+	/** parent component, zero if this component is the root */
+	u32 parent;
+} __attribute__((__packed__));
+
+/** size of hash map in bytes */
+#define OP_HASH_MAP_SIZE (OP_HASH_MAP_NR * sizeof(struct op_hash_index) + POOL_SIZE)
+
+#endif /* OP_INTERFACE_H */
diff --git a/libop/op_mangle.c b/libop/op_mangle.c
new file mode 100644
index 0000000..1efe5b1
--- /dev/null
+++ b/libop/op_mangle.c
@@ -0,0 +1,104 @@
+/**
+ * @file op_mangle.c
+ * Mangling of sample file names
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include "op_mangle.h"
+
+#include <string.h>
+#include <stdio.h>
+
+#include "op_libiberty.h"
+
+#include "op_sample_file.h"
+#include "op_config.h"
+
+static void append_image(char * dest, int flags, int anon, char const * name, char const * anon_name)
+{
+	if ((flags & MANGLE_KERNEL) && !strchr(name, '/')) {
+		strcat(dest, "{kern}/");
+	} else if (anon) {
+		strcat(dest, "{anon:");
+		strcat(dest, anon_name);
+		strcat(dest,"}/");
+	} else {
+		strcat(dest, "{root}/");
+	}
+
+	strcat(dest, name);
+	strcat(dest, "/");
+}
+
+char * op_mangle_filename(struct mangle_values const * values)
+{
+	char * mangled;
+	size_t len;
+	int anon = values->flags & MANGLE_ANON;
+	int cg_anon = values->flags & MANGLE_CG_ANON;
+	/* if dep_name != image_name we need to invert them (and so revert them
+	 * unconditionally because if they are equal it doesn't hurt to invert
+	 * them), see P:3, FIXME: this is a bit weirds, we prolly need to
+	 * reword pp_interface */
+	char const * image_name = values->dep_name;
+	char const * anon_name = values->anon_name;
+	char const * dep_name = values->image_name;
+	char const * cg_image_name = values->cg_image_name;
+
+	len = strlen(op_samples_current_dir) + strlen(dep_name) + 1
+		+ strlen(values->event_name) + 1 + strlen(image_name) + 1;
+
+	if (values->flags & MANGLE_CALLGRAPH)
+		len += strlen(cg_image_name) + 1;
+
+	if (anon || cg_anon)
+		len += strlen(anon_name);
+
+	/* provision for tgid, tid, unit_mask, cpu and some {root}, {dep},
+	 * {kern}, {anon} and {cg} marker */
+	/* FIXME: too ugly */
+	len += 256;
+
+	mangled = xmalloc(len);
+
+	strcpy(mangled, op_samples_current_dir);
+	append_image(mangled, values->flags, 0, image_name, anon_name);
+
+	strcat(mangled, "{dep}" "/");
+	append_image(mangled, values->flags, anon, dep_name, anon_name);
+
+	if (values->flags & MANGLE_CALLGRAPH) {
+		strcat(mangled, "{cg}" "/");
+		append_image(mangled, values->flags, cg_anon,
+		             cg_image_name, anon_name);
+	}
+
+	strcat(mangled, values->event_name);
+	sprintf(mangled + strlen(mangled), ".%d.%d.",
+	        values->count, values->unit_mask);
+
+	if (values->flags & MANGLE_TGID) {
+		sprintf(mangled + strlen(mangled), "%d.", values->tgid);
+	} else {
+		sprintf(mangled + strlen(mangled), "%s.", "all");
+	}
+
+	if (values->flags & MANGLE_TID) {
+		sprintf(mangled + strlen(mangled), "%d.", values->tid);
+	} else {
+		sprintf(mangled + strlen(mangled), "%s.", "all");
+	}
+
+	if (values->flags & MANGLE_CPU) {
+		sprintf(mangled + strlen(mangled), "%d", values->cpu);
+	} else {
+		sprintf(mangled + strlen(mangled), "%s", "all");
+	}
+
+	return mangled;
+}
diff --git a/libop/op_mangle.h b/libop/op_mangle.h
new file mode 100644
index 0000000..9b600dc
--- /dev/null
+++ b/libop/op_mangle.h
@@ -0,0 +1,66 @@
+/**
+ * @file op_mangle.h
+ * Mangling of sample file names
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_MANGLE_H
+#define OP_MANGLE_H
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum mangle_flags {
+	MANGLE_NONE      = 0,
+	MANGLE_CPU       = (1 << 0),
+	MANGLE_TGID      = (1 << 1),
+	MANGLE_TID       = (1 << 2),
+	MANGLE_KERNEL    = (1 << 3),
+	MANGLE_CALLGRAPH = (1 << 4),
+	MANGLE_ANON      = (1 << 5),
+	MANGLE_CG_ANON   = (1 << 6),
+};
+
+/**
+ * Temporary structure for passing parameters to
+ * op_mangle_filename.
+ */
+struct mangle_values {
+	int flags;
+
+	char const * image_name;
+	char const * anon_name;
+	char const * dep_name;
+	char const * cg_image_name;
+	char const * event_name;
+	int count;
+	unsigned int unit_mask;
+	pid_t tgid;
+	pid_t tid;
+	int cpu;
+};
+
+/**
+ * op_mangle_filename - mangle a sample filename
+ * @param values  parameters to use as mangling input
+ *
+ * See also PP:3 for the encoding scheme
+ *
+ * Returns a char* pointer to the mangled string. Caller
+ * is responsible for freeing this string.
+ */
+char * op_mangle_filename(struct mangle_values const * values);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* OP_MANGLE_H */
diff --git a/libop/op_parse_event.c b/libop/op_parse_event.c
new file mode 100644
index 0000000..920d617
--- /dev/null
+++ b/libop/op_parse_event.c
@@ -0,0 +1,120 @@
+/**
+ * @file op_parse_event.c
+ * event parsing
+ *
+ * You can have silliness here.
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "op_parse_event.h"
+#include "op_string.h"
+
+static char * next_part(char const ** str)
+{
+	char const * c;
+	char * ret;
+
+	if ((*str)[0] == '\0')
+		return NULL;
+
+	if ((*str)[0] == ':')
+		++(*str);
+
+	c = *str;
+
+	while (*c != '\0' && *c != ':')
+		++c;
+
+	if (c == *str)
+		return NULL;
+
+	ret = op_xstrndup(*str, c - *str);
+	*str += c - *str;
+	return ret;
+}
+
+
+static int parse_ulong(char const * str)
+{
+	unsigned long value;
+	char * end;
+	value = strtoul(str, &end, 0);
+	if (end && *end) {
+		fprintf(stderr, "Invalid event part %s\n", str);
+		exit(EXIT_FAILURE);
+	}
+
+	return value;
+}
+
+
+size_t parse_events(struct parsed_event * parsed_events, size_t max_events,
+                  char const * const * events)
+{
+	size_t i = 0;
+
+	while (events[i]) {
+		char const * cp = events[i];
+		char * part = next_part(&cp);
+
+		if (i >= max_events) {
+			fprintf(stderr, "Too many events specified: CPU "
+			        "only has %lu counters.\n",
+				(unsigned long) max_events);
+			exit(EXIT_FAILURE);
+		}
+
+		if (!part) {
+			fprintf(stderr, "Invalid event %s\n", cp);
+			exit(EXIT_FAILURE);
+		}
+
+		parsed_events[i].name = part;
+
+		part = next_part(&cp);
+
+		if (!part) {
+			fprintf(stderr, "Invalid count for event %s\n", events[i]);
+			exit(EXIT_FAILURE);
+		}
+
+		parsed_events[i].count = parse_ulong(part);
+		free(part);
+
+		parsed_events[i].unit_mask = 0;
+		part = next_part(&cp);
+
+		if (part) {
+			parsed_events[i].unit_mask = parse_ulong(part);
+			free(part);
+		}
+
+		parsed_events[i].kernel = 1;
+		part = next_part(&cp);
+
+		if (part) {
+			parsed_events[i].kernel = parse_ulong(part);
+			free(part);
+		}
+
+		parsed_events[i].user = 1;
+		part = next_part(&cp);
+
+		if (part) {
+			parsed_events[i].user = parse_ulong(part);
+			free(part);
+		}
+	
+		++i;
+	}
+
+	return i;
+}
diff --git a/libop/op_parse_event.h b/libop/op_parse_event.h
new file mode 100644
index 0000000..247a355
--- /dev/null
+++ b/libop/op_parse_event.h
@@ -0,0 +1,42 @@
+/**
+ * @file op_parse_event.h
+ * event parsing
+ *
+ * You can have silliness here.
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_PARSE_EVENT_H
+#define OP_PARSE_EVENT_H
+
+#include <stddef.h>
+
+struct parsed_event {
+	char * name;
+	int count;
+	int unit_mask;
+	int kernel;
+	int user;
+};
+
+/**
+ * @param parsed_events  array of events to fill in
+ * @param max_events  size of parsed_events
+ * @param events  null terminated array of events string on the form
+ *   event_name:count[:unit_mask:kernel:user]
+ *
+ * parse events given by the nil terminated array events and fill in
+ * parsed_events with results. Events validity are not checked except.
+ * A fatal error occur if number of events is greater than max_events.
+ *
+ * Return the number of events parsed.
+ */
+size_t parse_events(struct parsed_event * parsed_events, size_t max_events,
+                    char const * const * events);
+
+#endif /* !OP_PARSE_EVENT_H */
diff --git a/libop/op_sample_file.h b/libop/op_sample_file.h
new file mode 100644
index 0000000..4f9f1d0
--- /dev/null
+++ b/libop/op_sample_file.h
@@ -0,0 +1,42 @@
+/**
+ * @file op_sample_file.h
+ * Sample file format
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_SAMPLE_FILE_H
+#define OP_SAMPLE_FILE_H
+
+#include "op_types.h"
+
+#include <stdint.h>
+#include <time.h>
+
+/* header of the sample files */
+struct opd_header {
+	u8 magic[4];
+	u32 version;
+	u32 cpu_type;
+	u32 ctr_event;
+	u32 ctr_um;
+	u32 ctr_count;
+	// for cg file the from_cg_is_kernel
+	u32 is_kernel;
+	double cpu_speed;
+	time_t mtime;
+	u32 cg_to_is_kernel;
+	/* spu_profile=1 says sample file contains Cell BE SPU profile data */
+	u32 spu_profile;
+	uint64_t embedded_offset;
+	u64 anon_start;
+	u64 cg_to_anon_start;
+	/* binary compatibility reserve */
+	u32 reserved1[1];
+};
+
+#endif /* OP_SAMPLE_FILE_H */
diff --git a/libpopt/Android.mk b/libpopt/Android.mk
new file mode 100644
index 0000000..b20cf55
--- /dev/null
+++ b/libpopt/Android.mk
@@ -0,0 +1,15 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	findme.c \
+	popt.c \
+	poptconfig.c \
+	popthelp.c \
+	poptparse.c
+
+LOCAL_CFLAGS += -DHAVE_CONFIG_H
+
+LOCAL_MODULE := libpopt
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/libpopt/config.h b/libpopt/config.h
new file mode 100644
index 0000000..73a0817
--- /dev/null
+++ b/libpopt/config.h
@@ -0,0 +1,329 @@
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
+   systems. This function is required for `alloca.c' support on those systems.
+   */
+#undef CRAY_STACKSEG_END
+
+/* Define to 1 if using `alloca.c'. */
+#undef C_ALLOCA
+
+/* Define to 1 if translation of program messages to the user's native
+   language is requested. */
+#undef ENABLE_NLS
+
+/* Define to 1 if you have `alloca', as a function or macro. */
+#undef HAVE_ALLOCA
+
+/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
+   */
+#undef HAVE_ALLOCA_H
+
+/* Define to 1 if you have the <argz.h> header file. */
+#undef HAVE_ARGZ_H
+
+/* Define to 1 if you have the `asprintf' function. */
+#undef HAVE_ASPRINTF
+
+/* Define to 1 if you have the MacOS X function CFLocaleCopyCurrent in the
+   CoreFoundation framework. */
+#undef HAVE_CFLOCALECOPYCURRENT
+
+/* Define to 1 if you have the MacOS X function CFPreferencesCopyAppValue in
+   the CoreFoundation framework. */
+#undef HAVE_CFPREFERENCESCOPYAPPVALUE
+
+/* Define if the GNU dcgettext() function is already present or preinstalled.
+   */
+#undef HAVE_DCGETTEXT
+
+/* Define to 1 if you have the declaration of `feof_unlocked', and to 0 if you
+   don't. */
+#undef HAVE_DECL_FEOF_UNLOCKED
+
+/* Define to 1 if you have the declaration of `fgets_unlocked', and to 0 if
+   you don't. */
+#undef HAVE_DECL_FGETS_UNLOCKED
+
+/* Define to 1 if you have the declaration of `getc_unlocked', and to 0 if you
+   don't. */
+#undef HAVE_DECL_GETC_UNLOCKED
+
+/* Define to 1 if you have the declaration of `_snprintf', and to 0 if you
+   don't. */
+#undef HAVE_DECL__SNPRINTF
+
+/* Define to 1 if you have the declaration of `_snwprintf', and to 0 if you
+   don't. */
+#undef HAVE_DECL__SNWPRINTF
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the <float.h> header file. */
+#define HAVE_FLOAT_H 1
+
+/* Define to 1 if you have the `fwprintf' function. */
+#undef HAVE_FWPRINTF
+
+/* Define to 1 if you have the `getcwd' function. */
+#undef HAVE_GETCWD
+
+/* Define to 1 if you have the `getegid' function. */
+#undef HAVE_GETEGID
+
+/* Define to 1 if you have the `geteuid' function. */
+#undef HAVE_GETEUID
+
+/* Define to 1 if you have the `getgid' function. */
+#undef HAVE_GETGID
+
+/* Define to 1 if you have the `getpagesize' function. */
+#undef HAVE_GETPAGESIZE
+
+/* Define if the GNU gettext() function is already present or preinstalled. */
+#undef HAVE_GETTEXT
+
+/* Define to 1 if you have the `getuid' function. */
+#undef HAVE_GETUID
+
+/* Define if you have the iconv() function. */
+#undef HAVE_ICONV
+
+/* Define if you have the 'intmax_t' type in <stdint.h> or <inttypes.h>. */
+#undef HAVE_INTMAX_T
+
+/* Define if <inttypes.h> exists and doesn't clash with <sys/types.h>. */
+#define HAVE_INTTYPES_H 1
+
+/* Define if <inttypes.h> exists, doesn't clash with <sys/types.h>, and
+   declares uintmax_t. */
+#undef HAVE_INTTYPES_H_WITH_UINTMAX
+
+/* Define if you have <langinfo.h> and nl_langinfo(CODESET). */
+#undef HAVE_LANGINFO_CODESET
+
+/* Define if your <locale.h> file defines LC_MESSAGES. */
+#undef HAVE_LC_MESSAGES
+
+/* Define to 1 if you have the <libintl.h> header file. */
+#undef HAVE_LIBINTL_H
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define to 1 if you have the <locale.h> header file. */
+#undef HAVE_LOCALE_H
+
+/* Define if you have the 'long double' type. */
+#undef HAVE_LONG_DOUBLE
+
+/* Define if you have the 'long long' type. */
+#define HAVE_LONG_LONG 1
+
+/* Define to 1 if you have the <malloc.h> header file. */
+#define HAVE_MALLOC_H 1
+
+/* Define to 1 if you have the <mcheck.h> header file. */
+#undef HAVE_MCHECK_H
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `mempcpy' function. */
+#define HAVE_MEMPCPY 1
+
+/* Define to 1 if you have a working `mmap' system call. */
+#define HAVE_MMAP 1
+
+/* Define to 1 if you have the `mtrace' function. */
+#undef HAVE_MTRACE
+
+/* Define to 1 if you have the `munmap' function. */
+#define HAVE_MUNMAP 1
+
+/* Define to 1 if you have the <nl_types.h> header file. */
+#undef HAVE_NL_TYPES_H
+
+/* Define if your printf() function supports format strings with positions. */
+#define HAVE_POSIX_PRINTF 1
+
+/* Define to 1 if you have the `putenv' function. */
+#undef HAVE_PUTENV
+
+/* Define to 1 if you have the `setenv' function. */
+#undef HAVE_SETENV
+
+/* Define to 1 if you have the `setlocale' function. */
+#undef HAVE_SETLOCALE
+
+/* Define to 1 if you have the `setregid' function. */
+#undef HAVE_SETREGID
+
+/* Define to 1 if you have the `snprintf' function. */
+#define HAVE_SNPRINTF 1
+
+/* Define to 1 if you have the <stddef.h> header file. */
+#define HAVE_STDDEF_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define if <stdint.h> exists, doesn't clash with <sys/types.h>, and declares
+   uintmax_t. */
+#undef HAVE_STDINT_H_WITH_UINTMAX
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `stpcpy' function. */
+#undef HAVE_STPCPY
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#undef HAVE_STRCASECMP
+
+/* Define to 1 if you have the `strdup' function. */
+#undef HAVE_STRDUP
+
+/* Define to 1 if you have the `strerror' function. */
+#define HAVE_STRERROR 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strtoul' function. */
+#undef HAVE_STRTOUL
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#undef HAVE_SYS_PARAM_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the `tsearch' function. */
+#undef HAVE_TSEARCH
+
+/* Define if you have the 'uintmax_t' type in <stdint.h> or <inttypes.h>. */
+#undef HAVE_UINTMAX_T
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define if you have the 'unsigned long long' type. */
+#undef HAVE_UNSIGNED_LONG_LONG
+
+/* Define if you have the 'wchar_t' type. */
+#undef HAVE_WCHAR_T
+
+/* Define to 1 if you have the `wcslen' function. */
+#undef HAVE_WCSLEN
+
+/* Define if you have the 'wint_t' type. */
+#undef HAVE_WINT_T
+
+/* Define to 1 if you have the `__argz_count' function. */
+#undef HAVE___ARGZ_COUNT
+
+/* Define to 1 if you have the `__argz_next' function. */
+#undef HAVE___ARGZ_NEXT
+
+/* Define to 1 if you have the `__argz_stringify' function. */
+#undef HAVE___ARGZ_STRINGIFY
+
+/* Define to 1 if you have the `__fsetlocking' function. */
+#undef HAVE___FSETLOCKING
+
+/* Define to 1 if you have the `__secure_getenv' function. */
+#undef HAVE___SECURE_GETENV
+
+/* Define as const if the declaration of iconv() needs const. */
+#undef ICONV_CONST
+
+/* Define if integer division by zero raises signal SIGFPE. */
+#undef INTDIV0_RAISES_SIGFPE
+
+/* Name of package */
+#define PACKAGE "popt"
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Full path to popt top_srcdir. */
+#undef POPT_SOURCE_PATH
+
+/* Define if <inttypes.h> exists and defines unusable PRI* macros. */
+#undef PRI_MACROS_BROKEN
+
+/* Define to 1 if the C compiler supports function prototypes. */
+#undef PROTOTYPES
+
+/* Define as the maximum value of type 'size_t', if the system doesn't define
+   it. */
+#undef SIZE_MAX
+
+/* If using the C implementation of alloca, define if you know the
+   direction of stack growth for your system; otherwise it will be
+   automatically deduced at run-time.
+	STACK_DIRECTION > 0 => grows toward higher addresses
+	STACK_DIRECTION < 0 => grows toward lower addresses
+	STACK_DIRECTION = 0 => direction of growth unknown */
+#undef STACK_DIRECTION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Version number of package */
+#undef VERSION
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#undef _FILE_OFFSET_BITS
+
+/* Define for large files, on AIX-style hosts. */
+#undef _LARGE_FILES
+
+/* Define like PROTOTYPES; this can be used by system headers. */
+#undef __PROTOTYPES
+
+/* Define to empty if `const' does not conform to ANSI C. */
+#undef const
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+   calls it, or to nothing if 'inline' is not supported under any name.  */
+#ifndef __cplusplus
+#undef inline
+#endif
+
+/* Define to `long' if <sys/types.h> does not define. */
+#undef off_t
+
+/* Define as the type of the result of subtracting two pointers, if the system
+   doesn't define it. */
+#undef ptrdiff_t
+
+/* Define to empty if the C compiler doesn't support this keyword. */
+#undef signed
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+#undef size_t
+
+/* Define to unsigned long or unsigned long long if <stdint.h> and
+   <inttypes.h> don't define. */
+#undef uintmax_t
diff --git a/libpopt/findme.c b/libpopt/findme.c
new file mode 100644
index 0000000..78363e5
--- /dev/null
+++ b/libpopt/findme.c
@@ -0,0 +1,52 @@
+/** \ingroup popt
+ * \file popt/findme.c
+ */
+
+/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.rpm.org/pub/rpm/dist. */
+
+#include "system.h"
+#include "findme.h"
+#include <unistd.h>
+
+const char * findProgramPath(const char * argv0)
+{
+    char * path = getenv("PATH");
+    char * pathbuf;
+    char * start, * chptr;
+    char * buf;
+
+    if (argv0 == NULL) return NULL;	/* XXX can't happen */
+    /* If there is a / in the argv[0], it has to be an absolute path */
+    if (strchr(argv0, '/'))
+	return xstrdup(argv0);
+
+    if (path == NULL) return NULL;
+
+    start = pathbuf = alloca(strlen(path) + 1);
+    buf = malloc(strlen(path) + strlen(argv0) + sizeof("/"));
+    if (buf == NULL) return NULL;	/* XXX can't happen */
+    strcpy(pathbuf, path);
+
+    chptr = NULL;
+    /*@-branchstate@*/
+    do {
+	if ((chptr = strchr(start, ':')))
+	    *chptr = '\0';
+	sprintf(buf, "%s/%s", start, argv0);
+
+	if (!access(buf, X_OK))
+	    return buf;
+
+	if (chptr) 
+	    start = chptr + 1;
+	else
+	    start = NULL;
+    } while (start && *start);
+    /*@=branchstate@*/
+
+    free(buf);
+
+    return NULL;
+}
diff --git a/libpopt/findme.h b/libpopt/findme.h
new file mode 100644
index 0000000..a016b86
--- /dev/null
+++ b/libpopt/findme.h
@@ -0,0 +1,20 @@
+/** \ingroup popt
+ * \file popt/findme.h
+ */
+
+/* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.rpm.org/pub/rpm/dist. */
+
+#ifndef H_FINDME
+#define H_FINDME
+
+/**
+ * Return absolute path to executable by searching PATH.
+ * @param argv0		name of executable
+ * @return		(malloc'd) absolute path to executable (or NULL)
+ */
+/*@null@*/ const char * findProgramPath(/*@null@*/ const char * argv0)
+	/*@*/;
+
+#endif
diff --git a/libpopt/popt.c b/libpopt/popt.c
new file mode 100644
index 0000000..4f9e325
--- /dev/null
+++ b/libpopt/popt.c
@@ -0,0 +1,1262 @@
+/** \ingroup popt
+ * \file popt/popt.c
+ */
+
+/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from
+   ftp://ftp.rpm.org/pub/rpm/dist */
+
+#undef	MYDEBUG
+
+#include "system.h"
+
+#if HAVE_FLOAT_H
+#include <float.h>
+#endif
+#include <math.h>
+
+#include "findme.h"
+#include "poptint.h"
+
+#ifdef	MYDEBUG
+/*@unchecked@*/
+int _popt_debug = 0;
+#endif
+
+#if !defined(HAVE_STRERROR) && !defined(__LCLINT__)
+static char * strerror(int errno)
+{
+    extern int sys_nerr;
+    extern char * sys_errlist[];
+
+    if ((0 <= errno) && (errno < sys_nerr))
+	return sys_errlist[errno];
+    else
+	return POPT_("unknown errno");
+}
+#endif
+
+#ifdef MYDEBUG
+/*@unused@*/
+static void prtcon(const char *msg, poptContext con)
+{
+    if (msg) fprintf(stderr, "%s", msg);
+    fprintf(stderr, "\tcon %p os %p nextCharArg \"%s\" nextArg \"%s\" argv[%d] \"%s\"\n",
+	con, con->os,
+	(con->os->nextCharArg ? con->os->nextCharArg : ""),
+	(con->os->nextArg ? con->os->nextArg : ""),
+	con->os->next,
+	(con->os->argv && con->os->argv[con->os->next]
+		? con->os->argv[con->os->next] : ""));
+}
+#endif
+
+void poptSetExecPath(poptContext con, const char * path, int allowAbsolute)
+{
+    con->execPath = _free(con->execPath);
+    con->execPath = xstrdup(path);
+    con->execAbsolute = allowAbsolute;
+    /*@-nullstate@*/ /* LCL: con->execPath not NULL */
+    return;
+    /*@=nullstate@*/
+}
+
+static void invokeCallbacksPRE(poptContext con, const struct poptOption * opt)
+	/*@globals internalState@*/
+	/*@modifies internalState@*/
+{
+    if (opt != NULL)
+    for (; opt->longName || opt->shortName || opt->arg; opt++) {
+	if (opt->arg == NULL) continue;		/* XXX program error. */
+	if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+	    void * arg = opt->arg;
+/*@-branchstate@*/
+	    /* XXX sick hack to preserve pretense of ABI. */
+	    if (arg == poptHelpOptions) arg = poptHelpOptionsI18N;
+/*@=branchstate@*/
+	    /* Recurse on included sub-tables. */
+	    invokeCallbacksPRE(con, arg);
+	} else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK &&
+		   (opt->argInfo & POPT_CBFLAG_PRE))
+	{   /*@-castfcnptr@*/
+	    poptCallbackType cb = (poptCallbackType)opt->arg;
+	    /*@=castfcnptr@*/
+	    /* Perform callback. */
+	    /*@-noeffectuncon @*/
+	    cb(con, POPT_CALLBACK_REASON_PRE, NULL, NULL, opt->descrip);
+	    /*@=noeffectuncon @*/
+	}
+    }
+}
+
+static void invokeCallbacksPOST(poptContext con, const struct poptOption * opt)
+	/*@globals internalState@*/
+	/*@modifies internalState@*/
+{
+    if (opt != NULL)
+    for (; opt->longName || opt->shortName || opt->arg; opt++) {
+	if (opt->arg == NULL) continue;		/* XXX program error. */
+	if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+	    void * arg = opt->arg;
+/*@-branchstate@*/
+	    /* XXX sick hack to preserve pretense of ABI. */
+	    if (arg == poptHelpOptions) arg = poptHelpOptionsI18N;
+/*@=branchstate@*/
+	    /* Recurse on included sub-tables. */
+	    invokeCallbacksPOST(con, arg);
+	} else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK &&
+		   (opt->argInfo & POPT_CBFLAG_POST))
+	{   /*@-castfcnptr@*/
+	    poptCallbackType cb = (poptCallbackType)opt->arg;
+	    /*@=castfcnptr@*/
+	    /* Perform callback. */
+	    /*@-noeffectuncon @*/
+	    cb(con, POPT_CALLBACK_REASON_POST, NULL, NULL, opt->descrip);
+	    /*@=noeffectuncon @*/
+	}
+    }
+}
+
+static void invokeCallbacksOPTION(poptContext con,
+				  const struct poptOption * opt,
+				  const struct poptOption * myOpt,
+				  /*@null@*/ const void * myData, int shorty)
+	/*@globals internalState@*/
+	/*@modifies internalState@*/
+{
+    const struct poptOption * cbopt = NULL;
+
+    if (opt != NULL)
+    for (; opt->longName || opt->shortName || opt->arg; opt++) {
+	if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+	    void * arg = opt->arg;
+/*@-branchstate@*/
+	    /* XXX sick hack to preserve pretense of ABI. */
+	    if (arg == poptHelpOptions) arg = poptHelpOptionsI18N;
+/*@=branchstate@*/
+	    /* Recurse on included sub-tables. */
+	    if (opt->arg != NULL)	/* XXX program error */
+		invokeCallbacksOPTION(con, opt->arg, myOpt, myData, shorty);
+	} else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK &&
+		  !(opt->argInfo & POPT_CBFLAG_SKIPOPTION)) {
+	    /* Save callback info. */
+	    cbopt = opt;
+	} else if (cbopt != NULL &&
+		   ((myOpt->shortName && opt->shortName && shorty &&
+			myOpt->shortName == opt->shortName) ||
+		    (myOpt->longName && opt->longName &&
+		/*@-nullpass@*/		/* LCL: opt->longName != NULL */
+			!strcmp(myOpt->longName, opt->longName)))
+		/*@=nullpass@*/
+		   )
+	{   /*@-castfcnptr@*/
+	    poptCallbackType cb = (poptCallbackType)cbopt->arg;
+	    /*@=castfcnptr@*/
+	    const void * cbData = (cbopt->descrip ? cbopt->descrip : myData);
+	    /* Perform callback. */
+	    if (cb != NULL) {	/* XXX program error */
+		/*@-noeffectuncon @*/
+		cb(con, POPT_CALLBACK_REASON_OPTION, myOpt,
+			con->os->nextArg, cbData);
+		/*@=noeffectuncon @*/
+	    }
+	    /* Terminate (unless explcitly continuing). */
+	    if (!(cbopt->argInfo & POPT_CBFLAG_CONTINUE))
+		return;
+	}
+    }
+}
+
+poptContext poptGetContext(const char * name, int argc, const char ** argv,
+			   const struct poptOption * options, int flags)
+{
+    poptContext con = malloc(sizeof(*con));
+
+    if (con == NULL) return NULL;	/* XXX can't happen */
+    memset(con, 0, sizeof(*con));
+
+    con->os = con->optionStack;
+    con->os->argc = argc;
+    /*@-dependenttrans -assignexpose@*/	/* FIX: W2DO? */
+    con->os->argv = argv;
+    /*@=dependenttrans =assignexpose@*/
+    con->os->argb = NULL;
+
+    if (!(flags & POPT_CONTEXT_KEEP_FIRST))
+	con->os->next = 1;			/* skip argv[0] */
+
+    con->leftovers = calloc( (argc + 1), sizeof(*con->leftovers) );
+    /*@-dependenttrans -assignexpose@*/	/* FIX: W2DO? */
+    con->options = options;
+    /*@=dependenttrans =assignexpose@*/
+    con->aliases = NULL;
+    con->numAliases = 0;
+    con->flags = flags;
+    con->execs = NULL;
+    con->numExecs = 0;
+    con->finalArgvAlloced = argc * 2;
+    con->finalArgv = calloc( con->finalArgvAlloced, sizeof(*con->finalArgv) );
+    con->execAbsolute = 1;
+    con->arg_strip = NULL;
+
+    if (getenv("POSIXLY_CORRECT") || getenv("POSIX_ME_HARDER"))
+	con->flags |= POPT_CONTEXT_POSIXMEHARDER;
+
+    if (name) {
+	char * t = malloc(strlen(name) + 1);
+	if (t) con->appName = strcpy(t, name);
+    }
+
+    /*@-internalglobs@*/
+    invokeCallbacksPRE(con, con->options);
+    /*@=internalglobs@*/
+
+    return con;
+}
+
+static void cleanOSE(/*@special@*/ struct optionStackEntry *os)
+	/*@uses os @*/
+	/*@releases os->nextArg, os->argv, os->argb @*/
+	/*@modifies os @*/
+{
+    os->nextArg = _free(os->nextArg);
+    os->argv = _free(os->argv);
+    os->argb = PBM_FREE(os->argb);
+}
+
+/*@-boundswrite@*/
+void poptResetContext(poptContext con)
+{
+    int i;
+
+    if (con == NULL) return;
+    while (con->os > con->optionStack) {
+	cleanOSE(con->os--);
+    }
+    con->os->argb = PBM_FREE(con->os->argb);
+    con->os->currAlias = NULL;
+    con->os->nextCharArg = NULL;
+    con->os->nextArg = NULL;
+    con->os->next = 1;			/* skip argv[0] */
+
+    con->numLeftovers = 0;
+    con->nextLeftover = 0;
+    con->restLeftover = 0;
+    con->doExec = NULL;
+
+    if (con->finalArgv != NULL)
+    for (i = 0; i < con->finalArgvCount; i++) {
+	/*@-unqualifiedtrans@*/		/* FIX: typedef double indirection. */
+	con->finalArgv[i] = _free(con->finalArgv[i]);
+	/*@=unqualifiedtrans@*/
+    }
+
+    con->finalArgvCount = 0;
+    con->arg_strip = PBM_FREE(con->arg_strip);
+    /*@-nullstate@*/	/* FIX: con->finalArgv != NULL */
+    return;
+    /*@=nullstate@*/
+}
+/*@=boundswrite@*/
+
+/* Only one of longName, shortName should be set, not both. */
+/*@-boundswrite@*/
+static int handleExec(/*@special@*/ poptContext con,
+		/*@null@*/ const char * longName, char shortName)
+	/*@uses con->execs, con->numExecs, con->flags, con->doExec,
+		con->finalArgv, con->finalArgvAlloced, con->finalArgvCount @*/
+	/*@modifies con @*/
+{
+    poptItem item;
+    int i;
+
+    if (con->execs == NULL || con->numExecs <= 0) /* XXX can't happen */
+	return 0;
+
+    for (i = con->numExecs - 1; i >= 0; i--) {
+	item = con->execs + i;
+	if (longName && !(item->option.longName &&
+			!strcmp(longName, item->option.longName)))
+	    continue;
+	else if (shortName != item->option.shortName)
+	    continue;
+	break;
+    }
+    if (i < 0) return 0;
+
+
+    if (con->flags & POPT_CONTEXT_NO_EXEC)
+	return 1;
+
+    if (con->doExec == NULL) {
+	con->doExec = con->execs + i;
+	return 1;
+    }
+
+    /* We already have an exec to do; remember this option for next
+       time 'round */
+    if ((con->finalArgvCount + 1) >= (con->finalArgvAlloced)) {
+	con->finalArgvAlloced += 10;
+	con->finalArgv = realloc(con->finalArgv,
+			sizeof(*con->finalArgv) * con->finalArgvAlloced);
+    }
+
+    i = con->finalArgvCount++;
+    if (con->finalArgv != NULL)	/* XXX can't happen */
+    {	char *s  = malloc((longName ? strlen(longName) : 0) + 3);
+	if (s != NULL) {	/* XXX can't happen */
+	    if (longName)
+		sprintf(s, "--%s", longName);
+	    else
+		sprintf(s, "-%c", shortName);
+	    con->finalArgv[i] = s;
+	} else
+	    con->finalArgv[i] = NULL;
+    }
+
+    /*@-nullstate@*/	/* FIX: con->finalArgv[] == NULL */
+    return 1;
+    /*@=nullstate@*/
+}
+/*@=boundswrite@*/
+
+/* Only one of longName, shortName may be set at a time */
+static int handleAlias(/*@special@*/ poptContext con,
+		/*@null@*/ const char * longName, char shortName,
+		/*@exposed@*/ /*@null@*/ const char * nextCharArg)
+	/*@uses con->aliases, con->numAliases, con->optionStack, con->os,
+		con->os->currAlias, con->os->currAlias->option.longName @*/
+	/*@modifies con @*/
+{
+    poptItem item = con->os->currAlias;
+    int rc;
+    int i;
+
+    if (item) {
+	if (longName && (item->option.longName &&
+		!strcmp(longName, item->option.longName)))
+	    return 0;
+	if (shortName && shortName == item->option.shortName)
+	    return 0;
+    }
+
+    if (con->aliases == NULL || con->numAliases <= 0) /* XXX can't happen */
+	return 0;
+
+    for (i = con->numAliases - 1; i >= 0; i--) {
+	item = con->aliases + i;
+	if (longName && !(item->option.longName &&
+			!strcmp(longName, item->option.longName)))
+	    continue;
+	else if (shortName != item->option.shortName)
+	    continue;
+	break;
+    }
+    if (i < 0) return 0;
+
+    if ((con->os - con->optionStack + 1) == POPT_OPTION_DEPTH)
+	return POPT_ERROR_OPTSTOODEEP;
+
+/*@-boundsread@*/
+    if (nextCharArg && *nextCharArg)
+	con->os->nextCharArg = nextCharArg;
+/*@=boundsread@*/
+
+    con->os++;
+    con->os->next = 0;
+    con->os->stuffed = 0;
+    con->os->nextArg = NULL;
+    con->os->nextCharArg = NULL;
+    con->os->currAlias = con->aliases + i;
+    rc = poptDupArgv(con->os->currAlias->argc, con->os->currAlias->argv,
+		&con->os->argc, &con->os->argv);
+    con->os->argb = NULL;
+
+    return (rc ? rc : 1);
+}
+
+/*@-bounds -boundswrite @*/
+static int execCommand(poptContext con)
+	/*@globals internalState @*/
+	/*@modifies internalState @*/
+{
+    poptItem item = con->doExec;
+    const char ** argv;
+    int argc = 0;
+    int rc;
+
+    if (item == NULL) /*XXX can't happen*/
+	return POPT_ERROR_NOARG;
+
+    if (item->argv == NULL || item->argc < 1 ||
+	(!con->execAbsolute && strchr(item->argv[0], '/')))
+	    return POPT_ERROR_NOARG;
+
+    argv = malloc(sizeof(*argv) *
+			(6 + item->argc + con->numLeftovers + con->finalArgvCount));
+    if (argv == NULL) return POPT_ERROR_MALLOC;
+
+    if (!strchr(item->argv[0], '/') && con->execPath != NULL) {
+	char *s = alloca(strlen(con->execPath) + strlen(item->argv[0]) + sizeof("/"));
+	sprintf(s, "%s/%s", con->execPath, item->argv[0]);
+	argv[argc] = s;
+    } else
+	argv[argc] = findProgramPath(item->argv[0]);
+    if (argv[argc++] == NULL) return POPT_ERROR_NOARG;
+
+    if (item->argc > 1) {
+	memcpy(argv + argc, item->argv + 1, sizeof(*argv) * (item->argc - 1));
+	argc += (item->argc - 1);
+    }
+
+    if (con->finalArgv != NULL && con->finalArgvCount > 0) {
+	memcpy(argv + argc, con->finalArgv,
+		sizeof(*argv) * con->finalArgvCount);
+	argc += con->finalArgvCount;
+    }
+
+    if (con->leftovers != NULL && con->numLeftovers > 0) {
+	memcpy(argv + argc, con->leftovers, sizeof(*argv) * con->numLeftovers);
+	argc += con->numLeftovers;
+    }
+
+    argv[argc] = NULL;
+
+#if defined(hpux) || defined(__hpux)
+    rc = setresgid(getgid(), getgid(),-1);
+    if (rc) return POPT_ERROR_ERRNO;
+    rc = setresuid(getuid(), getuid(),-1);
+    if (rc) return POPT_ERROR_ERRNO;
+#else
+/*
+ * XXX " ... on BSD systems setuid() should be preferred over setreuid()"
+ * XXX 	sez' Timur Bakeyev <mc@bat.ru>
+ * XXX	from Norbert Warmuth <nwarmuth@privat.circular.de>
+ */
+#if defined(HAVE_SETUID)
+    rc = setgid(getgid());
+    if (rc) return POPT_ERROR_ERRNO;
+    rc = setuid(getuid());
+    if (rc) return POPT_ERROR_ERRNO;
+#elif defined (HAVE_SETREUID)
+    rc = setregid(getgid(), getgid());
+    if (rc) return POPT_ERROR_ERRNO;
+    rc = setreuid(getuid(), getuid());
+    if (rc) return POPT_ERROR_ERRNO;
+#else
+    ; /* Can't drop privileges */
+#endif
+#endif
+
+    if (argv[0] == NULL)
+	return POPT_ERROR_NOARG;
+
+#ifdef	MYDEBUG
+if (_popt_debug)
+    {	const char ** avp;
+	fprintf(stderr, "==> execvp(%s) argv[%d]:", argv[0], argc);
+	for (avp = argv; *avp; avp++)
+	    fprintf(stderr, " '%s'", *avp);
+	fprintf(stderr, "\n");
+    }
+#endif
+
+    rc = execvp(argv[0], (char *const *)argv);
+
+    return POPT_ERROR_ERRNO;
+}
+/*@=bounds =boundswrite @*/
+
+/*@-boundswrite@*/
+/*@observer@*/ /*@null@*/ static const struct poptOption *
+findOption(const struct poptOption * opt, /*@null@*/ const char * longName,
+		char shortName,
+		/*@null@*/ /*@out@*/ poptCallbackType * callback,
+		/*@null@*/ /*@out@*/ const void ** callbackData,
+		int singleDash)
+	/*@modifies *callback, *callbackData */
+{
+    const struct poptOption * cb = NULL;
+
+    /* This happens when a single - is given */
+    if (singleDash && !shortName && (longName && *longName == '\0'))
+	shortName = '-';
+
+    for (; opt->longName || opt->shortName || opt->arg; opt++) {
+
+	if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+	    const struct poptOption * opt2;
+	    void * arg = opt->arg;
+
+/*@-branchstate@*/
+	    /* XXX sick hack to preserve pretense of ABI. */
+	    if (arg == poptHelpOptions) arg = poptHelpOptionsI18N;
+/*@=branchstate@*/
+	    /* Recurse on included sub-tables. */
+	    if (arg == NULL) continue;	/* XXX program error */
+	    opt2 = findOption(arg, longName, shortName, callback,
+			      callbackData, singleDash);
+	    if (opt2 == NULL) continue;
+	    /* Sub-table data will be inheirited if no data yet. */
+	    if (!(callback && *callback)) return opt2;
+	    if (!(callbackData && *callbackData == NULL)) return opt2;
+	    /*@-observertrans -dependenttrans @*/
+	    *callbackData = opt->descrip;
+	    /*@=observertrans =dependenttrans @*/
+	    return opt2;
+	} else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) {
+	    cb = opt;
+	} else if (longName && opt->longName &&
+		   (!singleDash || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) &&
+		/*@-nullpass@*/		/* LCL: opt->longName != NULL */
+		   !strcmp(longName, opt->longName))
+		/*@=nullpass@*/
+	{
+	    break;
+	} else if (shortName && shortName == opt->shortName) {
+	    break;
+	}
+    }
+
+    if (!opt->longName && !opt->shortName)
+	return NULL;
+    /*@-modobserver -mods @*/
+    if (callback) *callback = NULL;
+    if (callbackData) *callbackData = NULL;
+    if (cb) {
+	if (callback)
+	/*@-castfcnptr@*/
+	    *callback = (poptCallbackType)cb->arg;
+	/*@=castfcnptr@*/
+	if (!(cb->argInfo & POPT_CBFLAG_INC_DATA)) {
+	    if (callbackData)
+		/*@-observertrans@*/	/* FIX: typedef double indirection. */
+		*callbackData = cb->descrip;
+		/*@=observertrans@*/
+	}
+    }
+    /*@=modobserver =mods @*/
+
+    return opt;
+}
+/*@=boundswrite@*/
+
+static const char * findNextArg(/*@special@*/ poptContext con,
+		unsigned argx, int delete_arg)
+	/*@uses con->optionStack, con->os,
+		con->os->next, con->os->argb, con->os->argc, con->os->argv @*/
+	/*@modifies con @*/
+{
+    struct optionStackEntry * os = con->os;
+    const char * arg;
+
+    do {
+	int i;
+	arg = NULL;
+	while (os->next == os->argc && os > con->optionStack) os--;
+	if (os->next == os->argc && os == con->optionStack) break;
+	if (os->argv != NULL)
+	for (i = os->next; i < os->argc; i++) {
+	    /*@-sizeoftype@*/
+	    if (os->argb && PBM_ISSET(i, os->argb))
+		/*@innercontinue@*/ continue;
+	    if (*os->argv[i] == '-')
+		/*@innercontinue@*/ continue;
+	    if (--argx > 0)
+		/*@innercontinue@*/ continue;
+	    arg = os->argv[i];
+	    if (delete_arg) {
+		if (os->argb == NULL) os->argb = PBM_ALLOC(os->argc);
+		if (os->argb != NULL)	/* XXX can't happen */
+		PBM_SET(i, os->argb);
+	    }
+	    /*@innerbreak@*/ break;
+	    /*@=sizeoftype@*/
+	}
+	if (os > con->optionStack) os--;
+    } while (arg == NULL);
+    return arg;
+}
+
+/*@-boundswrite@*/
+static /*@only@*/ /*@null@*/ const char *
+expandNextArg(/*@special@*/ poptContext con, const char * s)
+	/*@uses con->optionStack, con->os,
+		con->os->next, con->os->argb, con->os->argc, con->os->argv @*/
+	/*@modifies con @*/
+{
+    const char * a = NULL;
+    size_t alen;
+    char *t, *te;
+    size_t tn = strlen(s) + 1;
+    char c;
+
+    te = t = malloc(tn);;
+    if (t == NULL) return NULL;		/* XXX can't happen */
+    while ((c = *s++) != '\0') {
+	switch (c) {
+#if 0	/* XXX can't do this */
+	case '\\':	/* escape */
+	    c = *s++;
+	    /*@switchbreak@*/ break;
+#endif
+	case '!':
+	    if (!(s[0] == '#' && s[1] == ':' && s[2] == '+'))
+		/*@switchbreak@*/ break;
+	    /* XXX Make sure that findNextArg deletes only next arg. */
+	    if (a == NULL) {
+		if ((a = findNextArg(con, 1, 1)) == NULL)
+		    /*@switchbreak@*/ break;
+	    }
+	    s += 3;
+
+	    alen = strlen(a);
+	    tn += alen;
+	    *te = '\0';
+	    t = realloc(t, tn);
+	    te = t + strlen(t);
+	    strncpy(te, a, alen); te += alen;
+	    continue;
+	    /*@notreached@*/ /*@switchbreak@*/ break;
+	default:
+	    /*@switchbreak@*/ break;
+	}
+	*te++ = c;
+    }
+    *te = '\0';
+    t = realloc(t, strlen(t) + 1);	/* XXX memory leak, hard to plug */
+    return t;
+}
+/*@=boundswrite@*/
+
+static void poptStripArg(/*@special@*/ poptContext con, int which)
+	/*@uses con->arg_strip, con->optionStack @*/
+	/*@defines con->arg_strip @*/
+	/*@modifies con @*/
+{
+    /*@-sizeoftype@*/
+    if (con->arg_strip == NULL)
+	con->arg_strip = PBM_ALLOC(con->optionStack[0].argc);
+    if (con->arg_strip != NULL)		/* XXX can't happen */
+    PBM_SET(which, con->arg_strip);
+    /*@=sizeoftype@*/
+    /*@-compdef@*/ /* LCL: con->arg_strip udefined? */
+    return;
+    /*@=compdef@*/
+}
+
+int poptSaveLong(long * arg, int argInfo, long aLong)
+{
+    /* XXX Check alignment, may fail on funky platforms. */
+    if (arg == NULL || (((unsigned long)arg) & (sizeof(*arg)-1)))
+	return POPT_ERROR_NULLARG;
+
+    if (argInfo & POPT_ARGFLAG_NOT)
+	aLong = ~aLong;
+    switch (argInfo & POPT_ARGFLAG_LOGICALOPS) {
+    case 0:
+	*arg = aLong;
+	break;
+    case POPT_ARGFLAG_OR:
+	*arg |= aLong;
+	break;
+    case POPT_ARGFLAG_AND:
+	*arg &= aLong;
+	break;
+    case POPT_ARGFLAG_XOR:
+	*arg ^= aLong;
+	break;
+    default:
+	return POPT_ERROR_BADOPERATION;
+	/*@notreached@*/ break;
+    }
+    return 0;
+}
+
+int poptSaveInt(/*@null@*/ int * arg, int argInfo, long aLong)
+{
+    /* XXX Check alignment, may fail on funky platforms. */
+    if (arg == NULL || (((unsigned long)arg) & (sizeof(*arg)-1)))
+	return POPT_ERROR_NULLARG;
+
+    if (argInfo & POPT_ARGFLAG_NOT)
+	aLong = ~aLong;
+    switch (argInfo & POPT_ARGFLAG_LOGICALOPS) {
+    case 0:
+	*arg = aLong;
+	break;
+    case POPT_ARGFLAG_OR:
+	*arg |= aLong;
+	break;
+    case POPT_ARGFLAG_AND:
+	*arg &= aLong;
+	break;
+    case POPT_ARGFLAG_XOR:
+	*arg ^= aLong;
+	break;
+    default:
+	return POPT_ERROR_BADOPERATION;
+	/*@notreached@*/ break;
+    }
+    return 0;
+}
+
+/*@-boundswrite@*/
+/* returns 'val' element, -1 on last item, POPT_ERROR_* on error */
+int poptGetNextOpt(poptContext con)
+{
+    const struct poptOption * opt = NULL;
+    int done = 0;
+
+    if (con == NULL)
+	return -1;
+    while (!done) {
+	const char * origOptString = NULL;
+	poptCallbackType cb = NULL;
+	const void * cbData = NULL;
+	const char * longArg = NULL;
+	int canstrip = 0;
+	int shorty = 0;
+
+	while (!con->os->nextCharArg && con->os->next == con->os->argc
+		&& con->os > con->optionStack) {
+	    cleanOSE(con->os--);
+	}
+	if (!con->os->nextCharArg && con->os->next == con->os->argc) {
+	    /*@-internalglobs@*/
+	    invokeCallbacksPOST(con, con->options);
+	    /*@=internalglobs@*/
+	    if (con->doExec) return execCommand(con);
+	    return -1;
+	}
+
+	/* Process next long option */
+	if (!con->os->nextCharArg) {
+	    char * localOptString, * optString;
+	    int thisopt;
+
+	    /*@-sizeoftype@*/
+	    if (con->os->argb && PBM_ISSET(con->os->next, con->os->argb)) {
+		con->os->next++;
+		continue;
+	    }
+	    /*@=sizeoftype@*/
+	    thisopt = con->os->next;
+	    if (con->os->argv != NULL)	/* XXX can't happen */
+	    origOptString = con->os->argv[con->os->next++];
+
+	    if (origOptString == NULL)	/* XXX can't happen */
+		return POPT_ERROR_BADOPT;
+
+	    if (con->restLeftover || *origOptString != '-' ||
+		(*origOptString == '-' && origOptString[1] == '\0'))
+	    {
+		if (con->flags & POPT_CONTEXT_POSIXMEHARDER)
+		    con->restLeftover = 1;
+		if (con->flags & POPT_CONTEXT_ARG_OPTS) {
+		    con->os->nextArg = xstrdup(origOptString);
+		    return 0;
+		}
+		if (con->leftovers != NULL)	/* XXX can't happen */
+		    con->leftovers[con->numLeftovers++] = origOptString;
+		continue;
+	    }
+
+	    /* Make a copy we can hack at */
+	    localOptString = optString =
+		strcpy(alloca(strlen(origOptString) + 1), origOptString);
+
+	    if (optString[0] == '\0')
+		return POPT_ERROR_BADOPT;
+
+	    if (optString[1] == '-' && !optString[2]) {
+		con->restLeftover = 1;
+		continue;
+	    } else {
+		char *oe;
+		int singleDash;
+
+		optString++;
+		if (*optString == '-')
+		    singleDash = 0, optString++;
+		else
+		    singleDash = 1;
+
+		/* XXX aliases with arg substitution need "--alias=arg" */
+		if (handleAlias(con, optString, '\0', NULL))
+		    continue;
+
+		if (handleExec(con, optString, '\0'))
+		    continue;
+
+		/* Check for "--long=arg" option. */
+		for (oe = optString; *oe && *oe != '='; oe++)
+		    {};
+		if (*oe == '=') {
+		    *oe++ = '\0';
+		    /* XXX longArg is mapped back to persistent storage. */
+		    longArg = origOptString + (oe - localOptString);
+		}
+
+		opt = findOption(con->options, optString, '\0', &cb, &cbData,
+				 singleDash);
+		if (!opt && !singleDash)
+		    return POPT_ERROR_BADOPT;
+	    }
+
+	    if (!opt) {
+		con->os->nextCharArg = origOptString + 1;
+	    } else {
+		if (con->os == con->optionStack &&
+		   opt->argInfo & POPT_ARGFLAG_STRIP)
+		{
+		    canstrip = 1;
+		    poptStripArg(con, thisopt);
+		}
+		shorty = 0;
+	    }
+	}
+
+	/* Process next short option */
+	/*@-branchstate@*/		/* FIX: W2DO? */
+	if (con->os->nextCharArg) {
+	    origOptString = con->os->nextCharArg;
+
+	    con->os->nextCharArg = NULL;
+
+	    if (handleAlias(con, NULL, *origOptString, origOptString + 1))
+		continue;
+
+	    if (handleExec(con, NULL, *origOptString)) {
+		/* Restore rest of short options for further processing */
+		origOptString++;
+		if (*origOptString != '\0')
+		    con->os->nextCharArg = origOptString;
+		continue;
+	    }
+
+	    opt = findOption(con->options, NULL, *origOptString, &cb,
+			     &cbData, 0);
+	    if (!opt)
+		return POPT_ERROR_BADOPT;
+	    shorty = 1;
+
+	    origOptString++;
+	    if (*origOptString != '\0')
+		con->os->nextCharArg = origOptString;
+	}
+	/*@=branchstate@*/
+
+	if (opt == NULL) return POPT_ERROR_BADOPT;	/* XXX can't happen */
+	if (opt->arg && (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE) {
+	    if (poptSaveInt((int *)opt->arg, opt->argInfo, 1L))
+		return POPT_ERROR_BADOPERATION;
+	} else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL) {
+	    if (opt->arg) {
+		if (poptSaveInt((int *)opt->arg, opt->argInfo, (long)opt->val))
+		    return POPT_ERROR_BADOPERATION;
+	    }
+	} else if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE) {
+	    con->os->nextArg = _free(con->os->nextArg);
+	    /*@-usedef@*/	/* FIX: W2DO? */
+	    if (longArg) {
+	    /*@=usedef@*/
+		longArg = expandNextArg(con, longArg);
+		con->os->nextArg = longArg;
+	    } else if (con->os->nextCharArg) {
+		longArg = expandNextArg(con, con->os->nextCharArg);
+		con->os->nextArg = longArg;
+		con->os->nextCharArg = NULL;
+	    } else {
+		while (con->os->next == con->os->argc &&
+		       con->os > con->optionStack) {
+		    cleanOSE(con->os--);
+		}
+		if (con->os->next == con->os->argc) {
+		    if (!(opt->argInfo & POPT_ARGFLAG_OPTIONAL))
+			/*@-compdef@*/	/* FIX: con->os->argv not defined */
+			return POPT_ERROR_NOARG;
+			/*@=compdef@*/
+		    con->os->nextArg = NULL;
+		} else {
+
+		    /*
+		     * Make sure this isn't part of a short arg or the
+		     * result of an alias expansion.
+		     */
+		    if (con->os == con->optionStack &&
+			(opt->argInfo & POPT_ARGFLAG_STRIP) &&
+			canstrip) {
+			poptStripArg(con, con->os->next);
+		    }
+		
+		    if (con->os->argv != NULL) {	/* XXX can't happen */
+			/* XXX watchout: subtle side-effects live here. */
+			longArg = con->os->argv[con->os->next++];
+			longArg = expandNextArg(con, longArg);
+			con->os->nextArg = longArg;
+		    }
+		}
+	    }
+	    longArg = NULL;
+
+	    if (opt->arg) {
+		switch (opt->argInfo & POPT_ARG_MASK) {
+		case POPT_ARG_STRING:
+		    /* XXX memory leak, hard to plug */
+		    *((const char **) opt->arg) = (con->os->nextArg)
+			? xstrdup(con->os->nextArg) : NULL;
+		    /*@switchbreak@*/ break;
+
+		case POPT_ARG_INT:
+		case POPT_ARG_LONG:
+		{   long aLong = 0;
+		    char *end;
+
+		    if (con->os->nextArg) {
+			aLong = strtol(con->os->nextArg, &end, 0);
+			if (!(end && *end == '\0'))
+			    return POPT_ERROR_BADNUMBER;
+		    }
+
+		    if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_LONG) {
+			if (aLong == LONG_MIN || aLong == LONG_MAX)
+			    return POPT_ERROR_OVERFLOW;
+			if (poptSaveLong((long *)opt->arg, opt->argInfo, aLong))
+			    return POPT_ERROR_BADOPERATION;
+		    } else {
+			if (aLong > INT_MAX || aLong < INT_MIN)
+			    return POPT_ERROR_OVERFLOW;
+			if (poptSaveInt((int *)opt->arg, opt->argInfo, aLong))
+			    return POPT_ERROR_BADOPERATION;
+		    }
+		}   /*@switchbreak@*/ break;
+
+		case POPT_ARG_FLOAT:
+		case POPT_ARG_DOUBLE:
+		{   double aDouble = 0.0;
+		    char *end;
+
+		    if (con->os->nextArg) {
+			/*@-mods@*/
+			int saveerrno = errno;
+			errno = 0;
+			aDouble = strtod(con->os->nextArg, &end);
+			if (errno == ERANGE)
+			    return POPT_ERROR_OVERFLOW;
+			errno = saveerrno;
+			/*@=mods@*/
+			if (*end != '\0')
+			    return POPT_ERROR_BADNUMBER;
+		    }
+
+		    if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_DOUBLE) {
+			*((double *) opt->arg) = aDouble;
+		    } else {
+#define _ABS(a)	((((a) - 0.0) < DBL_EPSILON) ? -(a) : (a))
+			if ((_ABS(aDouble) - FLT_MAX) > DBL_EPSILON)
+			    return POPT_ERROR_OVERFLOW;
+			if ((FLT_MIN - _ABS(aDouble)) > DBL_EPSILON)
+			    return POPT_ERROR_OVERFLOW;
+			*((float *) opt->arg) = aDouble;
+		    }
+		}   /*@switchbreak@*/ break;
+		default:
+		    fprintf(stdout,
+			POPT_("option type (%d) not implemented in popt\n"),
+			(opt->argInfo & POPT_ARG_MASK));
+		    exit(EXIT_FAILURE);
+		    /*@notreached@*/ /*@switchbreak@*/ break;
+		}
+	    }
+	}
+
+	if (cb) {
+	    /*@-internalglobs@*/
+	    invokeCallbacksOPTION(con, con->options, opt, cbData, shorty);
+	    /*@=internalglobs@*/
+	} else if (opt->val && ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL))
+	    done = 1;
+
+	if ((con->finalArgvCount + 2) >= (con->finalArgvAlloced)) {
+	    con->finalArgvAlloced += 10;
+	    con->finalArgv = realloc(con->finalArgv,
+			    sizeof(*con->finalArgv) * con->finalArgvAlloced);
+	}
+
+	if (con->finalArgv != NULL)
+	{   char *s = malloc((opt->longName ? strlen(opt->longName) : 0) + 3);
+	    if (s != NULL) {	/* XXX can't happen */
+		if (opt->longName)
+		    sprintf(s, "%s%s",
+			((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"),
+			opt->longName);
+		else
+		    sprintf(s, "-%c", opt->shortName);
+		con->finalArgv[con->finalArgvCount++] = s;
+	    } else
+		con->finalArgv[con->finalArgvCount++] = NULL;
+	}
+
+	if (opt->arg && (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE)
+	    /*@-ifempty@*/ ; /*@=ifempty@*/
+	else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL)
+	    /*@-ifempty@*/ ; /*@=ifempty@*/
+	else if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE) {
+	    if (con->finalArgv != NULL && con->os->nextArg)
+	        con->finalArgv[con->finalArgvCount++] =
+			/*@-nullpass@*/	/* LCL: con->os->nextArg != NULL */
+			xstrdup(con->os->nextArg);
+			/*@=nullpass@*/
+	}
+    }
+
+    return (opt ? opt->val : -1);	/* XXX can't happen */
+}
+/*@=boundswrite@*/
+
+const char * poptGetOptArg(poptContext con)
+{
+    const char * ret = NULL;
+    /*@-branchstate@*/
+    if (con) {
+	ret = con->os->nextArg;
+	con->os->nextArg = NULL;
+    }
+    /*@=branchstate@*/
+    return ret;
+}
+
+const char * poptGetArg(poptContext con)
+{
+    const char * ret = NULL;
+    if (con && con->leftovers != NULL && con->nextLeftover < con->numLeftovers)
+	ret = con->leftovers[con->nextLeftover++];
+    return ret;
+}
+
+const char * poptPeekArg(poptContext con)
+{
+    const char * ret = NULL;
+    if (con && con->leftovers != NULL && con->nextLeftover < con->numLeftovers)
+	ret = con->leftovers[con->nextLeftover];
+    return ret;
+}
+
+/*@-boundswrite@*/
+const char ** poptGetArgs(poptContext con)
+{
+    if (con == NULL ||
+	con->leftovers == NULL || con->numLeftovers == con->nextLeftover)
+	return NULL;
+
+    /* some apps like [like RPM ;-) ] need this NULL terminated */
+    con->leftovers[con->numLeftovers] = NULL;
+
+    /*@-nullret -nullstate @*/	/* FIX: typedef double indirection. */
+    return (con->leftovers + con->nextLeftover);
+    /*@=nullret =nullstate @*/
+}
+/*@=boundswrite@*/
+
+poptContext poptFreeContext(poptContext con)
+{
+    poptItem item;
+    int i;
+
+    if (con == NULL) return con;
+    poptResetContext(con);
+    con->os->argb = _free(con->os->argb);
+
+    if (con->aliases != NULL)
+    for (i = 0; i < con->numAliases; i++) {
+	item = con->aliases + i;
+	/*@-modobserver -observertrans -dependenttrans@*/
+	item->option.longName = _free(item->option.longName);
+	item->option.descrip = _free(item->option.descrip);
+	item->option.argDescrip = _free(item->option.argDescrip);
+	/*@=modobserver =observertrans =dependenttrans@*/
+	item->argv = _free(item->argv);
+    }
+    con->aliases = _free(con->aliases);
+
+    if (con->execs != NULL)
+    for (i = 0; i < con->numExecs; i++) {
+	item = con->execs + i;
+	/*@-modobserver -observertrans -dependenttrans@*/
+	item->option.longName = _free(item->option.longName);
+	item->option.descrip = _free(item->option.descrip);
+	item->option.argDescrip = _free(item->option.argDescrip);
+	/*@=modobserver =observertrans =dependenttrans@*/
+	item->argv = _free(item->argv);
+    }
+    con->execs = _free(con->execs);
+
+    con->leftovers = _free(con->leftovers);
+    con->finalArgv = _free(con->finalArgv);
+    con->appName = _free(con->appName);
+    con->otherHelp = _free(con->otherHelp);
+    con->execPath = _free(con->execPath);
+    con->arg_strip = PBM_FREE(con->arg_strip);
+    
+    con = _free(con);
+    return con;
+}
+
+int poptAddAlias(poptContext con, struct poptAlias alias,
+		/*@unused@*/ int flags)
+{
+    poptItem item = alloca(sizeof(*item));
+    memset(item, 0, sizeof(*item));
+    item->option.longName = alias.longName;
+    item->option.shortName = alias.shortName;
+    item->option.argInfo = POPT_ARGFLAG_DOC_HIDDEN;
+    item->option.arg = 0;
+    item->option.val = 0;
+    item->option.descrip = NULL;
+    item->option.argDescrip = NULL;
+    item->argc = alias.argc;
+    item->argv = alias.argv;
+    return poptAddItem(con, item, 0);
+}
+
+/*@-boundswrite@*/
+/*@-mustmod@*/ /* LCL: con not modified? */
+int poptAddItem(poptContext con, poptItem newItem, int flags)
+{
+    poptItem * items, item;
+    int * nitems;
+
+    switch (flags) {
+    case 1:
+	items = &con->execs;
+	nitems = &con->numExecs;
+	break;
+    case 0:
+	items = &con->aliases;
+	nitems = &con->numAliases;
+	break;
+    default:
+	return 1;
+	/*@notreached@*/ break;
+    }
+
+    *items = realloc((*items), ((*nitems) + 1) * sizeof(**items));
+    if ((*items) == NULL)
+	return 1;
+
+    item = (*items) + (*nitems);
+
+    item->option.longName =
+	(newItem->option.longName ? xstrdup(newItem->option.longName) : NULL);
+    item->option.shortName = newItem->option.shortName;
+    item->option.argInfo = newItem->option.argInfo;
+    item->option.arg = newItem->option.arg;
+    item->option.val = newItem->option.val;
+    item->option.descrip =
+	(newItem->option.descrip ? xstrdup(newItem->option.descrip) : NULL);
+    item->option.argDescrip =
+       (newItem->option.argDescrip ? xstrdup(newItem->option.argDescrip) : NULL);
+    item->argc = newItem->argc;
+    item->argv = newItem->argv;
+
+    (*nitems)++;
+
+    return 0;
+}
+/*@=mustmod@*/
+/*@=boundswrite@*/
+
+const char * poptBadOption(poptContext con, int flags)
+{
+    struct optionStackEntry * os = NULL;
+
+    if (con != NULL)
+	os = (flags & POPT_BADOPTION_NOALIAS) ? con->optionStack : con->os;
+
+    /*@-nullderef@*/	/* LCL: os->argv != NULL */
+    return (os && os->argv ? os->argv[os->next - 1] : NULL);
+    /*@=nullderef@*/
+}
+
+const char * poptStrerror(const int error)
+{
+    switch (error) {
+      case POPT_ERROR_NOARG:
+	return POPT_("missing argument");
+      case POPT_ERROR_BADOPT:
+	return POPT_("unknown option");
+      case POPT_ERROR_BADOPERATION:
+	return POPT_("mutually exclusive logical operations requested");
+      case POPT_ERROR_NULLARG:
+	return POPT_("opt->arg should not be NULL");
+      case POPT_ERROR_OPTSTOODEEP:
+	return POPT_("aliases nested too deeply");
+      case POPT_ERROR_BADQUOTE:
+	return POPT_("error in parameter quoting");
+      case POPT_ERROR_BADNUMBER:
+	return POPT_("invalid numeric value");
+      case POPT_ERROR_OVERFLOW:
+	return POPT_("number too large or too small");
+      case POPT_ERROR_MALLOC:
+	return POPT_("memory allocation failed");
+      case POPT_ERROR_ERRNO:
+	return strerror(errno);
+      default:
+	return POPT_("unknown error");
+    }
+}
+
+int poptStuffArgs(poptContext con, const char ** argv)
+{
+    int argc;
+    int rc;
+
+    if ((con->os - con->optionStack) == POPT_OPTION_DEPTH)
+	return POPT_ERROR_OPTSTOODEEP;
+
+    for (argc = 0; argv[argc]; argc++)
+	{};
+
+    con->os++;
+    con->os->next = 0;
+    con->os->nextArg = NULL;
+    con->os->nextCharArg = NULL;
+    con->os->currAlias = NULL;
+    rc = poptDupArgv(argc, argv, &con->os->argc, &con->os->argv);
+    con->os->argb = NULL;
+    con->os->stuffed = 1;
+
+    return rc;
+}
+
+const char * poptGetInvocationName(poptContext con)
+{
+    return (con->os->argv ? con->os->argv[0] : "");
+}
+
+/*@-boundswrite@*/
+int poptStrippedArgv(poptContext con, int argc, char ** argv)
+{
+    int numargs = argc;
+    int j = 1;
+    int i;
+    
+    /*@-sizeoftype@*/
+    if (con->arg_strip)
+    for (i = 1; i < argc; i++) {
+	if (PBM_ISSET(i, con->arg_strip))
+	    numargs--;
+    }
+    
+    for (i = 1; i < argc; i++) {
+	if (con->arg_strip && PBM_ISSET(i, con->arg_strip))
+	    continue;
+	argv[j] = (j < numargs) ? argv[i] : NULL;
+	j++;
+    }
+    /*@=sizeoftype@*/
+    
+    return numargs;
+}
+/*@=boundswrite@*/
diff --git a/libpopt/popt.h b/libpopt/popt.h
new file mode 100644
index 0000000..4f85d9e
--- /dev/null
+++ b/libpopt/popt.h
@@ -0,0 +1,564 @@
+/** \file popt/popt.h
+ * \ingroup popt
+ */
+
+/* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.rpm.org/pub/rpm/dist. */
+
+#ifndef H_POPT
+#define H_POPT
+
+#include <stdio.h>			/* for FILE * */
+
+#define POPT_OPTION_DEPTH	10
+
+/** \ingroup popt
+ * \name Arg type identifiers
+ */
+/*@{*/
+#define POPT_ARG_NONE		0	/*!< no arg */
+#define POPT_ARG_STRING		1	/*!< arg will be saved as string */
+#define POPT_ARG_INT		2	/*!< arg will be converted to int */
+#define POPT_ARG_LONG		3	/*!< arg will be converted to long */
+#define POPT_ARG_INCLUDE_TABLE	4	/*!< arg points to table */
+#define POPT_ARG_CALLBACK	5	/*!< table-wide callback... must be
+					   set first in table; arg points 
+					   to callback, descrip points to 
+					   callback data to pass */
+#define POPT_ARG_INTL_DOMAIN    6       /*!< set the translation domain
+					   for this table and any
+					   included tables; arg points
+					   to the domain string */
+#define POPT_ARG_VAL		7	/*!< arg should take value val */
+#define	POPT_ARG_FLOAT		8	/*!< arg will be converted to float */
+#define	POPT_ARG_DOUBLE		9	/*!< arg will be converted to double */
+
+#define POPT_ARG_MASK		0x0000FFFF
+/*@}*/
+
+/** \ingroup popt
+ * \name Arg modifiers
+ */
+/*@{*/
+#define POPT_ARGFLAG_ONEDASH	0x80000000  /*!< allow -longoption */
+#define POPT_ARGFLAG_DOC_HIDDEN 0x40000000  /*!< don't show in help/usage */
+#define POPT_ARGFLAG_STRIP	0x20000000  /*!< strip this arg from argv(only applies to long args) */
+#define	POPT_ARGFLAG_OPTIONAL	0x10000000  /*!< arg may be missing */
+
+#define	POPT_ARGFLAG_OR		0x08000000  /*!< arg will be or'ed */
+#define	POPT_ARGFLAG_NOR	0x09000000  /*!< arg will be nor'ed */
+#define	POPT_ARGFLAG_AND	0x04000000  /*!< arg will be and'ed */
+#define	POPT_ARGFLAG_NAND	0x05000000  /*!< arg will be nand'ed */
+#define	POPT_ARGFLAG_XOR	0x02000000  /*!< arg will be xor'ed */
+#define	POPT_ARGFLAG_NOT	0x01000000  /*!< arg will be negated */
+#define POPT_ARGFLAG_LOGICALOPS \
+        (POPT_ARGFLAG_OR|POPT_ARGFLAG_AND|POPT_ARGFLAG_XOR)
+
+#define	POPT_BIT_SET	(POPT_ARG_VAL|POPT_ARGFLAG_OR)
+					/*!< set arg bit(s) */
+#define	POPT_BIT_CLR	(POPT_ARG_VAL|POPT_ARGFLAG_NAND)
+					/*!< clear arg bit(s) */
+
+#define	POPT_ARGFLAG_SHOW_DEFAULT 0x00800000 /*!< show default value in --help */
+
+/*@}*/
+
+/** \ingroup popt
+ * \name Callback modifiers
+ */
+/*@{*/
+#define POPT_CBFLAG_PRE		0x80000000  /*!< call the callback before parse */
+#define POPT_CBFLAG_POST	0x40000000  /*!< call the callback after parse */
+#define POPT_CBFLAG_INC_DATA	0x20000000  /*!< use data from the include line,
+					       not the subtable */
+#define POPT_CBFLAG_SKIPOPTION	0x10000000  /*!< don't callback with option */
+#define POPT_CBFLAG_CONTINUE	0x08000000  /*!< continue callbacks with option */
+/*@}*/
+
+/** \ingroup popt
+ * \name Error return values
+ */
+/*@{*/
+#define POPT_ERROR_NOARG	-10	/*!< missing argument */
+#define POPT_ERROR_BADOPT	-11	/*!< unknown option */
+#define POPT_ERROR_OPTSTOODEEP	-13	/*!< aliases nested too deeply */
+#define POPT_ERROR_BADQUOTE	-15	/*!< error in paramter quoting */
+#define POPT_ERROR_ERRNO	-16	/*!< errno set, use strerror(errno) */
+#define POPT_ERROR_BADNUMBER	-17	/*!< invalid numeric value */
+#define POPT_ERROR_OVERFLOW	-18	/*!< number too large or too small */
+#define	POPT_ERROR_BADOPERATION	-19	/*!< mutually exclusive logical operations requested */
+#define	POPT_ERROR_NULLARG	-20	/*!< opt->arg should not be NULL */
+#define	POPT_ERROR_MALLOC	-21	/*!< memory allocation failed */
+/*@}*/
+
+/** \ingroup popt
+ * \name poptBadOption() flags
+ */
+/*@{*/
+#define POPT_BADOPTION_NOALIAS  (1 << 0)  /*!< don't go into an alias */
+/*@}*/
+
+/** \ingroup popt
+ * \name poptGetContext() flags
+ */
+/*@{*/
+#define POPT_CONTEXT_NO_EXEC	(1 << 0)  /*!< ignore exec expansions */
+#define POPT_CONTEXT_KEEP_FIRST	(1 << 1)  /*!< pay attention to argv[0] */
+#define POPT_CONTEXT_POSIXMEHARDER (1 << 2) /*!< options can't follow args */
+#define POPT_CONTEXT_ARG_OPTS	(1 << 4) /*!< return args as options with value 0 */
+/*@}*/
+
+/** \ingroup popt
+ */
+struct poptOption {
+/*@observer@*/ /*@null@*/
+    const char * longName;	/*!< may be NULL */
+    char shortName;		/*!< may be NUL */
+    int argInfo;
+/*@shared@*/ /*@null@*/
+    void * arg;			/*!< depends on argInfo */
+    int val;			/*!< 0 means don't return, just update flag */
+/*@observer@*/ /*@null@*/
+    const char * descrip;	/*!< description for autohelp -- may be NULL */
+/*@observer@*/ /*@null@*/
+    const char * argDescrip;	/*!< argument description for autohelp */
+};
+
+/** \ingroup popt
+ * A popt alias argument for poptAddAlias().
+ */
+struct poptAlias {
+/*@owned@*/ /*@null@*/
+    const char * longName;	/*!< may be NULL */
+    char shortName;		/*!< may be NUL */
+    int argc;
+/*@owned@*/
+    const char ** argv;		/*!< must be free()able */
+};
+
+/** \ingroup popt
+ * A popt alias or exec argument for poptAddItem().
+ */
+/*@-exporttype@*/
+typedef struct poptItem_s {
+    struct poptOption option;	/*!< alias/exec name(s) and description. */
+    int argc;			/*!< (alias) no. of args. */
+/*@owned@*/
+    const char ** argv;		/*!< (alias) args, must be free()able. */
+} * poptItem;
+/*@=exporttype@*/
+
+/** \ingroup popt
+ * \name Auto-generated help/usage
+ */
+/*@{*/
+
+/**
+ * Empty table marker to enable displaying popt alias/exec options.
+ */
+/*@-exportvar@*/
+/*@unchecked@*/ /*@observer@*/
+extern struct poptOption poptAliasOptions[];
+/*@=exportvar@*/
+#define POPT_AUTOALIAS { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptAliasOptions, \
+			0, "Options implemented via popt alias/exec:", NULL },
+
+/**
+ * Auto help table options.
+ */
+/*@-exportvar@*/
+/*@unchecked@*/ /*@observer@*/
+extern struct poptOption poptHelpOptions[];
+/*@=exportvar@*/
+
+/*@-exportvar@*/
+/*@unchecked@*/ /*@observer@*/
+extern struct poptOption * poptHelpOptionsI18N;
+/*@=exportvar@*/
+
+#define POPT_AUTOHELP { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptHelpOptions, \
+			0, "Help options:", NULL },
+
+#define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }
+/*@}*/
+
+/** \ingroup popt
+ */
+/*@-exporttype@*/
+typedef /*@abstract@*/ struct poptContext_s * poptContext;
+/*@=exporttype@*/
+
+/** \ingroup popt
+ */
+#ifndef __cplusplus
+/*@-exporttype -typeuse@*/
+typedef struct poptOption * poptOption;
+/*@=exporttype =typeuse@*/
+#endif
+
+/*@-exportconst@*/
+enum poptCallbackReason {
+    POPT_CALLBACK_REASON_PRE	= 0, 
+    POPT_CALLBACK_REASON_POST	= 1,
+    POPT_CALLBACK_REASON_OPTION = 2
+};
+/*@=exportconst@*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*@-type@*/
+
+/** \ingroup popt
+ * Table callback prototype.
+ * @param con		context
+ * @param reason	reason for callback
+ * @param opt		option that triggered callback
+ * @param arg		@todo Document.
+ * @param data		@todo Document.
+ */
+typedef void (*poptCallbackType) (poptContext con, 
+		enum poptCallbackReason reason,
+		/*@null@*/ const struct poptOption * opt,
+		/*@null@*/ const char * arg,
+		/*@null@*/ const void * data)
+	/*@globals internalState @*/
+	/*@modifies internalState @*/;
+
+/** \ingroup popt
+ * Initialize popt context.
+ * @param name		context name (usually argv[0] program name)
+ * @param argc		no. of arguments
+ * @param argv		argument array
+ * @param options	address of popt option table
+ * @param flags		or'd POPT_CONTEXT_* bits
+ * @return		initialized popt context
+ */
+/*@only@*/ /*@null@*/
+poptContext poptGetContext(
+		/*@dependent@*/ /*@keep@*/ const char * name,
+		int argc, /*@dependent@*/ /*@keep@*/ const char ** argv,
+		/*@dependent@*/ /*@keep@*/ const struct poptOption * options,
+		int flags)
+	/*@*/;
+
+/** \ingroup popt
+ * Reinitialize popt context.
+ * @param con		context
+ */
+/*@unused@*/
+void poptResetContext(/*@null@*/poptContext con)
+	/*@modifies con @*/;
+
+/** \ingroup popt
+ * Return value of next option found.
+ * @param con		context
+ * @return		next option val, -1 on last item, POPT_ERROR_* on error
+ */
+int poptGetNextOpt(/*@null@*/poptContext con)
+	/*@globals fileSystem, internalState @*/
+	/*@modifies con, fileSystem, internalState @*/;
+
+/** \ingroup popt
+ * Return next option argument (if any).
+ * @param con		context
+ * @return		option argument, NULL if no argument is available
+ */
+/*@observer@*/ /*@null@*/ /*@unused@*/
+const char * poptGetOptArg(/*@null@*/poptContext con)
+	/*@modifies con @*/;
+
+/** \ingroup popt
+ * Return next argument.
+ * @param con		context
+ * @return		next argument, NULL if no argument is available
+ */
+/*@observer@*/ /*@null@*/ /*@unused@*/
+const char * poptGetArg(/*@null@*/poptContext con)
+	/*@modifies con @*/;
+
+/** \ingroup popt
+ * Peek at current argument.
+ * @param con		context
+ * @return		current argument, NULL if no argument is available
+ */
+/*@observer@*/ /*@null@*/ /*@unused@*/
+const char * poptPeekArg(/*@null@*/poptContext con)
+	/*@*/;
+
+/** \ingroup popt
+ * Return remaining arguments.
+ * @param con		context
+ * @return		argument array, NULL terminated
+ */
+/*@observer@*/ /*@null@*/
+const char ** poptGetArgs(/*@null@*/poptContext con)
+	/*@modifies con @*/;
+
+/** \ingroup popt
+ * Return the option which caused the most recent error.
+ * @param con		context
+ * @param flags
+ * @return		offending option
+ */
+/*@observer@*/
+const char * poptBadOption(/*@null@*/poptContext con, int flags)
+	/*@*/;
+
+/** \ingroup popt
+ * Destroy context.
+ * @param con		context
+ * @return		NULL always
+ */
+/*@null@*/
+poptContext poptFreeContext( /*@only@*/ /*@null@*/ poptContext con)
+	/*@modifies con @*/;
+
+/** \ingroup popt
+ * Add arguments to context.
+ * @param con		context
+ * @param argv		argument array, NULL terminated
+ * @return		0 on success, POPT_ERROR_OPTSTOODEEP on failure
+ */
+/*@unused@*/
+int poptStuffArgs(poptContext con, /*@keep@*/ const char ** argv)
+	/*@modifies con @*/;
+
+/** \ingroup popt
+ * Add alias to context.
+ * @todo Pass alias by reference, not value.
+ * @deprecated Use poptAddItem instead.
+ * @param con		context
+ * @param alias		alias to add
+ * @param flags		(unused)
+ * @return		0 on success
+ */
+/*@unused@*/
+int poptAddAlias(poptContext con, struct poptAlias alias, int flags)
+	/*@modifies con @*/;
+
+/** \ingroup popt
+ * Add alias/exec item to context.
+ * @param con		context
+ * @param newItem	alias/exec item to add
+ * @param flags		0 for alias, 1 for exec
+ * @return		0 on success
+ */
+int poptAddItem(poptContext con, poptItem newItem, int flags)
+	/*@modifies con @*/;
+
+/** \ingroup popt
+ * Read configuration file.
+ * @param con		context
+ * @param fn		file name to read
+ * @return		0 on success, POPT_ERROR_ERRNO on failure
+ */
+int poptReadConfigFile(poptContext con, const char * fn)
+	/*@globals errno, fileSystem, internalState @*/
+	/*@modifies con->execs, con->numExecs,
+		errno, fileSystem, internalState @*/;
+
+/** \ingroup popt
+ * Read default configuration from /etc/popt and $HOME/.popt.
+ * @param con		context
+ * @param useEnv	(unused)
+ * @return		0 on success, POPT_ERROR_ERRNO on failure
+ */
+/*@unused@*/
+int poptReadDefaultConfig(poptContext con, /*@unused@*/ int useEnv)
+	/*@globals fileSystem, internalState @*/
+	/*@modifies con->execs, con->numExecs,
+		fileSystem, internalState @*/;
+
+/** \ingroup popt
+ * Duplicate an argument array.
+ * @note: The argument array is malloc'd as a single area, so only argv must
+ * be free'd.
+ *
+ * @param argc		no. of arguments
+ * @param argv		argument array
+ * @retval argcPtr	address of returned no. of arguments
+ * @retval argvPtr	address of returned argument array
+ * @return		0 on success, POPT_ERROR_NOARG on failure
+ */
+int poptDupArgv(int argc, /*@null@*/ const char **argv,
+		/*@null@*/ /*@out@*/ int * argcPtr,
+		/*@null@*/ /*@out@*/ const char *** argvPtr)
+	/*@modifies *argcPtr, *argvPtr @*/;
+
+/** \ingroup popt
+ * Parse a string into an argument array.
+ * The parse allows ', ", and \ quoting, but ' is treated the same as " and
+ * both may include \ quotes.
+ * @note: The argument array is malloc'd as a single area, so only argv must
+ * be free'd.
+ *
+ * @param s		string to parse
+ * @retval argcPtr	address of returned no. of arguments
+ * @retval argvPtr	address of returned argument array
+ */
+int poptParseArgvString(const char * s,
+		/*@out@*/ int * argcPtr, /*@out@*/ const char *** argvPtr)
+	/*@modifies *argcPtr, *argvPtr @*/;
+
+/** \ingroup popt
+ * Parses an input configuration file and returns an string that is a 
+ * command line.  For use with popt.  You must free the return value when done.
+ *
+ * Given the file:
+\verbatim
+# this line is ignored
+    #   this one too
+aaa
+  bbb
+    ccc   
+bla=bla
+
+this_is   =   fdsafdas
+     bad_line=        
+  reall bad line  
+  reall bad line  = again
+5555=   55555   
+  test = with lots of spaces
+\endverbatim
+*
+* The result is:
+\verbatim
+--aaa --bbb --ccc --bla="bla" --this_is="fdsafdas" --5555="55555" --test="with lots of spaces"
+\endverbatim
+*
+* Passing this to poptParseArgvString() yields an argv of:
+\verbatim
+'--aaa'
+'--bbb' 
+'--ccc' 
+'--bla=bla' 
+'--this_is=fdsafdas' 
+'--5555=55555' 
+'--test=with lots of spaces' 
+\endverbatim
+ *
+ * @bug NULL is returned if file line is too long.
+ * @bug Silently ignores invalid lines.
+ *
+ * @param fp		file handle to read
+ * @param *argstrp	return string of options (malloc'd)
+ * @param flags		unused
+ * @return		0 on success
+ * @see			poptParseArgvString
+ */
+/*@-fcnuse@*/
+int poptConfigFileToString(FILE *fp, /*@out@*/ char ** argstrp, int flags)
+	/*@globals fileSystem @*/
+	/*@modifies *fp, *argstrp, fileSystem @*/;
+/*@=fcnuse@*/
+
+/** \ingroup popt
+ * Return formatted error string for popt failure.
+ * @param error		popt error
+ * @return		error string
+ */
+/*@observer@*/
+const char * poptStrerror(const int error)
+	/*@*/;
+
+/** \ingroup popt
+ * Limit search for executables.
+ * @param con		context
+ * @param path		single path to search for executables
+ * @param allowAbsolute	absolute paths only?
+ */
+/*@unused@*/
+void poptSetExecPath(poptContext con, const char * path, int allowAbsolute)
+	/*@modifies con @*/;
+
+/** \ingroup popt
+ * Print detailed description of options.
+ * @param con		context
+ * @param fp		ouput file handle
+ * @param flags		(unused)
+ */
+void poptPrintHelp(poptContext con, FILE * fp, /*@unused@*/ int flags)
+	/*@globals fileSystem @*/
+	/*@modifies *fp, fileSystem @*/;
+
+/** \ingroup popt
+ * Print terse description of options.
+ * @param con		context
+ * @param fp		ouput file handle
+ * @param flags		(unused)
+ */
+void poptPrintUsage(poptContext con, FILE * fp, /*@unused@*/ int flags)
+	/*@globals fileSystem @*/
+	/*@modifies *fp, fileSystem @*/;
+
+/** \ingroup popt
+ * Provide text to replace default "[OPTION...]" in help/usage output.
+ * @param con		context
+ * @param text		replacement text
+ */
+/*@-fcnuse@*/
+void poptSetOtherOptionHelp(poptContext con, const char * text)
+	/*@modifies con @*/;
+/*@=fcnuse@*/
+
+/** \ingroup popt
+ * Return argv[0] from context.
+ * @param con		context
+ * @return		argv[0]
+ */
+/*@-fcnuse@*/
+/*@observer@*/
+const char * poptGetInvocationName(poptContext con)
+	/*@*/;
+/*@=fcnuse@*/
+
+/** \ingroup popt
+ * Shuffle argv pointers to remove stripped args, returns new argc.
+ * @param con		context
+ * @param argc		no. of args
+ * @param argv		arg vector
+ * @return		new argc
+ */
+/*@-fcnuse@*/
+int poptStrippedArgv(poptContext con, int argc, char ** argv)
+	/*@modifies *argv @*/;
+/*@=fcnuse@*/
+
+/**
+ * Save a long, performing logical operation with value.
+ * @warning Alignment check may be too strict on certain platorms.
+ * @param arg		integer pointer, aligned on int boundary.
+ * @param argInfo	logical operation (see POPT_ARGFLAG_*)
+ * @param aLong		value to use
+ * @return		0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
+ */
+/*@-incondefs@*/
+/*@unused@*/
+int poptSaveLong(/*@null@*/ long * arg, int argInfo, long aLong)
+	/*@modifies *arg @*/
+	/*@requires maxSet(arg) >= 0 /\ maxRead(arg) == 0 @*/;
+/*@=incondefs@*/
+
+/**
+ * Save an integer, performing logical operation with value.
+ * @warning Alignment check may be too strict on certain platorms.
+ * @param arg		integer pointer, aligned on int boundary.
+ * @param argInfo	logical operation (see POPT_ARGFLAG_*)
+ * @param aLong		value to use
+ * @return		0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
+ */
+/*@-incondefs@*/
+/*@unused@*/
+int poptSaveInt(/*@null@*/ int * arg, int argInfo, long aLong)
+	/*@modifies *arg @*/
+	/*@requires maxSet(arg) >= 0 /\ maxRead(arg) == 0 @*/;
+/*@=incondefs@*/
+
+/*@=type@*/
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
diff --git a/libpopt/poptconfig.c b/libpopt/poptconfig.c
new file mode 100644
index 0000000..e5cba45
--- /dev/null
+++ b/libpopt/poptconfig.c
@@ -0,0 +1,182 @@
+/** \ingroup popt
+ * \file popt/poptconfig.c
+ */
+
+/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.rpm.org/pub/rpm/dist. */
+
+#include "system.h"
+#include "poptint.h"
+/*@access poptContext @*/
+
+/*@-compmempass@*/	/* FIX: item->option.longName kept, not dependent. */
+static void configLine(poptContext con, char * line)
+	/*@modifies con @*/
+{
+    size_t nameLength;
+    const char * entryType;
+    const char * opt;
+    poptItem item = alloca(sizeof(*item));
+    int i, j;
+
+    if (con->appName == NULL)
+	return;
+    nameLength = strlen(con->appName);
+    
+/*@-boundswrite@*/
+    memset(item, 0, sizeof(*item));
+
+    if (strncmp(line, con->appName, nameLength)) return;
+
+    line += nameLength;
+    if (*line == '\0' || !isspace(*line)) return;
+
+    while (*line != '\0' && isspace(*line)) line++;
+    entryType = line;
+    while (*line == '\0' || !isspace(*line)) line++;
+    *line++ = '\0';
+
+    while (*line != '\0' && isspace(*line)) line++;
+    if (*line == '\0') return;
+    opt = line;
+    while (*line == '\0' || !isspace(*line)) line++;
+    *line++ = '\0';
+
+    while (*line != '\0' && isspace(*line)) line++;
+    if (*line == '\0') return;
+
+    /*@-temptrans@*/ /* FIX: line alias is saved */
+    if (opt[0] == '-' && opt[1] == '-')
+	item->option.longName = opt + 2;
+    else if (opt[0] == '-' && opt[2] == '\0')
+	item->option.shortName = opt[1];
+    /*@=temptrans@*/
+
+    if (poptParseArgvString(line, &item->argc, &item->argv)) return;
+
+    /*@-modobserver@*/
+    item->option.argInfo = POPT_ARGFLAG_DOC_HIDDEN;
+    for (i = 0, j = 0; i < item->argc; i++, j++) {
+	const char * f;
+	if (!strncmp(item->argv[i], "--POPTdesc=", sizeof("--POPTdesc=")-1)) {
+	    f = item->argv[i] + sizeof("--POPTdesc=");
+	    if (f[0] == '$' && f[1] == '"') f++;
+	    item->option.descrip = f;
+	    item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
+	    j--;
+	} else
+	if (!strncmp(item->argv[i], "--POPTargs=", sizeof("--POPTargs=")-1)) {
+	    f = item->argv[i] + sizeof("--POPTargs=");
+	    if (f[0] == '$' && f[1] == '"') f++;
+	    item->option.argDescrip = f;
+	    item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
+	    item->option.argInfo |= POPT_ARG_STRING;
+	    j--;
+	} else
+	if (j != i)
+	    item->argv[j] = item->argv[i];
+    }
+    if (j != i) {
+	item->argv[j] = NULL;
+	item->argc = j;
+    }
+    /*@=modobserver@*/
+/*@=boundswrite@*/
+	
+    /*@-nullstate@*/ /* FIX: item->argv[] may be NULL */
+    if (!strcmp(entryType, "alias"))
+	(void) poptAddItem(con, item, 0);
+    else if (!strcmp(entryType, "exec"))
+	(void) poptAddItem(con, item, 1);
+    /*@=nullstate@*/
+}
+/*@=compmempass@*/
+
+int poptReadConfigFile(poptContext con, const char * fn)
+{
+    const char * file, * chptr, * end;
+    char * buf;
+/*@dependent@*/ char * dst;
+    int fd, rc;
+    off_t fileLength;
+
+    fd = open(fn, O_RDONLY);
+    if (fd < 0)
+	return (errno == ENOENT ? 0 : POPT_ERROR_ERRNO);
+
+    fileLength = lseek(fd, 0, SEEK_END);
+    if (fileLength == -1 || lseek(fd, 0, 0) == -1) {
+	rc = errno;
+	(void) close(fd);
+	errno = rc;
+	return POPT_ERROR_ERRNO;
+    }
+
+    file = alloca(fileLength + 1);
+    if (read(fd, (char *)file, fileLength) != fileLength) {
+	rc = errno;
+	(void) close(fd);
+	errno = rc;
+	return POPT_ERROR_ERRNO;
+    }
+    if (close(fd) == -1)
+	return POPT_ERROR_ERRNO;
+
+/*@-boundswrite@*/
+    dst = buf = alloca(fileLength + 1);
+
+    chptr = file;
+    end = (file + fileLength);
+    /*@-infloops@*/	/* LCL: can't detect chptr++ */
+    while (chptr < end) {
+	switch (*chptr) {
+	  case '\n':
+	    *dst = '\0';
+	    dst = buf;
+	    while (*dst && isspace(*dst)) dst++;
+	    if (*dst && *dst != '#')
+		configLine(con, dst);
+	    chptr++;
+	    /*@switchbreak@*/ break;
+	  case '\\':
+	    *dst++ = *chptr++;
+	    if (chptr < end) {
+		if (*chptr == '\n') 
+		    dst--, chptr++;	
+		    /* \ at the end of a line does not insert a \n */
+		else
+		    *dst++ = *chptr++;
+	    }
+	    /*@switchbreak@*/ break;
+	  default:
+	    *dst++ = *chptr++;
+	    /*@switchbreak@*/ break;
+	}
+    }
+    /*@=infloops@*/
+/*@=boundswrite@*/
+
+    return 0;
+}
+
+int poptReadDefaultConfig(poptContext con, /*@unused@*/ int useEnv)
+{
+    char * fn, * home;
+    int rc;
+
+    if (con->appName == NULL) return 0;
+
+    rc = poptReadConfigFile(con, "/etc/popt");
+    if (rc) return rc;
+
+    if ((home = getenv("HOME"))) {
+	fn = alloca(strlen(home) + 20);
+	strcpy(fn, home);
+	strcat(fn, "/.popt");
+	rc = poptReadConfigFile(con, fn);
+	if (rc) return rc;
+    }
+
+    return 0;
+}
diff --git a/libpopt/popthelp.c b/libpopt/popthelp.c
new file mode 100644
index 0000000..9430a2d
--- /dev/null
+++ b/libpopt/popthelp.c
@@ -0,0 +1,819 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/** \ingroup popt
+ * \file popt/popthelp.c
+ */
+
+/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.rpm.org/pub/rpm/dist. */
+
+#include "system.h"
+
+//#define	POPT_WCHAR_HACK
+#ifdef 	POPT_WCHAR_HACK
+#include <wchar.h>			/* for mbsrtowcs */
+/*@access mbstate_t @*/
+#endif
+#include "poptint.h"
+
+/*@access poptContext@*/
+
+/**
+ * Display arguments.
+ * @param con		context
+ * @param foo		(unused)
+ * @param key		option(s)
+ * @param arg		(unused)
+ * @param data		(unused)
+ */
+static void displayArgs(poptContext con,
+		/*@unused@*/ enum poptCallbackReason foo,
+		struct poptOption * key, 
+		/*@unused@*/ const char * arg, /*@unused@*/ void * data)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+{
+    if (key->shortName == '?')
+	poptPrintHelp(con, stdout, 0);
+    else
+	poptPrintUsage(con, stdout, 0);
+    exit(0);
+}
+
+#ifdef	NOTYET
+/*@unchecked@*/
+static int show_option_defaults = 0;
+#endif
+
+/**
+ * Empty table marker to enable displaying popt alias/exec options.
+ */
+/*@observer@*/ /*@unchecked@*/
+struct poptOption poptAliasOptions[] = {
+    POPT_TABLEEND
+};
+
+/**
+ * Auto help table options.
+ */
+/*@-castfcnptr@*/
+/*@observer@*/ /*@unchecked@*/
+struct poptOption poptHelpOptions[] = {
+  { NULL, '\0', POPT_ARG_CALLBACK, (void *)&displayArgs, '\0', NULL, NULL },
+  { "help", '?', 0, NULL, '?', N_("Show this help message"), NULL },
+  { "usage", '\0', 0, NULL, 'u', N_("Display brief usage message"), NULL },
+    POPT_TABLEEND
+} ;
+
+/*@observer@*/ /*@unchecked@*/
+static struct poptOption poptHelpOptions2[] = {
+/*@-readonlytrans@*/
+  { NULL, '\0', POPT_ARG_INTL_DOMAIN, PACKAGE, 0, NULL, NULL},
+/*@=readonlytrans@*/
+  { NULL, '\0', POPT_ARG_CALLBACK, (void *)&displayArgs, '\0', NULL, NULL },
+  { "help", '?', 0, NULL, '?', N_("Show this help message"), NULL },
+  { "usage", '\0', 0, NULL, 'u', N_("Display brief usage message"), NULL },
+#ifdef	NOTYET
+  { "defaults", '\0', POPT_ARG_NONE, &show_option_defaults, 0,
+	N_("Display option defaults in message"), NULL },
+#endif
+    POPT_TABLEEND
+} ;
+
+/*@observer@*/ /*@unchecked@*/
+struct poptOption * poptHelpOptionsI18N = poptHelpOptions2;
+/*@=castfcnptr@*/
+
+/**
+ * @param table		option(s)
+ */
+/*@observer@*/ /*@null@*/ static const char *
+getTableTranslationDomain(/*@null@*/ const struct poptOption *table)
+	/*@*/
+{
+    const struct poptOption *opt;
+
+    if (table != NULL)
+    for (opt = table; opt->longName || opt->shortName || opt->arg; opt++) {
+	if (opt->argInfo == POPT_ARG_INTL_DOMAIN)
+	    return opt->arg;
+    }
+    return NULL;
+}
+
+/**
+ * @param opt		option(s)
+ * @param translation_domain	translation domain
+ */
+/*@observer@*/ /*@null@*/ static const char *
+getArgDescrip(const struct poptOption * opt,
+		/*@-paramuse@*/ /* FIX: i18n macros disabled with lclint */
+		/*@null@*/ const char * translation_domain)
+		/*@=paramuse@*/
+	/*@*/
+{
+    if (!(opt->argInfo & POPT_ARG_MASK)) return NULL;
+
+    if (opt == (poptHelpOptions + 1) || opt == (poptHelpOptions + 2))
+	if (opt->argDescrip) return POPT_(opt->argDescrip);
+
+    if (opt->argDescrip) return D_(translation_domain, opt->argDescrip);
+
+    switch (opt->argInfo & POPT_ARG_MASK) {
+    case POPT_ARG_NONE:		return POPT_("NONE");
+#ifdef	DYING
+    case POPT_ARG_VAL:		return POPT_("VAL");
+#else
+    case POPT_ARG_VAL:		return NULL;
+#endif
+    case POPT_ARG_INT:		return POPT_("INT");
+    case POPT_ARG_LONG:		return POPT_("LONG");
+    case POPT_ARG_STRING:	return POPT_("STRING");
+    case POPT_ARG_FLOAT:	return POPT_("FLOAT");
+    case POPT_ARG_DOUBLE:	return POPT_("DOUBLE");
+    default:			return POPT_("ARG");
+    }
+}
+
+/**
+ * Display default value for an option.
+ * @param lineLength	display positions remaining
+ * @param opt		option(s)
+ * @param translation_domain	translation domain
+ * @return
+ */
+static /*@only@*/ /*@null@*/ char *
+singleOptionDefaultValue(size_t lineLength,
+		const struct poptOption * opt,
+		/*@-paramuse@*/ /* FIX: i18n macros disabled with lclint */
+		/*@null@*/ const char * translation_domain)
+		/*@=paramuse@*/
+	/*@*/
+{
+    const char * defstr = D_(translation_domain, "default");
+    char * le = malloc(4*lineLength + 1);
+    char * l = le;
+
+    if (le == NULL) return NULL;	/* XXX can't happen */
+/*@-boundswrite@*/
+    *le = '\0';
+    *le++ = '(';
+    strcpy(le, defstr);	le += strlen(le);
+    *le++ = ':';
+    *le++ = ' ';
+    if (opt->arg)	/* XXX programmer error */
+    switch (opt->argInfo & POPT_ARG_MASK) {
+    case POPT_ARG_VAL:
+    case POPT_ARG_INT:
+    {	long aLong = *((int *)opt->arg);
+	le += sprintf(le, "%ld", aLong);
+    }	break;
+    case POPT_ARG_LONG:
+    {	long aLong = *((long *)opt->arg);
+	le += sprintf(le, "%ld", aLong);
+    }	break;
+    case POPT_ARG_FLOAT:
+    {	double aDouble = *((float *)opt->arg);
+	le += sprintf(le, "%g", aDouble);
+    }	break;
+    case POPT_ARG_DOUBLE:
+    {	double aDouble = *((double *)opt->arg);
+	le += sprintf(le, "%g", aDouble);
+    }	break;
+    case POPT_ARG_STRING:
+    {	const char * s = *(const char **)opt->arg;
+	if (s == NULL) {
+	    strcpy(le, "null");	le += strlen(le);
+	} else {
+	    size_t slen = 4*lineLength - (le - l) - sizeof("\"...\")");
+	    *le++ = '"';
+	    strncpy(le, s, slen); le[slen] = '\0'; le += strlen(le);	
+	    if (slen < strlen(s)) {
+		strcpy(le, "...");	le += strlen(le);
+	    }
+	    *le++ = '"';
+	}
+    }	break;
+    case POPT_ARG_NONE:
+    default:
+	l = _free(l);
+	return NULL;
+	/*@notreached@*/ break;
+    }
+    *le++ = ')';
+    *le = '\0';
+/*@=boundswrite@*/
+
+    return l;
+}
+
+/**
+ * Display help text for an option.
+ * @param fp		output file handle
+ * @param maxLeftCol	largest argument display width
+ * @param opt		option(s)
+ * @param translation_domain	translation domain
+ */
+static void singleOptionHelp(FILE * fp, size_t maxLeftCol, 
+		const struct poptOption * opt,
+		/*@null@*/ const char * translation_domain)
+	/*@globals fileSystem @*/
+	/*@modifies *fp, fileSystem @*/
+{
+    size_t indentLength = maxLeftCol + 5;
+    size_t lineLength = 79 - indentLength;
+    const char * help = D_(translation_domain, opt->descrip);
+    const char * argDescrip = getArgDescrip(opt, translation_domain);
+    size_t helpLength;
+    char * defs = NULL;
+    char * left;
+    size_t nb = maxLeftCol + 1;
+    int displaypad = 0;
+
+    /* Make sure there's more than enough room in target buffer. */
+    if (opt->longName)	nb += strlen(opt->longName);
+    if (argDescrip)	nb += strlen(argDescrip);
+
+/*@-boundswrite@*/
+    left = malloc(nb);
+    if (left == NULL) return;	/* XXX can't happen */
+    left[0] = '\0';
+    left[maxLeftCol] = '\0';
+
+    if (opt->longName && opt->shortName)
+	sprintf(left, "-%c, %s%s", opt->shortName,
+		((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"),
+		opt->longName);
+    else if (opt->shortName != '\0') 
+	sprintf(left, "-%c", opt->shortName);
+    else if (opt->longName)
+	sprintf(left, "%s%s",
+		((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"),
+		opt->longName);
+    if (!*left) goto out;
+
+    if (argDescrip) {
+	char * le = left + strlen(left);
+
+	if (opt->argInfo & POPT_ARGFLAG_OPTIONAL)
+	    *le++ = '[';
+
+	/* Choose type of output */
+	/*@-branchstate@*/
+	if (opt->argInfo & POPT_ARGFLAG_SHOW_DEFAULT) {
+	    defs = singleOptionDefaultValue(lineLength, opt, translation_domain);
+	    if (defs) {
+		char * t = malloc((help ? strlen(help) : 0) +
+				strlen(defs) + sizeof(" "));
+		if (t) {
+		    char * te = t;
+		    *te = '\0';
+		    if (help) {
+			strcpy(te, help);	te += strlen(te);
+		    }
+		    *te++ = ' ';
+		    strcpy(te, defs);
+		    defs = _free(defs);
+		}
+		defs = t;
+	    }
+	}
+	/*@=branchstate@*/
+
+	if (opt->argDescrip == NULL) {
+	    switch (opt->argInfo & POPT_ARG_MASK) {
+	    case POPT_ARG_NONE:
+		break;
+	    case POPT_ARG_VAL:
+#ifdef	NOTNOW	/* XXX pug ugly nerdy output */
+	    {	long aLong = opt->val;
+		int ops = (opt->argInfo & POPT_ARGFLAG_LOGICALOPS);
+		int negate = (opt->argInfo & POPT_ARGFLAG_NOT);
+
+		/* Don't bother displaying typical values */
+		if (!ops && (aLong == 0L || aLong == 1L || aLong == -1L))
+		    break;
+		*le++ = '[';
+		switch (ops) {
+		case POPT_ARGFLAG_OR:
+		    *le++ = '|';
+		    /*@innerbreak@*/ break;
+		case POPT_ARGFLAG_AND:
+		    *le++ = '&';
+		    /*@innerbreak@*/ break;
+		case POPT_ARGFLAG_XOR:
+		    *le++ = '^';
+		    /*@innerbreak@*/ break;
+		default:
+		    /*@innerbreak@*/ break;
+		}
+		*le++ = (opt->longName != NULL ? '=' : ' ');
+		if (negate) *le++ = '~';
+		/*@-formatconst@*/
+		le += sprintf(le, (ops ? "0x%lx" : "%ld"), aLong);
+		/*@=formatconst@*/
+		*le++ = ']';
+	    }
+#endif
+		break;
+	    case POPT_ARG_INT:
+	    case POPT_ARG_LONG:
+	    case POPT_ARG_FLOAT:
+	    case POPT_ARG_DOUBLE:
+	    case POPT_ARG_STRING:
+		*le++ = (opt->longName != NULL ? '=' : ' ');
+		strcpy(le, argDescrip);		le += strlen(le);
+		break;
+	    default:
+		break;
+	    }
+	} else {
+	    size_t lelen;
+
+	    *le++ = '=';
+	    strcpy(le, argDescrip);
+	    lelen = strlen(le);
+	    le += lelen;
+
+#ifdef	POPT_WCHAR_HACK
+	    {	const char * scopy = argDescrip;
+		mbstate_t t;
+		size_t n;
+
+		memset ((void *)&t, '\0', sizeof (t));	/* In initial state.  */
+		/* Determine number of characters.  */
+		n = mbsrtowcs (NULL, &scopy, strlen(scopy), &t);
+
+		displaypad = (int) (lelen-n);
+	    }
+#endif
+	}
+	if (opt->argInfo & POPT_ARGFLAG_OPTIONAL)
+	    *le++ = ']';
+	*le = '\0';
+    }
+/*@=boundswrite@*/
+
+    if (help)
+	fprintf(fp,"  %-*s   ", maxLeftCol+displaypad, left);
+    else {
+	fprintf(fp,"  %s\n", left); 
+	goto out;
+    }
+
+    left = _free(left);
+/*@-branchstate@*/
+    if (defs) {
+	help = defs;
+	defs = NULL;
+    }
+/*@=branchstate@*/
+
+    helpLength = strlen(help);
+/*@-boundsread@*/
+    while (helpLength > lineLength) {
+	const char * ch;
+	char format[16];
+
+	ch = help + lineLength - 1;
+	while (ch > help && !isspace(*ch)) ch--;
+	if (ch == help) break;		/* give up */
+	while (ch > (help + 1) && isspace(*ch)) ch--;
+	ch++;
+
+	sprintf(format, "%%.%ds\n%%%ds", (int) (ch - help), (int) indentLength);
+	/*@-formatconst@*/
+	fprintf(fp, format, help, " ");
+	/*@=formatconst@*/
+	help = ch;
+	while (isspace(*help) && *help) help++;
+	helpLength = strlen(help);
+    }
+/*@=boundsread@*/
+
+    if (helpLength) fprintf(fp, "%s\n", help);
+
+out:
+    /*@-dependenttrans@*/
+    defs = _free(defs);
+    /*@=dependenttrans@*/
+    left = _free(left);
+}
+
+/**
+ * Find display width for longest argument string.
+ * @param opt		option(s)
+ * @param translation_domain	translation domain
+ * @return		display width
+ */
+static size_t maxArgWidth(const struct poptOption * opt,
+		       /*@null@*/ const char * translation_domain)
+	/*@*/
+{
+    size_t max = 0;
+    size_t len = 0;
+    const char * s;
+    
+    if (opt != NULL)
+    while (opt->longName || opt->shortName || opt->arg) {
+	if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+	    if (opt->arg)	/* XXX program error */
+	    len = maxArgWidth(opt->arg, translation_domain);
+	    if (len > max) max = len;
+	} else if (!(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
+	    len = sizeof("  ")-1;
+	    if (opt->shortName != '\0') len += sizeof("-X")-1;
+	    if (opt->shortName != '\0' && opt->longName) len += sizeof(", ")-1;
+	    if (opt->longName) {
+		len += ((opt->argInfo & POPT_ARGFLAG_ONEDASH)
+			? sizeof("-")-1 : sizeof("--")-1);
+		len += strlen(opt->longName);
+	    }
+
+	    s = getArgDescrip(opt, translation_domain);
+
+#ifdef POPT_WCHAR_HACK
+	    /* XXX Calculate no. of display characters. */
+	    if (s) {
+		const char * scopy = s;
+		mbstate_t t;
+		size_t n;
+
+/*@-boundswrite@*/
+		memset ((void *)&t, '\0', sizeof (t));	/* In initial state.  */
+/*@=boundswrite@*/
+		/* Determine number of characters.  */
+		n = mbsrtowcs (NULL, &scopy, strlen(scopy), &t);
+		len += sizeof("=")-1 + n;
+	    }
+#else
+	    if (s)
+		len += sizeof("=")-1 + strlen(s);
+#endif
+
+	    if (opt->argInfo & POPT_ARGFLAG_OPTIONAL) len += sizeof("[]")-1;
+	    if (len > max) max = len;
+	}
+
+	opt++;
+    }
+    
+    return max;
+}
+
+/**
+ * Display popt alias and exec help.
+ * @param fp		output file handle
+ * @param items		alias/exec array
+ * @param nitems	no. of alias/exec entries
+ * @param left		largest argument display width
+ * @param translation_domain	translation domain
+ */
+static void itemHelp(FILE * fp,
+		/*@null@*/ poptItem items, int nitems, size_t left,
+		/*@null@*/ const char * translation_domain)
+	/*@globals fileSystem @*/
+	/*@modifies *fp, fileSystem @*/
+{
+    poptItem item;
+    int i;
+
+    if (items != NULL)
+    for (i = 0, item = items; i < nitems; i++, item++) {
+	const struct poptOption * opt;
+	opt = &item->option;
+	if ((opt->longName || opt->shortName) && 
+	    !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
+	    singleOptionHelp(fp, left, opt, translation_domain);
+    }
+}
+
+/**
+ * Display help text for a table of options.
+ * @param con		context
+ * @param fp		output file handle
+ * @param table		option(s)
+ * @param left		largest argument display width
+ * @param translation_domain	translation domain
+ */
+static void singleTableHelp(poptContext con, FILE * fp,
+		/*@null@*/ const struct poptOption * table, size_t left,
+		/*@null@*/ const char * translation_domain)
+	/*@globals fileSystem @*/
+	/*@modifies *fp, fileSystem @*/
+{
+    const struct poptOption * opt;
+    const char *sub_transdom;
+
+    if (table == poptAliasOptions) {
+	itemHelp(fp, con->aliases, con->numAliases, left, NULL);
+	itemHelp(fp, con->execs, con->numExecs, left, NULL);
+	return;
+    }
+
+    if (table != NULL)
+    for (opt = table; (opt->longName || opt->shortName || opt->arg); opt++) {
+	if ((opt->longName || opt->shortName) && 
+	    !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
+	    singleOptionHelp(fp, left, opt, translation_domain);
+    }
+
+    if (table != NULL)
+    for (opt = table; (opt->longName || opt->shortName || opt->arg); opt++) {
+	if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_INCLUDE_TABLE)
+	    continue;
+	sub_transdom = getTableTranslationDomain(opt->arg);
+	if (sub_transdom == NULL)
+	    sub_transdom = translation_domain;
+	    
+	if (opt->descrip)
+	    fprintf(fp, "\n%s\n", D_(sub_transdom, opt->descrip));
+
+	singleTableHelp(con, fp, opt->arg, left, sub_transdom);
+    }
+}
+
+/**
+ * @param con		context
+ * @param fp		output file handle
+ */
+static int showHelpIntro(poptContext con, FILE * fp)
+	/*@globals fileSystem @*/
+	/*@modifies *fp, fileSystem @*/
+{
+    int len = 6;
+    const char * fn;
+
+    fprintf(fp, POPT_("Usage:"));
+    if (!(con->flags & POPT_CONTEXT_KEEP_FIRST)) {
+/*@-boundsread@*/
+	/*@-nullderef -type@*/	/* LCL: wazzup? */
+	fn = con->optionStack->argv[0];
+	/*@=nullderef =type@*/
+/*@=boundsread@*/
+	if (fn == NULL) return len;
+	if (strchr(fn, '/')) fn = strrchr(fn, '/') + 1;
+	fprintf(fp, " %s", fn);
+	len += strlen(fn) + 1;
+    }
+
+    return len;
+}
+
+void poptPrintHelp(poptContext con, FILE * fp, /*@unused@*/ int flags)
+{
+    size_t leftColWidth;
+
+    (void) showHelpIntro(con, fp);
+    if (con->otherHelp)
+	fprintf(fp, " %s\n", con->otherHelp);
+    else
+	fprintf(fp, " %s\n", POPT_("[OPTION...]"));
+
+    leftColWidth = maxArgWidth(con->options, NULL);
+    singleTableHelp(con, fp, con->options, leftColWidth, NULL);
+}
+
+/**
+ * Display usage text for an option.
+ * @param fp		output file handle
+ * @param cursor	current display position
+ * @param opt		option(s)
+ * @param translation_domain	translation domain
+ */
+static size_t singleOptionUsage(FILE * fp, size_t cursor, 
+		const struct poptOption * opt,
+		/*@null@*/ const char *translation_domain)
+	/*@globals fileSystem @*/
+	/*@modifies *fp, fileSystem @*/
+{
+    size_t len = 4;
+    char shortStr[2] = { '\0', '\0' };
+    const char * item = shortStr;
+    const char * argDescrip = getArgDescrip(opt, translation_domain);
+
+    if (opt->shortName != '\0' && opt->longName != NULL) {
+	len += 2;
+	if (!(opt->argInfo & POPT_ARGFLAG_ONEDASH)) len++;
+	len += strlen(opt->longName);
+    } else if (opt->shortName != '\0') {
+	len++;
+	shortStr[0] = opt->shortName;
+	shortStr[1] = '\0';
+    } else if (opt->longName) {
+	len += strlen(opt->longName);
+	if (!(opt->argInfo & POPT_ARGFLAG_ONEDASH)) len++;
+	item = opt->longName;
+    }
+
+    if (len == 4) return cursor;
+
+#ifdef POPT_WCHAR_HACK
+    /* XXX Calculate no. of display characters. */
+    if (argDescrip) {
+	const char * scopy = argDescrip;
+	mbstate_t t;
+	size_t n;
+
+/*@-boundswrite@*/
+	memset ((void *)&t, '\0', sizeof (t));	/* In initial state.  */
+/*@=boundswrite@*/
+	/* Determine number of characters.  */
+	n = mbsrtowcs (NULL, &scopy, strlen(scopy), &t);
+	len += sizeof("=")-1 + n;
+    }
+#else
+    if (argDescrip) 
+	len += sizeof("=")-1 + strlen(argDescrip);
+#endif
+
+    if ((cursor + len) > 79) {
+	fprintf(fp, "\n       ");
+	cursor = 7;
+    } 
+
+    if (opt->longName && opt->shortName) {
+	fprintf(fp, " [-%c|-%s%s%s%s]",
+	    opt->shortName, ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "" : "-"),
+	    opt->longName,
+	    (argDescrip ? " " : ""),
+	    (argDescrip ? argDescrip : ""));
+    } else {
+	fprintf(fp, " [-%s%s%s%s]",
+	    ((opt->shortName || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) ? "" : "-"),
+	    item,
+	    (argDescrip ? (opt->shortName != '\0' ? " " : "=") : ""),
+	    (argDescrip ? argDescrip : ""));
+    }
+
+    return cursor + len + 1;
+}
+
+/**
+ * Display popt alias and exec usage.
+ * @param fp		output file handle
+ * @param cursor	current display position
+ * @param item		alias/exec array
+ * @param nitems	no. of ara/exec entries
+ * @param translation_domain	translation domain
+ */
+static size_t itemUsage(FILE * fp, size_t cursor,
+		/*@null@*/ poptItem item, int nitems,
+		/*@null@*/ const char * translation_domain)
+	/*@globals fileSystem @*/
+	/*@modifies *fp, fileSystem @*/
+{
+    int i;
+
+    /*@-branchstate@*/		/* FIX: W2DO? */
+    if (item != NULL)
+    for (i = 0; i < nitems; i++, item++) {
+	const struct poptOption * opt;
+	opt = &item->option;
+        if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN) {
+	    translation_domain = (const char *)opt->arg;
+	} else if ((opt->longName || opt->shortName) &&
+		 !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
+	    cursor = singleOptionUsage(fp, cursor, opt, translation_domain);
+	}
+    }
+    /*@=branchstate@*/
+
+    return cursor;
+}
+
+/**
+ * Keep track of option tables already processed.
+ */
+typedef struct poptDone_s {
+    int nopts;
+    int maxopts;
+    const void ** opts;
+} * poptDone;
+
+/**
+ * Display usage text for a table of options.
+ * @param con		context
+ * @param fp		output file handle
+ * @param cursor	current display position
+ * @param opt		option(s)
+ * @param translation_domain	translation domain
+ * @param done		tables already processed
+ * @return
+ */
+static size_t singleTableUsage(poptContext con, FILE * fp, size_t cursor,
+		/*@null@*/ const struct poptOption * opt,
+		/*@null@*/ const char * translation_domain,
+		/*@null@*/ poptDone done)
+	/*@globals fileSystem @*/
+	/*@modifies *fp, done, fileSystem @*/
+{
+    /*@-branchstate@*/		/* FIX: W2DO? */
+    if (opt != NULL)
+    for (; (opt->longName || opt->shortName || opt->arg) ; opt++) {
+        if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN) {
+	    translation_domain = (const char *)opt->arg;
+	} else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+	    if (done) {
+		int i = 0;
+		for (i = 0; i < done->nopts; i++) {
+/*@-boundsread@*/
+		    const void * that = done->opts[i];
+/*@=boundsread@*/
+		    if (that == NULL || that != opt->arg)
+			/*@innercontinue@*/ continue;
+		    /*@innerbreak@*/ break;
+		}
+		/* Skip if this table has already been processed. */
+		if (opt->arg == NULL || i < done->nopts)
+		    continue;
+/*@-boundswrite@*/
+		if (done->nopts < done->maxopts)
+		    done->opts[done->nopts++] = (const void *) opt->arg;
+/*@=boundswrite@*/
+	    }
+	    cursor = singleTableUsage(con, fp, cursor, opt->arg,
+			translation_domain, done);
+	} else if ((opt->longName || opt->shortName) &&
+		 !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
+	    cursor = singleOptionUsage(fp, cursor, opt, translation_domain);
+	}
+    }
+    /*@=branchstate@*/
+
+    return cursor;
+}
+
+/**
+ * Return concatenated short options for display.
+ * @todo Sub-tables should be recursed.
+ * @param opt		option(s)
+ * @param fp		output file handle
+ * @retval str		concatenation of short options
+ * @return		length of display string
+ */
+static int showShortOptions(const struct poptOption * opt, FILE * fp,
+		/*@null@*/ char * str)
+	/*@globals fileSystem @*/
+	/*@modifies *str, *fp, fileSystem @*/
+	/*@requires maxRead(str) >= 0 @*/
+{
+    /* bufsize larger then the ascii set, lazy alloca on top level call. */
+    char * s = (str != NULL ? str : memset(alloca(300), 0, 300));
+    int len = 0;
+
+/*@-boundswrite@*/
+    if (opt != NULL)
+    for (; (opt->longName || opt->shortName || opt->arg); opt++) {
+	if (opt->shortName && !(opt->argInfo & POPT_ARG_MASK))
+	    s[strlen(s)] = opt->shortName;
+	else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE)
+	    if (opt->arg)	/* XXX program error */
+		len = showShortOptions(opt->arg, fp, s);
+    } 
+/*@=boundswrite@*/
+
+    /* On return to top level, print the short options, return print length. */
+    if (s == str && *s != '\0') {
+	fprintf(fp, " [-%s]", s);
+	len = strlen(s) + sizeof(" [-]")-1;
+    }
+    return len;
+}
+
+void poptPrintUsage(poptContext con, FILE * fp, /*@unused@*/ int flags)
+{
+    poptDone done = memset(alloca(sizeof(*done)), 0, sizeof(*done));
+    size_t cursor;
+
+    done->nopts = 0;
+    done->maxopts = 64;
+    cursor = done->maxopts * sizeof(*done->opts);
+/*@-boundswrite@*/
+    done->opts = memset(alloca(cursor), 0, cursor);
+    /*@-keeptrans@*/
+    done->opts[done->nopts++] = (const void *) con->options;
+    /*@=keeptrans@*/
+/*@=boundswrite@*/
+
+    cursor = showHelpIntro(con, fp);
+    cursor += showShortOptions(con->options, fp, NULL);
+    cursor = singleTableUsage(con, fp, cursor, con->options, NULL, done);
+    cursor = itemUsage(fp, cursor, con->aliases, con->numAliases, NULL);
+    cursor = itemUsage(fp, cursor, con->execs, con->numExecs, NULL);
+
+    if (con->otherHelp) {
+	cursor += strlen(con->otherHelp) + 1;
+	if (cursor > 79) fprintf(fp, "\n       ");
+	fprintf(fp, " %s", con->otherHelp);
+    }
+
+    fprintf(fp, "\n");
+}
+
+void poptSetOtherOptionHelp(poptContext con, const char * text)
+{
+    con->otherHelp = _free(con->otherHelp);
+    con->otherHelp = xstrdup(text);
+}
diff --git a/libpopt/poptint.h b/libpopt/poptint.h
new file mode 100644
index 0000000..5e75712
--- /dev/null
+++ b/libpopt/poptint.h
@@ -0,0 +1,116 @@
+/** \ingroup popt
+ * \file popt/poptint.h
+ */
+
+/* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.rpm.org/pub/rpm/dist. */
+
+#ifndef H_POPTINT
+#define H_POPTINT
+
+/**
+ * Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
+ * @param p		memory to free
+ * @retval		NULL always
+ */
+/*@unused@*/ static inline /*@null@*/ void *
+_free(/*@only@*/ /*@null@*/ const void * p)
+	/*@modifies p @*/
+{
+    if (p != NULL)	free((void *)p);
+    return NULL;
+}
+
+/* Bit mask macros. */
+/*@-exporttype -redef @*/
+typedef	unsigned int __pbm_bits;
+/*@=exporttype =redef @*/
+#define	__PBM_NBITS		(8 * sizeof (__pbm_bits))
+#define	__PBM_IX(d)		((d) / __PBM_NBITS)
+#define __PBM_MASK(d)		((__pbm_bits) 1 << (((unsigned)(d)) % __PBM_NBITS))
+/*@-exporttype -redef @*/
+typedef struct {
+    __pbm_bits bits[1];
+} pbm_set;
+/*@=exporttype =redef @*/
+#define	__PBM_BITS(set)	((set)->bits)
+
+#define	PBM_ALLOC(d)	calloc(__PBM_IX (d) + 1, sizeof(__pbm_bits))
+#define	PBM_FREE(s)	_free(s);
+#define PBM_SET(d, s)   (__PBM_BITS (s)[__PBM_IX (d)] |= __PBM_MASK (d))
+#define PBM_CLR(d, s)   (__PBM_BITS (s)[__PBM_IX (d)] &= ~__PBM_MASK (d))
+#define PBM_ISSET(d, s) ((__PBM_BITS (s)[__PBM_IX (d)] & __PBM_MASK (d)) != 0)
+
+struct optionStackEntry {
+    int argc;
+/*@only@*/ /*@null@*/
+    const char ** argv;
+/*@only@*/ /*@null@*/
+    pbm_set * argb;
+    int next;
+/*@only@*/ /*@null@*/
+    const char * nextArg;
+/*@observer@*/ /*@null@*/
+    const char * nextCharArg;
+/*@dependent@*/ /*@null@*/
+    poptItem currAlias;
+    int stuffed;
+};
+
+struct poptContext_s {
+    struct optionStackEntry optionStack[POPT_OPTION_DEPTH];
+/*@dependent@*/
+    struct optionStackEntry * os;
+/*@owned@*/ /*@null@*/
+    const char ** leftovers;
+    int numLeftovers;
+    int nextLeftover;
+/*@keep@*/
+    const struct poptOption * options;
+    int restLeftover;
+/*@only@*/ /*@null@*/
+    const char * appName;
+/*@only@*/ /*@null@*/
+    poptItem aliases;
+    int numAliases;
+    int flags;
+/*@owned@*/ /*@null@*/
+    poptItem execs;
+    int numExecs;
+/*@only@*/ /*@null@*/
+    const char ** finalArgv;
+    int finalArgvCount;
+    int finalArgvAlloced;
+/*@dependent@*/ /*@null@*/
+    poptItem doExec;
+/*@only@*/
+    const char * execPath;
+    int execAbsolute;
+/*@only@*/ /*@relnull@*/
+    const char * otherHelp;
+/*@null@*/
+    pbm_set * arg_strip;
+};
+
+#ifdef HAVE_LIBINTL_H
+#include <libintl.h>
+#endif
+
+#if defined(HAVE_GETTEXT) && !defined(__LCLINT__)
+#define _(foo) gettext(foo)
+#else
+#define _(foo) foo
+#endif
+
+#if defined(HAVE_DCGETTEXT) && !defined(__LCLINT__)
+#define D_(dom, str) dgettext(dom, str)
+#define POPT_(foo) D_("popt", foo)
+#else
+#define D_(dom, str) str
+#define POPT_(foo) foo
+#endif
+
+#define N_(foo) foo
+
+#endif
diff --git a/libpopt/poptparse.c b/libpopt/poptparse.c
new file mode 100644
index 0000000..a0dea80
--- /dev/null
+++ b/libpopt/poptparse.c
@@ -0,0 +1,227 @@
+/** \ingroup popt
+ * \file popt/poptparse.c
+ */
+
+/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.rpm.org/pub/rpm/dist. */
+
+#include "system.h"
+
+#define POPT_ARGV_ARRAY_GROW_DELTA 5
+
+/*@-boundswrite@*/
+int poptDupArgv(int argc, const char **argv,
+		int * argcPtr, const char *** argvPtr)
+{
+    size_t nb = (argc + 1) * sizeof(*argv);
+    const char ** argv2;
+    char * dst;
+    int i;
+
+    if (argc <= 0 || argv == NULL)	/* XXX can't happen */
+	return POPT_ERROR_NOARG;
+    for (i = 0; i < argc; i++) {
+	if (argv[i] == NULL)
+	    return POPT_ERROR_NOARG;
+	nb += strlen(argv[i]) + 1;
+    }
+	
+    dst = malloc(nb);
+    if (dst == NULL)			/* XXX can't happen */
+	return POPT_ERROR_MALLOC;
+    argv2 = (void *) dst;
+    dst += (argc + 1) * sizeof(*argv);
+
+    /*@-branchstate@*/
+    for (i = 0; i < argc; i++) {
+	argv2[i] = dst;
+	dst += strlen(strcpy(dst, argv[i])) + 1;
+    }
+    /*@=branchstate@*/
+    argv2[argc] = NULL;
+
+    if (argvPtr) {
+	*argvPtr = argv2;
+    } else {
+	free(argv2);
+	argv2 = NULL;
+    }
+    if (argcPtr)
+	*argcPtr = argc;
+    return 0;
+}
+/*@=boundswrite@*/
+
+/*@-bounds@*/
+int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
+{
+    const char * src;
+    char quote = '\0';
+    int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
+    const char ** argv = malloc(sizeof(*argv) * argvAlloced);
+    int argc = 0;
+    int buflen = strlen(s) + 1;
+    char * buf = memset(alloca(buflen), 0, buflen);
+    int rc = POPT_ERROR_MALLOC;
+
+    if (argv == NULL) return rc;
+    argv[argc] = buf;
+
+    for (src = s; *src != '\0'; src++) {
+	if (quote == *src) {
+	    quote = '\0';
+	} else if (quote != '\0') {
+	    if (*src == '\\') {
+		src++;
+		if (!*src) {
+		    rc = POPT_ERROR_BADQUOTE;
+		    goto exit;
+		}
+		if (*src != quote) *buf++ = '\\';
+	    }
+	    *buf++ = *src;
+	} else if (isspace(*src)) {
+	    if (*argv[argc] != '\0') {
+		buf++, argc++;
+		if (argc == argvAlloced) {
+		    argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
+		    argv = realloc(argv, sizeof(*argv) * argvAlloced);
+		    if (argv == NULL) goto exit;
+		}
+		argv[argc] = buf;
+	    }
+	} else switch (*src) {
+	  case '"':
+	  case '\'':
+	    quote = *src;
+	    /*@switchbreak@*/ break;
+	  case '\\':
+	    src++;
+	    if (!*src) {
+		rc = POPT_ERROR_BADQUOTE;
+		goto exit;
+	    }
+	    /*@fallthrough@*/
+	  default:
+	    *buf++ = *src;
+	    /*@switchbreak@*/ break;
+	}
+    }
+
+    if (strlen(argv[argc])) {
+	argc++, buf++;
+    }
+
+    rc = poptDupArgv(argc, argv, argcPtr, argvPtr);
+
+exit:
+    if (argv) free(argv);
+    return rc;
+}
+/*@=bounds@*/
+
+/* still in the dev stage.
+ * return values, perhaps 1== file erro
+ * 2== line to long
+ * 3== umm.... more?
+ */
+int poptConfigFileToString(FILE *fp, char ** argstrp, /*@unused@*/ int flags)
+{
+    char line[999];
+    char * argstr;
+    char * p;
+    char * q;
+    char * x;
+    int t;
+    int argvlen = 0;
+    size_t maxlinelen = sizeof(line);
+    size_t linelen;
+    int maxargvlen = 480;
+    int linenum = 0;
+
+    *argstrp = NULL;
+
+    /*   |   this_is   =   our_line
+     *	     p             q      x
+     */
+
+    if (fp == NULL)
+	return POPT_ERROR_NULLARG;
+
+    argstr = calloc(maxargvlen, sizeof(*argstr));
+    if (argstr == NULL) return POPT_ERROR_MALLOC;
+
+    while (fgets(line, (int)maxlinelen, fp) != NULL) {
+	linenum++;
+	p = line;
+
+	/* loop until first non-space char or EOL */
+	while( *p != '\0' && isspace(*p) )
+	    p++;
+
+	linelen = strlen(p);
+	if (linelen >= maxlinelen-1)
+	    return POPT_ERROR_OVERFLOW;	/* XXX line too long */
+
+	if (*p == '\0' || *p == '\n') continue;	/* line is empty */
+	if (*p == '#') continue;		/* comment line */
+
+	q = p;
+
+	while (*q != '\0' && (!isspace(*q)) && *q != '=')
+	    q++;
+
+	if (isspace(*q)) {
+	    /* a space after the name, find next non space */
+	    *q++='\0';
+	    while( *q != '\0' && isspace((int)*q) ) q++;
+	}
+	if (*q == '\0') {
+	    /* single command line option (ie, no name=val, just name) */
+	    q[-1] = '\0';		/* kill off newline from fgets() call */
+	    argvlen += (t = q - p) + (sizeof(" --")-1);
+	    if (argvlen >= maxargvlen) {
+		maxargvlen = (t > maxargvlen) ? t*2 : maxargvlen*2;
+		argstr = realloc(argstr, maxargvlen);
+		if (argstr == NULL) return POPT_ERROR_MALLOC;
+	    }
+	    strcat(argstr, " --");
+	    strcat(argstr, p);
+	    continue;
+	}
+	if (*q != '=')
+	    continue;	/* XXX for now, silently ignore bogus line */
+		
+	/* *q is an equal sign. */
+	*q++ = '\0';
+
+	/* find next non-space letter of value */
+	while (*q != '\0' && isspace(*q))
+	    q++;
+	if (*q == '\0')
+	    continue;	/* XXX silently ignore missing value */
+
+	/* now, loop and strip all ending whitespace */
+	x = p + linelen;
+	while (isspace(*--x))
+	    *x = 0;	/* null out last char if space (including fgets() NL) */
+
+	/* rest of line accept */
+	t = x - p;
+	argvlen += t + (sizeof("' --='")-1);
+	if (argvlen >= maxargvlen) {
+	    maxargvlen = (t > maxargvlen) ? t*2 : maxargvlen*2;
+	    argstr = realloc(argstr, maxargvlen);
+	    if (argstr == NULL) return POPT_ERROR_MALLOC;
+	}
+	strcat(argstr, " --");
+	strcat(argstr, p);
+	strcat(argstr, "=\"");
+	strcat(argstr, q);
+	strcat(argstr, "\"");
+    }
+
+    *argstrp = argstr;
+    return 0;
+}
diff --git a/libpopt/system.h b/libpopt/system.h
new file mode 100644
index 0000000..c3d46cb
--- /dev/null
+++ b/libpopt/system.h
@@ -0,0 +1,81 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if defined (__GLIBC__) && defined(__LCLINT__)
+/*@-declundef@*/
+/*@unchecked@*/
+extern __const __int32_t *__ctype_tolower;
+/*@unchecked@*/
+extern __const __int32_t *__ctype_toupper;
+/*@=declundef@*/
+#endif
+
+#include <ctype.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#if HAVE_MCHECK_H 
+#include <mcheck.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef __NeXT
+/* access macros are not declared in non posix mode in unistd.h -
+ don't try to use posix on NeXTstep 3.3 ! */
+#include <libc.h>
+#endif
+
+#if defined(__LCLINT__)
+/*@-declundef -incondefs @*/ /* LCL: missing annotation */
+/*@only@*/ /*@out@*/
+void * alloca (size_t __size)
+	/*@ensures MaxSet(result) == (__size - 1) @*/
+	/*@*/;
+/*@=declundef =incondefs @*/
+#endif
+
+/* AIX requires this to be the first thing in the file.  */ 
+#ifndef __GNUC__
+# if HAVE_ALLOCA_H
+#  include <alloca.h>
+# else
+#  ifdef _AIX
+#pragma alloca
+#  else
+#   ifndef alloca /* predefined by HP cc +Olibcalls */
+char *alloca ();
+#   endif
+#  endif
+# endif
+#elif defined(__GNUC__) && defined(__STRICT_ANSI__)
+#define alloca __builtin_alloca
+#endif
+
+/*@-redecl -redef@*/
+/*@mayexit@*/ /*@only@*/ /*@unused@*/
+char * xstrdup (const char *str)
+	/*@*/;
+/*@=redecl =redef@*/
+
+#if HAVE_MCHECK_H && defined(__GNUC__)
+#define	vmefail()	(fprintf(stderr, "virtual memory exhausted.\n"), exit(EXIT_FAILURE), NULL)
+#define xstrdup(_str)   (strcpy((malloc(strlen(_str)+1) ? : vmefail()), (_str)))
+#else
+#define	xstrdup(_str)	strdup(_str)
+#endif  /* HAVE_MCHECK_H && defined(__GNUC__) */
+
+#if HAVE___SECURE_GETENV && !defined(__LCLINT__)
+#define	getenv(_s)	__secure_getenv(_s)
+#endif
+
+#include "popt.h"
diff --git a/libutil/Android.mk b/libutil/Android.mk
new file mode 100644
index 0000000..29f3bfb
--- /dev/null
+++ b/libutil/Android.mk
@@ -0,0 +1,21 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	op_cpufreq.c \
+	op_deviceio.c \
+	op_file.c \
+	op_fileio.c \
+	op_get_time.c \
+	op_libiberty.c \
+	op_lockfile.c \
+	op_popt.c \
+	op_string.c \
+	op_version.c
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/..
+
+LOCAL_MODULE := libutil
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/libutil/fscanf.c b/libutil/fscanf.c
new file mode 100644
index 0000000..d567324
--- /dev/null
+++ b/libutil/fscanf.c
@@ -0,0 +1,22 @@
+#include <stdio.h> 
+
+// ugly hack because we don't have fscanf
+
+int fscanf(FILE* stream, const char* format, int* value)
+{
+    int c;
+    int r = 0;
+    do {
+        c = fgetc(stream);
+        if (c>='0' && c<='9') {
+            r = r*10 + (c-'0');
+            continue;
+        }
+        break;
+    } while (1);
+
+    *value = r;
+
+    // gahhhh
+    return 1;
+}
diff --git a/libutil/op_cpufreq.c b/libutil/op_cpufreq.c
new file mode 100644
index 0000000..78a6333
--- /dev/null
+++ b/libutil/op_cpufreq.c
@@ -0,0 +1,64 @@
+/**
+ * @file op_cpufreq.c
+ * get cpu frequency definition
+ *
+ * @remark Copyright 2003 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "op_fileio.h"
+
+double op_cpu_frequency(void)
+{
+	double fval = 0.0;
+	unsigned long uval;
+	char * line = NULL;
+
+	FILE * fp = op_try_open_file("/proc/cpuinfo", "r");
+	if (!fp)
+		return 0.0;
+
+	while (1) {
+		line = op_get_line(fp);
+
+		if (!line)
+			break;
+
+		if (line[0] == '\0') {
+			free(line);
+			continue;
+		}
+
+		/* x86/parisc/ia64/x86_64 */
+		if (sscanf(line, "cpu MHz : %lf", &fval) == 1)
+			break;
+		/* ppc/ppc64 */
+		if (sscanf(line, "clock : %lfMHz", &fval) == 1)
+			break;
+		/* alpha */
+		if (sscanf(line, "cycle frequency [Hz] : %lu", &uval) == 1) {
+			fval = uval / 1E6;
+			break;
+		}
+		/* sparc64 if CONFIG_SMP only */
+		if (sscanf(line, "Cpu0ClkTck : %lx", &uval) == 1) {
+			fval = uval / 1E6;
+			break;
+		}
+		/* s390 doesn't provide cpu freq, checked up to 2.6-test4 */
+
+		free(line);
+	}
+
+	if (line)
+		free(line);
+	op_close_file(fp);
+
+	return fval;
+}
diff --git a/libutil/op_cpufreq.h b/libutil/op_cpufreq.h
new file mode 100644
index 0000000..0a71172
--- /dev/null
+++ b/libutil/op_cpufreq.h
@@ -0,0 +1,30 @@
+/**
+ * @file op_cpufreq.h
+ * get cpu frequency declaration
+ *
+ * @remark Copyright 2003 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_CPUFREQ_H
+#define OP_CPUFREQ_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*
+ * return the estimated cpu frequency in Mhz through
+ * parsing /proc/cpuinfo, return 0 if this information
+ * is not avalaible e.g. sparc64 with a non SMP kernel
+ */
+double op_cpu_frequency(void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* !OP_CPUFREQ_H */
diff --git a/libutil/op_deviceio.c b/libutil/op_deviceio.c
new file mode 100644
index 0000000..8210304
--- /dev/null
+++ b/libutil/op_deviceio.c
@@ -0,0 +1,42 @@
+/**
+ * @file op_deviceio.c
+ * Reading from a special device
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include "op_deviceio.h"
+
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+fd_t op_open_device(char const * name)
+{
+	return open(name, O_RDONLY);
+}
+
+
+ssize_t op_read_device(fd_t devfd, void * buf, size_t size)
+{
+	ssize_t count;
+
+	lseek(devfd, 0, SEEK_SET);
+
+	count = read(devfd, buf, size);
+
+	if (count < 0 && errno != EINTR && errno != EAGAIN) {
+		perror("oprofiled:op_read_device: ");
+		exit(EXIT_FAILURE);
+	}
+
+	return count;
+}
diff --git a/libutil/op_deviceio.h b/libutil/op_deviceio.h
new file mode 100644
index 0000000..f3bd4c2
--- /dev/null
+++ b/libutil/op_deviceio.h
@@ -0,0 +1,57 @@
+/**
+ * @file op_deviceio.h
+ * Reading from a special device
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_DEVICEIO_H
+#define OP_DEVICEIO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "op_types.h"
+
+#include <unistd.h>
+
+/**
+ * op_open_device - open a special char device for reading
+ * @param name  file name of device file
+ *
+ * Open the special file name. Returns the file descriptor
+ * for the file or -1 on error.
+ */
+fd_t op_open_device(char const * name);
+
+/**
+ * op_read_device - read from a special char device
+ * @param devfd  file descriptor of device
+ * @param buf  buffer
+ * @param size  size of buffer
+ *
+ * Read size bytes from a device into buffer buf.
+ * A seek to the start of the device file is done first
+ * then a read is requested in one go of size bytes.
+ *
+ * It is the caller's responsibility to do further op_read_device()
+ * calls if the number of bytes read is not what is requested
+ * (where this is applicable).
+ *
+ * The number of bytes read is returned, or a negative number
+ * on failure (in which case errno will be set). If the call is
+ * interrupted, then errno will be EINTR, and the client should
+ * arrange for re-starting the read if necessary.
+ */
+ssize_t op_read_device(fd_t devfd, void * buf, size_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* OP_DEVICEIO_H */
diff --git a/libutil/op_file.c b/libutil/op_file.c
new file mode 100644
index 0000000..e3e6cb6
--- /dev/null
+++ b/libutil/op_file.c
@@ -0,0 +1,185 @@
+/**
+ * @file op_file.c
+ * Useful file management helpers
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <fnmatch.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+
+#include "op_file.h"
+#include "op_libiberty.h"
+
+int op_file_readable(char const * file)
+{
+	struct stat st;
+	return !stat(file, &st) && S_ISREG(st.st_mode) && !access(file, R_OK);
+}
+
+
+time_t op_get_mtime(char const * file)
+{
+	struct stat st;
+
+	if (stat(file, &st))
+		return 0;
+
+	return st.st_mtime;
+}
+
+
+int create_dir(char const * dir)
+{
+	if (mkdir(dir, 0755)) {
+		/* FIXME: Does not verify existing is a dir */
+		if (errno == EEXIST)
+			return 0;
+		return errno;
+	}
+
+	return 0;
+}
+
+
+int create_path(char const * path)
+{
+	int ret = 0;
+
+	char * str = xstrdup(path);
+
+	char * pos = str[0] == '/' ? str + 1 : str;
+
+	for ( ; (pos = strchr(pos, '/')) != NULL; ++pos) {
+		*pos = '\0';
+		ret = create_dir(str);
+		*pos = '/';
+		if (ret)
+			break;
+	}
+
+	free(str);
+	return ret;
+}
+
+
+inline static int is_dot_or_dotdot(char const * name)
+{
+	return name[0] == '.' &&
+		(name[1] == '\0' ||
+		 (name[1] == '.' && name[2] == '\0'));
+}
+
+
+/* If non-null is returned, the caller is responsible for freeing
+ * the memory allocated for the return value. */
+static char * make_pathname_from_dirent(char const * basedir,
+				      struct dirent * ent,
+				      struct stat * st_buf)
+{
+	int name_len;
+	char * name;
+	name_len = strlen(basedir) + strlen("/") + strlen(ent->d_name) + 1;
+	name = xmalloc(name_len);
+	sprintf(name, "%s/%s", basedir,	ent->d_name);
+	if (stat(name, st_buf) != 0) {
+		free(name);
+		name = NULL;
+	}
+	return name;
+}
+
+
+int get_matching_pathnames(void * name_list, get_pathname_callback getpathname,
+			   char const * base_dir, char const * filter,
+			   enum recursion_type recursion)
+{
+/* The algorithm below depends on recursion type (of which there are 3)
+ * and whether the current dirent matches the filter.  There are 6 possible
+ * different behaviors, which is why we define 6 case below in the switch
+ * statement of the algorithm.  Actually, when the recursion type is
+ * MATCH_DIR_ONLY_RECURSION, the behavior is the same, whether or not the dir
+ * entry matches the filter.  However, the behavior of the recursion types
+ * NO_RECURSION and MATCH_ANY_ENTRY_RECURSION do depend on the dir entry
+ * filter match, so for simplicity, we perform this match for all recursion
+ * types and logically OR the match result with the  value of the passed
+ * recursion_type.
+ */
+#define NO_MATCH 0
+#define MATCH 1
+
+	DIR * dir;
+	struct dirent * ent;
+	struct stat stat_buffer;
+	int match;
+	char * name = NULL;
+
+	if (!(dir = opendir(base_dir)))
+		return -1;
+	while ((ent = readdir(dir)) != 0) {
+		if (is_dot_or_dotdot(ent->d_name))
+			continue;
+		if (fnmatch(filter, ent->d_name, 0) == 0)
+			match = 1;
+		else
+			match = 0;
+
+		switch (recursion | match) {
+		case NO_RECURSION + NO_MATCH:
+		case MATCH_ANY_ENTRY_RECURSION + NO_MATCH:
+			// nothing to do but continue the loop
+			break;
+		case NO_RECURSION + MATCH:
+			getpathname(ent->d_name, name_list);
+			break;
+		case MATCH_ANY_ENTRY_RECURSION + MATCH:
+			name = make_pathname_from_dirent(base_dir, ent,
+						       &stat_buffer);
+			if (name && S_ISDIR(stat_buffer.st_mode) &&
+			    !S_ISLNK(stat_buffer.st_mode)) {
+				get_matching_pathnames(
+					name_list, getpathname,
+					name, filter, recursion);
+			} else {
+				getpathname(name, name_list);
+			}
+			free(name);
+			break;
+		case MATCH_DIR_ONLY_RECURSION + NO_MATCH:
+		case MATCH_DIR_ONLY_RECURSION + MATCH:
+			name = make_pathname_from_dirent(base_dir, ent,
+						       &stat_buffer);
+			if (name && S_ISDIR(stat_buffer.st_mode) &&
+			    !S_ISLNK(stat_buffer.st_mode)) {
+				/* Check if full directory name contains
+				 * match to the filter; if so, add it to
+				 * name_list and quit; else, recurse.
+				 */
+				if (!fnmatch(filter, name, 0)) {
+					getpathname(name, name_list);
+				} else {
+					get_matching_pathnames(
+						name_list, getpathname,
+						name, filter, recursion);
+				}
+			}
+			free(name);
+			break;
+		}
+	}
+	closedir(dir);
+
+	return 0;
+}
diff --git a/libutil/op_file.h b/libutil/op_file.h
new file mode 100644
index 0000000..d22862c
--- /dev/null
+++ b/libutil/op_file.h
@@ -0,0 +1,109 @@
+/**
+ * @file op_file.h
+ * Useful file management helpers
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_FILE_H
+#define OP_FILE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+/**
+ * op_file_readable - is a file readable
+ * @param file file name
+ *
+ * Return true if the given file is readable and regular.
+ *
+ * Beware of race conditions !
+ */
+int op_file_readable(char const * file);
+
+/**
+ * op_get_mtime - get mtime of file
+ * @param file  file name
+ *
+ * Returns the mtime of the given file or 0 on failure
+ */
+time_t op_get_mtime(char const * file);
+
+/**
+ * create_dir - create a directory
+ * @param dir  the directory name to create
+ *
+ * Returns 0 on success.
+ */
+int create_dir(char const * dir);
+
+
+/**
+ * create_path - create a path
+ * @param path  the path to create
+ *
+ * create directory for each dir components in path
+ * the last path component is not considered as a directory
+ * but as a filename
+ *
+ * Returns 0 on success.
+ */
+int create_path(char const * path);
+
+/**
+ * Clients of get_matching_pathnames must provide their own implementation
+ * of get_pathname_callback.
+ */
+typedef void (*get_pathname_callback)(char const * pathname, void * name_list);
+
+/* This enum is intended solely for the use of get_matching_pathnames(),
+ * bit 0 is reserved for internal use..*/
+enum recursion_type {
+	NO_RECURSION = 2,
+	MATCH_ANY_ENTRY_RECURSION = 4,
+	MATCH_DIR_ONLY_RECURSION = 8,
+};
+/**
+ * @param name_list where to store result
+ * @param get_pathname_callback client-provided callback function
+ * @param base_dir directory from where lookup starts
+ * @param filter a pathname filter
+ * @param recursion recursion_type -- see above enum and following description:
+ *	NO_RECURSION:  Find matching files from passed base_dir and call
+ *          get_pathname_callback to add entry to name_list to be returned.
+ *	MATCH_ANY_ENTRY_RECURSION: Starting at base_dir, for each entry in the
+ *	   dir that matches the filter: if entry is of type 'dir', recurse;
+ *         else call get_pathname_callback to add entry to name_list to be
+ *         returned.
+ *	MATCH_DIR_ONLY_RECURSION: Starting at base_dir, if an entry in the
+ *         dir is of type 'dir' and its complete pathname contains a match to
+ *         the filter, call get_pathname_callback to add entry to name_list to
+ *         be returned; else recurse.
+ *
+ * Returns 0 on success.
+ *
+ * Return a list of pathnames under base_dir, filtered by filter and optionally
+ * looking in sub-directory. See description above of the recursion_type 
+ * parameter for more details.
+ *    NOTE: For C clients: Your implementation of the get_pathname_callback
+ *          function will probably dynamically allocate storage for elements
+ *          added to name_list.  If so, remember to free that memory when it's
+ *          no longer needed.
+ */
+int get_matching_pathnames(void * name_list, get_pathname_callback,
+			   char const * base_dir, char const * filter,
+			   enum recursion_type recursion);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* OP_FILE_H */
diff --git a/libutil/op_fileio.c b/libutil/op_fileio.c
new file mode 100644
index 0000000..9b3e21d
--- /dev/null
+++ b/libutil/op_fileio.c
@@ -0,0 +1,228 @@
+/**
+ * @file op_fileio.c
+ * Reading from / writing to files
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include <unistd.h>
+
+#include "op_fileio.h"
+
+#include "op_libiberty.h"
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+static FILE * op_do_open_file(char const * name, char const * mode, int fatal)
+{
+	FILE * fp;
+
+	fp = fopen(name, mode);
+
+	if (!fp) {
+		if (fatal) {
+			fprintf(stderr,"oprofiled:op_do_open_file: %s: %s",
+				name, strerror(errno));
+			exit(EXIT_FAILURE);
+		}
+	}
+
+	return fp;
+}
+
+
+FILE * op_try_open_file(char const * name, char const * mode)
+{
+	return op_do_open_file(name, mode, 0);
+}
+
+
+FILE * op_open_file(char const * name, char const * mode)
+{
+	return op_do_open_file(name, mode, 1);
+}
+
+
+void op_close_file(FILE * fp)
+{
+	if (fclose(fp))
+		perror("oprofiled:op_close_file: ");
+}
+
+
+void op_write_file(FILE * fp, void const * buf, size_t size)
+{
+	size_t written;
+
+	if (size == 0)
+		return;
+
+	written = fwrite(buf, size, 1, fp);
+
+	if (written != 1) {
+		fprintf(stderr,
+			"oprofiled:op_write_file: wrote less than expected: %lu bytes.\n",
+			(unsigned long)size);
+		exit(EXIT_FAILURE);
+	}
+}
+
+
+void op_write_u8(FILE * fp, u8 val)
+{
+	op_write_file(fp, &val, sizeof(val));
+}
+
+
+void op_write_u32(FILE * fp, u32 val)
+{
+	op_write_file(fp, &val, sizeof(val));
+}
+
+
+void op_write_u64(FILE * fp, u64 val)
+{
+	op_write_file(fp, &val, sizeof(val));
+}
+
+
+u32 op_read_int_from_file(char const * filename, int fatal)
+{
+	FILE * fp;
+	u32 value;
+
+	fp = fopen(filename, "r");
+	if (fp == NULL) {
+		if (!fatal)
+			return (u32)-1;
+		fprintf(stderr,
+			"op_read_int_from_file: Failed to open %s, reason %s\n",
+			filename, strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	if (fscanf(fp, "%u", &value) != 1) {
+		fclose(fp);
+		if (!fatal)
+			return (u32)-1;
+		fprintf(stderr,
+			"op_read_int_from_file: Failed to convert contents of file %s to integer\n",
+			filename);
+		exit(EXIT_FAILURE);
+	}
+
+	fclose(fp);
+
+	return value;
+}
+
+
+char * op_get_line(FILE * fp)
+{
+	char * buf;
+	char * cp;
+	int c;
+	size_t max = 512;
+
+	buf = xmalloc(max);
+	cp = buf;
+
+	while (1) {
+		switch (c = getc(fp)) {
+			case EOF:
+				free(buf);
+				return NULL;
+				break;
+
+			case '\n':
+			case '\0':
+				*cp = '\0';
+				return buf;
+				break;
+
+			default:
+				*cp = (char)c;
+				cp++;
+				if (((size_t)(cp - buf)) == max) {
+					buf = xrealloc(buf, max + 128);
+					cp = buf + max;
+					max += 128;
+				}
+				break;
+		}
+	}
+}
+
+
+/* FIXME the debug info stuff should be handled by binutils */
+unsigned long
+calc_crc32(unsigned long crc, unsigned char * buf, size_t len)
+{
+	static const unsigned long crc32_table[256] =
+	{
+		0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
+		0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
+		0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
+		0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+		0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
+		0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+		0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
+		0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+		0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
+		0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
+		0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
+		0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+		0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
+ 		0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
+		0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
+		0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+		0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
+		0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+		0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
+		0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+		0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
+		0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
+		0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
+		0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+		0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
+		0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
+		0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
+		0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+		0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
+		0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+		0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
+		0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+		0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
+		0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
+		0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
+		0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+		0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
+		0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
+		0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
+		0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+		0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
+		0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+		0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
+		0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+		0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
+		0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
+		0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
+		0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+		0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
+		0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
+		0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
+		0x2d02ef8d
+	};
+	unsigned char * end;
+
+	crc = ~crc & 0xffffffff;
+	for (end = buf + len; buf < end; ++buf)
+		crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
+	return ~crc & 0xffffffff;
+}
diff --git a/libutil/op_fileio.h b/libutil/op_fileio.h
new file mode 100644
index 0000000..49b126d
--- /dev/null
+++ b/libutil/op_fileio.h
@@ -0,0 +1,140 @@
+/**
+ * @file op_fileio.h
+ * Reading from / writing to files
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_FILEIO_H
+#define OP_FILEIO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "op_types.h"
+
+#include <stdio.h>
+
+/**
+ * op_try_open_file - open a file
+ * @param name  file name
+ * @param mode  mode string
+ *
+ * Open a file name.
+ * Returns file handle or %NULL on failure.
+ */
+FILE * op_try_open_file(char const * name, char const * mode);
+
+/**
+ * op_open_file - open a file
+ * @param name  file name
+ * @param mode  mode string
+ *
+ * Open a file name.
+ * Failure to open is fatal.
+ */
+FILE * op_open_file(char const * name, char const * mode);
+
+/**
+ * op_read_int_from_file - parse an ASCII value from a file into an integer
+ * @param filename  name of file to parse integer value from
+ * @param fatal  non-zero if any error must be fatal
+ *
+ * Reads an ASCII integer from the given file. If an error occur and fatal is
+ * zero (u32)-1 is returned else the value read in is returned.
+ */
+u32 op_read_int_from_file(char const * filename, int fatal);
+
+/**
+ * op_close_file - close a file
+ * @param fp  file pointer
+ *
+ * Closes a file pointer. A non-fatal
+ * error message is produced if the
+ * close fails.
+ */
+void op_close_file(FILE * fp);
+
+/**
+ * op_write_file - write to a file
+ * @param fp  file pointer
+ * @param buf  buffer
+ * @param size  nr. of bytes to write
+ *
+ * Write size bytes of buffer buf to a file.
+ * Failure is fatal.
+ */
+void op_write_file(FILE * fp, void const * buf, size_t size);
+
+/**
+ * op_write_u32 - write four bytes to a file
+ * @param fp  file pointer
+ * @param val  value to write
+ *
+ * Write an unsigned four-byte value val to a file.
+ * Failure is fatal.
+ *
+ * No byte-swapping is done.
+ */
+void op_write_u32(FILE * fp, u32 val);
+
+/**
+ * op_write_u64 - write eight bytes to a file
+ * @param fp  file pointer
+ * @param val  value to write
+ *
+ * Write an unsigned eight-byte value val to a file.
+ * Failure is fatal.
+ *
+ * No byte-swapping is done.
+ */
+void op_write_u64(FILE * fp, u64 val);
+
+/**
+ * op_write_u8 - write a byte to a file
+ * @param fp  file pointer
+ * @param val  value to write
+ *
+ * Write an unsigned byte value val to a file.
+ * Failure is fatal.
+ */
+void op_write_u8(FILE * fp, u8 val);
+
+/**
+ * op_get_line - read an ASCII line from a file
+ * @param fp  file pointer
+ *
+ * Get a line of ASCII text from a file. The file is read
+ * up to the first '\0' or '\n'. A trailing '\n' is deleted.
+ *
+ * Returns the dynamically-allocated string containing
+ * that line. At the end of a file NULL will be returned.
+ * be returned.
+ *
+ * The string returned must be free()d by the caller.
+ *
+ * getline() is not a proper solution to replace this function
+ */
+char * op_get_line(FILE * fp);
+
+/**
+ * calc_crc32
+ * @param crc current value
+ * @param buf pointer to buffer
+ * @param len
+ *
+ * Returns current crc computed from the crc argument and the
+ * characters in len characters in buf.
+ */
+unsigned long calc_crc32(unsigned long crc, unsigned char * buf, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* OP_FILEIO_H */
diff --git a/libutil/op_get_time.c b/libutil/op_get_time.c
new file mode 100644
index 0000000..c094c63
--- /dev/null
+++ b/libutil/op_get_time.c
@@ -0,0 +1,24 @@
+/**
+ * @file op_get_time.c
+ * Get current time as a string
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include "op_get_time.h"
+
+#include <time.h>
+
+char * op_get_time(void)
+{
+	time_t t = time(NULL);
+
+	if (t == -1)
+		return "";
+
+	return ctime(&t);
+}
diff --git a/libutil/op_get_time.h b/libutil/op_get_time.h
new file mode 100644
index 0000000..8f96273
--- /dev/null
+++ b/libutil/op_get_time.h
@@ -0,0 +1,33 @@
+/**
+ * @file op_get_time.h
+ * Get current time as a string
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_GET_TIME_H
+#define OP_GET_TIME_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * op_get_time - get current date and time
+ *
+ * Returns a string representing the current date
+ * and time, or an empty string on error.
+ *
+ * The string is statically allocated and should not be freed.
+ */
+char * op_get_time(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* OP_GET_TIME_H */
diff --git a/libutil/op_growable_buffer.c b/libutil/op_growable_buffer.c
new file mode 100644
index 0000000..d138f36
--- /dev/null
+++ b/libutil/op_growable_buffer.c
@@ -0,0 +1,46 @@
+/**
+ * @file op_growable_buffer.c
+ * a growable buffer implementation
+ *
+ * @remark Copyright 2007 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Philippe Elie
+ */
+
+#include "op_growable_buffer.h"
+#include "op_libiberty.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+void init_buffer(struct growable_buffer * b)
+{
+	b->max_size = 0;
+	b->size = 0;
+	b->p = NULL;
+}
+
+
+void free_buffer(struct growable_buffer * b)
+{
+	free(b->p);
+}
+
+
+static void grow_buffer(struct growable_buffer * b)
+{
+	size_t new_size = (b->max_size + b->size) * 2;
+	b->p = xrealloc(b->p, new_size);
+	b->max_size = new_size;
+}
+
+
+void add_data(struct growable_buffer * b, void const * data, size_t len)
+{
+	size_t old_size = b->size;
+	b->size += len;
+	if (b->size > b->max_size)
+		grow_buffer(b);
+	memcpy(b->p + old_size, data, len);
+}
diff --git a/libutil/op_growable_buffer.h b/libutil/op_growable_buffer.h
new file mode 100644
index 0000000..491969a
--- /dev/null
+++ b/libutil/op_growable_buffer.h
@@ -0,0 +1,45 @@
+/**
+ * @file op_growable_buffer.h
+ * a growable buffer interface
+ *
+ * @remark Copyright 2007 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Philippe Elie
+ */
+
+#ifndef OP_GROWABLE_BUFFER_H
+#define OP_GROWABLE_BUFFER_H
+
+#include <stddef.h>
+
+struct growable_buffer {
+	void * p;
+	size_t size;
+	size_t max_size;
+};
+
+/**
+ * init_buffer - initialize an empty buffer
+ * @param buffer the buffer to initialize
+ *
+ * init_buffer do not do any allocation, the first allocation will occur
+ * when add_data() with a non zero len param will be called.
+ */
+void init_buffer(struct growable_buffer * buffer);
+
+/**
+ * free_buffer - free the memory allocated for this buffer
+ * @param buffer the buffer to free
+ */
+void free_buffer(struct growable_buffer * buffer);
+
+/**
+ * add_data - add data to this buffer
+ * @param b the buffer where to add data
+ * @param data a pointer to the data to add
+ * @param len number of byte to add to the buffer
+ */
+void add_data(struct growable_buffer * b, void const * data, size_t len);
+
+#endif /* !OP_GROWABLE_BUFFER_H */
diff --git a/libutil/op_libiberty.c b/libutil/op_libiberty.c
new file mode 100644
index 0000000..0cf45d3
--- /dev/null
+++ b/libutil/op_libiberty.c
@@ -0,0 +1,38 @@
+/**
+ * @file op_libiberty.c
+ * Wrapper for libiberty - always use this instead of
+ * libiberty.h
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include <string.h>
+
+#include "op_libiberty.h"
+
+#ifndef HAVE_XCALLOC
+/* some system have a valid libiberty without xcalloc */
+void * xcalloc(size_t n_elem, size_t sz)
+{
+	void * ptr = xmalloc(n_elem * sz);
+
+	memset(ptr, '\0', n_elem * sz);
+
+	return ptr;
+}
+#endif
+
+#ifndef HAVE_XMEMDUP
+void * xmemdup (void const * input, size_t copy_size, size_t alloc_size)
+{
+	void * output = xcalloc(1, alloc_size);
+
+	memcpy(output, input, copy_size);
+
+	return output;
+}
+#endif
diff --git a/libutil/op_libiberty.h b/libutil/op_libiberty.h
new file mode 100644
index 0000000..ea02a50
--- /dev/null
+++ b/libutil/op_libiberty.h
@@ -0,0 +1,81 @@
+/**
+ * @file op_libiberty.h
+ * Wrapper for libiberty - always use this instead of
+ * libiberty.h
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_LIBIBERTY_H
+#define OP_LIBIBERTY_H
+
+#include <stddef.h>
+
+#include "config.h"
+
+#ifdef MALLOC_ATTRIBUTE_OK
+#define OP_ATTRIB_MALLOC	__attribute__((malloc))
+#else
+#define OP_ATTRIB_MALLOC
+#endif
+
+#ifdef HAVE_LIBIBERTY_H
+#include <libiberty.h>
+#else
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* some system have a libiberty.a but no libiberty.h so we must provide
+ * ourself the missing proto */
+#ifndef HAVE_LIBIBERTY_H
+
+/* Set the program name used by xmalloc.  */
+void xmalloc_set_program_name(char const *);
+
+/* Allocate memory without fail.  If malloc fails, this will print a
+   message to stderr (using the name set by xmalloc_set_program_name,
+   if any) and then call xexit.  */
+void * xmalloc(size_t) OP_ATTRIB_MALLOC;
+
+/* Reallocate memory without fail.  This works like xmalloc.  Note,
+   realloc type functions are not suitable for attribute malloc since
+   they may return the same address across multiple calls. */
+void * xrealloc(void *, size_t);
+
+/* Allocate memory without fail and set it to zero.  This works like xmalloc */
+void * xcalloc(size_t, size_t) OP_ATTRIB_MALLOC;
+
+/* Copy a string into a memory buffer without fail.  */
+char * xstrdup(char const *) OP_ATTRIB_MALLOC;
+
+/**
+ * Duplicates a region of memory without fail.  First, alloc_size bytes
+ * are allocated, then copy_size bytes from input are copied into
+ * it, and the new memory is returned.  If fewer bytes are copied than were
+ * allocated, the remaining memory is zeroed.
+ */
+void * xmemdup(void const *, size_t, size_t) OP_ATTRIB_MALLOC;
+
+#endif	/* !HAVE_LIBIBERTY_H */
+
+#ifdef ANDROID
+#define xmalloc(s)      malloc(s)
+#define xrealloc(p,s)   realloc(p,s)
+#define xstrdup(str)    strdup(str)
+#define xmalloc_set_program_name(n)
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !HAVE_LIBIBERTY_H */
+
+#endif /* OP_LIBIBERTY_H */
diff --git a/libutil/op_list.h b/libutil/op_list.h
new file mode 100644
index 0000000..ed0cd8a
--- /dev/null
+++ b/libutil/op_list.h
@@ -0,0 +1,177 @@
+/**
+ * @file op_list.h
+ * Kernel-style lists
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Linux kernel authors
+ */
+
+#ifndef OP_LIST_H
+#define OP_LIST_H
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+	struct list_head * next, * prev;
+};
+
+/**
+ * list_init - init a new entry
+ * @param ptr  the list to init
+ *
+ * Init a list head to create an empty list from it
+ */
+static __inline__ void list_init(struct list_head * ptr)
+{
+	ptr->next = ptr;
+	ptr->prev = ptr;
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_add(struct list_head * new_entry,
+	struct list_head * prev,
+	struct list_head * next)
+{
+	next->prev = new_entry;
+	new_entry->next = next;
+	new_entry->prev = prev;
+	prev->next = new_entry;
+}
+
+/**
+ * list_add - add a new entry
+ * @param new  new entry to be added
+ * @param head  list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static __inline__ void list_add(struct list_head * new_entry, struct list_head * head)
+{
+	__list_add(new_entry, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @param new  new entry to be added
+ * @param head  list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static __inline__ void list_add_tail(struct list_head * new_entry, struct list_head * head)
+{
+	__list_add(new_entry, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_del(struct list_head * prev,
+				  struct list_head * next)
+{
+	next->prev = prev;
+	prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @param entry  the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is in an undefined state.
+ */
+static __inline__ void list_del(struct list_head * entry)
+{
+	__list_del(entry->prev, entry->next);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @param entry  the element to delete from the list.
+ */
+static __inline__ void list_del_init(struct list_head * entry)
+{
+	__list_del(entry->prev, entry->next);
+	list_init(entry);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @param head  the list to test.
+ */
+static __inline__ int list_empty(struct list_head const * head)
+{
+	return head->next == head;
+}
+
+/**
+ * list_splice - join two lists
+ * @param list  the new list to add.
+ * @param head  the place to add it in the first list.
+ */
+static __inline__ void list_splice(struct list_head * list, struct list_head * head)
+{
+	struct list_head * first = list->next;
+
+	if (first != list) {
+		struct list_head * last = list->prev;
+		struct list_head * at = head->next;
+
+		first->prev = head;
+		head->next = first;
+
+		last->next = at;
+		at->prev = last;
+	}
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @param ptr 	the &struct list_head pointer.
+ * @param type 	the type of the struct this is embedded in.
+ * @param member 	the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+/**
+ * list_for_each - iterate over a list
+ * @param pos 	the &struct list_head to use as a loop counter.
+ * @param head 	the head for your list.
+ */
+#define list_for_each(pos, head) \
+	for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @param pos 	the &struct list_head to use as a loop counter.
+ * @param n 		another &struct list_head to use as temporary storage
+ * @param head 	the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, n = pos->next)
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)
+
+#endif /* OP_LIST_H */
diff --git a/libutil/op_lockfile.c b/libutil/op_lockfile.c
new file mode 100644
index 0000000..26e3249
--- /dev/null
+++ b/libutil/op_lockfile.c
@@ -0,0 +1,69 @@
+/**
+ * @file op_lockfile.c
+ * PID-based lockfile management
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include "op_lockfile.h"
+#include "op_file.h"
+
+#include <errno.h>
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+
+static pid_t op_read_lock_file(char const * file)
+{
+	FILE * fp;
+	pid_t value;
+
+	fp = fopen(file, "r");
+	if (fp == NULL)
+		return 0;
+
+	if (fscanf(fp, "%d", &value) != 1) {
+		fclose(fp);
+		return 0;
+	}
+
+	fclose(fp);
+
+	return value;
+}
+
+
+int op_write_lock_file(char const * file)
+{
+	FILE * fp;
+
+	if (op_file_readable(file)) {
+		pid_t pid = op_read_lock_file(file);
+
+		/* FIXME: ESRCH vs. EPERM */
+		if (kill(pid, 0)) {
+			int err = unlink(file);
+			fprintf(stderr, "Removing stale lock file %s\n",
+				file);
+			if (err)
+				return err;
+		} else {
+			return EEXIST;
+		}
+	}
+
+	fp = fopen(file, "w");
+	if (!fp)
+		return errno;
+
+	fprintf(fp, "%d", getpid());
+	fclose(fp);
+
+	return 0;
+}
diff --git a/libutil/op_lockfile.h b/libutil/op_lockfile.h
new file mode 100644
index 0000000..fcc269c
--- /dev/null
+++ b/libutil/op_lockfile.h
@@ -0,0 +1,34 @@
+/**
+ * @file op_lockfile.h
+ * PID-based lockfile management
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_LOCKFILE_H
+#define OP_LOCKFILE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+/**
+ * op_write_lock_file - write a lock file
+ * \return errno on failure, or 0 on success
+ *
+ * Write the pid into the given lock file. Stale
+ * lock files are detected and reset.
+ */
+int op_write_lock_file(char const * file);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* OP_LOCKFILE_H */
diff --git a/libutil/op_popt.c b/libutil/op_popt.c
new file mode 100644
index 0000000..de96364
--- /dev/null
+++ b/libutil/op_popt.c
@@ -0,0 +1,43 @@
+/**
+ * @file op_popt.c
+ * Wrapper for libpopt - always use this rather
+ * than popt.h
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include <stdlib.h>
+#include "op_libiberty.h"
+#include "op_popt.h"
+
+poptContext op_poptGetContext(char const * name,
+		int argc, char const ** argv,
+		struct poptOption const * options, int flags)
+{
+	poptContext optcon;
+	int c;
+
+	xmalloc_set_program_name(argv[0]);
+
+#ifdef CONST_POPT
+	optcon = poptGetContext(name, argc, argv, options, flags);
+#else
+	optcon = poptGetContext((char *)name, argc, (char **)argv, options, flags);
+#endif
+
+	c = poptGetNextOpt(optcon);
+
+	if (c < -1) {
+		fprintf(stderr, "%s: %s: %s\n", argv[0],
+			poptBadOption(optcon, POPT_BADOPTION_NOALIAS),
+			poptStrerror(c));
+		poptPrintHelp(optcon, stderr, 0);
+		exit(EXIT_FAILURE);
+	}
+
+	return optcon;
+}
diff --git a/libutil/op_popt.h b/libutil/op_popt.h
new file mode 100644
index 0000000..c3dfa6c
--- /dev/null
+++ b/libutil/op_popt.h
@@ -0,0 +1,42 @@
+/**
+ * @file op_popt.h
+ * Wrapper for libpopt - always use this rather
+ * than popt.h
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_POPT_H
+#define OP_POPT_H
+
+#include <popt.h>
+
+// not in some versions of popt.h
+#ifndef POPT_TABLEEND
+#define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * op_poptGetContext - wrapper for popt
+ *
+ * Use this instead of poptGetContext to cope with
+ * different popt versions. This also handle unrecognized
+ * options. All error are fatal.
+ */
+poptContext op_poptGetContext(char const * name,
+                int argc, char const ** argv,
+                struct poptOption const * options, int flags);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* OP_POPT_H */
diff --git a/libutil/op_string.c b/libutil/op_string.c
new file mode 100644
index 0000000..d440299
--- /dev/null
+++ b/libutil/op_string.c
@@ -0,0 +1,62 @@
+/**
+ * @file op_string.c
+ * general purpose C string handling implementation.
+ *
+ * @remark Copyright 2003 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include <string.h>
+#include "op_libiberty.h"
+
+
+char * op_xstrndup(char const * s, size_t len)
+{
+	return xmemdup(s, len, len + 1);
+}
+
+
+size_t op_hash_string(char const * str)
+{
+	size_t hash = 0;
+	for (; *str; ++str)
+		hash ^= (hash << 16) ^ (hash >> 8) ^ *str;
+	return hash;
+}
+
+
+int strisprefix(char const * str, char const * prefix)
+{
+	return strstr(str, prefix) == str;
+}
+
+
+char const * skip_ws(char const * c)
+{
+	while (*c == ' ' || *c == '\t' || *c == '\n')
+		++c;
+	return c;
+}
+
+
+char const * skip_nonws(char const * c)
+{
+	while (*c && *c != ' ' && *c != '\t' && *c != '\n')
+		++c;
+	return c;
+}
+
+
+int empty_line(char const * c)
+{
+	return !*skip_ws(c);
+}
+
+
+int comment_line(char const * c)
+{
+	return *skip_ws(c) == '#';
+}
diff --git a/libutil/op_string.h b/libutil/op_string.h
new file mode 100644
index 0000000..993fa6f
--- /dev/null
+++ b/libutil/op_string.h
@@ -0,0 +1,81 @@
+/**
+ * @file op_string.h
+ * general purpose C string handling declarations.
+ *
+ * @remark Copyright 2003 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_STRING_H
+#define OP_STRING_H
+
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @param  s: input string
+ * @param len: len char to copy
+ *
+ * Allocate and copy len character from s to a newly allocated buffer then
+ * append a '\0' terminator. Return the newly allocated string
+ */
+char * op_xstrndup(char const * s, size_t len);
+
+/**
+ * @param  s: string to hash
+ *
+ * Generate a hash code from a string
+ */
+size_t op_hash_string(char const * s);
+
+/**
+ * @param str: string to test
+ * @param prefix: prefix string
+ *
+ * return non zero if prefix parameters is a prefix of str
+ */
+int strisprefix(char const * str, char const * prefix);
+
+/**
+ * @param c: input string
+ *
+ * return a pointer to the first location in c which is not a blank space
+ * where blank space are in " \t\n"
+ */
+char const * skip_ws(char const * c);
+
+/**
+ * @param c: input string
+ *
+ * return a pointer to the first location in c which is a blank space
+ * where blank space are in " \t\n"
+ */
+char const * skip_nonws(char const * c);
+
+/**
+ * @param c: input string
+ *
+ * return non zero if c string contains only blank space
+ * where blank space are in " \t\n"
+ */
+int empty_line(char const * c);
+
+/**
+ * @param c: input string
+ *
+ * return non zero if c string is a comment. Comment are lines with optional
+ * blank space at left then a '#' character. Blank space are in " \t\n"
+ */
+int comment_line(char const * c);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !OP_STRING_H */
diff --git a/libutil/op_types.h b/libutil/op_types.h
new file mode 100644
index 0000000..c025b23
--- /dev/null
+++ b/libutil/op_types.h
@@ -0,0 +1,37 @@
+/**
+ * @file op_types.h
+ * General-utility types
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_TYPES_H
+#define OP_TYPES_H
+
+#ifndef __KERNEL__
+
+#include <sys/types.h>
+
+/*@{\name miscellaneous types */
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+typedef unsigned long long u64;
+typedef int fd_t;
+/*@}*/
+
+/** generic type for holding addresses */
+typedef unsigned long long vma_t;
+
+/** generic type to hold a sample count in pp tools */
+typedef u64 count_type;
+
+#else
+#include <linux/types.h>
+#endif
+
+#endif /* OP_TYPES_H */
diff --git a/libutil/op_version.c b/libutil/op_version.c
new file mode 100644
index 0000000..99a844e
--- /dev/null
+++ b/libutil/op_version.c
@@ -0,0 +1,24 @@
+/**
+ * @file op_version.c
+ * output version string
+ *
+ * @remark Copyright 2003 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "op_version.h"
+#include "config.h"
+
+void show_version(char const * app_name)
+{
+	/* Do not change the version format: it is documented in html doc */
+	printf("%s: " PACKAGE " " VERSION " compiled on "
+	       __DATE__ " " __TIME__ "\n", app_name);
+	exit(EXIT_SUCCESS);
+}
diff --git a/libutil/op_version.h b/libutil/op_version.h
new file mode 100644
index 0000000..43a8365
--- /dev/null
+++ b/libutil/op_version.h
@@ -0,0 +1,26 @@
+/**
+ * @file op_version.h
+ * output version string
+ *
+ * @remark Copyright 2003 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#ifndef OP_VERSION_H
+#define OP_VERSION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** output the version string */
+void show_version(char const * app_name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !OP_VERSION_H */
diff --git a/opcontrol/Android.mk b/opcontrol/Android.mk
new file mode 100644
index 0000000..56211ad
--- /dev/null
+++ b/opcontrol/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	opcontrol.cpp
+
+LOCAL_STATIC_LIBRARIES := \
+	libpopt libutil libdb libabi libop
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/.. \
+	$(LOCAL_PATH)/../libop
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE:= opcontrol
+
+include $(BUILD_EXECUTABLE)
diff --git a/opcontrol/opcontrol.cpp b/opcontrol/opcontrol.cpp
new file mode 100644
index 0000000..2d9cb2f
--- /dev/null
+++ b/opcontrol/opcontrol.cpp
@@ -0,0 +1,542 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Binary implementation of the original opcontrol script due to missing tools
+ * like awk, test, etc.
+ */
+
+#include <unistd.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/stat.h>
+
+#include "op_config.h"
+
+#if 0
+#define verbose(fmt...) printf(fmt)
+#else
+#define verbose(fmt...)
+#endif
+
+/* Experiments found that using a small interval may hang the device, and the
+ * more events tracked simultaneously, the longer the interval has to be.
+ */
+int min_count[3] = {150000, 200000, 250000};
+int list_events; 
+int show_usage;
+int setup;
+int quick;
+int num_events;
+int start;
+int stop;
+int reset;
+
+int selected_events[3];
+int selected_counts[3];
+
+char kernel_range[512];
+char vmlinux[512];
+
+struct option long_options[] = {
+    {"help", 0, &show_usage, 1},
+    {"list-events", 0, &list_events, 1},
+    {"reset", 0, &reset, 1},
+    {"setup", 0, &setup, 1},
+    {"quick", 0, &quick, 1},
+    {"event", 1, 0, 'e'},
+    {"vmlinux", 1, 0, 'v'},
+    {"kernel-range", 1, 0, 'r'},
+    {"start", 0, &start, 1},
+    {"stop", 0, &stop, 1},
+    {"shutdown", 0, 0, 'h'},
+    {"status", 0, 0, 't'},
+    {0, 0, 0, 0},
+};
+
+struct event_info {
+    int id;
+    const char *name;
+    const char *explanation;
+} event_info[] = {
+    {0x00, "IFU_IFETCH_MISS", 
+     "number of instruction fetch misses"},
+    {0x01, "CYCLES_IFU_MEM_STALL", 
+     "cycles instruction fetch pipe is stalled"},
+    {0x02, "CYCLES_DATA_STALL", 
+     "cycles stall occurs for due to data dependency"},
+    {0x03, "ITLB_MISS", 
+     "number of Instruction MicroTLB misses"},
+    {0x04, "DTLB_MISS", 
+     "number of Data MicroTLB misses"},
+    {0x05, "BR_INST_EXECUTED", 
+     "branch instruction executed w/ or w/o program flow change"},
+    {0x06, "BR_INST_MISS_PRED", 
+     "branch mispredicted"},
+    {0x07, "INSN_EXECUTED", 
+     "instructions executed"},
+    {0x09, "DCACHE_ACCESS", 
+     "data cache access, cacheable locations"},
+    {0x0a, "DCACHE_ACCESS_ALL", 
+     "data cache access, all locations"},
+    {0x0b, "DCACHE_MISS", 
+     "data cache miss"},
+    {0x0c, "DCACHE_WB", 
+     "data cache writeback, 1 event for every half cacheline"},
+    {0x0d, "PC_CHANGE", 
+     "number of times the program counter was changed without a mode switch"},
+    {0x0f, "TLB_MISS", 
+     "Main TLB miss"},
+    {0x10, "EXP_EXTERNAL", 
+     "Explicit external data access"},
+    {0x11, "LSU_STALL", 
+     "cycles stalled because Load Store request queue is full"},
+    {0x12, "WRITE_DRAIN", 
+     "Times write buffer was drained"},
+    {0xff, "CPU_CYCLES", 
+     "clock cycles counter"}, 
+};
+
+void usage() {
+    printf("\nopcontrol: usage:\n"
+           "   --list-events    list event types\n"
+           "   --help           this message\n"
+           "   --setup          setup directories\n"
+           "   --quick          setup and select CPU_CYCLES:150000\n"
+           "   --status         show configuration\n"
+           "   --start          start data collection\n"
+           "   --stop           stop data collection\n"
+           "   --reset          clears out data from current session\n"
+           "   --shutdown       kill the oprofile daeman\n"
+           "   --event=eventspec\n"
+           "      Choose an event. May be specified multiple times.\n"
+           "      eventspec is in the form of name[:count], where :\n"
+           "        name:  event name, see \"opcontrol --list-events\"\n"
+           "        count: reset counter value\n" 
+           "   --vmlinux=file   vmlinux kernel image\n"
+           "   --kernel-range=start,end\n"
+           "                    kernel range vma address in hexadecimal\n"
+          );
+}
+
+void setup_session_dir() {
+    int fd;
+
+    fd = open(OP_DATA_DIR, O_RDONLY);
+    if (fd != -1) {
+        system("rm -r "OP_DATA_DIR);
+        close(fd);
+    }
+
+    if (mkdir(OP_DATA_DIR, 755)) {
+        fprintf(stderr, "Cannot create directory \"%s\": %s\n",
+                OP_DATA_DIR, strerror(errno));
+    }
+    if (mkdir(OP_DATA_DIR"/samples", 644)) {
+        fprintf(stderr, "Cannot create directory \"%s\": %s\n",
+                OP_DATA_DIR"/samples", strerror(errno));
+    }
+}
+
+int do_setup() {
+    char dir[1024];
+
+    setup_session_dir();
+
+    if (mkdir(OP_DRIVER_BASE, 644)) {
+        fprintf(stderr, "Cannot create directory "OP_DRIVER_BASE": %s\n",
+                strerror(errno));
+        return -1;
+    }
+    if (system("mount -t oprofilefs nodev "OP_DRIVER_BASE)) {
+        return -1;
+    }
+    return 0;
+}
+
+void do_list_events()
+{
+    unsigned int i;
+
+    printf("%-20s: %s\n", "name", "meaning");
+    printf("----------------------------------------"
+           "--------------------------------------\n");
+    for (i = 0; i < sizeof(event_info)/sizeof(struct event_info); i++) {
+        printf("%-20s: %s\n", event_info[i].name, event_info[i].explanation);
+    }
+}
+
+int find_event_id_from_name(const char *name) {
+    unsigned int i;
+
+    for (i = 0; i < sizeof(event_info)/sizeof(struct event_info); i++) {
+        if (!strcmp(name, event_info[i].name)) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+const char * find_event_name_from_id(int id) {
+    unsigned int i;
+
+    for (i = 0; i < sizeof(event_info)/sizeof(struct event_info); i++) {
+        if (event_info[i].id == id) {
+            return event_info[i].name;
+        }
+    }
+    return NULL;
+}
+
+int process_event(const char *event_spec) {
+    char event_name[512];
+    char count_name[512];
+    unsigned int i;
+    int event_id;
+    int count_val;
+
+    strncpy(event_name, event_spec, 512);
+    count_name[0] = 0;
+
+    /* First, check if the name is followed by ":" */
+    for (i = 0; i < strlen(event_name); i++) {
+        if (event_name[i] == 0) {
+            break;
+        }
+        if (event_name[i] == ':') {
+            strncpy(count_name, event_name+i+1, 512);
+            event_name[i] = 0;
+            break;
+        }
+    }
+    event_id = find_event_id_from_name(event_name);
+    if (event_id == -1) {
+        fprintf(stderr, "Unknown event name: %s\n", event_name);
+        return -1;
+    }
+
+    /* Use defualt count */
+    if (count_name[0] == 0) {
+        count_val = min_count[0];
+    } else {
+        count_val = atoi(count_name);
+    }
+
+    selected_events[num_events] = event_id;
+    selected_counts[num_events++] = count_val;
+    verbose("event_id is %d\n", event_id);
+    verbose("count_val is %d\n", count_val);
+    return 0;
+}
+
+int echo_dev(const char* str, int val, const char* file, int counter)
+{
+    char fullname[512];
+    char content[128];
+    int fd;
+    
+    if (counter >= 0) {
+        snprintf(fullname, 512, OP_DRIVER_BASE"/%d/%s", counter, file);
+    }
+    else {
+        snprintf(fullname, 512, OP_DRIVER_BASE"/%s", file);
+    }
+    fd = open(fullname, O_WRONLY);
+    if (fd<0) {
+        fprintf(stderr, "Cannot open %s: %s\n", fullname, strerror(errno));
+        return fd;
+    }
+    if (str == 0) {
+        sprintf(content, "%d", val);
+    }
+    else {
+        strncpy(content, str, 128);
+    }
+    verbose("Configure %s (%s)\n", fullname, content);
+    write(fd, content, strlen(content));
+    close(fd);
+    return 0;
+}
+
+int read_num(const char* file)
+{
+    char buffer[256];
+    int fd = open(file, O_RDONLY);
+    if (fd<0) return -1;
+    int rd = read(fd, buffer, sizeof(buffer)-1);
+    buffer[rd] = 0;
+    return atoi(buffer);
+}
+
+void do_status()
+{
+    int num;
+    char fullname[512];
+    int i;
+
+    printf("Driver directory: %s\n", OP_DRIVER_BASE);
+    printf("Session directory: %s\n", OP_DATA_DIR);
+    for (i = 0; i < 3; i++) {
+        sprintf(fullname, OP_DRIVER_BASE"/%d/enabled", i);
+        num = read_num(fullname);
+        if (num > 0) {
+            printf("Counter %d:\n", i);
+
+            /* event name */
+            sprintf(fullname, OP_DRIVER_BASE"/%d/event", i);
+            num = read_num(fullname);
+            printf("    name: %s\n", find_event_name_from_id(num));
+
+            /* profile interval */
+            sprintf(fullname, OP_DRIVER_BASE"/%d/count", i);
+            num = read_num(fullname);
+            printf("    count: %d\n", num);
+        }
+        else {
+            printf("Counter %d disabled\n", i);
+        }
+    }
+
+    num = read_num(OP_DATA_DIR"/lock");
+    if (num >= 0) {
+        int fd;
+        /* Still needs to check if this lock is left-over */
+        sprintf(fullname, "/proc/%d", num);
+        fd = open(fullname, O_RDONLY);
+        if (fd == -1) {
+            printf("Session directory is not clean - do \"opcontrol --setup\""
+                   " before you continue\n");
+            return;
+        }
+        else {
+            close(fd);
+            printf("oprofiled pid: %d\n", num);
+            num = read_num(OP_DRIVER_BASE"/enable");
+            printf("profiler is%s running\n", num == 0 ? " not" : "");
+            num = read_num(OP_DRIVER_BASE"/stats/cpu0/sample_received");
+            printf("  %9u samples received\n", num);
+            num = read_num(OP_DRIVER_BASE"/stats/cpu0/sample_lost_overflow");
+            printf("  %9u samples lost overflow\n", num);
+#if 0
+            /* FIXME - backtrace seems broken */
+            num = read_num(OP_DRIVER_BASE"/stats/cpu0/backtrace_aborted");
+            printf("  %9u backtrace aborted\n", num);
+            num = read_num(OP_DRIVER_BASE"/backtrace_depth");
+            printf("  %9u backtrace_depth\n", num);
+#endif
+        }
+    }
+    else {
+        printf("oprofiled is not running\n");
+    }
+}
+
+void do_reset() 
+{
+    int fd;
+
+    fd = open(OP_DATA_DIR"/samples/current", O_RDONLY);
+    if (fd == -1) {
+        return;
+    }
+    close(fd);
+    system("rm -r "OP_DATA_DIR"/samples/current");
+}
+
+int main(int argc, char * const argv[])
+{
+    int option_index;
+    char command[1024];
+
+    /* Initialize default strings */
+    strcpy(vmlinux, "--no-vmlinux");
+    strcpy(kernel_range, "");
+
+    while (1) {
+        int c = getopt_long(argc, argv, "", long_options, &option_index);
+        if (c == -1) {
+            break;
+        }
+        switch (c) {
+            case 0:
+                break;
+            /* --event */
+            case 'e':   
+                if (num_events == 3) {
+                    fprintf(stderr, "More than 3 events specified\n");
+                    exit(1);
+                }
+                if (process_event(optarg)) {
+                    exit(1);
+                }
+                break;
+            /* --vmlinux */
+            case 'v':
+                sprintf(vmlinux, "-k %s", optarg);
+                break;
+            /* --kernel-range */
+            case 'r':
+                sprintf(kernel_range, "-r %s", optarg);
+                break;
+            /* --shutdown */
+            case 'h': {
+                int pid = read_num(OP_DATA_DIR"/lock");
+                if (pid >= 0) {
+                    kill(pid, SIGKILL);
+                }   
+                setup_session_dir();
+                break;
+            }
+            /* --status */
+            case 't':
+                do_status();
+                break;
+            default:
+                usage();
+                exit(1);
+        }
+    }
+    verbose("list_events = %d\n", list_events);
+    verbose("setup = %d\n", setup);
+
+    if (list_events) {
+        do_list_events();
+    }
+
+    if (quick) {
+        process_event("CPU_CYCLES");
+        setup = 1;
+    }
+
+    if (reset) {
+        do_reset();
+    }
+
+    if (show_usage) {
+        usage();
+    }
+
+    if (setup) {
+        if (do_setup()) {
+            fprintf(stderr, "do_setup failed");
+            exit(1);
+        }
+    }
+
+    if (num_events != 0) {
+        int i;
+
+        strcpy(command, "oprofiled --session-dir="OP_DATA_DIR);
+
+        /* Since counter #3 can only handle CPU_CYCLES, check and shuffle the 
+         * order a bit so that the maximal number of events can be profiled
+         * simultaneously
+         */
+        if (num_events == 3) {
+            for (i = 0; i < num_events; i++) {
+                int event_idx = selected_events[i];
+
+                if (event_info[event_idx].id == 0xff) {
+                    break;
+                }
+            }
+
+            /* No CPU_CYCLES is found */
+            if (i == 3) {
+                fprintf(stderr, "You can only specify three events if one of "
+                                "them is CPU_CYCLES\n");
+                exit(1);
+            }
+            /* Swap CPU_CYCLES to counter #2 (starting from #0)*/
+            else if (i != 2) {
+                int temp;
+
+                temp = selected_events[2];
+                selected_events[2] = selected_events[i];
+                selected_events[i] = temp;
+
+                temp = selected_counts[2];
+                selected_counts[2] = selected_counts[i];
+                selected_counts[i] = temp;
+            }
+        }
+
+
+        /* Configure the counters and enable them */
+        for (i = 0; i < num_events; i++) {
+            int event_idx = selected_events[i];
+            int setup_result = 0;
+
+            if (i == 0) {
+                snprintf(command+strlen(command), 1024 - strlen(command), 
+                         " --events=");
+            }
+            else {
+                snprintf(command+strlen(command), 1024 - strlen(command), 
+                         ",");
+            }
+            /* Compose name:id:count:unit_mask:kernel:user, something like
+             * --events=CYCLES_DATA_STALL:2:0:200000:0:1:1,....
+             */
+            snprintf(command+strlen(command), 1024 - strlen(command), 
+                     "%s:%d:%d:%d:0:1:1",
+                     event_info[event_idx].name,
+                     event_info[event_idx].id,
+                     i,
+                     selected_counts[i]);
+
+            setup_result |= echo_dev("1", 0, "user", i);
+            setup_result |= echo_dev("1", 0, "kernel", i);
+            setup_result |= echo_dev("0", 0, "unit_mask", i);
+            setup_result |= echo_dev("1", 0, "enabled", i);
+            setup_result |= echo_dev(NULL, selected_counts[i], "count", i);
+            setup_result |= echo_dev(NULL, event_info[event_idx].id, 
+                                     "event", i);
+            if (setup_result) {
+                fprintf(stderr, "Counter configuration failed for %s\n",
+                        event_info[event_idx].name);
+                fprintf(stderr, "Did you do \"opcontrol --setup\" first?\n");
+                exit(1);
+            }
+        }
+
+        /* Disable the unused counters */
+        for (i = num_events; i < 3; i++) {
+            echo_dev("0", 0, "enabled", i);
+        }
+
+        snprintf(command+strlen(command), 1024 - strlen(command), " %s",
+                 vmlinux);
+        if (kernel_range[0]) {
+            snprintf(command+strlen(command), 1024 - strlen(command), " %s",
+                     kernel_range);
+        }
+        verbose("command: %s\n", command);
+        system(command);
+    }
+
+    if (start) {
+        echo_dev("1", 0, "enable", -1);
+    }
+
+    if (stop) {
+        echo_dev("0", 0, "enable", -1);
+    }
+}
diff --git a/opimport_pull b/opimport_pull
new file mode 100755
index 0000000..7dbac4a
--- /dev/null
+++ b/opimport_pull
@@ -0,0 +1,70 @@
+#!/usr/bin/python2.4 -E
+
+import os
+import re
+import sys
+
+def PrintUsage():
+    print "Usage:" + sys.argv[0] + " dir"
+    print "    dir: directory on the host to store profile results"
+
+if (len(sys.argv) != 2):
+    PrintUsage()
+    sys.exit(1)
+
+try:
+    oprofile_event_dir = os.environ['OPROFILE_EVENTS_DIR']
+except:
+    print "OPROFILE_EVENTS_DIR not set. Run \". envsetup.sh\" first"
+    sys.exit(1)
+
+output_dir = sys.argv[1];
+
+try:
+    os.makedirs(output_dir)
+except:
+    if os.path.exists(output_dir):
+        print "Directory already exists:", output_dir
+    else:
+        print "Cannot create", output_dir
+    sys.exit(1)
+
+# get the samples off the phone
+result = os.system("adb pull /data/oprofile/samples " + output_dir + \
+                   "/raw_samples > /dev/null 2>&1")
+if result != 0:
+    print "adb pull failure, exiting"
+    sys.exit(1)
+
+# enter the destination directory
+os.chdir(output_dir)
+stream = os.popen("find raw_samples -type f -name \*all")
+
+# now all the sample files are on the host, we need to invoke opimport one at a
+# time to convert the content from the ARM abi to x86 ABI
+
+# break the full filename into:
+# 1: leading dir: "raw_samples"
+# 2: intermediate dirs: "/blah/blah/blah"
+# 3: filename: e.g. "CPU_CYCLES.150000.0.all.all.all"
+pattern = re.compile("(^raw_samples)(.*)/(.*)$")
+for line in stream:
+    match = pattern.search(line)
+    leading_dir = match.group(1)
+    middle_part = match.group(2)
+    file_name = match.group(3)
+
+    dir = "samples" + middle_part
+
+    # if multiple events are collected the directory could have been setup
+    if not os.path.exists(dir):
+        os.makedirs(dir)
+
+    cmd = oprofile_event_dir + "/bin/opimport -a " + oprofile_event_dir + \
+          "/abi/arm_abi -o samples" + middle_part + "/" + file_name + " " + line
+    os.system(cmd)
+
+stream.close()
+
+# short summary of profiling results
+os.system(oprofile_event_dir + "/bin/opreport --session-dir=.")
diff --git a/popt.h b/popt.h
new file mode 100644
index 0000000..4f85d9e
--- /dev/null
+++ b/popt.h
@@ -0,0 +1,564 @@
+/** \file popt/popt.h
+ * \ingroup popt
+ */
+
+/* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.rpm.org/pub/rpm/dist. */
+
+#ifndef H_POPT
+#define H_POPT
+
+#include <stdio.h>			/* for FILE * */
+
+#define POPT_OPTION_DEPTH	10
+
+/** \ingroup popt
+ * \name Arg type identifiers
+ */
+/*@{*/
+#define POPT_ARG_NONE		0	/*!< no arg */
+#define POPT_ARG_STRING		1	/*!< arg will be saved as string */
+#define POPT_ARG_INT		2	/*!< arg will be converted to int */
+#define POPT_ARG_LONG		3	/*!< arg will be converted to long */
+#define POPT_ARG_INCLUDE_TABLE	4	/*!< arg points to table */
+#define POPT_ARG_CALLBACK	5	/*!< table-wide callback... must be
+					   set first in table; arg points 
+					   to callback, descrip points to 
+					   callback data to pass */
+#define POPT_ARG_INTL_DOMAIN    6       /*!< set the translation domain
+					   for this table and any
+					   included tables; arg points
+					   to the domain string */
+#define POPT_ARG_VAL		7	/*!< arg should take value val */
+#define	POPT_ARG_FLOAT		8	/*!< arg will be converted to float */
+#define	POPT_ARG_DOUBLE		9	/*!< arg will be converted to double */
+
+#define POPT_ARG_MASK		0x0000FFFF
+/*@}*/
+
+/** \ingroup popt
+ * \name Arg modifiers
+ */
+/*@{*/
+#define POPT_ARGFLAG_ONEDASH	0x80000000  /*!< allow -longoption */
+#define POPT_ARGFLAG_DOC_HIDDEN 0x40000000  /*!< don't show in help/usage */
+#define POPT_ARGFLAG_STRIP	0x20000000  /*!< strip this arg from argv(only applies to long args) */
+#define	POPT_ARGFLAG_OPTIONAL	0x10000000  /*!< arg may be missing */
+
+#define	POPT_ARGFLAG_OR		0x08000000  /*!< arg will be or'ed */
+#define	POPT_ARGFLAG_NOR	0x09000000  /*!< arg will be nor'ed */
+#define	POPT_ARGFLAG_AND	0x04000000  /*!< arg will be and'ed */
+#define	POPT_ARGFLAG_NAND	0x05000000  /*!< arg will be nand'ed */
+#define	POPT_ARGFLAG_XOR	0x02000000  /*!< arg will be xor'ed */
+#define	POPT_ARGFLAG_NOT	0x01000000  /*!< arg will be negated */
+#define POPT_ARGFLAG_LOGICALOPS \
+        (POPT_ARGFLAG_OR|POPT_ARGFLAG_AND|POPT_ARGFLAG_XOR)
+
+#define	POPT_BIT_SET	(POPT_ARG_VAL|POPT_ARGFLAG_OR)
+					/*!< set arg bit(s) */
+#define	POPT_BIT_CLR	(POPT_ARG_VAL|POPT_ARGFLAG_NAND)
+					/*!< clear arg bit(s) */
+
+#define	POPT_ARGFLAG_SHOW_DEFAULT 0x00800000 /*!< show default value in --help */
+
+/*@}*/
+
+/** \ingroup popt
+ * \name Callback modifiers
+ */
+/*@{*/
+#define POPT_CBFLAG_PRE		0x80000000  /*!< call the callback before parse */
+#define POPT_CBFLAG_POST	0x40000000  /*!< call the callback after parse */
+#define POPT_CBFLAG_INC_DATA	0x20000000  /*!< use data from the include line,
+					       not the subtable */
+#define POPT_CBFLAG_SKIPOPTION	0x10000000  /*!< don't callback with option */
+#define POPT_CBFLAG_CONTINUE	0x08000000  /*!< continue callbacks with option */
+/*@}*/
+
+/** \ingroup popt
+ * \name Error return values
+ */
+/*@{*/
+#define POPT_ERROR_NOARG	-10	/*!< missing argument */
+#define POPT_ERROR_BADOPT	-11	/*!< unknown option */
+#define POPT_ERROR_OPTSTOODEEP	-13	/*!< aliases nested too deeply */
+#define POPT_ERROR_BADQUOTE	-15	/*!< error in paramter quoting */
+#define POPT_ERROR_ERRNO	-16	/*!< errno set, use strerror(errno) */
+#define POPT_ERROR_BADNUMBER	-17	/*!< invalid numeric value */
+#define POPT_ERROR_OVERFLOW	-18	/*!< number too large or too small */
+#define	POPT_ERROR_BADOPERATION	-19	/*!< mutually exclusive logical operations requested */
+#define	POPT_ERROR_NULLARG	-20	/*!< opt->arg should not be NULL */
+#define	POPT_ERROR_MALLOC	-21	/*!< memory allocation failed */
+/*@}*/
+
+/** \ingroup popt
+ * \name poptBadOption() flags
+ */
+/*@{*/
+#define POPT_BADOPTION_NOALIAS  (1 << 0)  /*!< don't go into an alias */
+/*@}*/
+
+/** \ingroup popt
+ * \name poptGetContext() flags
+ */
+/*@{*/
+#define POPT_CONTEXT_NO_EXEC	(1 << 0)  /*!< ignore exec expansions */
+#define POPT_CONTEXT_KEEP_FIRST	(1 << 1)  /*!< pay attention to argv[0] */
+#define POPT_CONTEXT_POSIXMEHARDER (1 << 2) /*!< options can't follow args */
+#define POPT_CONTEXT_ARG_OPTS	(1 << 4) /*!< return args as options with value 0 */
+/*@}*/
+
+/** \ingroup popt
+ */
+struct poptOption {
+/*@observer@*/ /*@null@*/
+    const char * longName;	/*!< may be NULL */
+    char shortName;		/*!< may be NUL */
+    int argInfo;
+/*@shared@*/ /*@null@*/
+    void * arg;			/*!< depends on argInfo */
+    int val;			/*!< 0 means don't return, just update flag */
+/*@observer@*/ /*@null@*/
+    const char * descrip;	/*!< description for autohelp -- may be NULL */
+/*@observer@*/ /*@null@*/
+    const char * argDescrip;	/*!< argument description for autohelp */
+};
+
+/** \ingroup popt
+ * A popt alias argument for poptAddAlias().
+ */
+struct poptAlias {
+/*@owned@*/ /*@null@*/
+    const char * longName;	/*!< may be NULL */
+    char shortName;		/*!< may be NUL */
+    int argc;
+/*@owned@*/
+    const char ** argv;		/*!< must be free()able */
+};
+
+/** \ingroup popt
+ * A popt alias or exec argument for poptAddItem().
+ */
+/*@-exporttype@*/
+typedef struct poptItem_s {
+    struct poptOption option;	/*!< alias/exec name(s) and description. */
+    int argc;			/*!< (alias) no. of args. */
+/*@owned@*/
+    const char ** argv;		/*!< (alias) args, must be free()able. */
+} * poptItem;
+/*@=exporttype@*/
+
+/** \ingroup popt
+ * \name Auto-generated help/usage
+ */
+/*@{*/
+
+/**
+ * Empty table marker to enable displaying popt alias/exec options.
+ */
+/*@-exportvar@*/
+/*@unchecked@*/ /*@observer@*/
+extern struct poptOption poptAliasOptions[];
+/*@=exportvar@*/
+#define POPT_AUTOALIAS { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptAliasOptions, \
+			0, "Options implemented via popt alias/exec:", NULL },
+
+/**
+ * Auto help table options.
+ */
+/*@-exportvar@*/
+/*@unchecked@*/ /*@observer@*/
+extern struct poptOption poptHelpOptions[];
+/*@=exportvar@*/
+
+/*@-exportvar@*/
+/*@unchecked@*/ /*@observer@*/
+extern struct poptOption * poptHelpOptionsI18N;
+/*@=exportvar@*/
+
+#define POPT_AUTOHELP { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptHelpOptions, \
+			0, "Help options:", NULL },
+
+#define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }
+/*@}*/
+
+/** \ingroup popt
+ */
+/*@-exporttype@*/
+typedef /*@abstract@*/ struct poptContext_s * poptContext;
+/*@=exporttype@*/
+
+/** \ingroup popt
+ */
+#ifndef __cplusplus
+/*@-exporttype -typeuse@*/
+typedef struct poptOption * poptOption;
+/*@=exporttype =typeuse@*/
+#endif
+
+/*@-exportconst@*/
+enum poptCallbackReason {
+    POPT_CALLBACK_REASON_PRE	= 0, 
+    POPT_CALLBACK_REASON_POST	= 1,
+    POPT_CALLBACK_REASON_OPTION = 2
+};
+/*@=exportconst@*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*@-type@*/
+
+/** \ingroup popt
+ * Table callback prototype.
+ * @param con		context
+ * @param reason	reason for callback
+ * @param opt		option that triggered callback
+ * @param arg		@todo Document.
+ * @param data		@todo Document.
+ */
+typedef void (*poptCallbackType) (poptContext con, 
+		enum poptCallbackReason reason,
+		/*@null@*/ const struct poptOption * opt,
+		/*@null@*/ const char * arg,
+		/*@null@*/ const void * data)
+	/*@globals internalState @*/
+	/*@modifies internalState @*/;
+
+/** \ingroup popt
+ * Initialize popt context.
+ * @param name		context name (usually argv[0] program name)
+ * @param argc		no. of arguments
+ * @param argv		argument array
+ * @param options	address of popt option table
+ * @param flags		or'd POPT_CONTEXT_* bits
+ * @return		initialized popt context
+ */
+/*@only@*/ /*@null@*/
+poptContext poptGetContext(
+		/*@dependent@*/ /*@keep@*/ const char * name,
+		int argc, /*@dependent@*/ /*@keep@*/ const char ** argv,
+		/*@dependent@*/ /*@keep@*/ const struct poptOption * options,
+		int flags)
+	/*@*/;
+
+/** \ingroup popt
+ * Reinitialize popt context.
+ * @param con		context
+ */
+/*@unused@*/
+void poptResetContext(/*@null@*/poptContext con)
+	/*@modifies con @*/;
+
+/** \ingroup popt
+ * Return value of next option found.
+ * @param con		context
+ * @return		next option val, -1 on last item, POPT_ERROR_* on error
+ */
+int poptGetNextOpt(/*@null@*/poptContext con)
+	/*@globals fileSystem, internalState @*/
+	/*@modifies con, fileSystem, internalState @*/;
+
+/** \ingroup popt
+ * Return next option argument (if any).
+ * @param con		context
+ * @return		option argument, NULL if no argument is available
+ */
+/*@observer@*/ /*@null@*/ /*@unused@*/
+const char * poptGetOptArg(/*@null@*/poptContext con)
+	/*@modifies con @*/;
+
+/** \ingroup popt
+ * Return next argument.
+ * @param con		context
+ * @return		next argument, NULL if no argument is available
+ */
+/*@observer@*/ /*@null@*/ /*@unused@*/
+const char * poptGetArg(/*@null@*/poptContext con)
+	/*@modifies con @*/;
+
+/** \ingroup popt
+ * Peek at current argument.
+ * @param con		context
+ * @return		current argument, NULL if no argument is available
+ */
+/*@observer@*/ /*@null@*/ /*@unused@*/
+const char * poptPeekArg(/*@null@*/poptContext con)
+	/*@*/;
+
+/** \ingroup popt
+ * Return remaining arguments.
+ * @param con		context
+ * @return		argument array, NULL terminated
+ */
+/*@observer@*/ /*@null@*/
+const char ** poptGetArgs(/*@null@*/poptContext con)
+	/*@modifies con @*/;
+
+/** \ingroup popt
+ * Return the option which caused the most recent error.
+ * @param con		context
+ * @param flags
+ * @return		offending option
+ */
+/*@observer@*/
+const char * poptBadOption(/*@null@*/poptContext con, int flags)
+	/*@*/;
+
+/** \ingroup popt
+ * Destroy context.
+ * @param con		context
+ * @return		NULL always
+ */
+/*@null@*/
+poptContext poptFreeContext( /*@only@*/ /*@null@*/ poptContext con)
+	/*@modifies con @*/;
+
+/** \ingroup popt
+ * Add arguments to context.
+ * @param con		context
+ * @param argv		argument array, NULL terminated
+ * @return		0 on success, POPT_ERROR_OPTSTOODEEP on failure
+ */
+/*@unused@*/
+int poptStuffArgs(poptContext con, /*@keep@*/ const char ** argv)
+	/*@modifies con @*/;
+
+/** \ingroup popt
+ * Add alias to context.
+ * @todo Pass alias by reference, not value.
+ * @deprecated Use poptAddItem instead.
+ * @param con		context
+ * @param alias		alias to add
+ * @param flags		(unused)
+ * @return		0 on success
+ */
+/*@unused@*/
+int poptAddAlias(poptContext con, struct poptAlias alias, int flags)
+	/*@modifies con @*/;
+
+/** \ingroup popt
+ * Add alias/exec item to context.
+ * @param con		context
+ * @param newItem	alias/exec item to add
+ * @param flags		0 for alias, 1 for exec
+ * @return		0 on success
+ */
+int poptAddItem(poptContext con, poptItem newItem, int flags)
+	/*@modifies con @*/;
+
+/** \ingroup popt
+ * Read configuration file.
+ * @param con		context
+ * @param fn		file name to read
+ * @return		0 on success, POPT_ERROR_ERRNO on failure
+ */
+int poptReadConfigFile(poptContext con, const char * fn)
+	/*@globals errno, fileSystem, internalState @*/
+	/*@modifies con->execs, con->numExecs,
+		errno, fileSystem, internalState @*/;
+
+/** \ingroup popt
+ * Read default configuration from /etc/popt and $HOME/.popt.
+ * @param con		context
+ * @param useEnv	(unused)
+ * @return		0 on success, POPT_ERROR_ERRNO on failure
+ */
+/*@unused@*/
+int poptReadDefaultConfig(poptContext con, /*@unused@*/ int useEnv)
+	/*@globals fileSystem, internalState @*/
+	/*@modifies con->execs, con->numExecs,
+		fileSystem, internalState @*/;
+
+/** \ingroup popt
+ * Duplicate an argument array.
+ * @note: The argument array is malloc'd as a single area, so only argv must
+ * be free'd.
+ *
+ * @param argc		no. of arguments
+ * @param argv		argument array
+ * @retval argcPtr	address of returned no. of arguments
+ * @retval argvPtr	address of returned argument array
+ * @return		0 on success, POPT_ERROR_NOARG on failure
+ */
+int poptDupArgv(int argc, /*@null@*/ const char **argv,
+		/*@null@*/ /*@out@*/ int * argcPtr,
+		/*@null@*/ /*@out@*/ const char *** argvPtr)
+	/*@modifies *argcPtr, *argvPtr @*/;
+
+/** \ingroup popt
+ * Parse a string into an argument array.
+ * The parse allows ', ", and \ quoting, but ' is treated the same as " and
+ * both may include \ quotes.
+ * @note: The argument array is malloc'd as a single area, so only argv must
+ * be free'd.
+ *
+ * @param s		string to parse
+ * @retval argcPtr	address of returned no. of arguments
+ * @retval argvPtr	address of returned argument array
+ */
+int poptParseArgvString(const char * s,
+		/*@out@*/ int * argcPtr, /*@out@*/ const char *** argvPtr)
+	/*@modifies *argcPtr, *argvPtr @*/;
+
+/** \ingroup popt
+ * Parses an input configuration file and returns an string that is a 
+ * command line.  For use with popt.  You must free the return value when done.
+ *
+ * Given the file:
+\verbatim
+# this line is ignored
+    #   this one too
+aaa
+  bbb
+    ccc   
+bla=bla
+
+this_is   =   fdsafdas
+     bad_line=        
+  reall bad line  
+  reall bad line  = again
+5555=   55555   
+  test = with lots of spaces
+\endverbatim
+*
+* The result is:
+\verbatim
+--aaa --bbb --ccc --bla="bla" --this_is="fdsafdas" --5555="55555" --test="with lots of spaces"
+\endverbatim
+*
+* Passing this to poptParseArgvString() yields an argv of:
+\verbatim
+'--aaa'
+'--bbb' 
+'--ccc' 
+'--bla=bla' 
+'--this_is=fdsafdas' 
+'--5555=55555' 
+'--test=with lots of spaces' 
+\endverbatim
+ *
+ * @bug NULL is returned if file line is too long.
+ * @bug Silently ignores invalid lines.
+ *
+ * @param fp		file handle to read
+ * @param *argstrp	return string of options (malloc'd)
+ * @param flags		unused
+ * @return		0 on success
+ * @see			poptParseArgvString
+ */
+/*@-fcnuse@*/
+int poptConfigFileToString(FILE *fp, /*@out@*/ char ** argstrp, int flags)
+	/*@globals fileSystem @*/
+	/*@modifies *fp, *argstrp, fileSystem @*/;
+/*@=fcnuse@*/
+
+/** \ingroup popt
+ * Return formatted error string for popt failure.
+ * @param error		popt error
+ * @return		error string
+ */
+/*@observer@*/
+const char * poptStrerror(const int error)
+	/*@*/;
+
+/** \ingroup popt
+ * Limit search for executables.
+ * @param con		context
+ * @param path		single path to search for executables
+ * @param allowAbsolute	absolute paths only?
+ */
+/*@unused@*/
+void poptSetExecPath(poptContext con, const char * path, int allowAbsolute)
+	/*@modifies con @*/;
+
+/** \ingroup popt
+ * Print detailed description of options.
+ * @param con		context
+ * @param fp		ouput file handle
+ * @param flags		(unused)
+ */
+void poptPrintHelp(poptContext con, FILE * fp, /*@unused@*/ int flags)
+	/*@globals fileSystem @*/
+	/*@modifies *fp, fileSystem @*/;
+
+/** \ingroup popt
+ * Print terse description of options.
+ * @param con		context
+ * @param fp		ouput file handle
+ * @param flags		(unused)
+ */
+void poptPrintUsage(poptContext con, FILE * fp, /*@unused@*/ int flags)
+	/*@globals fileSystem @*/
+	/*@modifies *fp, fileSystem @*/;
+
+/** \ingroup popt
+ * Provide text to replace default "[OPTION...]" in help/usage output.
+ * @param con		context
+ * @param text		replacement text
+ */
+/*@-fcnuse@*/
+void poptSetOtherOptionHelp(poptContext con, const char * text)
+	/*@modifies con @*/;
+/*@=fcnuse@*/
+
+/** \ingroup popt
+ * Return argv[0] from context.
+ * @param con		context
+ * @return		argv[0]
+ */
+/*@-fcnuse@*/
+/*@observer@*/
+const char * poptGetInvocationName(poptContext con)
+	/*@*/;
+/*@=fcnuse@*/
+
+/** \ingroup popt
+ * Shuffle argv pointers to remove stripped args, returns new argc.
+ * @param con		context
+ * @param argc		no. of args
+ * @param argv		arg vector
+ * @return		new argc
+ */
+/*@-fcnuse@*/
+int poptStrippedArgv(poptContext con, int argc, char ** argv)
+	/*@modifies *argv @*/;
+/*@=fcnuse@*/
+
+/**
+ * Save a long, performing logical operation with value.
+ * @warning Alignment check may be too strict on certain platorms.
+ * @param arg		integer pointer, aligned on int boundary.
+ * @param argInfo	logical operation (see POPT_ARGFLAG_*)
+ * @param aLong		value to use
+ * @return		0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
+ */
+/*@-incondefs@*/
+/*@unused@*/
+int poptSaveLong(/*@null@*/ long * arg, int argInfo, long aLong)
+	/*@modifies *arg @*/
+	/*@requires maxSet(arg) >= 0 /\ maxRead(arg) == 0 @*/;
+/*@=incondefs@*/
+
+/**
+ * Save an integer, performing logical operation with value.
+ * @warning Alignment check may be too strict on certain platorms.
+ * @param arg		integer pointer, aligned on int boundary.
+ * @param argInfo	logical operation (see POPT_ARGFLAG_*)
+ * @param aLong		value to use
+ * @return		0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
+ */
+/*@-incondefs@*/
+/*@unused@*/
+int poptSaveInt(/*@null@*/ int * arg, int argInfo, long aLong)
+	/*@modifies *arg @*/
+	/*@requires maxSet(arg) >= 0 /\ maxRead(arg) == 0 @*/;
+/*@=incondefs@*/
+
+/*@=type@*/
+#ifdef  __cplusplus
+}
+#endif
+
+#endif