aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:30 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:30 -0800
commit10e23eebca4175a8dfe3a788b2bebacb1fcfce54 (patch)
treee7b15dddb89338d148474c4e4c16acb087e322c7
parentdfb3f050a7cebd2030ea23dc6fa8964530e4ddcc (diff)
downloadoprofile-10e23eebca4175a8dfe3a788b2bebacb1fcfce54.tar.gz
-rw-r--r--Android.mk3
-rw-r--r--MODULE_LICENSE_GPL0
-rw-r--r--NOTICE340
-rw-r--r--config.h101
-rw-r--r--daemon/Android.mk33
-rw-r--r--daemon/init.c373
-rw-r--r--daemon/opd_anon.c228
-rw-r--r--daemon/opd_anon.h54
-rw-r--r--daemon/opd_cookie.c209
-rw-r--r--daemon/opd_cookie.h39
-rw-r--r--daemon/opd_events.c165
-rw-r--r--daemon/opd_events.h47
-rw-r--r--daemon/opd_interface.h45
-rw-r--r--daemon/opd_kernel.c229
-rw-r--r--daemon/opd_kernel.h43
-rw-r--r--daemon/opd_mangling.c205
-rw-r--r--daemon/opd_mangling.h33
-rw-r--r--daemon/opd_perfmon.c496
-rw-r--r--daemon/opd_perfmon.h106
-rw-r--r--daemon/opd_pipe.c96
-rw-r--r--daemon/opd_pipe.h45
-rw-r--r--daemon/opd_printf.h35
-rw-r--r--daemon/opd_sfile.c619
-rw-r--r--daemon/opd_sfile.h113
-rw-r--r--daemon/opd_spu.c176
-rw-r--r--daemon/opd_stats.c85
-rw-r--r--daemon/opd_stats.h31
-rw-r--r--daemon/opd_trans.c349
-rw-r--r--daemon/opd_trans.h85
-rw-r--r--daemon/oprofiled.c522
-rw-r--r--daemon/oprofiled.h69
-rw-r--r--libabi/Android.mk14
-rw-r--r--libabi/abi.cpp75
-rw-r--r--libabi/abi.h42
-rw-r--r--libabi/op_abi.c94
-rw-r--r--libabi/op_abi.h43
-rw-r--r--libabi/opimport.cpp225
-rw-r--r--libdb/Android.mk17
-rw-r--r--libdb/db_debug.c132
-rw-r--r--libdb/db_insert.c102
-rw-r--r--libdb/db_manage.c311
-rw-r--r--libdb/db_stat.c88
-rw-r--r--libdb/db_travel.c18
-rw-r--r--libdb/odb.h223
-rw-r--r--libop/Android.mk19
-rw-r--r--libop/op_alloc_counter.c213
-rw-r--r--libop/op_alloc_counter.h43
-rw-r--r--libop/op_config.c77
-rw-r--r--libop/op_config.h58
-rw-r--r--libop/op_config_24.h79
-rw-r--r--libop/op_cpu_type.c158
-rw-r--r--libop/op_cpu_type.h139
-rw-r--r--libop/op_events.c862
-rw-r--r--libop/op_events.h125
-rw-r--r--libop/op_get_interface.c32
-rw-r--r--libop/op_hw_config.h30
-rw-r--r--libop/op_interface.h87
-rw-r--r--libop/op_mangle.c104
-rw-r--r--libop/op_mangle.h66
-rw-r--r--libop/op_parse_event.c120
-rw-r--r--libop/op_parse_event.h42
-rw-r--r--libop/op_sample_file.h42
-rw-r--r--libpopt/Android.mk15
-rw-r--r--libpopt/config.h329
-rw-r--r--libpopt/findme.c52
-rw-r--r--libpopt/findme.h20
-rw-r--r--libpopt/popt.c1262
-rw-r--r--libpopt/popt.h564
-rw-r--r--libpopt/poptconfig.c182
-rw-r--r--libpopt/popthelp.c819
-rw-r--r--libpopt/poptint.h116
-rw-r--r--libpopt/poptparse.c227
-rw-r--r--libpopt/system.h81
-rw-r--r--libutil/Android.mk21
-rw-r--r--libutil/fscanf.c22
-rw-r--r--libutil/op_cpufreq.c64
-rw-r--r--libutil/op_cpufreq.h30
-rw-r--r--libutil/op_deviceio.c42
-rw-r--r--libutil/op_deviceio.h57
-rw-r--r--libutil/op_file.c185
-rw-r--r--libutil/op_file.h109
-rw-r--r--libutil/op_fileio.c228
-rw-r--r--libutil/op_fileio.h140
-rw-r--r--libutil/op_get_time.c24
-rw-r--r--libutil/op_get_time.h33
-rw-r--r--libutil/op_growable_buffer.c46
-rw-r--r--libutil/op_growable_buffer.h45
-rw-r--r--libutil/op_libiberty.c38
-rw-r--r--libutil/op_libiberty.h81
-rw-r--r--libutil/op_list.h177
-rw-r--r--libutil/op_lockfile.c69
-rw-r--r--libutil/op_lockfile.h34
-rw-r--r--libutil/op_popt.c43
-rw-r--r--libutil/op_popt.h42
-rw-r--r--libutil/op_string.c62
-rw-r--r--libutil/op_string.h81
-rw-r--r--libutil/op_types.h37
-rw-r--r--libutil/op_version.c24
-rw-r--r--libutil/op_version.h26
-rw-r--r--opcontrol/Android.mk18
-rw-r--r--opcontrol/opcontrol.cpp542
-rwxr-xr-xopimport_pull70
-rw-r--r--popt.h564
103 files changed, 15275 insertions, 0 deletions
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