pax_global_header00006660000000000000000000000064124701527250014517gustar00rootroot0000000000000052 comment=d1c4ed3989167625fb198dd1b772305d2cf024d3 kcov_25+dfsg.orig/000077500000000000000000000000001247015272500142115ustar00rootroot00000000000000kcov_25+dfsg.orig/.gitignore000066400000000000000000000001131247015272500161740ustar00rootroot00000000000000*.o *~ *.pyc *.pyo *.cmd .tmp_versions *.ko *.symvers *.order *.mod.c kcov kcov_25+dfsg.orig/.gitmodules000066400000000000000000000002031247015272500163610ustar00rootroot00000000000000[submodule "tests/unit-tests/trompeloeil"] path = tests/unit-tests/trompeloeil url = https://github.com/rollbear/trompeloeil.git kcov_25+dfsg.orig/.travis.yml000066400000000000000000000021361247015272500163240ustar00rootroot00000000000000# Build matrix / environment variable are explained on: # http://about.travis-ci.org/docs/user/build-configuration/ # This file can be validated on: # http://lint.travis-ci.org/ before_install: make -f travis/Makefile prepare_environment language: cpp compiler: - gcc - clang env: global: # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created # via the "travis encrypt" command using the project repo's public key - secure: "WG/iday4nudNJ9acP/gjjuXzI0Jw0MN0zP9JA3WueMvMV0UrVRnBKM+papB06XiB4Gyiaoy92pfOGzmby0ciMAvydBn2DyuGZhM1nXuEIbkQqfP3AR3z6nCZHY27NAhVnpnEAyb56FwD353JMVZj34cnRmcjgKArMM3Y8W66Zls=" addons: coverity_scan: project: name: "SimonKagstrom/kcov" description: "code coverage" notification_email: simon.kagstrom@gmail.com build_command_prepend: "mkdir -p coverity-build && cd coverity-build && cmake .. && cd .." build_command: "make -C coverity-build" branch_pattern: master script: make -f travis/Makefile run-tests notifications: recipients: - simon.kagstrom@gmail.com email: on_success: change on_failure: always kcov_25+dfsg.orig/CMakeLists.txt000066400000000000000000000021551247015272500167540ustar00rootroot00000000000000cmake_minimum_required (VERSION 2.6) # ==================================== # project name and version # ==================================== project (kcov) if(NOT CMAKE_BUILD_TYPE) set (CMAKE_BUILD_TYPE Release) endif(NOT CMAKE_BUILD_TYPE) # ==================================== # configuring # ==================================== set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) find_package (PkgConfig REQUIRED) set (SPECIFY_RPATH OFF CACHE BOOL "Specify RPATH for installed executables") # ==================================== # default install paths for targets # ==================================== include (CPack.local.cmake OPTIONAL) set (INSTALL_TARGETS_PATH RUNTIME DESTINATION bin LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX}) if (NOT MAN_DIR) set (MAN_DIR share/man) endif (NOT MAN_DIR) set (INSTALL_MAN_PATH DESTINATION ${MAN_DIR}) # ==================================== # build all # ==================================== add_subdirectory (src) add_subdirectory (doc) kcov_25+dfsg.orig/COPYING000066400000000000000000000431101247015272500152430ustar00rootroot00000000000000 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. Copyright (C) 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. , 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. kcov_25+dfsg.orig/COPYING.externals000066400000000000000000000000771247015272500172540ustar00rootroot00000000000000tempo.js: Apache 2.0 tablesorter (mottie): GPL/MIT jquery: MIT kcov_25+dfsg.orig/CPack.local.cmake000066400000000000000000000015721247015272500172720ustar00rootroot00000000000000# ==================================== # cpack settings # ==================================== set (CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) set (CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) set (CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Code coverage tester based on Bcov") set (CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/README") set (CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/COPYING") set (CPACK_RESOURCE_FILE_README "${PROJECT_SOURCE_DIR}/README") set (CPACK_PACKAGE_VENDOR "Simon Kagstrom") set (CPACK_PACKAGE_CONTACT "Simon Kagstrom ") set (CPACK_STRIP_FILES TRUE) set (CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}") set (CPACK_SOURCE_IGNORE_FILES /Debug/ /Release/ /build/ /.git/ /.gitignore .*~ .*.log ) include (CPack) kcov_25+dfsg.orig/ChangeLog000066400000000000000000000347141247015272500157740ustar00rootroot00000000000000Kcov (25): * Make database files smaller by storing only executed addresses * Issue #52: Don't include bash-helper.sh in the output * Issue #54: Implement handling of coverage accumulation for shared libraries * Issue #50: Make sure that Bash "heredoc" markers are words * Fix some failure handling coverity errors * Issue #49 (WIP): Implement broken gcov engine (multiple issues still pending) * Refactor configuration handling to be more generic (avoid setters, use key/value pairs via strings instead) * Add "capabilities" class to make parts of the implementation clearer * HTML-writer: Correct off-by-one access to static data -- Simon Kagstrom , Tue Feb 10 19:38:04 CET 2015 Kcov (24): * Misc performance improvements here and there * Build: Use the travis-ci.org build service to make tests more reliable. * Unbreak Python and Bash coverage collection on 32-bit builds * Correct clang build (well, in Fedora 20 it still fails) * Issue #43: Clarify elfutils dependency * Issue #46: Add support for uploading to coveralls.io via the --coveralls-id= option * Issue #45: Bash: Fix coverage collection when executing sub-scripts as programs. * Issue #31: Fix handling of attaching to pid in a multithreaded process. Implementation taken from GDB: kcov now loops through all threads in a process and attaches to each one. * Issue #44: Bash: Fix issues with <<< redirection, which were parsed as here documents * Update the HTML/Cobertura output to display single-shot, limited and unlimited output in a logical way * Allow three hit modes for parsers/engines: Single-shot (covered or not, as in the merge parser), limited (as in ptrace) and unlimited (line execution count used in Python/Bash) * Remove some ptrace-specific stuff from the Engine interface * Document classes better -- Simon Kagstrom , Mon Jan 5 09:46:29 CET 2015 Kcov (23): * Correct use-after-free case when the solib thread is killed * Default to 5s output interval to avoid costly write-to-disk sessions too often * Performance improvements for the Cobertura writer by reducing the amount of string operations * Add a kcov-merge command which merges data from multiple kcov directories into a single report (Chris Love) * Issue #39: Reduce the amount of extra output files by using filename as a tie breaker instead of file data. * Issue #38: Fix several bugs which caused merged output to miss data present in the regular output * Issue #37: Correct coverage accumulation for Python/bash * Issue #36: Collect coverage from dlopen:ed libraries (through unconditional breakpoints in the dlopen call). Should also improve reliability of general shared library coverage collection -- Simon Kagstrom , Tue Dec 2 20:42:04 CET 2014 Kcov (22): * Issue #35: Support installation RPATH for build (Chris Love) * Issue #32: Fix problems when running Python unittest modules * Various coverity resource leak fixes * Peek into files to improve performance for matching (also see Issue #33) * Break out solib handling from ptrace (not really related) * Improve ptrace performance a bit, while also simplifying code -- Simon Kagstrom , Wed Oct 29 12:58:18 CET 2014 Kcov (21): * Fix regression for coverage-of-32-bit-binaries-on-64-bit-systems introduced in kcov 20 * Slightly better file stat caching (slight performance improvement) * Avoid outputting merged cobertura output for single coverage runs -- Simon Kagstrom , Thu Aug 7 20:00:20 CEST 2014 Kcov (20): * Issue #25: Fix starvation bug where long report generation time could starve the actual application. The bug was caused by a timestamp issue. * Issue #18: Provide explanation and possible workaround for SIGILL problems with GCC 4.1.2 * Issue #23: Warn if a binary has no debug symbols * Replace std::list/std::map with std::list/std::unordered_map where possible (slight performance improvement) * Remove C++11 constructs (auto, nullptr, range-based for-loops) to allow building with older compilers * Correct bug in --report-only (crash in ptrace upon termination) * Slight performance improvement and simplification in the lineid code -- Simon Kagstrom , Mon Aug 4 12:57:19 CEST 2014 Kcov (19): * bash coverage: Implemented support for collecting shell script (sh/bash) coverage! Loosely based on shcov, which is now deprecated because of this * html-writer: Correct executable lines/covered lines in the summary (was wrong in some cases) -- Simon Kagstrom , Mon Jun 30 18:30:37 CEST 2014 Kcov (18): * Performance: Improve performance by cacheing realpath() * HTML: Always put merged results last and don't include them in sorting * HTML: Coloring improvements (?) of the HTML output * HTML: Refactor HTML output completely to use javascript-based pages. This allows selecting how to sort directly from the web page, and greatly simplifies the generating C++ code * Correct filtering bug in the merged output, where the last run would filter out data from the previous runs * Issue #20: Fix hanging-on-killed-by-signal -- Simon Kagstrom , Mon Jun 9 19:12:38 CEST 2014 Kcov (17): * Issue #19: Fix bug where kcov would let children which called setpgid live even after terminating kcov itself (Oliver) * Issue #16: Output: Implement a merged mode, which shows a combination of all covered programs under the [merged] link * Fix an HTML output bug introduced in kcov 14 where the summary counts in the index page would be wrong. * Issue #15: Fix a crash when source lines are > ~2000 characters long * Issue #17: Fix parsing of multiple include/exclude patterns (Jamie Kirkpatrick) * Split usage in common/uncommon options * Improve unit tests and robot framework tests * Various work on the kernel-engine and general refactoring -- Simon Kagstrom , Sun May 18 19:24:14 CEST 2014 Kcov (16): * kernel-engine: Implement an engine to handle kernel code coverage collection (not finished) * kprobe-coverage: Refactor the kernel module so that it basically works now * Code refactoring and more C++11-izing * Python: Filter out some false negatives (i.e., non-executable lines marked as red) * Python: Report absolute paths so that output filtering works * Python: Catch exceptions in the python helper to avoid EOF etc being propagated upwards (and coverage collection to fail) * Python: Fix a case where the engine would simply hang on waiting for more data -- Simon Kagstrom , Fri Mar 21 16:38:28 CET 2014 Kcov (15): * Workaround a shared-library issue where kcov would sometimes hang on the malloc. The workaround uses a static memory buffer instead. * Implemented support for collecting Python code coverage! Usage is the same as for compiled code. * Code-restructuring to allow multiple "engines", which makes it possible to add the python support above. * More C++11-izing of the code -- Simon Kagstrom , Sun Mar 2 10:50:00 CET 2014 Kcov (14): * Add two options, --collect-only and --report-only to only collect coverage and produce Cobertura/HTML output respectively. Can be used for example on embedded systems where the source code isn't available. * PowerPC support has been added (thanks to Aleksandr Vilchinskii and Konstantin Polivtsev for testing this) -- Simon Kagstrom , Wed Feb 5 19:42:03 CET 2014 Kcov (13): * Don't hang the kcov main process if a breakpoint comes in before the solib handler constructor has executed (depends on the construction order) * Correct bug with realloc in solib handling (malloc all data once only) -- Simon Kagstrom , Sun Dec 8 19:34:37 CET 2013 Kcov (12): * Fix lockup bug in the solib code path (where processes with many solibs would hang at startup) * Correct a crash in the reporter * Add test case for signals * Report from main thread to avoid threading issues * Fairly major refactoring of ptrace code to avoid an issue where two PTRACE_CONTINUE calls could be done without waitpid() in between. This would cause crashes in the traced program. * Add support for looking for debug info in separate files via build-id (Alexander Larsson) * Add --exit-first-process option to fork kcov when covering daemons (otherwise kcov will wait for all children to finish) * Add --skip-solibs option to disable processing of shared libraries, which also works around a bug which sometimes triggers * Add --replace-source-path option for cases where the sources have been moved (James Warner) * Fix linking (Igor Murzov) * Some build fixes to make it build with clang++ * Fix various warnings * Add experimental profiler based on kcov * Add sorting by reverse-percent (high -> low, -u option) * Cache non-existing files to avoid doing lstat calls all the time (slight performance improvement) * closedir() was not called from the html-writer, which creates a resource leak (Karl Vogel) -- Simon Kagstrom , Sat Oct 19 11:06:42 CEST 2013 Kcov (11): * Fix handling of common paths in HTML writer * Fix bug in shared library handling where repeated exec()s would cause strange hangs. Fixed by unsetting the KCOV_SOLIB_PATH environment variable after the first executable has been started. Add test-popen test case to check for this. * Expand ~'s in the --include-path and --exclude-path options * Unbreak accumulating data from multiple runs again (introduced sometime during refactoring), and add test case for this -- Simon Kagstrom , Sat Aug 18 08:45:03 CEST 2012 Kcov (10): * Fix bug where multiple files with the same name would only be shown once in the HTML output. * Fix icache conflict bug (present while instrumenting shared libraries) on multi-CPU systems by forcing all traced processes to one CPU * Re-enable the --pid=PID argument to trace a running process again * Fix race condition in the reporter * Only set breakpoints in executable sections. This avoids problems where the DWARF line information points to non-code sections (which can happen) * Fix coverage reporting of orphaned children (where the parent does not wait(2) for the child * Use std::unordered_map for breakpoints (improves performance somewhat) * Improve unit tests * Add robot-framework testsuite that can be run from a Jenkins server -- Simon Kagstrom , Mon Jul 23 13:28:43 CEST 2012 Kcov (9): * Completely refactor the source code (yes, everything!), and add unit tests to verify kcov itself. The code is now in C++. * Implement transparent coverage instrumentation for shared libraries (done automatically) * Implement a Cobertura-compatible XML backend. This allows integrating kcov coverage information in Jenkins. * Improve performance a lot by filtering out files already at the instrumentation step (and not only during HTML generation). This means that --include-pattern/--include-path can improve kcov performance by quite a bit. -- Simon Kagstrom , Fri Jun 15 17:39:17 CEST 2012 Kcov (8): * Import the man-page from Debian and update it. * Simplify parts of the ptrace code * Correct the sorting of files-by-percentage when the difference is less than 0.5% (thanks Martin!). * Correct the percentage-bar color according to the configured limits in the report. -- Simon Kagstrom , Sun Dec 4 14:13:47 CET 2011 Kcov (7): * Weed out common parts of source paths from the report, display the full path as the HTML title= tag * Fix crash bug in argument passing * Handle hit count a bit better -- Simon Kagstrom , 2011-07-10 Kcov (6): * Correct hit count. The hit count is no longer over the number of possible hits * Better error reporting when stripped or otherwise broken binaries are passed to kcov * Handle files with relative directory paths (lookup the absolute path via DWARF source files). Otherwise, some files are not reported in the output. * Add test program to detect regressions * Detect stripped binaries and report that you'll need to build with -g * Correct --include/exclude-path behavior. The previous version will match PATH*/* instead of just PATH/* (Igor Murzov) * Various small fixes -- Simon Kagstrom , Sat Apr 16 08:28:27 CEST 2011 Kcov (5): * Switch from libdwarf to libdw (from elfutils) for building kcov. libdw is a bit more logical than libdwarf. * Implement actual path matching (Igor Murzov) * Rename path-matching to pattern matching (which is what it does) and set it up using long options. * Parse options with getopt * Don't escape all multibyte characters in the report. That will break display of UTF-8 etc (Igor Murzov) * Improve cmake packaging support (Igor Murzov) -- Simon Kagstrom , Sat Feb 12 15:25:57 CET 2011 Kcov (4): * Build system is now based on cmake (Igor Murzov) * Add MIPS32, ARM and PowerPC architecture support. This is untested though and might very well not work at all. * Add -t option to set the title of the program to test (otherwise it will be the filename) * Refactored the architecture support to allow running i386 programs on x86_64 machines, and easier add other architectures * Make it possible to collect coverage from multiple programs in a single directory, useful for example when running a test suite * Add coverage color to the header as well * Misc refactoring -- Simon Kagstrom , Tue Dec 7 18:20:28 CET 2010 Kcov (3): * Various bug fixes * Add ability to trace running processes by PID * Fix copyright headers -- Simon Kagstrom , Sat Nov 13 08:13:03 CET 2010 Kcov (2): * Misc fixes * Add ability to specify low/high limits * Add state saving between runs. Re-starting the same program will then add to existing coverage instead of starting from scratch. * Link to web page in the report * Add option to sort by percentage or file name -- Simon Kagstrom , Sun Oct 24 09:15:39 CEST 2010 Kcov (1): * Initial release -- Simon Kagstrom kcov_25+dfsg.orig/INSTALL000066400000000000000000000022261247015272500152440ustar00rootroot00000000000000Building Kcov ============= You need development headers and libraries for libstdc++, curl and elfutils to buildd kcov. Create an empty build dir and do the following steps: cmake [options] make make install Useful options include -DCMAKE_INSTALL_PREFIX= (installation prefix), -DCMAKE_BUILD_TYPE= and -DCMAKE_C_FLAGS=. Basic example: cd /path/to/kcov/source/dir mkdir build cd build cmake .. make make install More advanced example: cd /path/to/kcov/source/dir mkdir build cd build cmake \ -DCMAKE_C_FLAGS:STRING="-O3 -march=i686" \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/usr \ .. make -j2 || exit 1 make install DESTDIR=/tmp/kcov-build || exit 1 For further information refer to cmake documentation: http://www.cmake.org/cmake/help/cmake-2-8-docs.html. Troubleshooting =============== If you have elfutils installed, but cmake fails to find it, specify elfutils install prefix explicitly to cmake. Here is an example: cd kcov CMAKE_PREFIX_PATH=/opt/elfutils-dir/ \ cmake . make make install kcov_25+dfsg.orig/README000077700000000000000000000000001247015272500163432README.mdustar00rootroot00000000000000kcov_25+dfsg.orig/README.md000066400000000000000000000021531247015272500154710ustar00rootroot00000000000000[![Build Status](https://travis-ci.org/SimonKagstrom/kcov.svg?branch=master)](https://travis-ci.org/SimonKagstrom/kcov) [![Coverage Status](https://img.shields.io/coveralls/SimonKagstrom/kcov.svg)](https://coveralls.io/r/SimonKagstrom/kcov?branch=master) Kcov is a code coverage tester for compiled languages, Python and Bash. Kcov was originally a fork of Bcov (http://bcov.sf.net), but has since evolved to support a large feature set in addition to that of Bcov. Kcov, like Bcov, uses DWARF debugging information for compiled programs to make it possible to collect coverage information without special compiler switches. The usage is simple: kcov /path/to/outdir executable [args for the executable] /path/to/outdir will contain lcov-style HTML output generated continuously while the application runs. Kcov will also write cobertura- compatible XML output and can upload coverage data directly to http://coveralls.io for easy integration with travis-ci. kcov is written by Simon Kagstrom and more information can be found at the web page, http://simonkagstrom.github.com/kcov/index.html kcov_25+dfsg.orig/cmake/000077500000000000000000000000001247015272500152715ustar00rootroot00000000000000kcov_25+dfsg.orig/cmake/FindElfutils.cmake000066400000000000000000000030741247015272500206670ustar00rootroot00000000000000# - Try to find libdwarf # Once done this will define # # LIBDWARF_FOUND - system has libdwarf # LIBDWARF_INCLUDE_DIRS - the libdwarf include directory # LIBDWARF_LIBRARIES - Link these to use libdwarf # LIBDWARF_DEFINITIONS - Compiler switches required for using libdwarf # # Locate libelf library at first if (NOT LIBELF_FOUND) find_package (LibElf REQUIRED) endif (NOT LIBELF_FOUND) if (LIBDWARF_LIBRARIES AND LIBDWARF_INCLUDE_DIRS) set (LibDwarf_FIND_QUIETLY TRUE) endif (LIBDWARF_LIBRARIES AND LIBDWARF_INCLUDE_DIRS) find_path (DWARF_INCLUDE_DIR NAMES dwarf.h PATHS /usr/include /usr/local/include /opt/local/include /sw/include ENV CPATH) # PATH and INCLUDE will also work find_path (LIBDW_INCLUDE_DIR NAMES elfutils/libdw.h PATHS /usr/include /usr/local/include /opt/local/include /sw/include ENV CPATH) if (DWARF_INCLUDE_DIR AND LIBDW_INCLUDE_DIR) set (LIBDWARF_INCLUDE_DIRS ${DWARF_INCLUDE_DIR} ${LIBDW_INCLUDE_DIR}) endif (DWARF_INCLUDE_DIR AND LIBDW_INCLUDE_DIR) find_library (LIBDW_LIBRARIES NAMES dw PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib ENV LIBRARY_PATH # PATH and LIB will also work ENV LD_LIBRARY_PATH) include (FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBDWARF_FOUND to TRUE # if all listed variables are TRUE FIND_PACKAGE_HANDLE_STANDARD_ARGS(ElfUtils DEFAULT_MSG LIBDW_LIBRARIES LIBDW_INCLUDE_DIR) mark_as_advanced(LIBDW_INCLUDE_DIR LIBDW_LIBRARIES) kcov_25+dfsg.orig/cmake/FindLibCRPCUT.cmake000066400000000000000000000027661247015272500205360ustar00rootroot00000000000000# - Try to find libcrpcut # Once done this will define # # LIBCRPCUT_FOUND - system has libcrpcut # LIBCRPCUT_INCLUDE_DIRS - the libcrpcut include directory # LIBCRPCUT_LIBRARIES - Link these to use libcrpcut # LIBCRPCUT_DEFINITIONS - Compiler switches required for using libcrpcut # # Based on: # # Copyright (c) 2008 Bernhard Walle # # Redistribution and use is allowed according to the terms of the New # BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # if (LIBCRPCUT_LIBRARIES AND LIBCRPCUT_INCLUDE_DIRS) set (LibCRPCUT_FIND_QUIETLY TRUE) endif (LIBCRPCUT_LIBRARIES AND LIBCRPCUT_INCLUDE_DIRS) find_path (LIBCRPCUT_INCLUDE_DIRS NAMES crpcut.hpp PATHS /usr/include /usr/include/crpcut /usr/local/include /usr/local/include/crpcut /opt/local/include /opt/local/include/crpcut /home/ska/local/include ENV CPATH) find_library (LIBCRPCUT_LIBRARIES NAMES crpcut PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 /opt/local/lib /opt/usr/lib64 /home/ska/local/lib ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) include (FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBCRPCUT_FOUND to TRUE if all listed variables are TRUE FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibCRPCUT DEFAULT_MSG LIBCRPCUT_LIBRARIES LIBCRPCUT_INCLUDE_DIRS) mark_as_advanced(LIBCRPCUT_INCLUDE_DIRS LIBCRPCUT_LIBRARIES) kcov_25+dfsg.orig/cmake/FindLibElf.cmake000066400000000000000000000025451247015272500202370ustar00rootroot00000000000000# - Try to find libelf # Once done this will define # # LIBELF_FOUND - system has libelf # LIBELF_INCLUDE_DIRS - the libelf include directory # LIBELF_LIBRARIES - Link these to use libelf # LIBELF_DEFINITIONS - Compiler switches required for using libelf # # Copyright (c) 2008 Bernhard Walle # # Redistribution and use is allowed according to the terms of the New # BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # if (LIBELF_LIBRARIES AND LIBELF_INCLUDE_DIRS) set (LibElf_FIND_QUIETLY TRUE) endif (LIBELF_LIBRARIES AND LIBELF_INCLUDE_DIRS) find_path (LIBELF_INCLUDE_DIRS NAMES libelf.h PATHS /usr/include /usr/include/libelf /usr/local/include /usr/local/include/libelf /opt/local/include /opt/local/include/libelf /sw/include /sw/include/libelf ENV CPATH) find_library (LIBELF_LIBRARIES NAMES elf PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) include (FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBELF_FOUND to TRUE if all listed variables are TRUE FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibElf DEFAULT_MSG LIBELF_LIBRARIES LIBELF_INCLUDE_DIRS) mark_as_advanced(LIBELF_INCLUDE_DIRS LIBELF_LIBRARIES) kcov_25+dfsg.orig/cmake/FindLibUDIS86.cmake000066400000000000000000000026651247015272500204560ustar00rootroot00000000000000# - Try to find libudis # Once done this will define # # LIBUDIS86_FOUND - system has libudis86 # LIBUDIS86_INCLUDE_DIRS - the libudis86 include directory # LIBUDIS86_LIBRARIES - Link these to use libudis86 # LIBUDIS86_DEFINITIONS - Compiler switches required for using libudis86 # # Based on: # # Copyright (c) 2008 Bernhard Walle # # Redistribution and use is allowed according to the terms of the New # BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # if (LIBUDIS86_LIBRARIES AND LIBUDIS86_INCLUDE_DIRS) set (LibUDIS86_FIND_QUIETLY TRUE) endif (LIBUDIS86_LIBRARIES AND LIBUDIS86_INCLUDE_DIRS) find_path (LIBUDIS86_INCLUDE_DIRS NAMES udis86.h PATHS /usr/include /usr/include/udis86 /usr/local/include /usr/local/include/udis86 /opt/local/include /opt/local/include/udis86 /home/ska/local/include ENV CPATH) find_library (LIBUDIS86_LIBRARIES NAMES udis86 PATHS /usr/lib /usr/local/lib /opt/local/lib /home/ska/local/lib ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) include (FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBUDIS86_FOUND to TRUE if all listed variables are TRUE FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibUDIS86 DEFAULT_MSG LIBUDIS86_LIBRARIES LIBUDIS86_INCLUDE_DIRS) mark_as_advanced(LIBUDIS86_INCLUDE_DIRS LIBUDIS86_LIBRARIES) kcov_25+dfsg.orig/data/000077500000000000000000000000001247015272500151225ustar00rootroot00000000000000kcov_25+dfsg.orig/data/amber.png000066400000000000000000000002151247015272500167140ustar00rootroot00000000000000PNG  IHDR%VtIME( pHYs  ~gAMA aPLTEPz~ IDATxc`'IENDB`kcov_25+dfsg.orig/data/bcov.css000066400000000000000000000044021247015272500165650ustar00rootroot00000000000000/* Based upon the lcov CSS style, style files can be reused */ body { color: #000000; background-color: #FFFFFF; } a:link { color: #284FA8; text-decoration: underline; } a:visited { color: #00CB40; text-decoration: underline; } a:active { color: #FF0040; text-decoration: underline; } td.title { text-align: center; padding-bottom: 10px; font-size: 20pt; font-weight: bold; } td.ruler { background-color: #6688D4; } td.headerItem { text-align: right; padding-right: 6px; font-family: sans-serif; font-weight: bold; } td.headerValue { text-align: left; color: #284FA8; font-family: sans-serif; font-weight: bold; } td.versionInfo { text-align: center; padding-top: 2px; } th.headerItem { text-align: right; padding-right: 6px; font-family: sans-serif; font-weight: bold; } th.headerValue { text-align: left; color: #284FA8; font-family: sans-serif; font-weight: bold; } pre.source { font-family: monospace; white-space: pre; } span.lineNum { background-color: #EFE383; } span.lineCov { background-color: #adff9a; } span.linePartCov { background-color: #fffe80; } span.lineNoCov { background-color: #ffbbbb; } span.coverHits { background-color: #DFE383; padding-left: 3px; padding-right: 1px; text-align: right; list-style-type: none; width: 48px; display: inline-block; width: 64px; } td.tableHead { text-align: center; color: #FFFFFF; background-color: #6688D4; font-family: sans-serif; font-size: 120%; font-weight: bold; } td.coverFile { text-align: left; padding-left: 10px; padding-right: 20px; color: #284FA8; font-family: monospace; } td.coverBar { padding-left: 10px; padding-right: 10px; background-color: #DAE7FE; } td.coverBarOutline { background-color: #000000; } td.coverPer { text-align: left; padding-left: 10px; padding-right: 10px; font-weight: bold; } td.coverPerLeftMed { text-align: left; padding-left: 10px; padding-right: 10px; background-color: #fffe80; font-weight: bold; } td.coverPerLeftLo { text-align: left; padding-left: 10px; padding-right: 10px; background-color: #ffbbbb; font-weight: bold; } td.coverPerLeftHi { text-align: left; padding-left: 10px; padding-right: 10px; background-color: #adff9a; font-weight: bold; } td.coverNum { text-align: right; padding-left: 10px; padding-right: 10px; } td.coverNum { text-align: right; padding-left: 10px; padding-right: 10px; } kcov_25+dfsg.orig/data/glass.png000066400000000000000000000002471247015272500167440ustar00rootroot00000000000000PNG  IHDR%VgAMA aPLTEU~tRNS@fbKGDH pHYs  ~tIME@V IDATxc`HqIENDB`kcov_25+dfsg.orig/data/index.html000066400000000000000000000071561247015272500171300ustar00rootroot00000000000000 ???
Coverage Report
Command: ???
Date: Instrumented lines: ???
Code covered: ??? Executed lines: ???

Generated by: Kcov
kcov_25+dfsg.orig/data/js/000077500000000000000000000000001247015272500155365ustar00rootroot00000000000000kcov_25+dfsg.orig/data/js/handlebars.js000066400000000000000000003050441247015272500202050ustar00rootroot00000000000000/*! handlebars v2.0.0 Copyright (C) 2011-2014 by Yehuda Katz Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @license */ /* exported Handlebars */ (function (root, factory) { if (typeof define === 'function' && define.amd) { define([], factory); } else if (typeof exports === 'object') { module.exports = factory(); } else { root.Handlebars = root.Handlebars || factory(); } }(this, function () { // handlebars/safe-string.js var __module4__ = (function() { "use strict"; var __exports__; // Build out our basic SafeString type function SafeString(string) { this.string = string; } SafeString.prototype.toString = function() { return "" + this.string; }; __exports__ = SafeString; return __exports__; })(); // handlebars/utils.js var __module3__ = (function(__dependency1__) { "use strict"; var __exports__ = {}; /*jshint -W004 */ var SafeString = __dependency1__; var escape = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'", "`": "`" }; var badChars = /[&<>"'`]/g; var possible = /[&<>"'`]/; function escapeChar(chr) { return escape[chr]; } function extend(obj /* , ...source */) { for (var i = 1; i < arguments.length; i++) { for (var key in arguments[i]) { if (Object.prototype.hasOwnProperty.call(arguments[i], key)) { obj[key] = arguments[i][key]; } } } return obj; } __exports__.extend = extend;var toString = Object.prototype.toString; __exports__.toString = toString; // Sourced from lodash // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt var isFunction = function(value) { return typeof value === 'function'; }; // fallback for older versions of Chrome and Safari /* istanbul ignore next */ if (isFunction(/x/)) { isFunction = function(value) { return typeof value === 'function' && toString.call(value) === '[object Function]'; }; } var isFunction; __exports__.isFunction = isFunction; /* istanbul ignore next */ var isArray = Array.isArray || function(value) { return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false; }; __exports__.isArray = isArray; function escapeExpression(string) { // don't escape SafeStrings, since they're already safe if (string instanceof SafeString) { return string.toString(); } else if (string == null) { return ""; } else if (!string) { return string + ''; } // Force a string conversion as this will be done by the append regardless and // the regex test will do this transparently behind the scenes, causing issues if // an object's to string has escaped characters in it. string = "" + string; if(!possible.test(string)) { return string; } return string.replace(badChars, escapeChar); } __exports__.escapeExpression = escapeExpression;function isEmpty(value) { if (!value && value !== 0) { return true; } else if (isArray(value) && value.length === 0) { return true; } else { return false; } } __exports__.isEmpty = isEmpty;function appendContextPath(contextPath, id) { return (contextPath ? contextPath + '.' : '') + id; } __exports__.appendContextPath = appendContextPath; return __exports__; })(__module4__); // handlebars/exception.js var __module5__ = (function() { "use strict"; var __exports__; var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; function Exception(message, node) { var line; if (node && node.firstLine) { line = node.firstLine; message += ' - ' + line + ':' + node.firstColumn; } var tmp = Error.prototype.constructor.call(this, message); // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. for (var idx = 0; idx < errorProps.length; idx++) { this[errorProps[idx]] = tmp[errorProps[idx]]; } if (line) { this.lineNumber = line; this.column = node.firstColumn; } } Exception.prototype = new Error(); __exports__ = Exception; return __exports__; })(); // handlebars/base.js var __module2__ = (function(__dependency1__, __dependency2__) { "use strict"; var __exports__ = {}; var Utils = __dependency1__; var Exception = __dependency2__; var VERSION = "2.0.0"; __exports__.VERSION = VERSION;var COMPILER_REVISION = 6; __exports__.COMPILER_REVISION = COMPILER_REVISION; var REVISION_CHANGES = { 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it 2: '== 1.0.0-rc.3', 3: '== 1.0.0-rc.4', 4: '== 1.x.x', 5: '== 2.0.0-alpha.x', 6: '>= 2.0.0-beta.1' }; __exports__.REVISION_CHANGES = REVISION_CHANGES; var isArray = Utils.isArray, isFunction = Utils.isFunction, toString = Utils.toString, objectType = '[object Object]'; function HandlebarsEnvironment(helpers, partials) { this.helpers = helpers || {}; this.partials = partials || {}; registerDefaultHelpers(this); } __exports__.HandlebarsEnvironment = HandlebarsEnvironment;HandlebarsEnvironment.prototype = { constructor: HandlebarsEnvironment, logger: logger, log: log, registerHelper: function(name, fn) { if (toString.call(name) === objectType) { if (fn) { throw new Exception('Arg not supported with multiple helpers'); } Utils.extend(this.helpers, name); } else { this.helpers[name] = fn; } }, unregisterHelper: function(name) { delete this.helpers[name]; }, registerPartial: function(name, partial) { if (toString.call(name) === objectType) { Utils.extend(this.partials, name); } else { this.partials[name] = partial; } }, unregisterPartial: function(name) { delete this.partials[name]; } }; function registerDefaultHelpers(instance) { instance.registerHelper('helperMissing', function(/* [args, ]options */) { if(arguments.length === 1) { // A missing field in a {{foo}} constuct. return undefined; } else { // Someone is actually trying to call something, blow up. throw new Exception("Missing helper: '" + arguments[arguments.length-1].name + "'"); } }); instance.registerHelper('blockHelperMissing', function(context, options) { var inverse = options.inverse, fn = options.fn; if(context === true) { return fn(this); } else if(context === false || context == null) { return inverse(this); } else if (isArray(context)) { if(context.length > 0) { if (options.ids) { options.ids = [options.name]; } return instance.helpers.each(context, options); } else { return inverse(this); } } else { if (options.data && options.ids) { var data = createFrame(options.data); data.contextPath = Utils.appendContextPath(options.data.contextPath, options.name); options = {data: data}; } return fn(context, options); } }); instance.registerHelper('each', function(context, options) { if (!options) { throw new Exception('Must pass iterator to #each'); } var fn = options.fn, inverse = options.inverse; var i = 0, ret = "", data; var contextPath; if (options.data && options.ids) { contextPath = Utils.appendContextPath(options.data.contextPath, options.ids[0]) + '.'; } if (isFunction(context)) { context = context.call(this); } if (options.data) { data = createFrame(options.data); } if(context && typeof context === 'object') { if (isArray(context)) { for(var j = context.length; i 0) { throw new Exception("Invalid path: " + original, this); } else if (part === "..") { depth++; depthString += '../'; } else { this.isScoped = true; } } else { dig.push(part); } } this.original = original; this.parts = dig; this.string = dig.join('.'); this.depth = depth; this.idName = depthString + this.string; // an ID is simple if it only has one part, and that part is not // `..` or `this`. this.isSimple = parts.length === 1 && !this.isScoped && depth === 0; this.stringModeValue = this.string; }, PartialNameNode: function(name, locInfo) { LocationInfo.call(this, locInfo); this.type = "PARTIAL_NAME"; this.name = name.original; }, DataNode: function(id, locInfo) { LocationInfo.call(this, locInfo); this.type = "DATA"; this.id = id; this.stringModeValue = id.stringModeValue; this.idName = '@' + id.stringModeValue; }, StringNode: function(string, locInfo) { LocationInfo.call(this, locInfo); this.type = "STRING"; this.original = this.string = this.stringModeValue = string; }, NumberNode: function(number, locInfo) { LocationInfo.call(this, locInfo); this.type = "NUMBER"; this.original = this.number = number; this.stringModeValue = Number(number); }, BooleanNode: function(bool, locInfo) { LocationInfo.call(this, locInfo); this.type = "BOOLEAN"; this.bool = bool; this.stringModeValue = bool === "true"; }, CommentNode: function(comment, locInfo) { LocationInfo.call(this, locInfo); this.type = "comment"; this.comment = comment; this.strip = { inlineStandalone: true }; } }; // Must be exported as an object rather than the root of the module as the jison lexer // most modify the object to operate properly. __exports__ = AST; return __exports__; })(__module5__); // handlebars/compiler/parser.js var __module9__ = (function() { "use strict"; var __exports__; /* jshint ignore:start */ /* istanbul ignore next */ /* Jison generated parser */ var handlebars = (function(){ var parser = {trace: function trace() { }, yy: {}, symbols_: {"error":2,"root":3,"program":4,"EOF":5,"program_repetition0":6,"statement":7,"mustache":8,"block":9,"rawBlock":10,"partial":11,"CONTENT":12,"COMMENT":13,"openRawBlock":14,"END_RAW_BLOCK":15,"OPEN_RAW_BLOCK":16,"sexpr":17,"CLOSE_RAW_BLOCK":18,"openBlock":19,"block_option0":20,"closeBlock":21,"openInverse":22,"block_option1":23,"OPEN_BLOCK":24,"CLOSE":25,"OPEN_INVERSE":26,"inverseAndProgram":27,"INVERSE":28,"OPEN_ENDBLOCK":29,"path":30,"OPEN":31,"OPEN_UNESCAPED":32,"CLOSE_UNESCAPED":33,"OPEN_PARTIAL":34,"partialName":35,"param":36,"partial_option0":37,"partial_option1":38,"sexpr_repetition0":39,"sexpr_option0":40,"dataName":41,"STRING":42,"NUMBER":43,"BOOLEAN":44,"OPEN_SEXPR":45,"CLOSE_SEXPR":46,"hash":47,"hash_repetition_plus0":48,"hashSegment":49,"ID":50,"EQUALS":51,"DATA":52,"pathSegments":53,"SEP":54,"$accept":0,"$end":1}, terminals_: {2:"error",5:"EOF",12:"CONTENT",13:"COMMENT",15:"END_RAW_BLOCK",16:"OPEN_RAW_BLOCK",18:"CLOSE_RAW_BLOCK",24:"OPEN_BLOCK",25:"CLOSE",26:"OPEN_INVERSE",28:"INVERSE",29:"OPEN_ENDBLOCK",31:"OPEN",32:"OPEN_UNESCAPED",33:"CLOSE_UNESCAPED",34:"OPEN_PARTIAL",42:"STRING",43:"NUMBER",44:"BOOLEAN",45:"OPEN_SEXPR",46:"CLOSE_SEXPR",50:"ID",51:"EQUALS",52:"DATA",54:"SEP"}, productions_: [0,[3,2],[4,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[10,3],[14,3],[9,4],[9,4],[19,3],[22,3],[27,2],[21,3],[8,3],[8,3],[11,5],[11,4],[17,3],[17,1],[36,1],[36,1],[36,1],[36,1],[36,1],[36,3],[47,1],[49,3],[35,1],[35,1],[35,1],[41,2],[30,1],[53,3],[53,1],[6,0],[6,2],[20,0],[20,1],[23,0],[23,1],[37,0],[37,1],[38,0],[38,1],[39,0],[39,2],[40,0],[40,1],[48,1],[48,2]], performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) { var $0 = $$.length - 1; switch (yystate) { case 1: yy.prepareProgram($$[$0-1].statements, true); return $$[$0-1]; break; case 2:this.$ = new yy.ProgramNode(yy.prepareProgram($$[$0]), {}, this._$); break; case 3:this.$ = $$[$0]; break; case 4:this.$ = $$[$0]; break; case 5:this.$ = $$[$0]; break; case 6:this.$ = $$[$0]; break; case 7:this.$ = new yy.ContentNode($$[$0], this._$); break; case 8:this.$ = new yy.CommentNode($$[$0], this._$); break; case 9:this.$ = new yy.RawBlockNode($$[$0-2], $$[$0-1], $$[$0], this._$); break; case 10:this.$ = new yy.MustacheNode($$[$0-1], null, '', '', this._$); break; case 11:this.$ = yy.prepareBlock($$[$0-3], $$[$0-2], $$[$0-1], $$[$0], false, this._$); break; case 12:this.$ = yy.prepareBlock($$[$0-3], $$[$0-2], $$[$0-1], $$[$0], true, this._$); break; case 13:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], yy.stripFlags($$[$0-2], $$[$0]), this._$); break; case 14:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], yy.stripFlags($$[$0-2], $$[$0]), this._$); break; case 15:this.$ = { strip: yy.stripFlags($$[$0-1], $$[$0-1]), program: $$[$0] }; break; case 16:this.$ = {path: $$[$0-1], strip: yy.stripFlags($$[$0-2], $$[$0])}; break; case 17:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], yy.stripFlags($$[$0-2], $$[$0]), this._$); break; case 18:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], yy.stripFlags($$[$0-2], $$[$0]), this._$); break; case 19:this.$ = new yy.PartialNode($$[$0-3], $$[$0-2], $$[$0-1], yy.stripFlags($$[$0-4], $$[$0]), this._$); break; case 20:this.$ = new yy.PartialNode($$[$0-2], undefined, $$[$0-1], yy.stripFlags($$[$0-3], $$[$0]), this._$); break; case 21:this.$ = new yy.SexprNode([$$[$0-2]].concat($$[$0-1]), $$[$0], this._$); break; case 22:this.$ = new yy.SexprNode([$$[$0]], null, this._$); break; case 23:this.$ = $$[$0]; break; case 24:this.$ = new yy.StringNode($$[$0], this._$); break; case 25:this.$ = new yy.NumberNode($$[$0], this._$); break; case 26:this.$ = new yy.BooleanNode($$[$0], this._$); break; case 27:this.$ = $$[$0]; break; case 28:$$[$0-1].isHelper = true; this.$ = $$[$0-1]; break; case 29:this.$ = new yy.HashNode($$[$0], this._$); break; case 30:this.$ = [$$[$0-2], $$[$0]]; break; case 31:this.$ = new yy.PartialNameNode($$[$0], this._$); break; case 32:this.$ = new yy.PartialNameNode(new yy.StringNode($$[$0], this._$), this._$); break; case 33:this.$ = new yy.PartialNameNode(new yy.NumberNode($$[$0], this._$)); break; case 34:this.$ = new yy.DataNode($$[$0], this._$); break; case 35:this.$ = new yy.IdNode($$[$0], this._$); break; case 36: $$[$0-2].push({part: $$[$0], separator: $$[$0-1]}); this.$ = $$[$0-2]; break; case 37:this.$ = [{part: $$[$0]}]; break; case 38:this.$ = []; break; case 39:$$[$0-1].push($$[$0]); break; case 48:this.$ = []; break; case 49:$$[$0-1].push($$[$0]); break; case 52:this.$ = [$$[$0]]; break; case 53:$$[$0-1].push($$[$0]); break; } }, table: [{3:1,4:2,5:[2,38],6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],31:[2,38],32:[2,38],34:[2,38]},{1:[3]},{5:[1,4]},{5:[2,2],7:5,8:6,9:7,10:8,11:9,12:[1,10],13:[1,11],14:16,16:[1,20],19:14,22:15,24:[1,18],26:[1,19],28:[2,2],29:[2,2],31:[1,12],32:[1,13],34:[1,17]},{1:[2,1]},{5:[2,39],12:[2,39],13:[2,39],16:[2,39],24:[2,39],26:[2,39],28:[2,39],29:[2,39],31:[2,39],32:[2,39],34:[2,39]},{5:[2,3],12:[2,3],13:[2,3],16:[2,3],24:[2,3],26:[2,3],28:[2,3],29:[2,3],31:[2,3],32:[2,3],34:[2,3]},{5:[2,4],12:[2,4],13:[2,4],16:[2,4],24:[2,4],26:[2,4],28:[2,4],29:[2,4],31:[2,4],32:[2,4],34:[2,4]},{5:[2,5],12:[2,5],13:[2,5],16:[2,5],24:[2,5],26:[2,5],28:[2,5],29:[2,5],31:[2,5],32:[2,5],34:[2,5]},{5:[2,6],12:[2,6],13:[2,6],16:[2,6],24:[2,6],26:[2,6],28:[2,6],29:[2,6],31:[2,6],32:[2,6],34:[2,6]},{5:[2,7],12:[2,7],13:[2,7],16:[2,7],24:[2,7],26:[2,7],28:[2,7],29:[2,7],31:[2,7],32:[2,7],34:[2,7]},{5:[2,8],12:[2,8],13:[2,8],16:[2,8],24:[2,8],26:[2,8],28:[2,8],29:[2,8],31:[2,8],32:[2,8],34:[2,8]},{17:21,30:22,41:23,50:[1,26],52:[1,25],53:24},{17:27,30:22,41:23,50:[1,26],52:[1,25],53:24},{4:28,6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],28:[2,38],29:[2,38],31:[2,38],32:[2,38],34:[2,38]},{4:29,6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],28:[2,38],29:[2,38],31:[2,38],32:[2,38],34:[2,38]},{12:[1,30]},{30:32,35:31,42:[1,33],43:[1,34],50:[1,26],53:24},{17:35,30:22,41:23,50:[1,26],52:[1,25],53:24},{17:36,30:22,41:23,50:[1,26],52:[1,25],53:24},{17:37,30:22,41:23,50:[1,26],52:[1,25],53:24},{25:[1,38]},{18:[2,48],25:[2,48],33:[2,48],39:39,42:[2,48],43:[2,48],44:[2,48],45:[2,48],46:[2,48],50:[2,48],52:[2,48]},{18:[2,22],25:[2,22],33:[2,22],46:[2,22]},{18:[2,35],25:[2,35],33:[2,35],42:[2,35],43:[2,35],44:[2,35],45:[2,35],46:[2,35],50:[2,35],52:[2,35],54:[1,40]},{30:41,50:[1,26],53:24},{18:[2,37],25:[2,37],33:[2,37],42:[2,37],43:[2,37],44:[2,37],45:[2,37],46:[2,37],50:[2,37],52:[2,37],54:[2,37]},{33:[1,42]},{20:43,27:44,28:[1,45],29:[2,40]},{23:46,27:47,28:[1,45],29:[2,42]},{15:[1,48]},{25:[2,46],30:51,36:49,38:50,41:55,42:[1,52],43:[1,53],44:[1,54],45:[1,56],47:57,48:58,49:60,50:[1,59],52:[1,25],53:24},{25:[2,31],42:[2,31],43:[2,31],44:[2,31],45:[2,31],50:[2,31],52:[2,31]},{25:[2,32],42:[2,32],43:[2,32],44:[2,32],45:[2,32],50:[2,32],52:[2,32]},{25:[2,33],42:[2,33],43:[2,33],44:[2,33],45:[2,33],50:[2,33],52:[2,33]},{25:[1,61]},{25:[1,62]},{18:[1,63]},{5:[2,17],12:[2,17],13:[2,17],16:[2,17],24:[2,17],26:[2,17],28:[2,17],29:[2,17],31:[2,17],32:[2,17],34:[2,17]},{18:[2,50],25:[2,50],30:51,33:[2,50],36:65,40:64,41:55,42:[1,52],43:[1,53],44:[1,54],45:[1,56],46:[2,50],47:66,48:58,49:60,50:[1,59],52:[1,25],53:24},{50:[1,67]},{18:[2,34],25:[2,34],33:[2,34],42:[2,34],43:[2,34],44:[2,34],45:[2,34],46:[2,34],50:[2,34],52:[2,34]},{5:[2,18],12:[2,18],13:[2,18],16:[2,18],24:[2,18],26:[2,18],28:[2,18],29:[2,18],31:[2,18],32:[2,18],34:[2,18]},{21:68,29:[1,69]},{29:[2,41]},{4:70,6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],29:[2,38],31:[2,38],32:[2,38],34:[2,38]},{21:71,29:[1,69]},{29:[2,43]},{5:[2,9],12:[2,9],13:[2,9],16:[2,9],24:[2,9],26:[2,9],28:[2,9],29:[2,9],31:[2,9],32:[2,9],34:[2,9]},{25:[2,44],37:72,47:73,48:58,49:60,50:[1,74]},{25:[1,75]},{18:[2,23],25:[2,23],33:[2,23],42:[2,23],43:[2,23],44:[2,23],45:[2,23],46:[2,23],50:[2,23],52:[2,23]},{18:[2,24],25:[2,24],33:[2,24],42:[2,24],43:[2,24],44:[2,24],45:[2,24],46:[2,24],50:[2,24],52:[2,24]},{18:[2,25],25:[2,25],33:[2,25],42:[2,25],43:[2,25],44:[2,25],45:[2,25],46:[2,25],50:[2,25],52:[2,25]},{18:[2,26],25:[2,26],33:[2,26],42:[2,26],43:[2,26],44:[2,26],45:[2,26],46:[2,26],50:[2,26],52:[2,26]},{18:[2,27],25:[2,27],33:[2,27],42:[2,27],43:[2,27],44:[2,27],45:[2,27],46:[2,27],50:[2,27],52:[2,27]},{17:76,30:22,41:23,50:[1,26],52:[1,25],53:24},{25:[2,47]},{18:[2,29],25:[2,29],33:[2,29],46:[2,29],49:77,50:[1,74]},{18:[2,37],25:[2,37],33:[2,37],42:[2,37],43:[2,37],44:[2,37],45:[2,37],46:[2,37],50:[2,37],51:[1,78],52:[2,37],54:[2,37]},{18:[2,52],25:[2,52],33:[2,52],46:[2,52],50:[2,52]},{12:[2,13],13:[2,13],16:[2,13],24:[2,13],26:[2,13],28:[2,13],29:[2,13],31:[2,13],32:[2,13],34:[2,13]},{12:[2,14],13:[2,14],16:[2,14],24:[2,14],26:[2,14],28:[2,14],29:[2,14],31:[2,14],32:[2,14],34:[2,14]},{12:[2,10]},{18:[2,21],25:[2,21],33:[2,21],46:[2,21]},{18:[2,49],25:[2,49],33:[2,49],42:[2,49],43:[2,49],44:[2,49],45:[2,49],46:[2,49],50:[2,49],52:[2,49]},{18:[2,51],25:[2,51],33:[2,51],46:[2,51]},{18:[2,36],25:[2,36],33:[2,36],42:[2,36],43:[2,36],44:[2,36],45:[2,36],46:[2,36],50:[2,36],52:[2,36],54:[2,36]},{5:[2,11],12:[2,11],13:[2,11],16:[2,11],24:[2,11],26:[2,11],28:[2,11],29:[2,11],31:[2,11],32:[2,11],34:[2,11]},{30:79,50:[1,26],53:24},{29:[2,15]},{5:[2,12],12:[2,12],13:[2,12],16:[2,12],24:[2,12],26:[2,12],28:[2,12],29:[2,12],31:[2,12],32:[2,12],34:[2,12]},{25:[1,80]},{25:[2,45]},{51:[1,78]},{5:[2,20],12:[2,20],13:[2,20],16:[2,20],24:[2,20],26:[2,20],28:[2,20],29:[2,20],31:[2,20],32:[2,20],34:[2,20]},{46:[1,81]},{18:[2,53],25:[2,53],33:[2,53],46:[2,53],50:[2,53]},{30:51,36:82,41:55,42:[1,52],43:[1,53],44:[1,54],45:[1,56],50:[1,26],52:[1,25],53:24},{25:[1,83]},{5:[2,19],12:[2,19],13:[2,19],16:[2,19],24:[2,19],26:[2,19],28:[2,19],29:[2,19],31:[2,19],32:[2,19],34:[2,19]},{18:[2,28],25:[2,28],33:[2,28],42:[2,28],43:[2,28],44:[2,28],45:[2,28],46:[2,28],50:[2,28],52:[2,28]},{18:[2,30],25:[2,30],33:[2,30],46:[2,30],50:[2,30]},{5:[2,16],12:[2,16],13:[2,16],16:[2,16],24:[2,16],26:[2,16],28:[2,16],29:[2,16],31:[2,16],32:[2,16],34:[2,16]}], defaultActions: {4:[2,1],44:[2,41],47:[2,43],57:[2,47],63:[2,10],70:[2,15],73:[2,45]}, parseError: function parseError(str, hash) { throw new Error(str); }, parse: function parse(input) { var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1; this.lexer.setInput(input); this.lexer.yy = this.yy; this.yy.lexer = this.lexer; this.yy.parser = this; if (typeof this.lexer.yylloc == "undefined") this.lexer.yylloc = {}; var yyloc = this.lexer.yylloc; lstack.push(yyloc); var ranges = this.lexer.options && this.lexer.options.ranges; if (typeof this.yy.parseError === "function") this.parseError = this.yy.parseError; function popStack(n) { stack.length = stack.length - 2 * n; vstack.length = vstack.length - n; lstack.length = lstack.length - n; } function lex() { var token; token = self.lexer.lex() || 1; if (typeof token !== "number") { token = self.symbols_[token] || token; } return token; } var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected; while (true) { state = stack[stack.length - 1]; if (this.defaultActions[state]) { action = this.defaultActions[state]; } else { if (symbol === null || typeof symbol == "undefined") { symbol = lex(); } action = table[state] && table[state][symbol]; } if (typeof action === "undefined" || !action.length || !action[0]) { var errStr = ""; if (!recovering) { expected = []; for (p in table[state]) if (this.terminals_[p] && p > 2) { expected.push("'" + this.terminals_[p] + "'"); } if (this.lexer.showPosition) { errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'"; } else { errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'"); } this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); } } if (action[0] instanceof Array && action.length > 1) { throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol); } switch (action[0]) { case 1: stack.push(symbol); vstack.push(this.lexer.yytext); lstack.push(this.lexer.yylloc); stack.push(action[1]); symbol = null; if (!preErrorSymbol) { yyleng = this.lexer.yyleng; yytext = this.lexer.yytext; yylineno = this.lexer.yylineno; yyloc = this.lexer.yylloc; if (recovering > 0) recovering--; } else { symbol = preErrorSymbol; preErrorSymbol = null; } break; case 2: len = this.productions_[action[1]][1]; yyval.$ = vstack[vstack.length - len]; yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column}; if (ranges) { yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]]; } r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); if (typeof r !== "undefined") { return r; } if (len) { stack = stack.slice(0, -1 * len * 2); vstack = vstack.slice(0, -1 * len); lstack = lstack.slice(0, -1 * len); } stack.push(this.productions_[action[1]][0]); vstack.push(yyval.$); lstack.push(yyval._$); newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; stack.push(newState); break; case 3: return true; } } return true; } }; /* Jison generated lexer */ var lexer = (function(){ var lexer = ({EOF:1, parseError:function parseError(str, hash) { if (this.yy.parser) { this.yy.parser.parseError(str, hash); } else { throw new Error(str); } }, setInput:function (input) { this._input = input; this._more = this._less = this.done = false; this.yylineno = this.yyleng = 0; this.yytext = this.matched = this.match = ''; this.conditionStack = ['INITIAL']; this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; if (this.options.ranges) this.yylloc.range = [0,0]; this.offset = 0; return this; }, input:function () { var ch = this._input[0]; this.yytext += ch; this.yyleng++; this.offset++; this.match += ch; this.matched += ch; var lines = ch.match(/(?:\r\n?|\n).*/g); if (lines) { this.yylineno++; this.yylloc.last_line++; } else { this.yylloc.last_column++; } if (this.options.ranges) this.yylloc.range[1]++; this._input = this._input.slice(1); return ch; }, unput:function (ch) { var len = ch.length; var lines = ch.split(/(?:\r\n?|\n)/g); this._input = ch + this._input; this.yytext = this.yytext.substr(0, this.yytext.length-len-1); //this.yyleng -= len; this.offset -= len; var oldLines = this.match.split(/(?:\r\n?|\n)/g); this.match = this.match.substr(0, this.match.length-1); this.matched = this.matched.substr(0, this.matched.length-1); if (lines.length-1) this.yylineno -= lines.length-1; var r = this.yylloc.range; this.yylloc = {first_line: this.yylloc.first_line, last_line: this.yylineno+1, first_column: this.yylloc.first_column, last_column: lines ? (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length: this.yylloc.first_column - len }; if (this.options.ranges) { this.yylloc.range = [r[0], r[0] + this.yyleng - len]; } return this; }, more:function () { this._more = true; return this; }, less:function (n) { this.unput(this.match.slice(n)); }, pastInput:function () { var past = this.matched.substr(0, this.matched.length - this.match.length); return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); }, upcomingInput:function () { var next = this.match; if (next.length < 20) { next += this._input.substr(0, 20-next.length); } return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); }, showPosition:function () { var pre = this.pastInput(); var c = new Array(pre.length + 1).join("-"); return pre + this.upcomingInput() + "\n" + c+"^"; }, next:function () { if (this.done) { return this.EOF; } if (!this._input) this.done = true; var token, match, tempMatch, index, col, lines; if (!this._more) { this.yytext = ''; this.match = ''; } var rules = this._currentRules(); for (var i=0;i < rules.length; i++) { tempMatch = this._input.match(this.rules[rules[i]]); if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { match = tempMatch; index = i; if (!this.options.flex) break; } } if (match) { lines = match[0].match(/(?:\r\n?|\n).*/g); if (lines) this.yylineno += lines.length; this.yylloc = {first_line: this.yylloc.last_line, last_line: this.yylineno+1, first_column: this.yylloc.last_column, last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length}; this.yytext += match[0]; this.match += match[0]; this.matches = match; this.yyleng = this.yytext.length; if (this.options.ranges) { this.yylloc.range = [this.offset, this.offset += this.yyleng]; } this._more = false; this._input = this._input.slice(match[0].length); this.matched += match[0]; token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]); if (this.done && this._input) this.done = false; if (token) return token; else return; } if (this._input === "") { return this.EOF; } else { return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), {text: "", token: null, line: this.yylineno}); } }, lex:function lex() { var r = this.next(); if (typeof r !== 'undefined') { return r; } else { return this.lex(); } }, begin:function begin(condition) { this.conditionStack.push(condition); }, popState:function popState() { return this.conditionStack.pop(); }, _currentRules:function _currentRules() { return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; }, topState:function () { return this.conditionStack[this.conditionStack.length-2]; }, pushState:function begin(condition) { this.begin(condition); }}); lexer.options = {}; lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { function strip(start, end) { return yy_.yytext = yy_.yytext.substr(start, yy_.yyleng-end); } var YYSTATE=YY_START switch($avoiding_name_collisions) { case 0: if(yy_.yytext.slice(-2) === "\\\\") { strip(0,1); this.begin("mu"); } else if(yy_.yytext.slice(-1) === "\\") { strip(0,1); this.begin("emu"); } else { this.begin("mu"); } if(yy_.yytext) return 12; break; case 1:return 12; break; case 2: this.popState(); return 12; break; case 3: yy_.yytext = yy_.yytext.substr(5, yy_.yyleng-9); this.popState(); return 15; break; case 4: return 12; break; case 5:strip(0,4); this.popState(); return 13; break; case 6:return 45; break; case 7:return 46; break; case 8: return 16; break; case 9: this.popState(); this.begin('raw'); return 18; break; case 10:return 34; break; case 11:return 24; break; case 12:return 29; break; case 13:this.popState(); return 28; break; case 14:this.popState(); return 28; break; case 15:return 26; break; case 16:return 26; break; case 17:return 32; break; case 18:return 31; break; case 19:this.popState(); this.begin('com'); break; case 20:strip(3,5); this.popState(); return 13; break; case 21:return 31; break; case 22:return 51; break; case 23:return 50; break; case 24:return 50; break; case 25:return 54; break; case 26:// ignore whitespace break; case 27:this.popState(); return 33; break; case 28:this.popState(); return 25; break; case 29:yy_.yytext = strip(1,2).replace(/\\"/g,'"'); return 42; break; case 30:yy_.yytext = strip(1,2).replace(/\\'/g,"'"); return 42; break; case 31:return 52; break; case 32:return 44; break; case 33:return 44; break; case 34:return 43; break; case 35:return 50; break; case 36:yy_.yytext = strip(1,2); return 50; break; case 37:return 'INVALID'; break; case 38:return 5; break; } }; lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/,/^(?:[^\x00]*?(?=(\{\{\{\{\/)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{\{\{)/,/^(?:\}\}\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^\s*(~)?\}\})/,/^(?:\{\{(~)?\s*else\s*(~)?\}\})/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/]; lexer.conditions = {"mu":{"rules":[6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[5],"inclusive":false},"raw":{"rules":[3,4],"inclusive":false},"INITIAL":{"rules":[0,1,38],"inclusive":true}}; return lexer;})() parser.lexer = lexer; function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser; return new Parser; })();__exports__ = handlebars; /* jshint ignore:end */ return __exports__; })(); // handlebars/compiler/helpers.js var __module10__ = (function(__dependency1__) { "use strict"; var __exports__ = {}; var Exception = __dependency1__; function stripFlags(open, close) { return { left: open.charAt(2) === '~', right: close.charAt(close.length-3) === '~' }; } __exports__.stripFlags = stripFlags; function prepareBlock(mustache, program, inverseAndProgram, close, inverted, locInfo) { /*jshint -W040 */ if (mustache.sexpr.id.original !== close.path.original) { throw new Exception(mustache.sexpr.id.original + ' doesn\'t match ' + close.path.original, mustache); } var inverse = inverseAndProgram && inverseAndProgram.program; var strip = { left: mustache.strip.left, right: close.strip.right, // Determine the standalone candiacy. Basically flag our content as being possibly standalone // so our parent can determine if we actually are standalone openStandalone: isNextWhitespace(program.statements), closeStandalone: isPrevWhitespace((inverse || program).statements) }; if (mustache.strip.right) { omitRight(program.statements, null, true); } if (inverse) { var inverseStrip = inverseAndProgram.strip; if (inverseStrip.left) { omitLeft(program.statements, null, true); } if (inverseStrip.right) { omitRight(inverse.statements, null, true); } if (close.strip.left) { omitLeft(inverse.statements, null, true); } // Find standalone else statments if (isPrevWhitespace(program.statements) && isNextWhitespace(inverse.statements)) { omitLeft(program.statements); omitRight(inverse.statements); } } else { if (close.strip.left) { omitLeft(program.statements, null, true); } } if (inverted) { return new this.BlockNode(mustache, inverse, program, strip, locInfo); } else { return new this.BlockNode(mustache, program, inverse, strip, locInfo); } } __exports__.prepareBlock = prepareBlock; function prepareProgram(statements, isRoot) { for (var i = 0, l = statements.length; i < l; i++) { var current = statements[i], strip = current.strip; if (!strip) { continue; } var _isPrevWhitespace = isPrevWhitespace(statements, i, isRoot, current.type === 'partial'), _isNextWhitespace = isNextWhitespace(statements, i, isRoot), openStandalone = strip.openStandalone && _isPrevWhitespace, closeStandalone = strip.closeStandalone && _isNextWhitespace, inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace; if (strip.right) { omitRight(statements, i, true); } if (strip.left) { omitLeft(statements, i, true); } if (inlineStandalone) { omitRight(statements, i); if (omitLeft(statements, i)) { // If we are on a standalone node, save the indent info for partials if (current.type === 'partial') { current.indent = (/([ \t]+$)/).exec(statements[i-1].original) ? RegExp.$1 : ''; } } } if (openStandalone) { omitRight((current.program || current.inverse).statements); // Strip out the previous content node if it's whitespace only omitLeft(statements, i); } if (closeStandalone) { // Always strip the next node omitRight(statements, i); omitLeft((current.inverse || current.program).statements); } } return statements; } __exports__.prepareProgram = prepareProgram;function isPrevWhitespace(statements, i, isRoot) { if (i === undefined) { i = statements.length; } // Nodes that end with newlines are considered whitespace (but are special // cased for strip operations) var prev = statements[i-1], sibling = statements[i-2]; if (!prev) { return isRoot; } if (prev.type === 'content') { return (sibling || !isRoot ? (/\r?\n\s*?$/) : (/(^|\r?\n)\s*?$/)).test(prev.original); } } function isNextWhitespace(statements, i, isRoot) { if (i === undefined) { i = -1; } var next = statements[i+1], sibling = statements[i+2]; if (!next) { return isRoot; } if (next.type === 'content') { return (sibling || !isRoot ? (/^\s*?\r?\n/) : (/^\s*?(\r?\n|$)/)).test(next.original); } } // Marks the node to the right of the position as omitted. // I.e. {{foo}}' ' will mark the ' ' node as omitted. // // If i is undefined, then the first child will be marked as such. // // If mulitple is truthy then all whitespace will be stripped out until non-whitespace // content is met. function omitRight(statements, i, multiple) { var current = statements[i == null ? 0 : i + 1]; if (!current || current.type !== 'content' || (!multiple && current.rightStripped)) { return; } var original = current.string; current.string = current.string.replace(multiple ? (/^\s+/) : (/^[ \t]*\r?\n?/), ''); current.rightStripped = current.string !== original; } // Marks the node to the left of the position as omitted. // I.e. ' '{{foo}} will mark the ' ' node as omitted. // // If i is undefined then the last child will be marked as such. // // If mulitple is truthy then all whitespace will be stripped out until non-whitespace // content is met. function omitLeft(statements, i, multiple) { var current = statements[i == null ? statements.length - 1 : i - 1]; if (!current || current.type !== 'content' || (!multiple && current.leftStripped)) { return; } // We omit the last node if it's whitespace only and not preceeded by a non-content node. var original = current.string; current.string = current.string.replace(multiple ? (/\s+$/) : (/[ \t]+$/), ''); current.leftStripped = current.string !== original; return current.leftStripped; } return __exports__; })(__module5__); // handlebars/compiler/base.js var __module8__ = (function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var __exports__ = {}; var parser = __dependency1__; var AST = __dependency2__; var Helpers = __dependency3__; var extend = __dependency4__.extend; __exports__.parser = parser; var yy = {}; extend(yy, Helpers, AST); function parse(input) { // Just return if an already-compile AST was passed in. if (input.constructor === AST.ProgramNode) { return input; } parser.yy = yy; return parser.parse(input); } __exports__.parse = parse; return __exports__; })(__module9__, __module7__, __module10__, __module3__); // handlebars/compiler/compiler.js var __module11__ = (function(__dependency1__, __dependency2__) { "use strict"; var __exports__ = {}; var Exception = __dependency1__; var isArray = __dependency2__.isArray; var slice = [].slice; function Compiler() {} __exports__.Compiler = Compiler;// the foundHelper register will disambiguate helper lookup from finding a // function in a context. This is necessary for mustache compatibility, which // requires that context functions in blocks are evaluated by blockHelperMissing, // and then proceed as if the resulting value was provided to blockHelperMissing. Compiler.prototype = { compiler: Compiler, equals: function(other) { var len = this.opcodes.length; if (other.opcodes.length !== len) { return false; } for (var i = 0; i < len; i++) { var opcode = this.opcodes[i], otherOpcode = other.opcodes[i]; if (opcode.opcode !== otherOpcode.opcode || !argEquals(opcode.args, otherOpcode.args)) { return false; } } // We know that length is the same between the two arrays because they are directly tied // to the opcode behavior above. len = this.children.length; for (i = 0; i < len; i++) { if (!this.children[i].equals(other.children[i])) { return false; } } return true; }, guid: 0, compile: function(program, options) { this.opcodes = []; this.children = []; this.depths = {list: []}; this.options = options; this.stringParams = options.stringParams; this.trackIds = options.trackIds; // These changes will propagate to the other compiler components var knownHelpers = this.options.knownHelpers; this.options.knownHelpers = { 'helperMissing': true, 'blockHelperMissing': true, 'each': true, 'if': true, 'unless': true, 'with': true, 'log': true, 'lookup': true }; if (knownHelpers) { for (var name in knownHelpers) { this.options.knownHelpers[name] = knownHelpers[name]; } } return this.accept(program); }, accept: function(node) { return this[node.type](node); }, program: function(program) { var statements = program.statements; for(var i=0, l=statements.length; i 0) { varDeclarations += ", " + locals.join(", "); } // Generate minimizer alias mappings for (var alias in this.aliases) { if (this.aliases.hasOwnProperty(alias)) { varDeclarations += ', ' + alias + '=' + this.aliases[alias]; } } var params = ["depth0", "helpers", "partials", "data"]; if (this.useDepths) { params.push('depths'); } // Perform a second pass over the output to merge content when possible var source = this.mergeSource(varDeclarations); if (asObject) { params.push(source); return Function.apply(this, params); } else { return 'function(' + params.join(',') + ') {\n ' + source + '}'; } }, mergeSource: function(varDeclarations) { var source = '', buffer, appendOnly = !this.forceBuffer, appendFirst; for (var i = 0, len = this.source.length; i < len; i++) { var line = this.source[i]; if (line.appendToBuffer) { if (buffer) { buffer = buffer + '\n + ' + line.content; } else { buffer = line.content; } } else { if (buffer) { if (!source) { appendFirst = true; source = buffer + ';\n '; } else { source += 'buffer += ' + buffer + ';\n '; } buffer = undefined; } source += line + '\n '; if (!this.environment.isSimple) { appendOnly = false; } } } if (appendOnly) { if (buffer || !source) { source += 'return ' + (buffer || '""') + ';\n'; } } else { varDeclarations += ", buffer = " + (appendFirst ? '' : this.initializeBuffer()); if (buffer) { source += 'return buffer + ' + buffer + ';\n'; } else { source += 'return buffer;\n'; } } if (varDeclarations) { source = 'var ' + varDeclarations.substring(2) + (appendFirst ? '' : ';\n ') + source; } return source; }, // [blockValue] // // On stack, before: hash, inverse, program, value // On stack, after: return value of blockHelperMissing // // The purpose of this opcode is to take a block of the form // `{{#this.foo}}...{{/this.foo}}`, resolve the value of `foo`, and // replace it on the stack with the result of properly // invoking blockHelperMissing. blockValue: function(name) { this.aliases.blockHelperMissing = 'helpers.blockHelperMissing'; var params = [this.contextName(0)]; this.setupParams(name, 0, params); var blockName = this.popStack(); params.splice(1, 0, blockName); this.push('blockHelperMissing.call(' + params.join(', ') + ')'); }, // [ambiguousBlockValue] // // On stack, before: hash, inverse, program, value // Compiler value, before: lastHelper=value of last found helper, if any // On stack, after, if no lastHelper: same as [blockValue] // On stack, after, if lastHelper: value ambiguousBlockValue: function() { this.aliases.blockHelperMissing = 'helpers.blockHelperMissing'; // We're being a bit cheeky and reusing the options value from the prior exec var params = [this.contextName(0)]; this.setupParams('', 0, params, true); this.flushInline(); var current = this.topStack(); params.splice(1, 0, current); this.pushSource("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }"); }, // [appendContent] // // On stack, before: ... // On stack, after: ... // // Appends the string value of `content` to the current buffer appendContent: function(content) { if (this.pendingContent) { content = this.pendingContent + content; } this.pendingContent = content; }, // [append] // // On stack, before: value, ... // On stack, after: ... // // Coerces `value` to a String and appends it to the current buffer. // // If `value` is truthy, or 0, it is coerced into a string and appended // Otherwise, the empty string is appended append: function() { // Force anything that is inlined onto the stack so we don't have duplication // when we examine local this.flushInline(); var local = this.popStack(); this.pushSource('if (' + local + ' != null) { ' + this.appendToBuffer(local) + ' }'); if (this.environment.isSimple) { this.pushSource("else { " + this.appendToBuffer("''") + " }"); } }, // [appendEscaped] // // On stack, before: value, ... // On stack, after: ... // // Escape `value` and append it to the buffer appendEscaped: function() { this.aliases.escapeExpression = 'this.escapeExpression'; this.pushSource(this.appendToBuffer("escapeExpression(" + this.popStack() + ")")); }, // [getContext] // // On stack, before: ... // On stack, after: ... // Compiler value, after: lastContext=depth // // Set the value of the `lastContext` compiler value to the depth getContext: function(depth) { this.lastContext = depth; }, // [pushContext] // // On stack, before: ... // On stack, after: currentContext, ... // // Pushes the value of the current context onto the stack. pushContext: function() { this.pushStackLiteral(this.contextName(this.lastContext)); }, // [lookupOnContext] // // On stack, before: ... // On stack, after: currentContext[name], ... // // Looks up the value of `name` on the current context and pushes // it onto the stack. lookupOnContext: function(parts, falsy, scoped) { /*jshint -W083 */ var i = 0, len = parts.length; if (!scoped && this.options.compat && !this.lastContext) { // The depthed query is expected to handle the undefined logic for the root level that // is implemented below, so we evaluate that directly in compat mode this.push(this.depthedLookup(parts[i++])); } else { this.pushContext(); } for (; i < len; i++) { this.replaceStack(function(current) { var lookup = this.nameLookup(current, parts[i], 'context'); // We want to ensure that zero and false are handled properly if the context (falsy flag) // needs to have the special handling for these values. if (!falsy) { return ' != null ? ' + lookup + ' : ' + current; } else { // Otherwise we can use generic falsy handling return ' && ' + lookup; } }); } }, // [lookupData] // // On stack, before: ... // On stack, after: data, ... // // Push the data lookup operator lookupData: function(depth, parts) { /*jshint -W083 */ if (!depth) { this.pushStackLiteral('data'); } else { this.pushStackLiteral('this.data(data, ' + depth + ')'); } var len = parts.length; for (var i = 0; i < len; i++) { this.replaceStack(function(current) { return ' && ' + this.nameLookup(current, parts[i], 'data'); }); } }, // [resolvePossibleLambda] // // On stack, before: value, ... // On stack, after: resolved value, ... // // If the `value` is a lambda, replace it on the stack by // the return value of the lambda resolvePossibleLambda: function() { this.aliases.lambda = 'this.lambda'; this.push('lambda(' + this.popStack() + ', ' + this.contextName(0) + ')'); }, // [pushStringParam] // // On stack, before: ... // On stack, after: string, currentContext, ... // // This opcode is designed for use in string mode, which // provides the string value of a parameter along with its // depth rather than resolving it immediately. pushStringParam: function(string, type) { this.pushContext(); this.pushString(type); // If it's a subexpression, the string result // will be pushed after this opcode. if (type !== 'sexpr') { if (typeof string === 'string') { this.pushString(string); } else { this.pushStackLiteral(string); } } }, emptyHash: function() { this.pushStackLiteral('{}'); if (this.trackIds) { this.push('{}'); // hashIds } if (this.stringParams) { this.push('{}'); // hashContexts this.push('{}'); // hashTypes } }, pushHash: function() { if (this.hash) { this.hashes.push(this.hash); } this.hash = {values: [], types: [], contexts: [], ids: []}; }, popHash: function() { var hash = this.hash; this.hash = this.hashes.pop(); if (this.trackIds) { this.push('{' + hash.ids.join(',') + '}'); } if (this.stringParams) { this.push('{' + hash.contexts.join(',') + '}'); this.push('{' + hash.types.join(',') + '}'); } this.push('{\n ' + hash.values.join(',\n ') + '\n }'); }, // [pushString] // // On stack, before: ... // On stack, after: quotedString(string), ... // // Push a quoted version of `string` onto the stack pushString: function(string) { this.pushStackLiteral(this.quotedString(string)); }, // [push] // // On stack, before: ... // On stack, after: expr, ... // // Push an expression onto the stack push: function(expr) { this.inlineStack.push(expr); return expr; }, // [pushLiteral] // // On stack, before: ... // On stack, after: value, ... // // Pushes a value onto the stack. This operation prevents // the compiler from creating a temporary variable to hold // it. pushLiteral: function(value) { this.pushStackLiteral(value); }, // [pushProgram] // // On stack, before: ... // On stack, after: program(guid), ... // // Push a program expression onto the stack. This takes // a compile-time guid and converts it into a runtime-accessible // expression. pushProgram: function(guid) { if (guid != null) { this.pushStackLiteral(this.programExpression(guid)); } else { this.pushStackLiteral(null); } }, // [invokeHelper] // // On stack, before: hash, inverse, program, params..., ... // On stack, after: result of helper invocation // // Pops off the helper's parameters, invokes the helper, // and pushes the helper's return value onto the stack. // // If the helper is not found, `helperMissing` is called. invokeHelper: function(paramSize, name, isSimple) { this.aliases.helperMissing = 'helpers.helperMissing'; var nonHelper = this.popStack(); var helper = this.setupHelper(paramSize, name); var lookup = (isSimple ? helper.name + ' || ' : '') + nonHelper + ' || helperMissing'; this.push('((' + lookup + ').call(' + helper.callParams + '))'); }, // [invokeKnownHelper] // // On stack, before: hash, inverse, program, params..., ... // On stack, after: result of helper invocation // // This operation is used when the helper is known to exist, // so a `helperMissing` fallback is not required. invokeKnownHelper: function(paramSize, name) { var helper = this.setupHelper(paramSize, name); this.push(helper.name + ".call(" + helper.callParams + ")"); }, // [invokeAmbiguous] // // On stack, before: hash, inverse, program, params..., ... // On stack, after: result of disambiguation // // This operation is used when an expression like `{{foo}}` // is provided, but we don't know at compile-time whether it // is a helper or a path. // // This operation emits more code than the other options, // and can be avoided by passing the `knownHelpers` and // `knownHelpersOnly` flags at compile-time. invokeAmbiguous: function(name, helperCall) { this.aliases.functionType = '"function"'; this.aliases.helperMissing = 'helpers.helperMissing'; this.useRegister('helper'); var nonHelper = this.popStack(); this.emptyHash(); var helper = this.setupHelper(0, name, helperCall); var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper'); this.push( '((helper = (helper = ' + helperName + ' || ' + nonHelper + ') != null ? helper : helperMissing' + (helper.paramsInit ? '),(' + helper.paramsInit : '') + '),' + '(typeof helper === functionType ? helper.call(' + helper.callParams + ') : helper))'); }, // [invokePartial] // // On stack, before: context, ... // On stack after: result of partial invocation // // This operation pops off a context, invokes a partial with that context, // and pushes the result of the invocation back. invokePartial: function(name, indent) { var params = [this.nameLookup('partials', name, 'partial'), "'" + indent + "'", "'" + name + "'", this.popStack(), this.popStack(), "helpers", "partials"]; if (this.options.data) { params.push("data"); } else if (this.options.compat) { params.push('undefined'); } if (this.options.compat) { params.push('depths'); } this.push("this.invokePartial(" + params.join(", ") + ")"); }, // [assignToHash] // // On stack, before: value, ..., hash, ... // On stack, after: ..., hash, ... // // Pops a value off the stack and assigns it to the current hash assignToHash: function(key) { var value = this.popStack(), context, type, id; if (this.trackIds) { id = this.popStack(); } if (this.stringParams) { type = this.popStack(); context = this.popStack(); } var hash = this.hash; if (context) { hash.contexts.push("'" + key + "': " + context); } if (type) { hash.types.push("'" + key + "': " + type); } if (id) { hash.ids.push("'" + key + "': " + id); } hash.values.push("'" + key + "': (" + value + ")"); }, pushId: function(type, name) { if (type === 'ID' || type === 'DATA') { this.pushString(name); } else if (type === 'sexpr') { this.pushStackLiteral('true'); } else { this.pushStackLiteral('null'); } }, // HELPERS compiler: JavaScriptCompiler, compileChildren: function(environment, options) { var children = environment.children, child, compiler; for(var i=0, l=children.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); } return this.topStackName(); }, topStackName: function() { return "stack" + this.stackSlot; }, flushInline: function() { var inlineStack = this.inlineStack; if (inlineStack.length) { this.inlineStack = []; for (var i = 0, len = inlineStack.length; i < len; i++) { var entry = inlineStack[i]; if (entry instanceof Literal) { this.compileStack.push(entry); } else { this.pushStack(entry); } } } }, isInline: function() { return this.inlineStack.length; }, popStack: function(wrapped) { var inline = this.isInline(), item = (inline ? this.inlineStack : this.compileStack).pop(); if (!wrapped && (item instanceof Literal)) { return item.value; } else { if (!inline) { /* istanbul ignore next */ if (!this.stackSlot) { throw new Exception('Invalid stack pop'); } this.stackSlot--; } return item; } }, topStack: function() { var stack = (this.isInline() ? this.inlineStack : this.compileStack), item = stack[stack.length - 1]; if (item instanceof Literal) { return item.value; } else { return item; } }, contextName: function(context) { if (this.useDepths && context) { return 'depths[' + context + ']'; } else { return 'depth' + context; } }, quotedString: function(str) { return '"' + str .replace(/\\/g, '\\\\') .replace(/"/g, '\\"') .replace(/\n/g, '\\n') .replace(/\r/g, '\\r') .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4 .replace(/\u2029/g, '\\u2029') + '"'; }, objectLiteral: function(obj) { var pairs = []; for (var key in obj) { if (obj.hasOwnProperty(key)) { pairs.push(this.quotedString(key) + ':' + obj[key]); } } return '{' + pairs.join(',') + '}'; }, setupHelper: function(paramSize, name, blockHelper) { var params = [], paramsInit = this.setupParams(name, paramSize, params, blockHelper); var foundHelper = this.nameLookup('helpers', name, 'helper'); return { params: params, paramsInit: paramsInit, name: foundHelper, callParams: [this.contextName(0)].concat(params).join(", ") }; }, setupOptions: function(helper, paramSize, params) { var options = {}, contexts = [], types = [], ids = [], param, inverse, program; options.name = this.quotedString(helper); options.hash = this.popStack(); if (this.trackIds) { options.hashIds = this.popStack(); } if (this.stringParams) { options.hashTypes = this.popStack(); options.hashContexts = this.popStack(); } inverse = this.popStack(); program = this.popStack(); // Avoid setting fn and inverse if neither are set. This allows // helpers to do a check for `if (options.fn)` if (program || inverse) { if (!program) { program = 'this.noop'; } if (!inverse) { inverse = 'this.noop'; } options.fn = program; options.inverse = inverse; } // The parameters go on to the stack in order (making sure that they are evaluated in order) // so we need to pop them off the stack in reverse order var i = paramSize; while (i--) { param = this.popStack(); params[i] = param; if (this.trackIds) { ids[i] = this.popStack(); } if (this.stringParams) { types[i] = this.popStack(); contexts[i] = this.popStack(); } } if (this.trackIds) { options.ids = "[" + ids.join(",") + "]"; } if (this.stringParams) { options.types = "[" + types.join(",") + "]"; options.contexts = "[" + contexts.join(",") + "]"; } if (this.options.data) { options.data = "data"; } return options; }, // the params and contexts arguments are passed in arrays // to fill in setupParams: function(helperName, paramSize, params, useRegister) { var options = this.objectLiteral(this.setupOptions(helperName, paramSize, params)); if (useRegister) { this.useRegister('options'); params.push('options'); return 'options=' + options; } else { params.push(options); return ''; } } }; var reservedWords = ( "break else new var" + " case finally return void" + " catch for switch while" + " continue function this with" + " default if throw" + " delete in try" + " do instanceof typeof" + " abstract enum int short" + " boolean export interface static" + " byte extends long super" + " char final native synchronized" + " class float package throws" + " const goto private transient" + " debugger implements protected volatile" + " double import public let yield" ).split(" "); var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {}; for(var i=0, l=reservedWords.length; i': return (v1 > v2) ? options.fn(this) : options.inverse(this); case '>=': return (v1 >= v2) ? options.fn(this) : options.inverse(this); case '&&': return (v1 && v2) ? options.fn(this) : options.inverse(this); case '||': return (v1 || v2) ? options.fn(this) : options.inverse(this); default: return options.inverse(this); } }); // http://stackoverflow.com/questions/17095813/handlebars-if-and-numeric-zeroes Handlebars.registerHelper('exists', function(variable, options) { if (typeof variable !== 'undefined') { return options.fn(this); } else { return options.inverse(this); } }); var filesElement = document.getElementById("files-template") if (filesElement) { var source = filesElement.innerHTML; var template = Handlebars.compile(source); document.getElementById('files-placeholder').innerHTML = template(data); } var linesElement = document.getElementById("lines-template") if (linesElement) { var source = linesElement.innerHTML; var template = Handlebars.compile(source); document.getElementById('lines-placeholder').innerHTML = template(data); } elem = document.getElementById('header-percent-covered') elem.className = toCoverPercentString(header.covered, header.instrumented); elem.innerHTML = ((header.covered / header.instrumented) * 100).toFixed(1) + "%"; document.getElementById('header-command').innerHTML = header.command; document.getElementById('window-title').innerHTML = "Coverage report - " + header.command; document.getElementById('header-date').innerHTML = header.date; document.getElementById('header-covered').innerHTML = header.covered document.getElementById('header-instrumented').innerHTML = header.instrumented $("#index-table").tablesorter({ theme : 'blue', sortList : [[1,0]], cssInfoBlock : "tablesorter-no-sort", widgets: ["saveSort"], }); } function toCoverPercentString (covered, instrumented) { perc = (covered / instrumented) * 100; if (perc <= percent_low) return "coverPerLeftLo"; else if (perc >= percent_high) return "coverPerLeftHi"; return "coverPerLeftMed"; } kcov_25+dfsg.orig/data/source-file.html000066400000000000000000000056621247015272500202360ustar00rootroot00000000000000 ???
Coverage Report
Command: ???
Date: Instrumented lines: ???
Code covered: ??? Executed lines: ???
Generated by: Kcov
kcov_25+dfsg.orig/data/tablesorter-theme.css000066400000000000000000000155251247015272500212720ustar00rootroot00000000000000/************* Blue Theme *************/ /* overall */ .tablesorter-blue { width: 100%; background-color: #fff; margin: 10px 0 15px; text-align: left; border-spacing: 0; border: #cdcdcd 1px solid; border-width: 1px 0 0 1px; } .tablesorter-blue th, .tablesorter-blue td { border: #cdcdcd 1px solid; border-width: 0 1px 1px 0; } /* header */ .tablesorter-blue th, .tablesorter-blue thead td { font: bold 12px/18px Arial, Sans-serif; color: #000; background-color: #99bfe6; border-collapse: collapse; padding: 4px; text-shadow: 0 1px 0 rgba(204, 204, 204, 0.7); } .tablesorter-blue tbody td, .tablesorter-blue tfoot th, .tablesorter-blue tfoot td { padding: 4px; vertical-align: top; } .tablesorter-blue .header, .tablesorter-blue .tablesorter-header { /* black (unsorted) double arrow */ background-image: url(data:image/gif;base64,R0lGODlhFQAJAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAkAAAIXjI+AywnaYnhUMoqt3gZXPmVg94yJVQAAOw==); /* white (unsorted) double arrow */ /* background-image: url(data:image/gif;base64,R0lGODlhFQAJAIAAAP///////yH5BAEAAAEALAAAAAAVAAkAAAIXjI+AywnaYnhUMoqt3gZXPmVg94yJVQAAOw==); */ /* image */ /* background-image: url(images/black-unsorted.gif); */ background-repeat: no-repeat; background-position: center right; padding: 4px 18px 4px 4px; white-space: normal; cursor: pointer; } .tablesorter-blue .headerSortUp, .tablesorter-blue .tablesorter-headerSortUp, .tablesorter-blue .tablesorter-headerAsc { background-color: #9fbfdf; /* black asc arrow */ background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7); /* white asc arrow */ /* background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7); */ /* image */ /* background-image: url(images/black-asc.gif); */ } .tablesorter-blue .headerSortDown, .tablesorter-blue .tablesorter-headerSortDown, .tablesorter-blue .tablesorter-headerDesc { background-color: #8cb3d9; /* black desc arrow */ background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAQAAAINjB+gC+jP2ptn0WskLQA7); /* white desc arrow */ /* background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjB+gC+jP2ptn0WskLQA7); */ /* image */ /* background-image: url(images/black-desc.gif); */ } .tablesorter-blue thead .sorter-false { background-image: none; cursor: default; padding: 4px; } /* tfoot */ .tablesorter-blue tfoot .tablesorter-headerSortUp, .tablesorter-blue tfoot .tablesorter-headerSortDown, .tablesorter-blue tfoot .tablesorter-headerAsc, .tablesorter-blue tfoot .tablesorter-headerDesc { /* remove sort arrows from footer */ background-image: none; } /* tbody */ .tablesorter-blue td { color: #3d3d3d; background-color: #fff; padding: 4px; vertical-align: top; } /* hovered row colors you'll need to add additional lines for rows with more than 2 child rows */ .tablesorter-blue tbody > tr:hover > td, .tablesorter-blue tbody > tr:hover + tr.tablesorter-childRow > td, .tablesorter-blue tbody > tr:hover + tr.tablesorter-childRow + tr.tablesorter-childRow > td, .tablesorter-blue tbody > tr.even:hover > td, .tablesorter-blue tbody > tr.even:hover + tr.tablesorter-childRow > td, .tablesorter-blue tbody > tr.even:hover + tr.tablesorter-childRow + tr.tablesorter-childRow > td { background: #d9d9d9; } .tablesorter-blue tbody > tr.odd:hover > td, .tablesorter-blue tbody > tr.odd:hover + tr.tablesorter-childRow > td, .tablesorter-blue tbody > tr.odd:hover + tr.tablesorter-childRow + tr.tablesorter-childRow > td { background: #bfbfbf; } /* table processing indicator */ .tablesorter-blue .tablesorter-processing { background-position: center center !important; background-repeat: no-repeat !important; /* background-image: url(../addons/pager/icons/loading.gif) !important; */ background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=') !important; } /* Zebra Widget - row alternating colors */ .tablesorter-blue tbody tr.odd td { background-color: #ebf2fa; } .tablesorter-blue tbody tr.even td { background-color: #fff; } /* Column Widget - column sort colors */ .tablesorter-blue td.primary, .tablesorter-blue tr.odd td.primary { background-color: #99b3e6; } .tablesorter-blue tr.even td.primary { background-color: #c2d1f0; } .tablesorter-blue td.secondary, .tablesorter-blue tr.odd td.secondary { background-color: #c2d1f0; } .tablesorter-blue tr.even td.secondary { background-color: #d6e0f5; } .tablesorter-blue td.tertiary, .tablesorter-blue tr.odd td.tertiary { background-color: #d6e0f5; } .tablesorter-blue tr.even td.tertiary { background-color: #ebf0fa; } /* caption */ caption { background: #fff; } /* filter widget */ .tablesorter-blue .tablesorter-filter-row td { background: #eee; line-height: normal; text-align: center; /* center the input */ -webkit-transition: line-height 0.1s ease; -moz-transition: line-height 0.1s ease; -o-transition: line-height 0.1s ease; transition: line-height 0.1s ease; } /* optional disabled input styling */ .tablesorter-blue .tablesorter-filter-row .disabled { opacity: 0.5; filter: alpha(opacity=50); cursor: not-allowed; } /* hidden filter row */ .tablesorter-blue .tablesorter-filter-row.hideme td { /*** *********************************************** ***/ /*** change this padding to modify the thickness ***/ /*** of the closed filter row (height = padding x 2) ***/ padding: 2px; /*** *********************************************** ***/ margin: 0; line-height: 0; cursor: pointer; } .tablesorter-blue .tablesorter-filter-row.hideme .tablesorter-filter { height: 1px; min-height: 0; border: 0; padding: 0; margin: 0; /* don't use visibility: hidden because it disables tabbing */ opacity: 0; filter: alpha(opacity=0); } /* filters */ .tablesorter-blue .tablesorter-filter { width: 98%; height: auto; margin: 0; padding: 4px; background-color: #fff; border: 1px solid #bbb; color: #333; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; -webkit-transition: height 0.1s ease; -moz-transition: height 0.1s ease; -o-transition: height 0.1s ease; transition: height 0.1s ease; } /* rows hidden by filtering (needed for child rows) */ .tablesorter .filtered { display: none; } /* ajax error row */ .tablesorter .tablesorter-errorRow td { text-align: center; cursor: pointer; background-color: #e6bf99; } kcov_25+dfsg.orig/doc/000077500000000000000000000000001247015272500147565ustar00rootroot00000000000000kcov_25+dfsg.orig/doc/CMakeLists.txt000066400000000000000000000001261247015272500175150ustar00rootroot00000000000000file (GLOB man_files_1 *.1) install (FILES ${man_files_1} ${INSTALL_MAN_PATH}/man1) kcov_25+dfsg.orig/doc/design.txt000066400000000000000000000022131247015272500167660ustar00rootroot00000000000000Classes ======= The kcov design is based on a number of classes: FileParser ---------- Reads the binary to parse debugging information, i.e., file/line -> address mappings, and reports these to listeners. For ELF binaries, this is done on startup, Python and Bash report during runtime. Collector --------- Controls execution, setup breakpoints, catch signals etc, and report breakpoint hits. Engine ------ Low-level control of execution (i.e., setup breakpoints, start/stop execution). Reporter -------- Reports breakpoint hits and can be queried for address -> file/line mappings. Also reads back accumulated data from previous runs. OutputHandler ------------- Receives files from the parser and allows writers to register. Will call into writers at regular intervals. Writer ------ Queries the reporter to produce HTML and cobertura (currently) output with coverage data. Python/bash =========== The above classes are mainly modelled for dwarf/ptrace-based applications. When covering python/bash code, the FileParser and Engine interfaces are implemented by the same class since coverage data is collected during runtime. Merge parser ============ kcov_25+dfsg.orig/doc/kcov-merge.1000066400000000000000000000040461247015272500171030ustar00rootroot00000000000000.\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH KCOV-MERGE 1 "November 24, 2011" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME kcov-merge \- Merge code coverage data from multiple kcov output directories .SH SYNOPSIS .B kcov-merge .RI [ options ] " outdir dir1 [dir2...] .SH DESCRIPTION .PP This manual page documents briefly the \fBkcov-merge\fP command. \fBkcov\-merge\fP is a utility to merge the code coverage data gathered from multiple \fBkcov\fP output directories. .SH OPTIONS .TP \fB\-h\fP, \fB\-\-help\fP Display help message and exit .TP \fB\-l\fP, \fB\-\-limits\fP=\fIlow,high\fP Setup limits for low/high coverage (default: 25,75). .RE .SH EXAMPLES .PP Generate aggregate coverage from ./kcov1 and ./kcov2 directories and generate HTML output in ./kcov-aggregate. .PP .RS kcov-merge ./kcov-aggregate ./kcov1 ./kcov2 .RE .RE .SH HTML OUTPUT .PP The HTML output shows a summary of the executed and non-executed lines aggregated from each of the individual kcov output directories. It also shows coverage data for each file/binary included in any of the individual kcov output directories. .SH COBERTURA OUTPUT .PP \fBKcov-merge\fP also outputs data in the Cobertura XML format, which allows integrating kcov-merge output in Jenkins (see http://cobertura.sf.net and http://jenkins-ci.org). .PP The Cobertura output is placed in a file named out-path/cobertura.xml. .SH AUTHOR .PP Kcov was written by Simon Kagstrom, building upon bcov by Thomas Neumann. .PP Kcov-merge was written by Chris Love. kcov_25+dfsg.orig/doc/kcov.1000066400000000000000000000113231247015272500160020ustar00rootroot00000000000000.\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH KCOV 1 "November 24, 2011" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME kcov \- Code coverage analysis for compiled programs and Python scripts .SH SYNOPSIS .B kcov .RI [ options ] " outdir executable [ args for executable ] .SH DESCRIPTION .PP This manual page documents briefly the \fBkcov\fP command. \fBkcov\fP is a code coverage tester for ELF binaries, Python scripts and shell scripts. It allows collecting code coverage information from executables without special compiler directives, and continuously produces output from long-running applications. .\" TeX users may be more comfortable with the \fB\fP and .\" \fI\fP escape sequences to invoke bold face and italics, .\" respectively. .SH OPTIONS .TP \fB\-p\fP, \fB\-\-pid\fP=\fIPID\fP Trace PID instead of executing executable, executable is optional in this case. .TP \fB\-l\fP, \fB\-\-limits\fP=\fIlow,high\fP Setup limits for low/high coverage (default: 16,50). .TP \fB\-\-include\-path\fP=\fIP1\fP[\fI,P2\fP...] Comma-separated list of paths to include in the report. .TP \fB\-\-exclude\-path\fP=\fIP1\fP[\fI,P2\fP...] Comma-separated list of paths to exclude from the report. .TP \fB\-\-include\-pattern\fP=\fIP1\fP[\fI,P2\fP...] Comma-separated list of path patterns to include in the report. .TP \fB\-\-exclude\-pattern\fP=\fIP1\fP[\fI,P2\fP...] Comma-separated list of path patterns to exclude from the report. .TP \fB\-\-collect\-only Only collect coverage data, don't produce HTML/Cobertura output .TP \fB\-\-report\-only Only report HTML/Cobertura output, don't collect data .TP \fB\-\-coveralls\-id\fP=\fIid\fP Upload data to coveralls.io using secret repo_token or Travis CI service job ID \fIid\fP. The ID is taken as a repo_token if it's longer or equal to 32 characters. .SH UNCOMMON OPTIONS .TP \fB\-\-path\-strip\-level\fP=\fIN\fP Number of path levels to show for common paths (default: 2). .TP \fB\-\-skip\-solibs Skip coverage collection for shared libraries (improves performance) .TP \fB\-\-exit\-first\-process exit when the first process exits, i.e., honor the behavior of daemons. The default behavior is to return to the console when the last process exits. .TP \fB\-\-python\-parser\fP=\fIPARSER\fP Set the python parser to use for Python programs (the default is python). Can be used to run with Python 3 on systems where Python 2 is the default. .TP \fB\-\-bash\-parser\fP=\fIPARSER\fP Set the bash parser to use for shell scripts (the default is /bin/bash). .TP \fB\-\-replace\-src\-path\fP=\fIP1\fP:\fIP2\fP Replace source file path P1 with P2, if found. .RE .SH EXAMPLES .PP Check coverage for ./frodo and generate HTML output in /tmp/kcov and cobertura output in /tmp/kcov/frodo/cobertura.xml .PP .RS kcov /tmp/kcov ./frodo .RE .PP Check coverage for ./frodo but only include source files names with the string src/frodo .PP .RS kcov \-\-include\-pattern=src/frodo /tmp/kcov ./frodo .RE .PP Same as above but split collecting and reporting (perhaps on two different computers) .PP .RS kcov --collect-only /tmp/kcov ./frodo .PP kcov --report-only \-\-include\-pattern=src/frodo /tmp/kcov ./frodo .RE .SH HTML OUTPUT .PP The HTML output shows executed and non-executed lines of the source code. Some lines can map to multiple instrumentation points, for example for inlined functions (where every inlining of them will generate a separate instrumentation point). This is shown in the left column as 1/3 for example, which means that one of the three instrumentation points has been executed. .PP A special output link is [merged], which shows the union of all covered programs. This can be useful for example when you have unit tests in multiple binaries which share a subset of source files. .SH COBERTURA OUTPUT .PP Kcov also outputs data in the Cobertura XML format, which allows integrating kcov output in Jenkins (see http://cobertura.sf.net and http://jenkins-ci.org). .PP The Cobertura output is placed in a file named out-path/exec-filename/cobertura.xml. .SH AUTHOR .PP Kcov was written by Simon Kagstrom, building upon bcov by Thomas Neumann. .PP This manual page was written by Michael Tautschnig , for the Debian project (but may be used by others). kcov_25+dfsg.orig/src/000077500000000000000000000000001247015272500150005ustar00rootroot00000000000000kcov_25+dfsg.orig/src/CMakeLists.txt000066400000000000000000000114671247015272500175510ustar00rootroot00000000000000cmake_minimum_required (VERSION 2.6) project (kcov) set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../../cmake) find_package (LibElf REQUIRED) find_package (Elfutils REQUIRED) pkg_check_modules(LIBZ REQUIRED zlib) pkg_check_modules(LIBCURL REQUIRED libcurl) # ==================================== # project name and version # ==================================== project (kcov) set (KCOV kcov) #set (CMAKE_EXE_LINKER_FLAGS "-static") set (SOLIB kcov_sowrapper) set (${SOLIB}_SRCS solib-parser/phdr_data.c solib-parser/lib.c ) set (${KCOV}_SRCS capabilities.cc collector.cc configuration.cc engine-factory.cc engines/bash-engine.cc engines/gcov-engine.cc engines/kernel-engine.cc engines/ptrace.cc engines/python-engine.cc filter.cc gcov.cc main.cc merge-file-parser.cc output-handler.cc parsers/elf-parser.cc parser-manager.cc reporter.cc solib-handler.cc solib-parser/phdr_data.c utils.cc writers/cobertura-writer.cc writers/coveralls-writer.cc writers/html-writer.cc writers/writer-base.cc ) set (KCOV_LIBRARY_PREFIX "/tmp") set (CMAKE_CXX_FLAGS "-std=c++0x -g -Wall -D_GLIBCXX_USE_NANOSLEEP -DKCOV_LIBRARY_PREFIX=${KCOV_LIBRARY_PREFIX}") include_directories( include/ ${LIBELF_INCLUDE_DIRS} ${LIBDW_INCLUDE_DIRS} ${LIBZ_INCLUDE_DIRS} ${LIBCURL_INCLUDE_DIRS} ) link_directories (/home/ska/local/lib) add_library (${SOLIB} SHARED ${${SOLIB}_SRCS}) target_link_libraries(${SOLIB} dl) add_custom_command( OUTPUT library.cc COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/bin-to-c-source.py lib${SOLIB}.so __library > library.cc DEPENDS ${SOLIB} ${CMAKE_CURRENT_SOURCE_DIR}/bin-to-c-source.py ) add_custom_command( OUTPUT python-helper.cc COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/bin-to-c-source.py ${CMAKE_CURRENT_SOURCE_DIR}/engines/python-helper.py python_helper > python-helper.cc DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/engines/python-helper.py ${CMAKE_CURRENT_SOURCE_DIR}/bin-to-c-source.py ) add_custom_command( OUTPUT bash-helper.cc COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/bin-to-c-source.py ${CMAKE_CURRENT_SOURCE_DIR}/engines/bash-helper.sh bash_helper > bash-helper.cc DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/engines/bash-helper.sh ${CMAKE_CURRENT_SOURCE_DIR}/bin-to-c-source.py ) add_custom_command( OUTPUT html-data-files.cc COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/bin-to-c-source.py ${CMAKE_CURRENT_SOURCE_DIR}/../data/bcov.css css_text ${CMAKE_CURRENT_SOURCE_DIR}/../data/amber.png icon_amber ${CMAKE_CURRENT_SOURCE_DIR}/../data/glass.png icon_glass ${CMAKE_CURRENT_SOURCE_DIR}/../data/source-file.html source_file_text ${CMAKE_CURRENT_SOURCE_DIR}/../data/index.html index_text ${CMAKE_CURRENT_SOURCE_DIR}/../data/js/handlebars.js handlebars_text ${CMAKE_CURRENT_SOURCE_DIR}/../data/js/kcov.js kcov_text ${CMAKE_CURRENT_SOURCE_DIR}/../data/js/jquery.min.js jquery_text ${CMAKE_CURRENT_SOURCE_DIR}/../data/js/jquery.tablesorter.min.js tablesorter_text ${CMAKE_CURRENT_SOURCE_DIR}/../data/js/jquery.tablesorter.widgets.min.js tablesorter_widgets_text ${CMAKE_CURRENT_SOURCE_DIR}/../data/tablesorter-theme.css tablesorter_theme_text > html-data-files.cc DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../data/bcov.css ${CMAKE_CURRENT_SOURCE_DIR}/../data/amber.png ${CMAKE_CURRENT_SOURCE_DIR}/../data/glass.png ${CMAKE_CURRENT_SOURCE_DIR}/../data/source-file.html ${CMAKE_CURRENT_SOURCE_DIR}/../data/index.html ${CMAKE_CURRENT_SOURCE_DIR}/../data/js/handlebars.js ${CMAKE_CURRENT_SOURCE_DIR}/../data/js/kcov.js ${CMAKE_CURRENT_SOURCE_DIR}/../data/js/jquery.min.js ${CMAKE_CURRENT_SOURCE_DIR}/../data/js/jquery.tablesorter.min.js ${CMAKE_CURRENT_SOURCE_DIR}/../data/js/jquery.tablesorter.widgets.min.js ${CMAKE_CURRENT_SOURCE_DIR}/../data/tablesorter-theme.css ${CMAKE_CURRENT_SOURCE_DIR}/bin-to-c-source.py ) # Reference: http://www.cmake.org/Wiki/CMake_RPATH_handling if(SPECIFY_RPATH) set (CMAKE_SKIP_BUILD_RPATH FALSE) set (CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) set (CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) # the RPATH to be used when installing, but only if it's not a system directory LIST(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir) IF("${isSystemDir}" STREQUAL "-1") SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") ENDIF("${isSystemDir}" STREQUAL "-1") endif(SPECIFY_RPATH) add_executable (${KCOV} ${${KCOV}_SRCS} library.cc python-helper.cc bash-helper.cc html-data-files.cc) target_link_libraries(${KCOV} ${LIBDW_LIBRARIES} ${LIBELF_LIBRARIES} stdc++ dl pthread ${LIBCURL_LIBRARIES} m ${LIBZ_LIBRARIES}) install (TARGETS ${PROJECT_NAME} ${INSTALL_TARGETS_PATH}) file ( GLOB kcov-merge kcov-merge ) install (PROGRAMS ${kcov-merge} DESTINATION bin ) kcov_25+dfsg.orig/src/bin-to-c-source.py000077500000000000000000000015431247015272500202660ustar00rootroot00000000000000#!/usr/bin/env python import sys, struct def generate(data_in, base_name): print "const uint8_t %s_data_raw[] = {" % (base_name) for i in range(0, len(data), 20): line = data[i:i+20] for val in line: v = struct.unpack(">B", val) print "0x%02x," % (v), print "" print "};" print "GeneratedData %s_data(%s_data_raw, sizeof(%s_data_raw));" % (base_name, base_name, base_name) if __name__ == "__main__": if len(sys.argv) < 3 or (len(sys.argv) - 1) % 2 != 0: print "Usage: bin-to-source.py [ ]" sys.exit(1) print "#include " print "#include " print "#include " print "using namespace kcov;" for i in range(1,len(sys.argv), 2): file = sys.argv[i] base_name = sys.argv[i + 1] f = open(file) data = f.read() f.close() generate(data, base_name) kcov_25+dfsg.orig/src/capabilities.cc000066400000000000000000000015771247015272500177520ustar00rootroot00000000000000#include #include #include using namespace kcov; class Capabilities : public ICapabilities { public: Capabilities() { m_capabilities["handle-solibs"] = false; } void addCapability(const std::string &name) { validateCapability(name); m_capabilities[name] = true; } void removeCapability(const std::string &name) { validateCapability(name); m_capabilities[name] = false; } bool hasCapability(const std::string &name) { validateCapability(name); return m_capabilities[name]; } private: void validateCapability(const std::string &name) { panic_if(m_capabilities.find(name) == m_capabilities.end(), "Capability %s not present", name.c_str()); } std::unordered_map m_capabilities; }; static Capabilities g_capabilities; ICapabilities &ICapabilities::getInstance() { return g_capabilities; } kcov_25+dfsg.orig/src/collector.cc000066400000000000000000000101501247015272500172720ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include using namespace kcov; class Collector : public ICollector, public IFileParser::ILineListener, public IEngine::IEventListener { public: Collector(IFileParser &fileParser, IEngine &engine, IFilter &filter) : m_fileParser(fileParser), m_engine(engine), m_exitCode(-1), m_filter(filter) { m_fileParser.registerLineListener(*this); } void registerListener(ICollector::IListener &listener) { m_listeners.push_back(&listener); } void registerEventTickListener(IEventTickListener &listener) { m_eventTickListeners.push_back(&listener); } int run(const std::string &filename) { if (!m_engine.start(*this, filename)) { error("Can't start/attach to %s", filename.c_str()); return -1; } // This will set all breakpoints m_fileParser.parse(); while (1) { bool shouldContinue = m_engine.continueExecution(); for (EventTickListenerList_t::iterator it = m_eventTickListeners.begin(); it != m_eventTickListeners.end(); ++it) (*it)->onTick(); if (!shouldContinue) break; } return m_exitCode; } virtual void stop() { } private: std::string eventToName(IEngine::Event ev) { switch (ev.type) { case ev_breakpoint: return fmt("breakpoint at 0x%llx", (unsigned long long)ev.addr); case ev_exit: return fmt("exit code %d", ev.data); case ev_signal: case ev_signal_exit: { if (ev.data == SIGABRT) return std::string("SIGABRT"); if (ev.data == SIGSEGV) return std::string("SIGSEGV"); if (ev.data == SIGILL) return std::string("SIGILL"); if (ev.data == SIGTERM) return std::string("SIGTERM"); if (ev.data == SIGINT) return std::string("SIGINT"); if (ev.data == SIGBUS) return std::string("SIGBUS"); if (ev.data == SIGFPE) return std::string("SIGFPE"); return fmt("unknown signal %d", ev.data); } case ev_error: return std::string("error"); default: break; } return std::string("unknown"); } // From IEngine void onEvent(const IEngine::Event &ev) { switch (ev.type) { case ev_error: break; case ev_signal: break; case ev_signal_exit: kcov_debug(STATUS_MSG, "kcov: Process exited with signal %d (%s) at 0x%llx\n", ev.data, eventToName(ev).c_str(), (unsigned long long)ev.addr); if (ev.data == SIGILL) kcov_debug(STATUS_MSG, "\nkcov: Illegal instructions are sometimes caused by some GCC versions\n" "kcov: miscompiling C++ headers. If the problem is persistent, try running\n" "kcov: with --exclude-pattern=/usr/include. For more information, see\n" "kcov: http://github.com/SimonKagstrom/kcov/issues/18\n"); break; case ev_exit_first_process: if (IConfiguration::getInstance().keyAsInt("daemonize-on-first-process-exit")) { IConfiguration &conf = IConfiguration::getInstance(); std::string fifoName = conf.keyAsString("target-directory") + "/done.fifo"; std::string exitCode = fmt("%u", ev.data); write_file(exitCode.c_str(), exitCode.size(), "%s", fifoName.c_str()); } m_exitCode = ev.data; break; case ev_exit: m_exitCode = ev.data; break; case ev_breakpoint: for (ListenerList_t::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) (*it)->onAddressHit(ev.addr, 1); break; default: panic("Unknown event %d", ev.type); } } // From IFileParser void onLine(const std::string &file, unsigned int lineNr, uint64_t addr) { if (!m_filter.runFilters(file)) { return; } m_engine.registerBreakpoint(addr); } typedef std::vector ListenerList_t; typedef std::vector EventTickListenerList_t; IFileParser &m_fileParser; IEngine &m_engine; ListenerList_t m_listeners; EventTickListenerList_t m_eventTickListeners; int m_exitCode; IFilter &m_filter; }; ICollector &ICollector::create(IFileParser &elf, IEngine &engine, IFilter &filter) { return *new Collector(elf, engine, filter); } kcov_25+dfsg.orig/src/configuration.cc000066400000000000000000000327351247015272500201700ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include using namespace kcov; class Configuration : public IConfiguration { public: Configuration() { m_programArgs = NULL; m_argc = 0; m_printUncommon = false; setupDefaults(); } const std::string &keyAsString(const std::string &key) { panic_if(m_strings.find(key) == m_strings.end(), "key %s not found", key.c_str()); return m_strings[key]; } int keyAsInt(const std::string &key) { panic_if(m_ints.find(key) == m_ints.end(), "key %s not found", key.c_str()); return m_ints[key]; } const std::vector &keyAsList(const std::string &key) { panic_if(m_stringVectors.find(key) == m_stringVectors.end(), "key %s not found", key.c_str()); return m_stringVectors[key]; } bool usage(void) { printf("Usage: kcov [OPTIONS] out-dir in-file [args...]\n" "\n" "Where [OPTIONS] are\n" " -h, --help this text\n" " -p, --pid=PID trace PID instead of executing in-file,\n" " in-file is optional in this case\n" " -l, --limits=low,high setup limits for low/high coverage (default %u,%u)\n" "\n" " --collect-only Only collect coverage data (don't produce HTML/\n" " Cobertura output)\n" " --report-only Produce output from stored databases, don't collect\n" "\n" " --include-path=path comma-separated paths to include in the coverage report\n" " --exclude-path=path comma-separated paths to exclude from the coverage\n" " report\n" " --include-pattern=pat comma-separated path patterns to include in the\n" " coverage report\n" " --exclude-pattern=pat comma-separated path patterns to exclude from the \n" " coverage report\n" "\n" " --coveralls-id=id Travis CI job ID or secret repo_token for uploads to\n" " Coveralls.io\n" "%s" "\n" "Examples:\n" " kcov /tmp/frodo ./frodo # Check coverage for ./frodo\n" " kcov --pid=1000 /tmp/frodo ./frodo # Check coverage for PID 1000\n" " kcov --include-pattern=/src/frodo/ /tmp/frodo ./frodo # Only include files\n" " # including /src/frodo\n" " kcov --collect-only /tmp/kcov ./frodo # Collect coverage, don't report\n" " kcov --report-only /tmp/kcov ./frodo # Report coverage collected above\n" "", keyAsInt("low-limit"), keyAsInt("high-limit"), uncommonOptions().c_str()); return false; } void printUsage() { usage(); } bool parse(unsigned int argc, const char *argv[]) { static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"pid", required_argument, 0, 'p'}, {"limits", required_argument, 0, 'l'}, {"output-interval", required_argument, 0, 'O'}, {"path-strip-level", required_argument, 0, 'S'}, {"skip-solibs", no_argument, 0, 'L'}, {"exit-first-process", no_argument, 0, 'F'}, {"gcov", no_argument, 0, 'g'}, {"exclude-pattern", required_argument, 0, 'x'}, {"include-pattern", required_argument, 0, 'i'}, {"exclude-path", required_argument, 0, 'X'}, {"include-path", required_argument, 0, 'I'}, {"coveralls-id", required_argument, 0, 'T'}, {"debug", required_argument, 0, 'D'}, {"replace-src-path", required_argument, 0, 'R'}, {"collect-only", no_argument, 0, 'C'}, {"report-only", no_argument, 0, 'r'}, {"python-parser", required_argument, 0, 'P'}, {"bash-parser", required_argument, 0, 'B'}, {"uncommon-options", no_argument, 0, 'U'}, /*{"write-file", required_argument, 0, 'w'}, Take back when the kernel stuff works */ /*{"read-file", required_argument, 0, 'r'}, Ditto */ {0,0,0,0} }; unsigned int afterOpts = 0; unsigned int extraNeeded = 2; unsigned int lastArg; /* Scan through the parameters for an ELF file: That will be the * second last argument in the list. * * After that it's arguments to the external program. */ for (lastArg = 1; lastArg < argc; lastArg++) { if (IParserManager::getInstance().matchParser(argv[lastArg])) break; } bool printUsage = false; /* Hooray for reentrancy... */ optind = 0; optarg = 0; while (1) { int option_index = 0; int c; c = getopt_long (lastArg, (char **)argv, "hp:s:l:t:", long_options, &option_index); /* No more options */ if (c == -1) break; switch (c) { case 0: break; case 'h': printUsage = true; break; case 'U': m_printUncommon = true; break; case 'L': setKey("parse-solibs", 0); break; case 'F': setKey("daemonize-on-first-process-exit", 1); break; case 'g': setKey("gcov", 1); break; case 'p': if (!isInteger(std::string(optarg))) return usage(); setKey("attach-pid", stoul(std::string(optarg))); extraNeeded = 1; break; case 'O': if (!isInteger(std::string(optarg))) return usage(); setKey("output-interval", stoul(std::string(optarg))); break; case 'S': { if (!isInteger(std::string(optarg))) return usage(); int pathStripLevel = stoul(std::string(optarg)); if (pathStripLevel == 0) pathStripLevel = INT_MAX; setKey("path-strip-level", pathStripLevel); break; } case 'D': if (!isInteger(std::string(optarg))) return usage(); g_kcov_debug_mask = stoul(std::string(optarg)); break; case 'i': setKey("include-pattern", getCommaSeparatedList(std::string(optarg))); break; case 'P': setKey("python-command", optarg); break; case 'B': setKey("bash-command", optarg); break; case 'T': setKey("coveralls-id", optarg); break; case 'x': setKey("exclude-pattern", getCommaSeparatedList(std::string(optarg))); break; case 'I': { StrVecMap_t onlyIncludePath = getCommaSeparatedList(std::string(optarg)); expandPath(onlyIncludePath); setKey("include-path", onlyIncludePath); break; } case 'X': { StrVecMap_t excludePath = getCommaSeparatedList(std::string(optarg)); expandPath(excludePath); setKey("exclude-path", excludePath); break; } case 'C': setKey("running-mode", IConfiguration::MODE_COLLECT_ONLY); break; case 'r': setKey("running-mode", IConfiguration::MODE_REPORT_ONLY); break; case 'l': { StrVecMap_t vec = getCommaSeparatedList(std::string(optarg)); if (vec.size() != 2) return usage(); if (!isInteger(vec[0]) || !isInteger(vec[1])) return usage(); setKey("low-limit", stoul(vec[0])); setKey("high-limit", stoul(vec[1])); break; } case 'R': { std::string tmpArg = std::string(optarg); size_t tokenPosFront = tmpArg.find_first_of(":"); size_t tokenPosBack = tmpArg.find_last_of(":"); if ((tokenPosFront != std::string::npos) && (tokenPosFront == tokenPosBack)) { std::string originalPathPrefix = tmpArg.substr(0, tokenPosFront); std::string newPathPrefix = tmpArg.substr(tokenPosFront + 1); char* rp = ::realpath(newPathPrefix.c_str(), NULL); if (rp) { free((void*) rp); } else { panic("%s is not a valid path.\n", newPathPrefix.c_str()); } setKey("orig-path-prefix", originalPathPrefix); setKey("new-path-prefix", newPathPrefix); } else { panic("%s is formatted incorrectly\n", tmpArg.c_str()); } break; } default: error("Unrecognized option: -%c\n", optopt); return usage(); } } if (printUsage) return usage(); afterOpts = optind; /* When tracing by PID, the filename is optional */ if (argc < afterOpts + extraNeeded) return usage(); std::string outDirectory = argv[afterOpts]; if (outDirectory[outDirectory.size() - 1] != '/') outDirectory += "/"; std::string binaryName; if (argc >= afterOpts + 2) { StringPair_t tmp = splitPath(argv[afterOpts + 1]); setKey("binary-path", tmp.first); binaryName = tmp.second; } setKey("out-directory", outDirectory); setKey("target-directory", outDirectory + "/" + binaryName); setKey("binary-name", binaryName); m_programArgs = &argv[afterOpts + 1]; m_argc = argc - afterOpts - 1; return true; } const char **getArgv() { return m_programArgs; } unsigned int getArgc() { return m_argc; } void registerListener(IListener &listener, const std::vector &keys) { for (std::vector::const_iterator it = keys.begin(); it != keys.end(); ++it) m_listeners[*it] = &listener; } // "private", but we ignore that in the unit test typedef std::vector StrVecMap_t; typedef std::pair StringPair_t; typedef std::unordered_map StringKeyMap_t; typedef std::unordered_map IntKeyMap_t; typedef std::unordered_map StrVecKeyMap_t; typedef std::unordered_map ListenerMap_t; // Setup the default key:value pairs void setupDefaults() { setKey("binary-path", ""); setKey("python-command", "python"); setKey("bash-command", "/bin/bash"); setKey("kernel-coverage-path", "/sys/kernel/debug/kprobe-coverage"); setKey("path-strip-level", 2); setKey("attach-pid", 0); setKey("parse-solibs", 1); setKey("gcov", 0); setKey("low-limit", 25); setKey("high-limit", 75); setKey("output-interval", 5000); setKey("daemonize-on-first-process-exit", 0); setKey("coveralls-id", ""); setKey("orig-path-prefix", ""); setKey("new-path-prefix", ""); setKey("running-mode", IConfiguration::MODE_COLLECT_AND_REPORT); setKey("exclude-pattern", StrVecMap_t()); setKey("include-pattern", StrVecMap_t()); setKey("exclude-path", StrVecMap_t()); setKey("include-path", StrVecMap_t()); } void setKey(const std::string &key, const std::string &val) { m_strings[key] = val; notifyKeyListeners(key); } void setKey(const std::string &key, int val) { m_ints[key] = val; notifyKeyListeners(key); } void setKey(const std::string &key, const std::vector &val) { m_stringVectors[key] = val; notifyKeyListeners(key); } void notifyKeyListeners(const std::string &key) { ListenerMap_t::iterator it = m_listeners.find(key); if (it == m_listeners.end()) return; // The callback should re-read the configuration item it->second->onConfigurationChanged(key); } std::string uncommonOptions() { if (!m_printUncommon) return " --uncommon-options print uncommon options for --help\n"; return fmt( " --replace-src-path=path replace the string found before the : with the string \n" " found after the :\n" " --path-strip-level=num path levels to show for common paths (default: %d)\n" "\n" " --gcov use gcov parser instead of DWARF debugging info\n" " --skip-solibs don't parse shared libraries (default: parse solibs)\n" " --exit-first-process exit when the first process exits, i.e., honor the\n" " behavior of daemons (default: wait until last)\n" " --output-interval=ms Interval to produce output in milliseconds (0 to\n" " only output when kcov terminates, default %d)\n" "\n" " --debug=X set kcov debugging level (max 31, default 0)\n" "\n" " --python-parser=cmd Python parser to use (for python script coverage),\n" " default: %s\n" " --bash-parser=cmd Bash parser to use (for bash/sh script coverage),\n" " default: %s\n", keyAsInt("path-strip-level"), keyAsInt("output-interval"), keyAsString("python-command").c_str(), keyAsString("bash-command").c_str() ); } bool isInteger(std::string str) { size_t pos; try { stoul(str, &pos); } catch(std::invalid_argument &e) { return false; } return pos == str.size(); } void expandPath(StrVecMap_t &paths) { for (StrVecMap_t::iterator it = paths.begin(); it != paths.end(); ++it) { std::string &s = *it; if (s[0] == '~') s = get_home() + s.substr(1, s.size()); *it = s; } } StrVecMap_t getCommaSeparatedList(std::string str) { StrVecMap_t out; if (str.find(',') == std::string::npos) { out.push_back(str); return out; } size_t pos, lastPos; lastPos = 0; for (pos = str.find_first_of(","); pos != std::string::npos; pos = str.find_first_of(",", pos + 1)) { std::string cur = str.substr(lastPos, pos - lastPos); out.push_back(cur); lastPos = pos + 1; } out.push_back(str.substr(lastPos, str.size() - lastPos)); return out; } StringPair_t splitPath(const char *pathStr) { StringPair_t out; std::string path(pathStr); size_t pos = path.rfind('/'); out.first = ""; out.second = path; if (pos != std::string::npos) { out.first= path.substr(0, pos + 1); out.second = path.substr(pos + 1, std::string::npos); } return out; } StringKeyMap_t m_strings; IntKeyMap_t m_ints; StrVecKeyMap_t m_stringVectors; const char **m_programArgs; unsigned int m_argc; bool m_printUncommon; ListenerMap_t m_listeners; }; IConfiguration & IConfiguration::getInstance() { static Configuration *g_instance; if (!g_instance) g_instance = new Configuration(); return *g_instance; } kcov_25+dfsg.orig/src/engine-factory.cc000066400000000000000000000021151247015272500202200ustar00rootroot00000000000000#include #include #include using namespace kcov; class EngineFactory : public IEngineFactory { public: EngineFactory() { } void registerEngine(IEngineCreator &engine) { m_engines.push_back(&engine); } IEngineCreator &matchEngine(const std::string &fileName) { IEngineCreator *best = NULL; size_t sz; uint8_t *data = (uint8_t *)peek_file(&sz, "%s", fileName.c_str()); for (EngineList_t::iterator it = m_engines.begin(); it != m_engines.end(); ++it) { if (!best) best = *it; if ((*it)->matchFile(fileName, data, sz) > best->matchFile(fileName, data, sz)) best = *it; } free((void *)data); if (!best) panic("No engines registered"); return *best; } private: typedef std::vector EngineList_t; EngineList_t m_engines; }; IEngineFactory::IEngineCreator::IEngineCreator() { IEngineFactory::getInstance().registerEngine(*this); } static EngineFactory *g_instance; IEngineFactory &IEngineFactory::getInstance() { if (!g_instance) g_instance = new EngineFactory(); return *g_instance; } kcov_25+dfsg.orig/src/engines/000077500000000000000000000000001247015272500164305ustar00rootroot00000000000000kcov_25+dfsg.orig/src/engines/bash-engine.cc000066400000000000000000000217201247015272500211210ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "script-engine-base.hh" using namespace kcov; extern GeneratedData bash_helper_data; class BashEngine : public ScriptEngineBase { public: BashEngine() : ScriptEngineBase(), m_child(0), m_stderr(NULL) { } ~BashEngine() { kill(SIGTERM); } bool start(IEventListener &listener, const std::string &executable) { int stderrPipe[2]; if (pipe(stderrPipe) < 0) { error("Failed to open pipe"); return false; } std::string helperPath = IOutputHandler::getInstance().getBaseDirectory() + "bash-helper.sh"; if (write_file(bash_helper_data.data(), bash_helper_data.size(), "%s", helperPath.c_str()) < 0) { error("Can't write helper at %s", helperPath.c_str()); return false; } m_listener = &listener; /* Launch child */ m_child = fork(); if (m_child > 0) { // Close the parents write end of the pipe close(stderrPipe[1]); // And open a FILE * to stderr m_stderr = fdopen(stderrPipe[0], "r"); if (!m_stderr) { error("Can't reopen the stderr pipe"); return false; } } else if (m_child == 0) { IConfiguration &conf = IConfiguration::getInstance(); const char **argv = conf.getArgv(); unsigned int argc = conf.getArgc(); const std::string command = conf.keyAsString("bash-command"); /* Close the childs read end of the pipe */ close(stderrPipe[0]); if (dup2(stderrPipe[1], 2) < 0) { perror("Failed to exchange stderr for pipe"); return false; } /* Close the childs old write end of the pipe */ close(stderrPipe[1]); /* Set up PS4 for tracing */ doSetenv("PS4=kcov@${BASH_SOURCE}@${LINENO}@"); doSetenv(fmt("BASH_ENV=%s", helperPath.c_str())); // Make a copy of the vector, now with "bash -x" first char **vec; vec = (char **)xmalloc(sizeof(char *) * (argc + 3)); vec[0] = xstrdup(conf.keyAsString("bash-command").c_str()); vec[1] = xstrdup("-x"); for (unsigned i = 0; i < argc; i++) vec[2 + i] = xstrdup(argv[i]); /* Execute the script */ if (execv(vec[0], vec)) { perror("Failed to execute script"); free(vec); return false; } } else if (m_child < 0) { perror("fork"); return false; } return true; } bool checkEvents() { char *curLine; ssize_t len; size_t linecap = 0; len = getline(&curLine, &linecap, m_stderr); if (len < 0) return false; std::string cur(curLine); // Line markers always start with kcov@ size_t kcovStr = cur.find("kcov@"); if (kcovStr == std::string::npos) { fprintf(stderr, "%s", cur.c_str()); return true; } std::vector parts = split_string(cur.substr(kcovStr), "@"); // kcov@FILENAME@LINENO@... if (parts.size() < 3) return true; // Resolve filename (might be relative) const std::string &filename = get_real_path(parts[1]); const std::string &lineNo = parts[2]; // Skip the helper library if (filename.find("bash-helper.sh") != std::string::npos) return true; if (!string_is_integer(lineNo)) { error("%s is not an integer", lineNo.c_str()); return false; } if (!m_reportedFiles[filename]) { m_reportedFiles[filename] = true; for (FileListenerList_t::const_iterator it = m_fileListeners.begin(); it != m_fileListeners.end(); ++it) (*it)->onFile(File(filename, IFileParser::FLG_NONE)); parseFile(filename); } if (m_listener) { uint64_t address = 0; Event ev; LineIdToAddressMap_t::iterator it = m_lineIdToAddress.find(getLineId(filename, string_to_integer(lineNo))); if (it != m_lineIdToAddress.end()) address = it->second; ev.type = ev_breakpoint; ev.addr = address; ev.data = 1; m_listener->onEvent(ev); } return true; } bool continueExecution() { if (checkEvents()) return true; // Otherwise wait for child int status; int rv; rv = waitpid(m_child, &status, WNOHANG); if (rv != m_child) return true; if (WIFEXITED(status)) { reportEvent(ev_exit_first_process, WEXITSTATUS(status)); } else { warning("Other status: 0x%x\n", status); reportEvent(ev_error, -1); } return false; } void kill(int signal) { if (m_child == 0) return; ::kill(m_child, signal); } std::string getParserType() { return "bash"; } unsigned int matchParser(const std::string &filename, uint8_t *data, size_t dataSize) { std::string s((const char *)data, 80); if (filename.substr(filename.size() - 3, filename.size()) == ".sh") return 200; if (s.find("#!/bin/bash") != std::string::npos) return 400; if (s.find("#!/bin/sh") != std::string::npos) return 300; if (s.find("bash") != std::string::npos) return 100; if (s.find("sh") != std::string::npos) return 50; return match_none; } private: void doSetenv(const std::string &val) { // "Leak" some bytes, but the environment needs that char *envString = (char *)xmalloc(val.size() + 1); strcpy(envString, val.c_str()); putenv(envString); } // Sweep through lines in a file to determine what is valid code void parseFile(const std::string &filename) { if (!m_listener) return; size_t sz; char *p = (char *)read_file(&sz, "%s", filename.c_str()); // Can't handle this file if (!p) return; std::string fileData(p, sz); // Compute hash for this file uint32_t crc = hash_block((const uint8_t *)p, sz); free((void*)p); const std::vector &stringList = split_string(fileData, "\n"); unsigned int lineNo = 0; enum { none, backslash, heredoc } state = none; bool caseActive = false; std::string heredocMarker; for (std::vector::const_iterator it = stringList.begin(); it != stringList.end(); ++it) { std::string s = trim_string(*it); lineNo++; // Remove comments size_t comment = s.find("#"); if (comment != std::string::npos) { s = s.substr(0, comment); s = trim_string(s); } // Empty line, ignore if (s == "") continue; if (s.size() >= 2 && s[0] == ';' && s[1] == ';') continue; // Empty braces if (s[0] == '{' || s[0] == '}') continue; // While, if, switch endings if (s == "esac" || s == "fi" || s == "do" || s == "done" || s == "else" || s == "then") continue; // Functions if (s.find("function") == 0) continue; // fn() { .... Yes, regexes would have been better size_t fnPos = s.find("()"); if (fnPos != std::string::npos) { std::string substr = s.substr(0, fnPos); // Should be a single word (the function name) if (substr.find(" ") == std::string::npos) continue; } // Handle backslashes - only the first line is code if (state == backslash) { if (s[s.size() - 1] != '\\') state = none; continue; } // HERE documents if (state == heredoc) { if (s == heredocMarker) state = none; continue; } if (s[s.size() - 1] == '\\') { state = backslash; } else { size_t heredocStart = s.find("<<"); if (heredocStart != std::string::npos) { // Skip << and remove spaces before and after "EOF" heredocMarker = trim_string(s.substr(heredocStart + 2, s.size())); // Make sure the heredoc marker is a word for (unsigned int i = 0; i < heredocMarker.size(); i++) { if (heredocMarker[i] == ' ' || heredocMarker[i] == '\t') { heredocMarker = heredocMarker.substr(0, i); break; } } if (heredocMarker.size() > 0 && heredocMarker[0] != '<') state = heredoc; } } if (s.find("case") == 0) caseActive = true; else if (s.find("esac") == 0) caseActive = false; // Case switches are nocode if (caseActive && s[s.size() - 1] == ')') { // But let functions be if (s.find("(") == std::string::npos) continue; } fileLineFound(crc, filename, lineNo); } } pid_t m_child; FILE *m_stderr; }; // This ugly stuff should be fixed static BashEngine *g_bashEngine; class BashCtor { public: BashCtor() { g_bashEngine = new BashEngine(); } }; static BashCtor g_bashCtor; class BashEngineCreator : public IEngineFactory::IEngineCreator { public: virtual ~BashEngineCreator() { } virtual IEngine *create(IFileParser &parser) { return g_bashEngine; } unsigned int matchFile(const std::string &filename, uint8_t *data, size_t dataSize) { std::string s((const char *)data, 80); if (filename.substr(filename.size() - 3, filename.size()) == ".sh") return 200; if (s.find("#!/bin/bash") != std::string::npos) return 400; if (s.find("#!/bin/sh") != std::string::npos) return 300; if (s.find("bash") != std::string::npos) return 100; if (s.find("sh") != std::string::npos) return 50; return match_none; } }; static BashEngineCreator g_bashEngineCreator; kcov_25+dfsg.orig/src/engines/bash-helper.sh000066400000000000000000000000441247015272500211540ustar00rootroot00000000000000#!/bin/sh # Turn on tracing set -x kcov_25+dfsg.orig/src/engines/gcov-engine.cc000066400000000000000000000103301247015272500211350ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include // std::pair using namespace kcov; class GcovEngine : public IEngine, IFileParser::IFileListener { public: GcovEngine(IFileParser &parser) : m_listener(NULL), m_child(-1) { parser.registerFileListener(*this); } int registerBreakpoint(unsigned long addr) { return 0; } bool start(IEventListener &listener, const std::string &executable) { char *const *argv = (char *const *)IConfiguration::getInstance().getArgv(); m_listener = &listener; // Run the program until completion m_child = fork(); if (m_child == 0) { execv(argv[0], argv); } else if (m_child < 0) { perror("fork"); return false; } return true; } void kill(int sig) { } bool continueExecution() { if (m_child < 0) return false; int status; // Wait for the child waitpid(m_child, &status, 0); m_child = -1; // All coverage collection is done after the program has been run for (FileList_t::const_iterator it = m_gcdaFiles.begin(); it != m_gcdaFiles.end(); ++it) { const std::string &gcda = *it; std::string gcno = gcda; size_t sz = gcno.size(); // .gcda -> .gcno gcno[sz - 2] = 'n'; gcno[sz - 1] = 'o'; // Need a pair if (!file_exists(gcno) || !file_exists(gcda)) continue; parseGcovFiles(gcno, gcda); } Event ev(ev_exit, WEXITSTATUS(status)); // Report the exit status m_listener->onEvent(ev); return false; } private: typedef std::vector FileList_t; void parseGcovFiles(const std::string &gcnoFile, const std::string gcdaFile) { size_t sz; void *d = read_file(&sz, "%s", gcnoFile.c_str()); if (!d) return; GcnoParser gcno((uint8_t *)d, sz); void *d2 = read_file(&sz, "%s", gcdaFile.c_str()); if (!d2) return; GcdaParser gcda((uint8_t *)d2, sz); gcno.parse(); gcda.parse(); std::unordered_map bbsByNumber; const GcnoParser::BasicBlockList_t &bbs = gcno.getBasicBlocks(); const GcnoParser::ArcList_t &arcs = gcno.getArcs(); for (GcnoParser::BasicBlockList_t::const_iterator it = bbs.begin(); it != bbs.end(); ++it) { const GcnoParser::BasicBlockMapping &cur = *it; bbsByNumber[cur.m_basicBlock].push_back(cur); } std::unordered_map counterByFunction; for (GcnoParser::ArcList_t::const_iterator it = arcs.begin(); it != arcs.end(); ++it) { const GcnoParser::Arc &cur = *it; int64_t counter = gcda.getCounter(cur.m_function, counterByFunction[cur.m_function]); counterByFunction[cur.m_function]++; // Not found? if (counter < 0) { warning("Arc %u but no counter\n", counterByFunction[cur.m_function]); continue; } // No hit, so ignore if (counter == 0) continue; reportBasicBlockHit(bbsByNumber[cur.m_dstBlock], counter); reportBasicBlockHit(bbsByNumber[cur.m_srcBlock], counter); } } void reportBasicBlockHit(const GcnoParser::BasicBlockList_t &bbs, int64_t counter) { for (GcnoParser::BasicBlockList_t::const_iterator it = bbs.begin(); it != bbs.end(); ++it) { const GcnoParser::BasicBlockMapping &bb = *it; uint64_t addr = gcovGetAddress(bb.m_file, bb.m_function, bb.m_basicBlock, bb.m_index); Event ev(ev_breakpoint, counter, addr); m_listener->onEvent(ev); } } // From IFileParser::IFileListener void onFile(const IFileParser::File &file) { if (!(file.m_flags & IFileParser::FLG_TYPE_COVERAGE_DATA)) return; m_gcdaFiles.push_back(file.m_filename); } FileList_t m_gcdaFiles; IEventListener *m_listener; pid_t m_child; }; class GcovEngineCreator : public IEngineFactory::IEngineCreator { public: virtual ~GcovEngineCreator() { } virtual IEngine *create(IFileParser &parser) { return new GcovEngine(parser); } unsigned int matchFile(const std::string &filename, uint8_t *data, size_t dataSize) { if (!IConfiguration::getInstance().keyAsInt("gcov")) return match_none; return match_perfect; } }; static GcovEngineCreator g_gcovEngineCreator; kcov_25+dfsg.orig/src/engines/kernel-engine.cc000066400000000000000000000067661247015272500215010ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include using namespace kcov; class KernelEngine : public IEngine { public: KernelEngine() : m_control(NULL), m_show(NULL), m_listener(NULL) { } ~KernelEngine() { kill(0); } // From IEngine int registerBreakpoint(unsigned long addr) { // Don't register the same BP twice if (m_addresses.find(addr) != m_addresses.end()) return 0; m_addresses[addr] = true; std::string s = fmt("%s0x%llx\n", m_module.c_str(), (unsigned long long)addr); kcov_debug(ENGINE_MSG, "KNRL set BP at 0x%llx\n", (unsigned long long)addr); panic_if (!m_control, "Control file not open???"); fprintf(m_control, "%s", s.c_str()); fflush(m_control); return 0; } void setupAllBreakpoints() { } bool clearBreakpoint(const Event &ev) { return true; } bool start(IEventListener &listener, const std::string &executable) { std::string path = IConfiguration::getInstance().keyAsString("kernel-coverage-path"); std::string control = path + "/control"; std::string show = path + "/show"; m_listener = &listener; // Open kprobe-coverage files m_control = fopen(control.c_str(), "w"); m_show = fopen(show.c_str(), "r"); if (!m_control || !m_show) { error("Can't open kprobe-coverage files. Is the kprobe-coverage module loaded?"); kill(0); return false; } return true; } bool continueExecution() { char buf[256]; // More than enough for one line char *p; p = fgets(buf, sizeof(buf) - 1, m_show); if (!p) return false; buf[sizeof(buf) - 1] = '\0'; buf[strlen(buf) - 1] = '\0'; // Remove the \n parseOneLine(buf); return true; } void kill(int sig) { if (m_control) { fprintf(m_control, "clear\n"); fclose(m_control); } if (m_show) fclose(m_show); m_control = NULL; m_show = NULL; } private: void parseOneLine(const std::string &line) { std::string moduleName; std::string addr; size_t pos = line.find(":"); if (pos != std::string::npos) { moduleName = line.substr(0, pos); addr = line.substr(pos + 1); } else { addr = line; } // Invalid? if (!string_is_integer(addr, 16)) return; uint64_t value = string_to_integer(addr, 16); kcov_debug(ENGINE_MSG, "KNRL BP at 0x%llx\n", (unsigned long long)value); m_listener->onEvent(Event(ev_breakpoint, 0, value)); } FILE *m_control; FILE *m_show; IEventListener *m_listener; std::unordered_map m_addresses; std::string m_module; }; class KernelEngineCreator : public IEngineFactory::IEngineCreator { public: virtual ~KernelEngineCreator() { } virtual IEngine *create(IFileParser &parser) { return new KernelEngine(); } unsigned int matchFile(const std::string &filename, uint8_t *data, size_t dataSize) { if (filename.find("vmlinux") != std::string::npos) return match_perfect; if (filename.find(".ko") == std::string::npos) return match_none; // Fix this somehow #if 0 m_module = filename; size_t slashPos = m_module.rfind("/"); if (slashPos == std::string::npos) slashPos = 0; else slashPos++; // Skip the actual slash // Remove path before and .ko after the name m_module = m_module.substr(slashPos, m_module.size() - slashPos - 3) + ":"; #endif return match_perfect; } }; static KernelEngineCreator g_kernelEngineCreator; kcov_25+dfsg.orig/src/engines/ptrace.cc000066400000000000000000000405671247015272500202310ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace kcov; #define str(s) #s #define xstr(s) str(s) enum { i386_EIP = 12, x86_64_RIP = 16, ppc_NIP = 32, arm_PC = 15, }; static unsigned long getAligned(unsigned long addr) { return (addr / sizeof(unsigned long)) * sizeof(unsigned long); } static unsigned long arch_getPcFromRegs(unsigned long *regs) { unsigned long out; #if defined(__i386__) out = regs[i386_EIP] - 1; #elif defined(__x86_64__) out = regs[x86_64_RIP] - 1; #elif defined(__arm__) out = regs[arm_PC]; #elif defined(__powerpc__) out = regs[ppc_NIP]; #else # error Unsupported architecture #endif return out; } static void arch_adjustPcAfterBreakpoint(unsigned long *regs) { #if defined(__i386__) regs[i386_EIP]--; #elif defined(__x86_64__) regs[x86_64_RIP]--; #elif defined(__powerpc__) || defined(__arm__) // Do nothing #else # error Unsupported architecture #endif } static unsigned long arch_setupBreakpoint(unsigned long addr, unsigned long old_data) { unsigned long val; #if defined(__i386__) || defined(__x86_64__) unsigned long aligned_addr = getAligned(addr); unsigned long offs = addr - aligned_addr; unsigned long shift = 8 * offs; val = (old_data & ~(0xffUL << shift)) | (0xccUL << shift); #elif defined(__powerpc__) val = 0x7fe00008; /* tw */ #elif defined(__arm__) val = 0xfedeffe7; // Undefined insn #else # error Unsupported architecture #endif return val; } static unsigned long arch_clearBreakpoint(unsigned long addr, unsigned long old_data, unsigned long cur_data) { unsigned long val; #if defined(__i386__) || defined(__x86_64__) unsigned long aligned_addr = getAligned(addr); unsigned long offs = addr - aligned_addr; unsigned long shift = 8 * offs; unsigned long old_byte = (old_data >> shift) & 0xffUL; val = (cur_data & ~(0xffUL << shift)) | (old_byte << shift); #elif defined(__powerpc__) || defined(__arm__) val = old_data; #else # error Unsupported architecture #endif return val; } /* Return non-zero if 'State' of /proc/PID/status contains STATE. */ static int linux_proc_get_int (pid_t lwpid, const char *field) { size_t field_len = strlen (field); FILE *status_file; char buf[100]; int retval = -1; snprintf (buf, sizeof (buf), "/proc/%d/status", (int) lwpid); status_file = fopen(buf, "r"); if (status_file == NULL) { warning("unable to open /proc file '%s'", buf); return -1; } while (fgets (buf, sizeof (buf), status_file)) { if (strncmp (buf, field, field_len) == 0 && buf[field_len] == ':') { retval = (int)strtol (&buf[field_len + 1], NULL, 10); break; } } fclose (status_file); return retval; } static int linux_proc_pid_has_state (pid_t pid, const char *state) { char buffer[100]; FILE *procfile; int retval; int have_state; xsnprintf (buffer, sizeof (buffer), "/proc/%d/status", (int) pid); procfile = fopen(buffer, "r"); if (procfile == NULL) { warning("unable to open /proc file '%s'", buffer); return 0; } have_state = 0; while (fgets (buffer, sizeof (buffer), procfile) != NULL) { if (strncmp (buffer, "State:", 6) == 0) { have_state = 1; break; } } retval = (have_state && strstr (buffer, state) != NULL); fclose (procfile); return retval; } /* Detect `T (stopped)' in `/proc/PID/status'. * Other states including `T (tracing stop)' are reported as false. */ static int linux_proc_pid_is_stopped (pid_t pid) { return linux_proc_pid_has_state (pid, "T (stopped)"); } static int linux_proc_get_tgid (pid_t lwpid) { return linux_proc_get_int (lwpid, "Tgid"); } static int kill_lwp (unsigned long lwpid, int signo) { /* Use tkill, if possible, in case we are using nptl threads. If tkill * fails, then we are not using nptl threads and we should be using kill. */ #ifdef __NR_tkill { static int tkill_failed; if (!tkill_failed) { int ret; errno = 0; ret = syscall (__NR_tkill, lwpid, signo); if (errno != ENOSYS) return ret; tkill_failed = 1; } } #endif return kill (lwpid, signo); } class Ptrace : public IEngine { public: Ptrace() : m_firstBreakpoint(true), m_activeChild(0), m_child(0), m_firstChild(0), m_parentCpu(0), m_listener(NULL), m_signal(0) { } ~Ptrace() { kill(SIGTERM); ptrace(PTRACE_DETACH, m_activeChild, 0, 0); } bool start(IEventListener &listener, const std::string &executable) { m_listener = &listener; m_parentCpu = kcov_get_current_cpu(); kcov_tie_process_to_cpu(getpid(), m_parentCpu); m_instructionMap.clear(); /* Basic check first */ if (access(executable.c_str(), X_OK) != 0) return false; unsigned int pid = IConfiguration::getInstance().keyAsInt("attach-pid"); bool res = false; if (pid != 0) res = attachPid(pid); else res = forkChild(executable.c_str()); return res; } int registerBreakpoint(unsigned long addr) { if (addr == 0) return -1; unsigned long data; // There already? if (m_instructionMap.find(addr) != m_instructionMap.end()) return 0; data = peekWord(addr); m_instructionMap[addr] = data; m_pendingBreakpoints.push_back(addr); kcov_debug(BP_MSG, "BP registered at 0x%lx\n", addr); return 0; } bool clearBreakpoint(unsigned long addr) { if (m_instructionMap.find(addr) == m_instructionMap.end()) { kcov_debug(BP_MSG, "Can't find breakpoint at 0x%lx\n", addr); return false; } // Clear the actual breakpoint instruction unsigned long val = m_instructionMap[addr]; val = arch_clearBreakpoint(addr, val, peekWord(addr)); pokeWord(addr, val); return true; } void singleStep() { unsigned long regs[1024]; ptrace((__ptrace_request)PTRACE_GETREGS, m_activeChild, 0, ®s); // Step back one instruction arch_adjustPcAfterBreakpoint(regs); ptrace((__ptrace_request)PTRACE_SETREGS, m_activeChild, 0, ®s); } const Event waitEvent() { static uint64_t lastSignalAddress; Event out; int status; int who; // Assume error out.type = ev_error; out.data = -1; who = waitpid(-1, &status, __WALL); if (who == -1) { kcov_debug(ENGINE_MSG, "Returning error\n"); return out; } m_children[who] = 1; m_activeChild = who; out.addr = getPc(m_activeChild); kcov_debug(ENGINE_MSG, "PT stopped PID %d 0x%08x\n", m_activeChild, status); // A signal? if (WIFSTOPPED(status)) { siginfo_t siginfo; int sig = WSTOPSIG(status); int sigill = SIGUNUSED; // Arm is using an undefined instruction, so we'll get a SIGILL here #if defined(__arm__) sigill = SIGILL; #endif out.type = ev_signal; out.data = sig; ptrace(PTRACE_GETSIGINFO, m_activeChild, NULL, (void *)&siginfo); // A trap? if (sig == SIGTRAP || sig == SIGSTOP || sig == sigill) { out.type = ev_breakpoint; out.data = -1; kcov_debug(ENGINE_MSG, "PT BP at 0x%llx:%d for %d\n", (unsigned long long)out.addr, out.data, m_activeChild); // Single-step if we have this BP if (m_instructionMap.find(out.addr) != m_instructionMap.end()) singleStep(); else skipInstruction(); if (m_firstBreakpoint) { blockUntilSolibDataRead(); m_firstBreakpoint = false; } return out; } else if ((status >> 16) == PTRACE_EVENT_CLONE || (status >> 16) == PTRACE_EVENT_FORK) { kcov_debug(ENGINE_MSG, "PT clone at 0x%llx for %d\n", (unsigned long long)out.addr, m_activeChild); out.data = 0; } kcov_debug(ENGINE_MSG, "PT signal %d at 0x%llx for %d\n", WSTOPSIG(status), (unsigned long long)out.addr, m_activeChild); lastSignalAddress = out.addr; } else if (WIFSIGNALED(status)) { // Crashed/killed int sig = WTERMSIG(status); out.type = ev_signal; out.data = sig; out.addr = lastSignalAddress; kcov_debug(ENGINE_MSG, "PT terminating signal %d at 0x%llx for %d\n", sig, (unsigned long long)out.addr, m_activeChild); m_children.erase(who); if (!childrenLeft()) out.type = ev_signal_exit; } else if (WIFEXITED(status)) { int exitStatus = WEXITSTATUS(status); kcov_debug(ENGINE_MSG, "PT exit %d at 0x%llx for %d%s\n", exitStatus, (unsigned long long)out.addr, m_activeChild, m_activeChild == m_firstChild ? " (first child)" : ""); m_children.erase(who); if (who == m_firstChild) out.type = ev_exit_first_process; else out.type = ev_exit; out.data = exitStatus; } return out; } bool childrenLeft() { return m_children.size() > 0 && m_children.find(m_firstChild) != m_children.end(); } /** * Continue execution with an event */ bool continueExecution() { int res; setupAllBreakpoints(); kcov_debug(ENGINE_MSG, "PT continuing %d with signal %lu\n", m_activeChild, m_signal); res = ptrace(PTRACE_CONT, m_activeChild, 0, m_signal); if (res < 0) { kcov_debug(ENGINE_MSG, "PT error for %d: %d\n", m_activeChild, res); m_children.erase(m_activeChild); } Event ev = waitEvent(); m_signal = ev.type == ev_signal ? ev.data : 0; if (m_listener) m_listener->onEvent(ev); if (ev.type == ev_breakpoint) clearBreakpoint(ev.addr); if (ev.type == ev_error) return false; return true; } void kill(int signal) { // Don't kill kcov itself (PID 0) if (m_activeChild != 0) ::kill(m_activeChild, signal); } private: void setupAllBreakpoints() { for (PendingBreakpointList_t::const_iterator addrIt = m_pendingBreakpoints.begin(); addrIt != m_pendingBreakpoints.end(); ++addrIt) { unsigned long addr = *addrIt; unsigned long cur_data = peekWord(addr); // Set the breakpoint pokeWord(addr, arch_setupBreakpoint(addr, cur_data)); } m_pendingBreakpoints.clear(); } bool forkChild(const char *executable) { char *const *argv = (char *const *)IConfiguration::getInstance().getArgv(); pid_t child, who; int status; /* Executable exists, try to launch it */ if ((child = fork()) == 0) { int res; /* And launch the process */ res = ptrace(PTRACE_TRACEME, 0, 0, 0); if (res < 0) { perror("Can't set me as ptraced"); return false; } kcov_tie_process_to_cpu(getpid(), m_parentCpu); execv(executable, argv); /* Exec failed */ return false; } /* Fork error? */ if (child < 0) { perror("fork"); return false; } m_child = m_activeChild = m_firstChild = child; // Might not be completely necessary (the child should inherit this // from the parent), but better safe than sorry kcov_tie_process_to_cpu(m_child, m_parentCpu); kcov_debug(ENGINE_MSG, "PT forked %d\n", child); /* Wait for the initial stop */ who = waitpid(child, &status, 0); if (who < 0) { perror("waitpid"); return false; } if (!WIFSTOPPED(status)) { fprintf(stderr, "Child hasn't stopped: %x\n", status); return false; } ptrace(PTRACE_SETOPTIONS, m_activeChild, 0, PTRACE_O_TRACECLONE | PTRACE_O_TRACEFORK); return true; } bool attachPid(pid_t pid) { int rv; m_child = m_activeChild = m_firstChild = pid; errno = 0; rv = linuxAttach(m_activeChild); //rv = ptrace(PTRACE_ATTACH, m_activeChild, 0, 0); if (rv < 0) { const char *err = strerror(errno); fprintf(stderr, "Can't attach to %d. Error %s\n", pid, err); return false; } /* Wait for the initial stop */ int status; int who = waitpid(m_activeChild, &status, 0); if (who < 0) { perror("waitpid"); return false; } if (!WIFSTOPPED(status)) { fprintf(stderr, "Child hasn't stopped: %x\n", status); return false; } kcov_tie_process_to_cpu(m_activeChild, m_parentCpu); ::kill(m_activeChild, SIGSTOP); ptrace(PTRACE_SETOPTIONS, m_activeChild, 0, PTRACE_O_TRACECLONE | PTRACE_O_TRACEFORK); return true; } /* Taken from GDB (loop through all threads and attach to each one) */ int linuxAttach (pid_t pid) { int err; /* Attach to PID. We will check for other threads soon. */ err = attachLwp (pid); if (err != 0) error ("Cannot attach to process %d\n", pid); if (linux_proc_get_tgid (pid) != pid) { return 0; } DIR *dir; char pathname[128]; sprintf (pathname, "/proc/%d/task", pid); dir = opendir (pathname); if (!dir) { error("Could not open /proc/%d/task.\n", pid); return 0; } /* At this point we attached to the tgid. Scan the task for * existing threads. */ int new_threads_found; int iterations = 0; std::unordered_map threads; threads[pid] = true; while (iterations < 2) { struct dirent *dp; new_threads_found = 0; /* Add all the other threads. While we go through the * threads, new threads may be spawned. Cycle through * the list of threads until we have done two iterations without * finding new threads. */ while ((dp = readdir (dir)) != NULL) { int lwp; /* Fetch one lwp. */ lwp = strtoul (dp->d_name, NULL, 10); /* Is this a new thread? */ if (lwp != 0 && threads.find(lwp) == threads.end()) { int err; threads[lwp] = true; err = attachLwp (lwp); if (err != 0) warning ("Cannot attach to lwp %d\n", lwp); new_threads_found++; } } if (!new_threads_found) iterations++; else iterations = 0; rewinddir (dir); } closedir (dir); return 0; } /* Attach to an inferior process. */ int attachLwp (int lwpid) { int rv; rv = ptrace (PTRACE_ATTACH, lwpid, 0, 0); if (rv < 0) return errno; if (linux_proc_pid_is_stopped (lwpid)) { /* The process is definitely stopped. It is in a job control * stop, unless the kernel predates the TASK_STOPPED / * TASK_TRACED distinction, in which case it might be in a * ptrace stop. Make sure it is in a ptrace stop; from there we * can kill it, signal it, et cetera. * * First make sure there is a pending SIGSTOP. Since we are * already attached, the process can not transition from stopped * to running without a PTRACE_CONT; so we know this signal will * go into the queue. The SIGSTOP generated by PTRACE_ATTACH is * probably already in the queue (unless this kernel is old * enough to use TASK_STOPPED for ptrace stops); but since * SIGSTOP is not an RT signal, it can only be queued once. */ kill_lwp (lwpid, SIGSTOP); /* Finally, resume the stopped process. This will deliver the * SIGSTOP (or a higher priority signal, just like normal * PTRACE_ATTACH), which we'll catch later on. */ ptrace (PTRACE_CONT, lwpid, 0, 0); } return 0; } // Skip over this instruction void skipInstruction() { // Nop on x86, op on PowerPC/ARM #if defined(__powerpc__) || defined(__arm__) unsigned long regs[1024]; ptrace((__ptrace_request)PTRACE_GETREGS, m_activeChild, 0, ®s); # if defined(__powerpc__) regs[ppc_NIP] += 4; #else regs[arm_PC] += 4; #endif ptrace((__ptrace_request)PTRACE_SETREGS, m_activeChild, 0, ®s); #endif } unsigned long getPcFromRegs(unsigned long *regs) { return arch_getPcFromRegs(regs); } unsigned long getPc(int pid) { unsigned long regs[1024]; memset(®s, 0, sizeof(regs)); ptrace((__ptrace_request)PTRACE_GETREGS, pid, 0, ®s); return getPcFromRegs(regs); } unsigned long peekWord(unsigned long addr) { unsigned long aligned = getAligned(addr); return ptrace((__ptrace_request)PTRACE_PEEKTEXT, m_activeChild, aligned, 0); } void pokeWord(unsigned long addr, unsigned long val) { ptrace((__ptrace_request)PTRACE_POKETEXT, m_activeChild, getAligned(addr), val); } typedef std::unordered_map instructionMap_t; typedef std::vector PendingBreakpointList_t; typedef std::unordered_map ChildMap_t; instructionMap_t m_instructionMap; PendingBreakpointList_t m_pendingBreakpoints; bool m_firstBreakpoint; pid_t m_activeChild; pid_t m_child; pid_t m_firstChild; ChildMap_t m_children; int m_parentCpu; IEventListener *m_listener; unsigned long m_signal; }; class PtraceEngineCreator : public IEngineFactory::IEngineCreator { public: virtual ~PtraceEngineCreator() { } virtual IEngine *create(IFileParser &parser) { return new Ptrace(); } unsigned int matchFile(const std::string &filename, uint8_t *data, size_t dataSize) { // Unless #!/bin/sh etc, this should win return 1; } }; static PtraceEngineCreator g_ptraceEngineCreator; kcov_25+dfsg.orig/src/engines/python-engine.cc000066400000000000000000000225521247015272500215310ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "script-engine-base.hh" using namespace kcov; extern GeneratedData python_helper_data; const uint64_t COVERAGE_MAGIC = 0x6d6574616c6c6775ULL; // "metallgut" /* Should be 8-byte aligned */ struct coverage_data { uint64_t magic; uint32_t size; uint32_t line; const char filename[]; }; class PythonEngine : public ScriptEngineBase { public: PythonEngine() : ScriptEngineBase(), m_child(0), m_pipe(NULL) { } ~PythonEngine() { kill(SIGTERM); } bool start(IEventListener &listener, const std::string &executable) { std::string kcov_python_pipe_path = IOutputHandler::getInstance().getOutDirectory() + "kcov-python.pipe"; std::string kcov_python_path = IOutputHandler::getInstance().getBaseDirectory() + "python-helper.py"; if (write_file(python_helper_data.data(), python_helper_data.size(), "%s", kcov_python_path.c_str()) < 0) { error("Can't write python helper at %s", kcov_python_path.c_str()); return false; } m_listener = &listener; std::string kcov_python_env = "KCOV_PYTHON_PIPE_PATH=" + kcov_python_pipe_path; unlink(kcov_python_pipe_path.c_str()); if (mkfifo(kcov_python_pipe_path.c_str(), 0600) < 0) { error("Can't create python FIFO %s\n", kcov_python_pipe_path.c_str()); return false; } char *envString = (char *)xmalloc(kcov_python_env.size() + 1); strcpy(envString, kcov_python_env.c_str()); putenv(envString); /* Launch the python helper */ m_child = fork(); if (m_child == 0) { IConfiguration &conf = IConfiguration::getInstance(); const char **argv = conf.getArgv(); unsigned int argc = conf.getArgc(); std::string s = fmt("%s %s ", conf.keyAsString("python-command").c_str(), kcov_python_path.c_str()); for (unsigned int i = 0; i < argc; i++) s += std::string(argv[i]) + " "; int res; res = system(s.c_str()); panic_if (res < 0, "Can't execute python helper"); exit(WEXITSTATUS(res)); } else if (m_child < 0) { perror("fork"); return false; } m_pipe = fopen(kcov_python_pipe_path.c_str(), "r"); panic_if (!m_pipe, "Can't open python pipe %s", kcov_python_pipe_path.c_str()); return true; } bool checkEvents() { uint8_t buf[8192]; struct coverage_data *p; p = readCoverageDatum(buf, sizeof(buf)); if (!p) { reportEvent(ev_error, -1); return false; } if (!m_reportedFiles[p->filename]) { m_reportedFiles[p->filename] = true; for (FileListenerList_t::const_iterator it = m_fileListeners.begin(); it != m_fileListeners.end(); ++it) (*it)->onFile(File(p->filename, IFileParser::FLG_NONE)); parseFile(p->filename); } if (m_listener) { uint64_t address = 0; Event ev; LineIdToAddressMap_t::const_iterator it = m_lineIdToAddress.find(getLineId(p->filename, p->line)); if (it != m_lineIdToAddress.end()) address = it->second; ev.type = ev_breakpoint; ev.addr = address; ev.data = 1; m_listener->onEvent(ev); } return true; } bool continueExecution() { if (checkEvents()) return true; // Otherwise wait for child int status; int rv; rv = waitpid(m_child, &status, WNOHANG); if (rv != m_child) return true; if (WIFEXITED(status)) { reportEvent(ev_exit_first_process, WEXITSTATUS(status)); } else { warning("Other status: 0x%x\n", status); reportEvent(ev_error, -1); } return false; } void kill(int signal) { if (m_child == 0) return; ::kill(m_child, signal); } std::string getParserType() { return "python"; } unsigned int matchParser(const std::string &filename, uint8_t *data, size_t dataSize) { std::string s((const char *)data, 80); if (filename.substr(filename.size() - 3, filename.size()) == ".py") return 200; if (s.find("python") != std::string::npos) return 100; return match_none; } private: void unmarshalCoverageData(struct coverage_data *p) { p->magic = be_to_host(p->magic); p->size = be_to_host(p->size); p->line = be_to_host(p->line); } // Sweep through lines in a file to determine what is valid code void parseFile(const std::string &filename) { if (!m_listener) return; size_t sz; char *p = (char *)read_file(&sz, "%s", filename.c_str()); // Can't handle this file if (!p) return; std::string fileData(p, sz); // Compute hash for this file uint32_t crc = hash_block((const uint8_t *)p, sz); free((void*)p); const std::vector &stringList = split_string(fileData, "\n"); unsigned int lineNo = 0; enum { start, multiline_active } state = start; bool multiLineStartLine = false; for (std::vector::const_iterator it = stringList.begin(); it != stringList.end(); ++it) { const std::string &s = trim_string(*it); lineNo++; // Empty line, ignore if (s == "") continue; // Non-empty, but comment if (s[0] == '#') continue; // Dict endings generate no code if (s == "}" || s == "},") continue; // Ditto for list ends if (s == "]" || s == "],") continue; // ... And starts if (s == "[") continue; // else: statements are nops if (s.find("else:") != std::string::npos) continue; // As is finally: if (s.find("finally:") != std::string::npos) continue; // Single-line python strings (e.g., 'description of the function') if (isPythonString(s)) continue; size_t idx = multilineIdx(s); switch (state) { case start: if (idx != std::string::npos) { kcov_debug(ENGINE_MSG, "python multiline ON %3d: %s\n", lineNo, s.c_str()); std::string s2 = s.substr(idx + 3, std::string::npos); if (multilineIdx(s2) == std::string::npos) state = multiline_active; // E.g., a = '''yadayada [...]''' if (idx > 0) multiLineStartLine = true; // Don't report this line continue; } break; case multiline_active: if (idx != std::string::npos) { kcov_debug(ENGINE_MSG, "python multiline OFF %3d: %s\n", lineNo, s.c_str()); state = start; // The last line of a multi-line string will get reported by the // python helper, so add this as a line if there was an assignment // above if (multiLineStartLine) { fileLineFound(crc, filename, lineNo); multiLineStartLine = false; } } continue; // Don't report this line default: panic("Unknown state %u", state); break; } fileLineFound(crc, filename, lineNo); } } size_t multilineIdx(const std::string &s) { size_t idx = s.find("'''"); if (idx == std::string::npos) idx = s.find("\"\"\""); return idx; } bool isStringDelimiter(char c) { return c == '\'' || c == '"'; } bool isPythonString(const std::string &s) { if (s.size() < 2) return false; char first = s[0]; char last = s[s.size() - 1]; if (!isStringDelimiter(first)) return false; // Can be multi-line string, handled elsewhere if (!isStringDelimiter(last)) return false; unsigned i; for (i = 0; i < s.size(); i++) { if (!isStringDelimiter(s[i])) break; } // Only string characters if (i == s.size()) return false; return true; } struct coverage_data *readCoverageDatum(uint8_t *buf, size_t totalSize) { struct coverage_data *p = (struct coverage_data *)buf; ssize_t rv; if (feof(m_pipe)) return NULL; // Not an error // No data? if (!file_readable(m_pipe, 100)) return NULL; rv = fread(buf, 1, sizeof(struct coverage_data), m_pipe); if (rv == 0) return NULL; // Not an error if (rv < (int)sizeof(struct coverage_data)) { error("Read too little %zd", rv); return NULL; } unmarshalCoverageData(p); bool valid = p->magic == COVERAGE_MAGIC && p->size < totalSize; kcov_debug(ENGINE_MSG, "datum: 0x%16llx, size %u, line %u (%svalid)\n", (unsigned long long)p->magic, (unsigned int)p->size, (unsigned int)p->line, valid ? "" : "in"); if (!valid) { uint32_t *p32 = (uint32_t *)buf; error("Data magic wrong or size too large: %08x %08x %08x %08x\n", p32[0], p32[1], p32[2], p32[3]); return NULL; } size_t remainder = p->size - sizeof(struct coverage_data); rv = fread(buf + sizeof(struct coverage_data), 1, remainder, m_pipe); if (rv < (ssize_t)remainder) { error("Read too little %zd vs %zu", rv, remainder); return NULL; } return p; } pid_t m_child; FILE *m_pipe; }; // This ugly stuff should be fixed static PythonEngine *g_pythonEngine; class PythonCtor { public: PythonCtor() { g_pythonEngine = new PythonEngine(); } }; static PythonCtor g_pythonCtor; class PythonEngineCreator : public IEngineFactory::IEngineCreator { public: virtual ~PythonEngineCreator() { } virtual IEngine *create(IFileParser &parser) { return g_pythonEngine; } unsigned int matchFile(const std::string &filename, uint8_t *data, size_t dataSize) { std::string s((const char *)data, 80); if (filename.substr(filename.size() - 3, filename.size()) == ".py") return 200; if (s.find("python") != std::string::npos) return 100; return match_none; } }; static PythonEngineCreator g_pythonEngineCreator; kcov_25+dfsg.orig/src/engines/python-helper.py000066400000000000000000000054001247015272500215770ustar00rootroot00000000000000#!/usr/bin/env python # encoding: utf-8 # Based on http://pymotw.com/2/sys/tracing.html, "Tracing a program as it runs" # and http://hg.python.org/cpython/file/2.7/Lib/trace.py import imp import sys import os import struct fifo_file = None report_trace_real = None try: # In Py 2.x, the builtins were in __builtin__ BUILTINS = sys.modules['__builtin__'] except KeyError: # In Py 3.x, they're in builtins BUILTINS = sys.modules['builtins'] def report_trace3(file, line): size = len(file) + 1 + 8 + 4 + 4 data = struct.pack(">QLL%dsb" % len(file), 0x6d6574616c6c6775, size, int(line), bytes(file, 'utf-8'), 0) fifo_file.write(data) def report_trace2(file, line): size = len(file) + 1 + 8 + 4 + 4 data = struct.pack(">QLL%dsb" % len(file), 0x6d6574616c6c6775, size, int(line), file, 0) fifo_file.write(data) def report_trace(file, line): try: report_trace_real(file, line) fifo_file.flush() except: # Ignore errors pass def trace_lines(frame, event, arg): if event != 'line': return co = frame.f_code func_name = co.co_name line_no = frame.f_lineno filename = co.co_filename report_trace(filename, line_no) def trace_calls(frame, event, arg): if event != 'call': return co = frame.f_code func_name = co.co_name line_no = frame.f_lineno filename = co.co_filename report_trace(filename, line_no) return trace_lines def runctx(cmd, globals): sys.settrace(trace_calls) try: exec(cmd, globals) finally: sys.settrace(None) if __name__ == "__main__": if sys.version_info >= (3, 0): report_trace_real = report_trace3 else: report_trace_real = report_trace2 prog_argv = sys.argv[1:] sys.argv = prog_argv progname = prog_argv[0] sys.path[0] = os.path.split(progname)[0] fifo_path = os.getenv("KCOV_PYTHON_PIPE_PATH") if fifo_path == None: sys.stderr.write("the KCOV_PYTHON_PIPE_PATH environment variable is not set") sys.exit(127) try: fifo_file = open(fifo_path, "wb") except: sys.stderr.write("Can't open fifo file") sys.exit(127) main_mod = imp.new_module('__main__') old_main_mod = sys.modules['__main__'] sys.modules['__main__'] = main_mod main_mod.__file__ = progname main_mod.__package__ = None main_mod.__builtins__ = BUILTINS try: with open(progname) as fp: # try to emulate __main__ namespace as much as possible code = compile(fp.read(), progname, 'exec') runctx(code, main_mod.__dict__) except IOError: sys.stderr.write("Cannot run file %r" % (sys.argv[0])) sys.exit(127) finally: sys.modules['__main__'] = old_main_mod kcov_25+dfsg.orig/src/engines/script-engine-base.hh000066400000000000000000000044271247015272500224370ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace kcov; /** * Base-class for script-based coverage engines. */ class ScriptEngineBase : public IEngine, public IFileParser { public: ScriptEngineBase() : m_listener(NULL) { IParserManager::getInstance().registerParser(*this); } virtual ~ScriptEngineBase() { } // From IEngine int registerBreakpoint(unsigned long addr) { // No breakpoints return 0; } // From IFileParser bool addFile(const std::string &filename, struct phdr_data_entry *phdr_data) { return true; } void registerLineListener(ILineListener &listener) { m_lineListeners.push_back(&listener); } void registerFileListener(IFileListener &listener) { m_fileListeners.push_back(&listener); } bool parse() { return true; } uint64_t getChecksum() { return 0; } enum IFileParser::PossibleHits maxPossibleHits() { return IFileParser::HITS_UNLIMITED; } void setupParser(IFilter *filter) { } protected: void reportEvent(enum event_type type, int data = -1, uint64_t address = 0) { if (!m_listener) return; m_listener->onEvent(Event(type, data, address)); } void fileLineFound(uint32_t crc, const std::string &filename, unsigned int lineNo) { uint64_t id = getLineId(filename, lineNo); uint64_t address = (uint64_t)crc | ((uint64_t)lineNo << 32ULL); m_lineIdToAddress[id] = address; for (LineListenerList_t::const_iterator lit = m_lineListeners.begin(); lit != m_lineListeners.end(); ++lit) (*lit)->onLine(get_real_path(filename).c_str(), lineNo, address); } typedef std::vector LineListenerList_t; typedef std::vector FileListenerList_t; typedef std::unordered_map ReportedFileMap_t; typedef std::unordered_map LineIdToAddressMap_t; LineListenerList_t m_lineListeners; FileListenerList_t m_fileListeners; ReportedFileMap_t m_reportedFiles; LineIdToAddressMap_t m_lineIdToAddress; IEventListener *m_listener; }; kcov_25+dfsg.orig/src/filter.cc000066400000000000000000000066061247015272500166040ustar00rootroot00000000000000#include #include #include #include #include #include #include using namespace kcov; class DummyFilter : public IFilter { public: DummyFilter() { } ~DummyFilter() { } // Allow anything bool runFilters(const std::string &file) { return true; } }; class Filter : public IFilter { public: Filter() { m_patternHandler = new PatternHandler(); m_pathHandler = new PathHandler(); } ~Filter() { delete m_patternHandler; delete m_pathHandler; } // Used by the unit test void setup() { delete m_patternHandler; delete m_pathHandler; m_patternHandler = new PatternHandler(); m_pathHandler = new PathHandler(); } bool runFilters(const std::string &file) { if (m_pathHandler->isSetup()) return m_pathHandler->includeFile(file); if (m_patternHandler->isSetup()) return m_patternHandler->includeFile(file); return true; } private: class PatternHandler { public: PatternHandler() : m_includePatterns(IConfiguration::getInstance().keyAsList("include-pattern")), m_excludePatterns(IConfiguration::getInstance().keyAsList("exclude-pattern")) { } bool isSetup() { return !(m_includePatterns.size() == 0 && m_excludePatterns.size() == 0); } bool includeFile(std::string file) { if (m_includePatterns.size() == 0 && m_excludePatterns.size() == 0) return true; bool out = true; if (m_includePatterns.size() != 0) out = false; for (PatternMap_t::const_iterator it = m_includePatterns.begin(); it != m_includePatterns.end(); ++it) { const std::string &pattern = *it; if (file.find(pattern) != std::string::npos) out = true; } for (PatternMap_t::const_iterator it = m_excludePatterns.begin(); it != m_excludePatterns.end(); ++it) { const std::string &pattern = *it; if (file.find(pattern) != std::string::npos) out = false; } return out; } private: typedef std::vector PatternMap_t; const PatternMap_t &m_includePatterns; const PatternMap_t &m_excludePatterns; }; class PathHandler { public: PathHandler() : m_includePaths(IConfiguration::getInstance().keyAsList("include-path")), m_excludePaths(IConfiguration::getInstance().keyAsList("exclude-path")) { } bool isSetup() { return !(m_includePaths.size() == 0 && m_excludePaths.size() == 0); } bool includeFile(const std::string &file) { if (m_includePaths.size() == 0 && m_excludePaths.size() == 0) return true; bool out = true; if (m_includePaths.size() != 0) out = false; const std::string pathStr = get_real_path(file); for (PathMap_t::const_iterator it = m_includePaths.begin(); it != m_includePaths.end(); ++it) { const std::string &pathPattern = *it; if (pathStr.find(pathPattern) == 0) out = true; } for (PathMap_t::const_iterator it = m_excludePaths.begin(); it != m_excludePaths.end(); ++it) { const std::string &pathPattern = *it; if (pathStr.find(pathPattern) == 0) out = false; } return out; } private: typedef std::vector PathMap_t; const PathMap_t &m_includePaths; const PathMap_t &m_excludePaths; }; PatternHandler *m_patternHandler; PathHandler *m_pathHandler; }; IFilter &IFilter::create() { return *new Filter(); } IFilter &IFilter::createDummy() { return *new DummyFilter(); } kcov_25+dfsg.orig/src/gcov.cc000066400000000000000000000235131247015272500162510ustar00rootroot00000000000000#include #include using namespace kcov; /* * From gcov-io.h: * * int32: byte3 byte2 byte1 byte0 | byte0 byte1 byte2 byte3 * int64: int32:low int32:high * string: int32:0 | int32:length char* char:0 padding * padding: | char:0 | char:0 char:0 | char:0 char:0 char:0 * item: int32 | int64 | string * * File: * file : int32:magic int32:version int32:stamp record* * * Record: * record: header data * header: int32:tag int32:length * data: item* * * gcno file records: * note: unit function-graph* * unit: header int32:checksum string:source * function-graph: announce_function basic_blocks {arcs | lines}* * announce_function: header int32:ident * int32:lineno_checksum int32:cfg_checksum * string:name string:source int32:lineno * basic_block: header int32:flags* * arcs: header int32:block_no arc* * arc: int32:dest_block int32:flags * lines: header int32:block_no line* * int32:0 string:NULL * line: int32:line_no | int32:0 string:filename * * gcda file records: * data: {unit summary:object summary:program* function-data*}* * unit: header int32:checksum * function-data: announce_function present counts * announce_function: header int32:ident * int32:lineno_checksum int32:cfg_checksum * present: header int32:present * counts: header int64:count* * summary: int32:checksum {count-summary}GCOV_COUNTERS_SUMMABLE * count-summary: int32:num int32:runs int64:sum * int64:max int64:sum_max histogram * histogram: {int32:bitvector}8 histogram-buckets* * histogram-buckets: int32:num int64:min int64:sum */ #define GCOV_DATA_MAGIC ((uint32_t)0x67636461) /* "gcda" */ #define GCOV_NOTE_MAGIC ((uint32_t)0x67636e6f) /* "gcno" */ #define GCOV_TAG_FUNCTION ((uint32_t)0x01000000) #define GCOV_TAG_FUNCTION_LENGTH (3) #define GCOV_TAG_BLOCKS ((uint32_t)0x01410000) #define GCOV_TAG_BLOCKS_LENGTH(NUM) (NUM) #define GCOV_TAG_BLOCKS_NUM(LENGTH) (LENGTH) #define GCOV_TAG_ARCS ((uint32_t)0x01430000) #define GCOV_TAG_ARCS_LENGTH(NUM) (1 + (NUM) * 2) #define GCOV_TAG_ARCS_NUM(LENGTH) (((LENGTH) - 1) / 2) #define GCOV_TAG_LINES ((uint32_t)0x01450000) #define GCOV_TAG_COUNTER_BASE ((uint32_t)0x01a10000) #define GCOV_TAG_COUNTER_LENGTH(NUM) ((NUM) * 2) #define GCOV_TAG_COUNTER_NUM(LENGTH) ((LENGTH) / 2) #define GCOV_TAG_OBJECT_SUMMARY ((uint32_t)0xa1000000) /* Obsolete */ #define GCOV_TAG_PROGRAM_SUMMARY ((uint32_t)0xa3000000) #define GCOV_TAG_SUMMARY_LENGTH(NUM) \ (1 + GCOV_COUNTERS_SUMMABLE * (10 + 3 * 2) + (NUM) * 5) #define GCOV_TAG_AFDO_FILE_NAMES ((uint32_t)0xaa000000) #define GCOV_TAG_AFDO_FUNCTION ((uint32_t)0xac000000) #define GCOV_TAG_AFDO_WORKING_SET ((uint32_t)0xaf000000) /* Arc flags */ #define GCOV_ARC_ON_TREE (1 << 0) #define GCOV_ARC_FAKE (1 << 1) #define GCOV_ARC_FALLTHROUGH (1 << 2) struct file_header { int32_t magic; int32_t version; int32_t stamp; }; struct header { int32_t tag; int32_t length; }; GcovParser::GcovParser(const uint8_t *data, size_t dataSize) : m_data(data), m_dataSize(dataSize) { } GcovParser::~GcovParser() { free((void *)m_data); } bool GcovParser::parse() { // Not a gcov file? if (!verify()) return false; const uint8_t *cur = m_data + sizeof(struct file_header); ssize_t left = (ssize_t)(m_dataSize - sizeof(struct file_header)); // Iterate through the headers while (1) { const struct header *header = (struct header *)cur; if (!onRecord(header, cur + sizeof(*header))) return false; size_t curLen = sizeof(struct header) + header->length * 4; left -= curLen; if (left <= 0) break; cur += curLen; } return true; } bool GcovParser::verify() { const struct file_header *header = (const struct file_header *)m_data; if (header->magic != GCOV_DATA_MAGIC && header->magic != GCOV_NOTE_MAGIC) return false; return true; } const uint8_t *GcovParser::readString(const uint8_t *p, std::string &out) { int32_t length = *(const int32_t*)p; const char *c_str = (const char *)&p[4]; out = std::string(c_str); return padPointer(p + length * 4 + 4); // Including the length field } // Convenience when using 32-bit pointers const int32_t *GcovParser::readString(const int32_t *p, std::string &out) { return (const int32_t *)readString((const uint8_t *)p, out); } const uint8_t *GcovParser::padPointer(const uint8_t *p) { unsigned long addr = (unsigned long)p; if ((addr & 3) != 0) p += 4 - (addr & 3); return p; } GcnoParser::BasicBlockMapping::BasicBlockMapping(const BasicBlockMapping &other) : m_function(other.m_function), m_basicBlock(other.m_basicBlock), m_file(other.m_file), m_line(other.m_line), m_index(other.m_index) { } GcnoParser::BasicBlockMapping::BasicBlockMapping(int32_t function, int32_t basicBlock, const std::string &file, int32_t line, int32_t index) : m_function(function), m_basicBlock(basicBlock), m_file(file), m_line(line), m_index(index) { } GcnoParser::Arc::Arc(const Arc &other) : m_function(other.m_function), m_srcBlock(other.m_srcBlock), m_dstBlock(other.m_dstBlock) { } GcnoParser::Arc::Arc(int32_t function, int32_t srcBlock, int32_t dstBlock) : m_function(function), m_srcBlock(srcBlock), m_dstBlock(dstBlock) { } GcnoParser::GcnoParser(const uint8_t *data, size_t dataSize) : GcovParser(data, dataSize), m_functionId(-1) { } const GcnoParser::BasicBlockList_t &GcnoParser::getBasicBlocks() { return m_basicBlocks; } const GcnoParser::FunctionList_t &GcnoParser::getFunctions() { return m_functions; } const GcnoParser::ArcList_t &GcnoParser::getArcs() { return m_arcs; } bool GcnoParser::onRecord(const struct header *header, const uint8_t *data) { kcov_debug(ENGINE_MSG, "GCNO record: 0x%08x (%d bytes)\n", header->tag, header->length * 4); switch (header->tag) { case GCOV_TAG_FUNCTION: onAnnounceFunction(header, data); break; case GCOV_TAG_BLOCKS: onBlocks(header, data); break; case GCOV_TAG_LINES: onLines(header, data); break; case GCOV_TAG_ARCS: onArcs(header, data); break; default: break; } return true; } void GcnoParser::onAnnounceFunction(const struct header *header, const uint8_t *data) { const int32_t *p32 = (const int32_t *)data; const uint8_t *p8 = data; int32_t ident = p32[0]; p8 = readString(p8 + 3 * 4, m_function); p8 = readString(p8, m_file); m_functionId = ident; m_functions.push_back(m_functionId); kcov_debug(ENGINE_MSG, "GCNO function %d: %s\n", m_functionId, m_file.c_str()); // The first line of this function comes next, but let's ignore that } void GcnoParser::onBlocks(const struct header *header, const uint8_t *data) { // Not used by kcov } void GcnoParser::onLines(const struct header *header, const uint8_t *data) { const int32_t *p32 = (const int32_t *)data; int32_t blockNo = p32[0]; const int32_t *last = &p32[header->length]; int32_t n = 0; // index p32++; // Skip blockNo // Iterate through lines while (p32 < last) { int32_t line = *p32; // File name if (line == 0) { std::string name; // Setup current file name p32 = readString(p32 + 1, name); if (name != "") m_file = name; continue; } p32++; kcov_debug(ENGINE_MSG, "GCNO basic block in function %d, nr %d %s:%d\n", m_functionId, blockNo, m_file.c_str(), line); m_basicBlocks.push_back(BasicBlockMapping(m_functionId, blockNo, m_file, line, n)); n++; } } void GcnoParser::onArcs(const struct header *header, const uint8_t *data) { const int32_t *p32 = (const int32_t *)data; int32_t blockNo = p32[0]; const int32_t *last = &p32[header->length]; unsigned int arc = 0; p32++; // Skip blockNo // Iterate through lines while (p32 < last) { int32_t destBlock = p32[0]; int32_t flags = p32[1]; // Report non-on-tree arcs if (!(flags & GCOV_ARC_ON_TREE)) m_arcs.push_back(Arc(m_functionId, blockNo, destBlock)); kcov_debug(ENGINE_MSG, "GCNO arc in function %d, %d->%d (flags %d%s)\n", m_functionId, blockNo, destBlock, flags, flags & GCOV_ARC_ON_TREE ? " OT" : ""); p32 += 2; arc++; } } GcdaParser::GcdaParser(const uint8_t *data, size_t dataSize) : GcovParser(data, dataSize), m_functionId(-1) { } size_t GcdaParser::countersForFunction(int32_t function) { if (function < 0) panic("Garbage in!"); if (m_functionToCounters.find(function) == m_functionToCounters.end()) return 0; // Simply the size of the vector return m_functionToCounters[function].size(); } int64_t GcdaParser::getCounter(int32_t function, int32_t counter) { if (function < 0 || counter < 0) panic("Garbage in!"); if (m_functionToCounters.find(function) == m_functionToCounters.end()) return -1; // List of counters CounterList_t &cur = m_functionToCounters[function]; if ((size_t)counter >= cur.size()) return -1; return cur[counter]; } bool GcdaParser::onRecord(const struct header *header, const uint8_t *data) { kcov_debug(ENGINE_MSG, "GCDA record: 0x%08x (%d bytes)\n", header->tag, header->length * 4); switch (header->tag) { case GCOV_TAG_FUNCTION: onAnnounceFunction(header, data); break; case GCOV_TAG_COUNTER_BASE: onCounterBase(header, data); break; default: break; } return true; } void GcdaParser::onAnnounceFunction(const struct header *header, const uint8_t *data) { const int32_t *p32 = (const int32_t *)data; int32_t ident = p32[0]; // FIXME! Handle checksums after this m_functionId = ident; kcov_debug(ENGINE_MSG, "GCDA function %d\n", ident); } void GcdaParser::onCounterBase(const struct header *header, const uint8_t *data) { const int32_t *p32 = (const int32_t *)data; int32_t count = header->length; // 32-bit data with 64-bit values // Store all counters in a list CounterList_t counters; for (int32_t i = 0; i < count; i += 2) { uint64_t v64 = (uint64_t)p32[i] | ((uint64_t)p32[i + 1] << 32ULL); counters.push_back(v64); kcov_debug(ENGINE_MSG, "GCDA counter %d %lld\n", i, (long long)counters.back()); } m_functionToCounters[m_functionId] = counters; } kcov_25+dfsg.orig/src/include/000077500000000000000000000000001247015272500164235ustar00rootroot00000000000000kcov_25+dfsg.orig/src/include/capabilities.hh000066400000000000000000000017221247015272500213770ustar00rootroot00000000000000#pragma once #include namespace kcov { /** * Singleton class for kcov capabilities, typically for different engines. */ class ICapabilities { public: virtual ~ICapabilities() { } /** * Add a kcov capability. * * Will panic if an unknown capability is added. * * @param name the name of the capability */ virtual void addCapability(const std::string &name) = 0; /** * Remove a kcov capability. * * Will panic if an unknown capability is removed. * * @param name the name of the capability */ virtual void removeCapability(const std::string &name) = 0; /** * Return if kcov has a capability. * * Will panic if an unknown capability is added. * * @param name the name of the capability. */ virtual bool hasCapability(const std::string &name) = 0; /** * Singleton getter. * * @return a reference to the capability singleton */ static ICapabilities &getInstance(); }; } kcov_25+dfsg.orig/src/include/collector.hh000066400000000000000000000021471247015272500207360ustar00rootroot00000000000000#pragma once #include namespace kcov { class IFileParser; class IEngine; class IFilter; /** * Run a program and collect coverage for it. */ class ICollector { public: class IListener { public: /** * Called when an address is hit. * * @param addr the address which just got executed * @param hits the number of hits for the address */ virtual void onAddressHit(uint64_t addr, unsigned long hits) = 0; }; class IEventTickListener { public: virtual void onTick() = 0; }; virtual ~ICollector() {}; /** * Register a listener for breakpoint address hits * * @param listener the listener */ virtual void registerListener(IListener &listener) = 0; /** * Register a listener for events (called on each breakpoint) */ virtual void registerEventTickListener(IEventTickListener &listener) = 0; /** * Run a program and collect coverage data * * @param filename the program to run */ virtual int run(const std::string &filename) = 0; static ICollector &create(IFileParser &elf, IEngine &engine, IFilter &filter); }; } kcov_25+dfsg.orig/src/include/configuration.hh000066400000000000000000000045451247015272500216230ustar00rootroot00000000000000#pragma once #include #include #include namespace kcov { /** * Class that hold kcov configuration (command line options) */ class IConfiguration { public: typedef enum { MODE_COLLECT_ONLY = 1, MODE_REPORT_ONLY = 2, MODE_COLLECT_AND_REPORT = 3, } RunMode_t; class IListener { public: virtual ~IListener() { } /** * Callback for configuration changes. * * Listeners will receive this call on each configuration change. The * call is made from a configuration thread, so watch out with races. * * @param key the name of the setting */ virtual void onConfigurationChanged(const std::string &key) = 0; }; typedef std::vector ConfigurationListener_t; virtual ~IConfiguration() {} /** * Print kcov usage. */ virtual void printUsage() = 0; /** * Return a value as a string. * * Will panic if the key is not present. * * @param key the key to lookup * * @return the string value */ virtual const std::string &keyAsString(const std::string &key) = 0; /** * Return a value as an integer. * * Will panic if the key is not present. * * @param key the key to lookup * * @return the integer value */ virtual int keyAsInt(const std::string &key) = 0; /** * Return a value as a string list. * * Will panic if the key is not present. * * @param key the key to lookup * * @return the values as a list, potentially empty */ virtual const std::vector &keyAsList(const std::string &key) = 0; /** * Return the coveree argv (i.e., without kcov and kcov options) * * @return The coveree argv */ virtual const char **getArgv() = 0; /** * Return the coveree argc (i.e., without kcov and kcov options) * * @return The coveree argc */ virtual unsigned int getArgc() = 0; /** * Register a configuration-changed listener * * @param listener the listener to call on changes * @param keys the keys to listen for */ virtual void registerListener(IListener &listener, const std::vector &keys) = 0; /** * Parse argc, argv and setup the configuration * * @return true if the configuration is OK */ virtual bool parse(unsigned int argc, const char *argv[]) = 0; static IConfiguration &getInstance(); }; } kcov_25+dfsg.orig/src/include/engine.hh000066400000000000000000000045551247015272500202220ustar00rootroot00000000000000#pragma once #include #include #include #include namespace kcov { class IFileParser; enum event_type { ev_error = -1, ev_breakpoint = 1, ev_signal = 2, ev_exit = 3, ev_exit_first_process = 4, ev_signal_exit = 5, }; /** * An "engine" which can run programs and collect events (e.g., breakpoints) */ class IEngine { public: class Event { public: Event(enum event_type type = ev_signal, int data = 0, uint64_t address = 0) : type(type), data(data), addr(address) { } enum event_type type; int data; // Typically the breakpoint uint64_t addr; }; class IEventListener { public: virtual void onEvent(const Event &ev) = 0; }; virtual ~IEngine() {} /** * Set a breakpoint * * @param addr the address to set the breakpoint on * * @return the ID of the breakpoint, or -1 on failure */ virtual int registerBreakpoint(unsigned long addr) = 0; /** * Fork a new process and attach to it * * @return true if OK, false otherwise */ virtual bool start(IEventListener &listener, const std::string &executable) = 0; /** * Kill child with signal * * @param sig the signal to send to the child */ virtual void kill(int sig) = 0; /** * Continue execution with an event * * @return true if the process should continue, false otherwise */ virtual bool continueExecution() = 0; }; /** * Factory class for getting one of multiple engines, which can * match different file types. */ class IEngineFactory { public: class IEngineCreator { public: IEngineCreator(); virtual IEngine *create(IFileParser &parser) = 0; /** * See if a particular file can be matched with this engine. * * Should return how well this engine fits, the higher the better * * @param filename the name of the file * @param data the first few bytes of the file * @param dataSize the size of @a data */ virtual unsigned int matchFile(const std::string &filename, uint8_t *data, size_t dataSize) = 0; }; typedef IEngine *(*EngineCreator_t)(IFileParser &parser); virtual ~IEngineFactory() { } virtual void registerEngine(IEngineCreator &creator) = 0; virtual IEngineCreator &matchEngine(const std::string &file) = 0; static IEngineFactory &getInstance(); }; } kcov_25+dfsg.orig/src/include/file-parser.hh000066400000000000000000000115361247015272500211630ustar00rootroot00000000000000#pragma once #include #include #include #include #include struct phdr_data_entry; namespace kcov { class IFilter; class IFileParser { public: enum FileFlags { FLG_NONE = 0, FLG_TYPE_SOLIB = 1, FLG_TYPE_COVERAGE_DATA = 2, //< Typically gcov data files }; enum PossibleHits { HITS_SINGLE, //< Yes/no (merge-parser) HITS_LIMITED, //< E.g., multiple branches HITS_UNLIMITED, //< Accumulated (Python/bash) }; /** * Holder class for address segments */ class Segment { public: Segment(uint64_t paddr, uint64_t vaddr, uint64_t size) : m_paddr(paddr), m_vaddr(vaddr), m_size(size) { } Segment(const Segment &other) : m_paddr(other.m_paddr), m_vaddr(other.m_vaddr), m_size(other.m_size) { } /** * Check if an address is contained within this segment * * @param addr the address to check * * @return true if valid */ bool addressIsWithinSegment(uint64_t addr) const { return addr >= m_paddr && addr < m_paddr + m_size; } /** * Adjust an address with the segment. * * @param addr the address to adjust * * @return the new address */ uint64_t adjustAddress(uint64_t addr) const { if (addressIsWithinSegment(addr)) return addr - m_paddr + m_vaddr; return addr; } uint64_t getBase() const { return m_vaddr; } size_t getSize() const { return m_size; } private: // Should really be const, but GCC 4.6 doesn't like that uint64_t m_paddr; uint64_t m_vaddr; size_t m_size; }; typedef std::vector SegmentList_t; /** * Holder class for files (e.g., ELF binaries) */ class File { public: File(const std::string &filename, const enum FileFlags flags = FLG_NONE) : m_filename(filename), m_flags(flags) { } File(const std::string &filename, const enum FileFlags flags, const SegmentList_t &segments) : m_filename(filename), m_flags(flags), m_segments(segments) { } const std::string m_filename; const enum FileFlags m_flags; const SegmentList_t m_segments; }; virtual ~IFileParser() {} /** * Listener for lines (lines in source files) * * This is the main way the (file,lineNr) -> address map is handled. */ class ILineListener { public: virtual void onLine(const std::string &file, unsigned int lineNr, uint64_t addr) = 0; }; /** * Listener for added files (typically an ELF binary) */ class IFileListener { public: virtual void onFile(const File &file) = 0; }; /** * Add a file to the parser * * @param filename the filename to add * @param phdr_data base address data for solibs * * @return true if the file could be added, false otherwise */ virtual bool addFile(const std::string &filename, struct phdr_data_entry *phdr_data = 0) = 0; /** * Register a listener for source lines. * * Will be called when new source file/line pairs are found * * @param listener the listener */ virtual void registerLineListener(ILineListener &listener) = 0; /** * Register a listener for coveree files. * * Will be called when new ELF binary (typically) is added * * @param listener the listener */ virtual void registerFileListener(IFileListener &listener) = 0; /** * Parse the added files * * @return true if parsing could be done */ virtual bool parse() = 0; /** * Get the checksum of the main file (not solibs) * * @return the file checksum */ virtual uint64_t getChecksum() = 0; /** * Get the name of the parser * * @return the name */ virtual std::string getParserType() = 0; /** * Return if this parser is of the multiple type (i.e., relying on * breakpoints which are cleared after hit, but can have branches), * or if every address can occur multiple times, or if only * covered/non-covered is possible. * * @return the possible hits of this parser */ virtual enum PossibleHits maxPossibleHits() = 0; /** * See if a particular file can be matched with this parser. * * Should return how well this parser fits, the higher the better * * @param filename the name of the file * @param data the first few bytes of the file * @param dataSize the size of @a data */ virtual unsigned int matchParser(const std::string &filename, uint8_t *data, size_t dataSize) = 0; // Setup once the parser has been chosen virtual void setupParser(IFilter *filter) = 0; }; /** * Manager class for getting one of multiple parsers, which can * match different file types. */ class IParserManager { public: virtual ~IParserManager() { } virtual void registerParser(IFileParser &parser) = 0; virtual IFileParser *matchParser(const std::string &file) = 0; static IParserManager &getInstance(); }; } kcov_25+dfsg.orig/src/include/filter.hh000066400000000000000000000011521247015272500202300ustar00rootroot00000000000000#pragma once #include namespace kcov { /** * Class for filtering output data. * * Used for the --include-path and --include-pattern command line options. */ class IFilter { public: class Handler { public: virtual bool includeFile(const std::string &file) = 0; }; /** * Run filters on @a path. * * @param path the path to check * * @return true if this path should be included in the output, false otherwise. */ virtual bool runFilters(const std::string &path) = 0; static IFilter &create(); static IFilter &createDummy(); virtual ~IFilter() {} }; } kcov_25+dfsg.orig/src/include/gcov.hh000066400000000000000000000106611247015272500177060ustar00rootroot00000000000000#include #include #include #include #include struct header; namespace kcov { /** * Combine basic block/file info into an "address" * * @param filename the source filename * @param function the function to map for * @param basicBlock the basic block number * @param lineIndex the index number in the basic block map */ static inline uint64_t gcovGetAddress(const std::string &filename, int32_t function, int32_t basicBlock, int32_t lineIndex) { uint32_t fn16 = ((uint32_t)function) & 0xffff; uint32_t bb16 = ((uint32_t)basicBlock) & 0xffff; uint64_t fnAndBb = (fn16 << 16) | bb16; uint64_t fileNameHash = (((uint64_t)std::hash()(filename)) & 0xffffff00ULL); uint64_t lineIndexMask = lineIndex & 0xffULL; return (fileNameHash << 32ULL) | (lineIndexMask << 32ULL) | fnAndBb; } class GcovParser { public: /** * Parse the gcov file. * * @return true if the file was OK, false otherwise */ bool parse(); protected: GcovParser(const uint8_t *data, size_t dataSize); virtual ~GcovParser(); virtual bool onRecord(const struct header *header, const uint8_t *data) = 0; bool verify(); const uint8_t *readString(const uint8_t *p, std::string &out); // Convenience when using 32-bit pointers const int32_t *readString(const int32_t *p, std::string &out); const uint8_t *padPointer(const uint8_t *p); private: const uint8_t *m_data; size_t m_dataSize; }; // Specialized parser for gcno files class GcnoParser : public GcovParser { public: // Holder-class for fn/bb -> file/line class BasicBlockMapping { public: int32_t m_function; int32_t m_basicBlock; std::string m_file; int32_t m_line; int32_t m_index; BasicBlockMapping(const BasicBlockMapping &other); BasicBlockMapping(int32_t function, int32_t basicBlock, const std::string &file, int32_t line, int32_t index); }; // Holder-class for arcs between blocks class Arc { public: int32_t m_function; int32_t m_srcBlock; int32_t m_dstBlock; Arc(const Arc &other); Arc(int32_t function, int32_t srcBlock, int32_t dstBlock); }; typedef std::vector BasicBlockList_t; typedef std::vector FunctionList_t; typedef std::vector ArcList_t; GcnoParser(const uint8_t *data, size_t dataSize); /* Return a reference to the basic blocks to file/line in the file. * Empty if parse() hasn't been called. * * @return the basic blocks */ const BasicBlockList_t &getBasicBlocks(); /** * Return a reference to the arcs in the file. Empty if parse() hasn't * been called. * * @return the arcs */ const ArcList_t &getArcs(); /** * Return a reference to the function IDs in the file. Empty if parse() * hasn't been called. * * @return the functions */ const FunctionList_t &getFunctions(); protected: bool onRecord(const struct header *header, const uint8_t *data); private: // Handler for record types void onAnnounceFunction(const struct header *header, const uint8_t *data); void onBlocks(const struct header *header, const uint8_t *data); void onLines(const struct header *header, const uint8_t *data); void onArcs(const struct header *header, const uint8_t *data); std::string m_file; std::string m_function; int32_t m_functionId; FunctionList_t m_functions; BasicBlockList_t m_basicBlocks; ArcList_t m_arcs; }; class GcdaParser : public GcovParser { public: GcdaParser(const uint8_t *data, size_t dataSize); /** * Return the number of counters for a particular function. * * @param function the function * * @return the number of counters, or 0 for invalid functions */ size_t countersForFunction(int32_t function); /** * Return the counter value for a function. * * @param function the function * @param counter the counter number * * @return the counter value, or -1 for invalid function/counters */ int64_t getCounter(int32_t function, int32_t counter); protected: bool onRecord(const struct header *header, const uint8_t *data); void onAnnounceFunction(const struct header *header, const uint8_t *data); void onCounterBase(const struct header *header, const uint8_t *data); typedef std::vector CounterList_t; typedef std::unordered_map FunctionToCountersMap_t; int32_t m_functionId; FunctionToCountersMap_t m_functionToCounters; }; } kcov_25+dfsg.orig/src/include/generated-data-base.hh000066400000000000000000000005241247015272500225220ustar00rootroot00000000000000#pragma once #include namespace kcov { class GeneratedData { public: GeneratedData(const uint8_t *p, size_t size) : m_data(p), m_size(size) { } const uint8_t *data() const { return m_data; } const size_t size() const { return m_size; } private: const uint8_t *m_data; size_t m_size; }; } kcov_25+dfsg.orig/src/include/lineid.hh000066400000000000000000000004041247015272500202060ustar00rootroot00000000000000#pragma once #include #include #include namespace kcov { static uint64_t getLineId(const std::string &fileName, unsigned int nr) { return (std::hash()(fileName) & 0xffffffff) | ((uint64_t)nr << 32ULL); } } kcov_25+dfsg.orig/src/include/manager.hh000066400000000000000000000001511247015272500203530ustar00rootroot00000000000000#pragma once namespace kcov { enum match_type { match_none = 0, match_perfect = 0xffffffff, }; } kcov_25+dfsg.orig/src/include/output-handler.hh000066400000000000000000000010541247015272500217170ustar00rootroot00000000000000#pragma once #include namespace kcov { class IWriter; class IReporter; class IFileParser; class ICollector; class IOutputHandler { public: virtual ~IOutputHandler() {} virtual void registerWriter(IWriter &writer) = 0; virtual void start() = 0; virtual void produce() = 0; virtual const std::string &getBaseDirectory() = 0; virtual const std::string &getOutDirectory() = 0; static IOutputHandler &create(IFileParser &parser, IReporter &reporter, ICollector &collector); static IOutputHandler &getInstance(); }; } kcov_25+dfsg.orig/src/include/phdr_data.h000066400000000000000000000013671247015272500205310ustar00rootroot00000000000000#pragma once #include #include #ifdef __cplusplus extern "C" { #endif struct phdr_data_segment { unsigned long paddr; unsigned long vaddr; unsigned long size; }; struct phdr_data_entry { char name[1024]; uint32_t n_segments; // "Reasonable" max struct phdr_data_segment segments[64]; }; struct phdr_data { uint32_t magic; uint32_t version; uint32_t n_entries; struct phdr_data_entry entries[]; }; struct dl_phdr_info; struct phdr_data *phdr_data_new(size_t size); void phdr_data_add(struct phdr_data *p, struct dl_phdr_info *info); void *phdr_data_marshal(struct phdr_data *p, size_t *out_sz); void phdr_data_free(struct phdr_data *p); struct phdr_data *phdr_data_unmarshal(void *p); #ifdef __cplusplus } #endif kcov_25+dfsg.orig/src/include/reporter.hh000066400000000000000000000047521247015272500206160ustar00rootroot00000000000000#pragma once #include #include namespace kcov { class IFileParser; class ICollector; class IFilter; /** * Interface class that reports addresses pairs being executed. * * Can also be queried for code lines etc. */ class IReporter { public: class LineExecutionCount { public: LineExecutionCount(unsigned int hits, unsigned int possibleHits) : m_hits(hits), m_possibleHits(possibleHits) { } unsigned int m_hits; unsigned int m_possibleHits; }; class ExecutionSummary { public: ExecutionSummary() : m_lines(0), m_executedLines(0), m_includeInTotals(true) { } ExecutionSummary(unsigned int lines, unsigned int executedLines) : m_lines(lines), m_executedLines(executedLines), m_includeInTotals(true) { } unsigned int m_lines; unsigned int m_executedLines; unsigned int m_includeInTotals; }; /** * Listener to executed addresses. */ class IListener { public: /** * An address has been executed by the covered program. Can also * report data from previous runs. * * @param addr the executed address * @param hits the number of hits of the address (typically 1) */ virtual void onAddress(uint64_t addr, unsigned long hits) = 0; }; virtual ~IReporter() {} /** * Register a listener for reported addresses. * * @param listener the listener */ virtual void registerListener(IListener &listener) = 0; /** * Return if a file path should be included in the output. * * @param file the file path to check * * @return true if the file should be included in the output */ virtual bool fileIsIncluded(const std::string &file) = 0; /** * Returns if a file/line pair contains executable code. * * @param file the filename * @param lineNr the line number in the file * * @return true if this is executable code, false otherwise */ virtual bool lineIsCode(const std::string &file, unsigned int lineNr) = 0; /** * Get the execution count for a file:line pair * * @param file the filename to check * @param lineNr the line to check * * @return the execution count */ virtual LineExecutionCount getLineExecutionCount(const std::string &file, unsigned int lineNr) = 0; /** * Get a summary of what has been executed so far * * @return the summary */ virtual ExecutionSummary getExecutionSummary() = 0; static IReporter &create(IFileParser &elf, ICollector &collector, IFilter &filter); }; } kcov_25+dfsg.orig/src/include/solib-handler.hh000066400000000000000000000005111247015272500214640ustar00rootroot00000000000000#pragma once #include #include namespace kcov { class ISolibHandler { public: virtual ~ISolibHandler() { } }; ISolibHandler &createSolibHandler(IFileParser &parser, ICollector &collector); // Wait for the solib thread to finish parsing the solib data void blockUntilSolibDataRead(); } kcov_25+dfsg.orig/src/include/swap-endian.hh000066400000000000000000000015461247015272500211600ustar00rootroot00000000000000#pragma once #include // From http://stackoverflow.com/questions/105252/how-do-i-convert-between-big-endian-and-little-endian-values-in-c template T swap_endian(T u) { union { T u; unsigned char u8[sizeof(T)]; } source, dest; source.u = u; for (size_t k = 0; k < sizeof(T); k++) dest.u8[k] = source.u8[sizeof(T) - k - 1]; return dest.u; } static inline bool cpu_is_little_endian() { static uint16_t data = 0x1122; uint8_t *p = (uint8_t *)&data; return p[0] == 0x22; } template T to_be(T u) { if (cpu_is_little_endian()) return swap_endian(u); else return u; } template T be_to_host(T u) { if (cpu_is_little_endian()) return swap_endian(u); else return u; } template T le_to_host(T u) { if (cpu_is_little_endian()) return u; else return swap_endian(u); } kcov_25+dfsg.orig/src/include/utils.hh000066400000000000000000000100541247015272500201040ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include #define error(x...) do \ { \ fprintf(stderr, "kcov: error: "); \ fprintf(stderr, x); \ fprintf(stderr, "\n"); \ } while(0) #define warning(x...) do \ { \ fprintf(stderr, "kcov: warning: "); \ fprintf(stderr, x); \ fprintf(stderr, "\n"); \ } while(0) #define panic(x...) do \ { \ error(x); \ exit(1); \ } while(0) enum debug_mask { INFO_MSG = 1, ENGINE_MSG = 2, ELF_MSG = 4, BP_MSG = 8, STATUS_MSG = 16, }; extern int g_kcov_debug_mask; static inline void kcov_debug(enum debug_mask dbg, const char *fmt, ...) __attribute__((format(printf,2,3))); static inline void kcov_debug(enum debug_mask dbg, const char *fmt, ...) { va_list ap; if ((g_kcov_debug_mask & dbg) == 0) return; va_start(ap, fmt); vfprintf(stdout, fmt, ap); va_end(ap); } #define panic_if(cond, x...) \ do { if ((cond)) panic(x); } while(0) static inline char *xstrdup(const char *s) { char *out = strdup(s); panic_if(!out, "strdup failed"); return out; } static inline void *xmalloc(size_t sz) { void *out = malloc(sz); panic_if(!out, "malloc failed"); memset(out, 0, sz); return out; } static inline void *xrealloc(void *p, size_t sz) { void *out = realloc(p, sz); panic_if(!out, "realloc failed"); return out; } extern int write_file(const void *data, size_t len, const char *fmt, ...) __attribute__((format(printf,3,4))); extern void *read_file(size_t *out_size, const char *fmt, ...) __attribute__((format(printf,2,3))); extern void *peek_file(size_t *out_size, const char *fmt, ...) __attribute__((format(printf,2,3))); extern std::string dir_concat(const std::string &dir, const std::string &filename); #define xwrite_file(data, len, dir...) do { \ int r = write_file(data, len, dir); \ panic_if (r != 0, "write_file failed with %d\n", r); \ } while(0) #define xsnprintf(buf, size, fmt, x...) do { \ int r = snprintf(buf, size, fmt, x); \ panic_if(r < 0 || r >= (int)(size), "snprintf failed for %s with %d\n", fmt, r); \ } while(0) extern bool file_exists(const std::string &path); extern uint64_t get_file_timestamp(const std::string &path); extern int concat_files(const char *dst, const char *file_a, const char *file_b); extern const char *get_home(); /** * Return true if a FILE * is readable without blocking. * * @param fp the file to read * @param ms the number of milliseconds to wait * * @return true if the file can be read without blocking, false otherwise */ bool file_readable(FILE *fp, unsigned int ms); unsigned long get_aligned(unsigned long addr); unsigned long get_aligned_4b(unsigned long addr); std::string fmt(const char *fmt, ...) __attribute__((format(printf,1,2))); int coin_get_current_cpu(void); int kcov_get_current_cpu(void); void kcov_tie_process_to_cpu(pid_t pid, int cpu); void mdelay(unsigned int ms); uint64_t get_ms_timestamp(void); bool machine_is_64bit(void); std::vector split_string(const std::string &s, const char *delims); std::string trim_string(const std::string &strIn); const std::string &get_real_path(const std::string &path); bool string_is_integer(const std::string &str, unsigned base = 0); int64_t string_to_integer(const std::string &str, unsigned base = 0); std::string escape_html(const std::string &str); std::string escape_json(const std::string &str); void msleep(uint64_t ms); class Semaphore { private: sem_t m_sem; public: Semaphore() { sem_init(&m_sem, 0, 0); } ~Semaphore() { sem_destroy(&m_sem); } void notify() { sem_post(&m_sem); } void wait() { sem_wait(&m_sem); } }; // Unit test stuff void mock_read_file(void *(*callback)(size_t *out_size, const char *path)); void mock_write_file(int (*callback)(const void *data, size_t size, const char *path)); void mock_file_exists(bool (*callback)(const std::string &path)); void mock_get_file_timestamp(uint64_t (*callback)(const std::string &path)); uint32_t hash_block(const void *buf, size_t len); kcov_25+dfsg.orig/src/include/writer.hh000066400000000000000000000007671247015272500202720ustar00rootroot00000000000000#pragma once namespace kcov { class IFileParser; class IReporter; /** * Class that writes output (HTML, Cobertura, ...) */ class IWriter { public: virtual ~IWriter() {} /** * Called once on startup to setup the writer. */ virtual void onStartup() = 0; /** * Called once on termination to shutdown the writer. */ virtual void onStop() = 0; /** * Write current data. * * Called in regular intervals during execution. */ virtual void write() = 0; }; } kcov_25+dfsg.orig/src/kcov-merge000077500000000000000000000300551247015272500167700ustar00rootroot00000000000000#!/usr/bin/python # # kcov-merge - merge line-based hit information from cobertura.xml files generated by # kcov and generate aggregate coverage information over all files as well # as coverage information per file # # Usage: # # kcov-merge [-o ] dir1 dir2 .. dirn # import os import shutil import sys import time from optparse import OptionParser import xml.dom.minidom from xml.dom.minidom import Node def parseFile(name): try: dom = xml.dom.minidom.parse(name) except: return None return dom #----------------------------------------------- class CovFile: def __init__(self, name, filename, low, high): self.name = name self.filename = filename self.low = low self.high = high # Dict of "line id" -> hits self.covLines={} # Coverage stats self.rate=0.0 self.lines=0 self.hits=0 # classElt is the element from cobertura XML def parse(self,classElt): lines = classElt.getElementsByTagName('lines') for linesElt in lines: for line in linesElt.childNodes: if line.nodeType == Node.ELEMENT_NODE: num=line.attributes['number'].value lineNum=int(num) hits=line.attributes['hits'].value if lineNum in self.covLines: self.covLines[lineNum] = self.covLines[lineNum] + int(hits) else: self.covLines[lineNum] = int(hits) def calculateCoverage(self): self.hits = 0 self.lines = 0 for line in self.covLines: self.lines=self.lines+1 if self.covLines[line] > 0: self.hits=self.hits+1 self.rate = (float(self.hits) / float(self.lines)) * 100 def displayCoverage(self,indexFile,coberturaFile): # print (' File %s covered lines %d total lines %d coverage %0.1f%%' % (self.filename, self.hits, self.lines, self.rate)) # Specify the type of coverage for the line based on the specified low and high limits type='lineNoCov' if self.rate > self.low: type='linePartCov' if self.rate > self.high: type='lineCov' indexFile.write("{'title':'%s','summary_name':'%s','covered_class':'%s','covered':'%0.1f','covered_lines':'%d','uncovered_lines':'%d','total_lines':'%d' },\n" % (self.filename, self.filename, type,self.rate, self.hits, (self.lines - self.hits), self.lines )) coberturaFile.write(''' ''' % ( self.name, self.filename, self.rate )) for key in sorted(self.covLines): coberturaFile.write(''' ''' % ( key, self.covLines[key] )) coberturaFile.write(''' ''') #----------------------------------------------- class CovReport: def __init__(self,directories,output,low,high): self.covFiles={} self.directories = directories self.xmlFiles={} self.output = output self.low = low self.high = high def generateIndexHtml(self): # Generate the top-level 'index.html' file outputFileName=os.path.join(self.output,'index.html') outputFile = open(outputFileName,'w') outputFile.write(''' ???
Aggregate Coverage Report
Command: ???
Date: Instrumented lines: ???
Code covered: ??? Executed lines: ???
Filename Coverage percent Covered lines Uncovered lines Executable lines
{{summary_name}} {{covered}}% {{covered_lines}} {{uncovered_lines}} {{total_lines}}
{{summary_name}} {{covered}}% {{covered_lines}} {{uncovered_lines}} {{total_lines}}

Generated by: Kcov
''') outputFile.close() def generateCoverageReport(self): for directory in self.directories: print ('Looking at directory %s' % directory) self.xmlFiles = [os.path.join(dp, f) for dp, dn, filenames in os.walk(directory) for f in filenames if os.path.splitext(f)[1] == '.xml'] for filename in self.xmlFiles: doc = parseFile(filename) if doc is None: print ('file not found') sys.exit(2) root = doc.documentElement classes = root.getElementsByTagName('classes') for classesElt in classes: for classElt in classesElt.childNodes: if classElt.nodeType == Node.ELEMENT_NODE: name = classElt.attributes['name'].value classfile = classElt.attributes['filename'].value classParser=None if name in self.covFiles: classParser=self.covFiles[name] else: self.covFiles[name]=CovFile(name,classfile,self.low,self.high) classParser=self.covFiles[name] classParser.parse(classElt) # Remove the output directory if it exists if os.path.isdir(self.output): shutil.rmtree(self.output) # Copy the 'data' directory tree from the first kcov output directory src = os.path.join(self.directories[0],'data') dst = os.path.join(self.output,'data') shutil.copytree(src,dst) # Generate the 'index.html' file self.generateIndexHtml() # Create the 'index.json' file indexFileName=os.path.join(self.output,'index.json') indexFile = open(indexFileName,'w') # Create the 'cobertura.xml' file coberturaFileName=os.path.join(self.output,'cobertura.xml') coberturaFile = open(coberturaFileName,'w') totalLines = 0 totalHits = 0 totalCov = 0.0 # Calculate coverage for all Cobertura XML files and output this to the # 'index.json' file for key in self.covFiles: c = self.covFiles[key] c.calculateCoverage() totalLines = totalLines + c.lines totalHits = totalHits + c.hits totalCov = (float(totalHits) / float(totalLines)) * 100 # print ('Total hits %d Total lines %d Total coverage %0.1f%%' % (totalHits, totalLines, totalCov)) timeStr=time.strftime('%Y-%m-%d %H:%M:%S') indexFile.write('var percent_low = %d; var percent_high = %d;\n' % (self.low,self.high)) indexFile.write("var header = { 'command' : 'Aggregate', 'date' : '%s', 'instrumented' : %d, 'covered' : %d,};\n" % (timeStr,totalLines, totalHits)) indexFile.write("var data = [\n") coberturaFile.write(''' ''' % (totalCov, int(time.time()), totalCov)) # Output individual coverate information for each Cobertura XML file for key in self.covFiles: c = self.covFiles[key] c.displayCoverage(indexFile,coberturaFile) # Close the 'index.json' file indexFile.write("];\n") indexFile.write("var merged_data = [];\n") indexFile.close() # Close the 'cobertura.xml' file coberturaFile.write(''' ''') coberturaFile.close() print ('Aggregate coverage report generated in %s' % self.output) #----------------------------------------------- class Main: def __init__(self): self.covFiles={} def main(self,argv): parser = OptionParser(usage="%prog [options] outdir dir1 [dir2 ... dirn]", version="%prog 1.0") parser.add_option("-l", "--limits", dest="limits", default="25,75", help="setup limits for low/high coverage (default 25,75)", metavar="LOW,HIGH") (options, args) = parser.parse_args() # Output directory and at least 1 kcov output directory must be specified if len(args) < 2: print ('output directory and at least 1 kcov output directory not specified') sys.exit(1) outputDir = args[0] args.remove(outputDir) directories = args limits = options.limits low=25 high=75 sp=limits.split(',') try: if len(sp) == 2: low = int(sp[0]) high = int(sp[1]) except: print ('Invalid coverage limits specified') sys.exit(1) report = CovReport(directories,outputDir,low,high) report.generateCoverageReport() if __name__ == "__main__": Main().main(sys.argv) kcov_25+dfsg.orig/src/kernel/000077500000000000000000000000001247015272500162605ustar00rootroot00000000000000kcov_25+dfsg.orig/src/kernel/Makefile000066400000000000000000000003451247015272500177220ustar00rootroot00000000000000KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) CFLAGS_kprobe-coverage.o += -g3 obj-m += kprobe-coverage.o default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules clean: rm -f *.o *.ko *.order *.symvers *.mod.c kcov_25+dfsg.orig/src/kernel/kprobe-coverage.c000066400000000000000000000315751247015272500215120ustar00rootroot00000000000000/* * lib/kprobe-coverage.c * * Copyright (C) 2014 Simon Kagstrom * * Author: Simon Kagstrom * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive * for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include struct kprobe_coverage { struct dentry *debugfs_root; struct workqueue_struct *workqueue; wait_queue_head_t wait_queue; /* Each coverage entry is on exactly one of these lists */ struct list_head deferred_list; /* Probes for not-yet-loaded-modules */ struct list_head pending_list; /* Probes which has not yet triggered */ struct list_head hit_list; /* Triggered probes awaiting readout */ const char *module_names[32]; unsigned int name_count; struct mutex lock; }; struct kprobe_coverage_entry { struct kprobe kp; int name_index; /* an index into the name table above (0 is always the kernel */ unsigned long base_addr; struct list_head lh; struct work_struct work; }; static struct kprobe_coverage *global_kpc; /* Return index into module name vector. Should be called with * mutex held. */ static int module_name_to_index(struct kprobe_coverage *kpc, const char *module_name) { int i; BUG_ON(!mutex_is_locked(&kpc->lock)); /* The first is always the kernel (NULL) */ if (module_name == NULL) return 0; for (i = 1; i < kpc->name_count; i++) { if (strcmp(kpc->module_names[i], module_name) == 0) return i; } return -1; } static int kpc_allocate_module_name_index(struct kprobe_coverage *kpc, const char *module_name) { int index; mutex_lock(&kpc->lock); index = module_name_to_index(kpc, module_name); /* Not found? If so allocate new entry unless vector is full */ if (index < 0 && kpc->name_count < ARRAY_SIZE(kpc->module_names)) { index = kpc->name_count; kpc->module_names[index] = kstrdup(module_name, GFP_KERNEL); kpc->name_count++; } mutex_unlock(&kpc->lock); return index; } static int kpc_pre_handler(struct kprobe *kp, struct pt_regs *regs) { struct kprobe_coverage_entry *entry = (struct kprobe_coverage_entry *) container_of(kp, struct kprobe_coverage_entry, kp); /* Schedule it for removal */ queue_work(global_kpc->workqueue, &entry->work); return 0; } static void kpc_probe_work(struct work_struct *work) { struct kprobe_coverage_entry *entry = (struct kprobe_coverage_entry *) container_of(work, struct kprobe_coverage_entry, work); struct kprobe_coverage *kpc = global_kpc; unregister_kprobe(&entry->kp); /* Move from pending list to the hit list */ mutex_lock(&kpc->lock); list_del(&entry->lh); list_add_tail(&entry->lh, &kpc->hit_list); /* Wake up the listener */ wake_up(&kpc->wait_queue); mutex_unlock(&kpc->lock); } static void free_entry(struct kprobe_coverage_entry *entry) { kfree(entry); } static struct kprobe_coverage_entry *new_entry(struct kprobe_coverage *kpc, const char *module_name, struct module *mod, unsigned long where) { struct kprobe_coverage_entry *out; unsigned long base_addr = 0; out = kzalloc(sizeof(*out), GFP_KERNEL); if (!out) return NULL; /* Setup the base address if we know it, otherwise keep at 0 */ if (mod) base_addr = (unsigned long)mod->module_core; out->name_index = kpc_allocate_module_name_index(kpc, module_name); out->base_addr = base_addr; out->kp.addr = (void *)(base_addr + where); out->kp.pre_handler = kpc_pre_handler; INIT_WORK(&out->work, kpc_probe_work); return out; } /* Called with the lock held */ static int enable_probe(struct kprobe_coverage *kpc, struct kprobe_coverage_entry *entry) { int err; BUG_ON(!mutex_is_locked(&kpc->lock)); /* Done before enable_kprobe so that it's really on the list if * triggered */ list_add(&entry->lh, &kpc->pending_list); if ( (err = register_kprobe(&entry->kp)) < 0) { list_del(&entry->lh); return -EINVAL; } return 0; } /* Called with the lock held */ static void defer_probe(struct kprobe_coverage *kpc, struct kprobe_coverage_entry *entry) { list_add(&entry->lh, &kpc->deferred_list); } static void kpc_add_probe(struct kprobe_coverage *kpc, const char *module_name, unsigned long where) { struct module *module = NULL; struct kprobe_coverage_entry *entry; int rv = 0; /* Lookup the module which should be instrumented */ if (module_name) { preempt_disable(); module = find_module(module_name); preempt_enable(); } entry = new_entry(kpc, module_name, module, where); /* Three cases: * * 1. pending module - module_name is !NULL, module is NULL: Defer * instrumentation * * 2. vmlinux - module_name and module is NULL: Instrument directly * * 3. loaded module - module_name and module is !NULL: Instrument directly */ mutex_lock(&kpc->lock); if (module_name && !module) defer_probe(kpc, entry); else rv = enable_probe(kpc, entry); mutex_unlock(&kpc->lock); /* Probe enabling might fail, just free the entry */ if (rv < 0) free_entry(entry); } /* Called with lock held */ static void clear_list(struct kprobe_coverage *kpc, struct list_head *list, int do_unregister) { struct list_head *iter; struct list_head *tmp; BUG_ON(!mutex_is_locked(&kpc->lock)); list_for_each_safe(iter, tmp, list) { struct kprobe_coverage_entry *entry; entry = (struct kprobe_coverage_entry *)container_of(iter, struct kprobe_coverage_entry, lh); if (do_unregister) unregister_kprobe(&entry->kp); list_del(&entry->lh); free_entry(entry); } } static void kpc_clear(struct kprobe_coverage *kpc) { int i; /* Free everything on the lists */ mutex_lock(&kpc->lock); clear_list(kpc, &kpc->deferred_list, 0); clear_list(kpc, &kpc->hit_list, 1); clear_list(kpc, &kpc->pending_list, 1); INIT_LIST_HEAD(&kpc->deferred_list); INIT_LIST_HEAD(&kpc->pending_list); INIT_LIST_HEAD(&kpc->hit_list); for (i = 0; i < kpc->name_count; i++) { kfree(kpc->module_names[i]); kpc->module_names[i] = NULL; } /* Kernel is still at 0 */ kpc->name_count = 1; mutex_unlock(&kpc->lock); } static void *kpc_unlink_next(struct kprobe_coverage *kpc) { struct kprobe_coverage_entry *entry = NULL; /* Remove the next entry, if any */ mutex_lock(&kpc->lock); if (!list_empty(&kpc->hit_list)) { entry = list_first_entry(&kpc->hit_list, struct kprobe_coverage_entry, lh); list_del(&entry->lh); } mutex_unlock(&kpc->lock); return entry; } static void *kpc_seq_start(struct seq_file *s, loff_t *pos) { struct kprobe_coverage *kpc = s->private; int rv; /* Wait for something to arrive on the hit list, abort on signal */ rv = wait_event_interruptible(kpc->wait_queue, !list_empty(&kpc->hit_list)); if (rv< 0) return NULL; return kpc_unlink_next(kpc); } static void *kpc_seq_next(struct seq_file *s, void *v, loff_t *pos) { struct kprobe_coverage *kpc = s->private; /* Returns NULL if there is nothing more to handle */ return kpc_unlink_next(kpc); } static void kpc_seq_stop(struct seq_file *s, void *v) { } static int kpc_seq_show(struct seq_file *s, void *v) { struct kprobe_coverage *kpc = s->private; struct kprobe_coverage_entry *entry = v; const char *module_name; unsigned long addr; /* Lookup the module name from the module table */ module_name = kpc->module_names[entry->name_index]; addr = (unsigned long)entry->kp.addr - entry->base_addr; seq_printf(s, "%s%s0x%016lx\n", module_name ? module_name : "", module_name ? ":" : "", addr); free_entry(entry); return 0; } static struct seq_operations kpc_seq_ops = { .start = kpc_seq_start, .next = kpc_seq_next, .stop = kpc_seq_stop, .show = kpc_seq_show }; static int kpc_show_open(struct inode *inode, struct file *file) { struct kprobe_coverage *kpc = inode->i_private; int rv; rv = seq_open(file, &kpc_seq_ops); if (rv == 0) { struct seq_file *s = file->private_data; s->private = kpc; } return rv; } static ssize_t kpc_control_write(struct file *file, const char __user *user_buf, size_t count, loff_t *off) { struct kprobe_coverage *kpc = file->private_data; ssize_t out = 0; char *line; char *buf; char *p; if (count > PAGE_SIZE) return -E2BIG; /* Assure it's NULL terminated */ buf = (char *)kzalloc(count + 1, GFP_KERNEL); if (!buf) return -ENOMEM; if (copy_from_user(buf, user_buf, count)) { kfree(buf); return -EFAULT; } p = buf; while ( (line = strsep(&p, "\r\n")) ) { unsigned long addr; char *module = NULL; /* Assume for the kernel */ char *colon; char *addr_p; char *endp; if (!*line || !p) break; out += p - line; if (strcmp(line, "clear") == 0) { kpc_clear(kpc); break; } colon = strstr(line, ":"); if (colon) { /* For a module */ addr_p = colon + 1; *colon = '\0'; module = line; } else addr_p = line; addr = simple_strtoul(addr_p, &endp, 16); if (endp == addr_p || *endp != '\0') continue; kpc_add_probe(kpc, module, addr); } kfree(buf); *off += out; return out; } static int kpc_control_open(struct inode *inode, struct file *file) { file->private_data = inode->i_private; /* kpc */ return 0; } static void kpc_handle_coming_module(struct kprobe_coverage *kpc, struct module *mod) { struct list_head *iter; struct list_head *tmp; mutex_lock(&kpc->lock); list_for_each_safe(iter, tmp, &kpc->deferred_list) { struct kprobe_coverage_entry *entry; entry = (struct kprobe_coverage_entry *)container_of(iter, struct kprobe_coverage_entry, lh); if (module_name_to_index(kpc, mod->name) != entry->name_index) continue; /* Move the deferred entry to the pending list and enable */ list_del(&entry->lh); entry->base_addr = (unsigned long)mod->module_core; entry->kp.addr += entry->base_addr; if (enable_probe(kpc, entry) < 0) free_entry(entry); } mutex_unlock(&kpc->lock); } static void kpc_handle_going_module(struct kprobe_coverage *kpc, struct module *mod) { struct list_head *iter; struct list_head *tmp; mutex_lock(&kpc->lock); list_for_each_safe(iter, tmp, &kpc->pending_list) { struct kprobe_coverage_entry *entry; entry = (struct kprobe_coverage_entry *)container_of(iter, struct kprobe_coverage_entry, lh); if (module_name_to_index(kpc, mod->name) != entry->name_index) continue; /* Remove pending entries for the current module */ unregister_kprobe(&entry->kp); list_del(&entry->lh); free_entry(entry); } mutex_unlock(&kpc->lock); } static int kpc_module_notifier(struct notifier_block *nb, unsigned long event, void *data) { struct module *mod = data; if (event == MODULE_STATE_COMING) kpc_handle_coming_module(global_kpc, mod); else if (event == MODULE_STATE_GOING) kpc_handle_going_module(global_kpc, mod); return 0; } static const struct file_operations kpc_control_fops = { .owner = THIS_MODULE, .open = kpc_control_open, .write = kpc_control_write }; static const struct file_operations kpc_show_fops = { .owner = THIS_MODULE, .open = kpc_show_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release, }; static struct notifier_block kpc_module_notifier_block = { .notifier_call = kpc_module_notifier, }; static int __init kpc_init(struct kprobe_coverage *kpc) { kpc->workqueue = create_singlethread_workqueue("kprobe-coverage"); if (!kpc->workqueue) return -ENODEV; /* Create debugfs entries */ kpc->debugfs_root = debugfs_create_dir("kprobe-coverage", NULL); if (!kpc->debugfs_root) { printk(KERN_ERR "kprobe-coverage: creating root dir failed\n"); goto out_workqueue; } if (!debugfs_create_file("control", 0200, kpc->debugfs_root, kpc, &kpc_control_fops)) goto out_files; if (!debugfs_create_file("show", 0400, kpc->debugfs_root, kpc, &kpc_show_fops)) goto out_files; if (register_module_notifier(&kpc_module_notifier_block) < 0) goto out_files; INIT_LIST_HEAD(&kpc->pending_list); INIT_LIST_HEAD(&kpc->hit_list); INIT_LIST_HEAD(&kpc->deferred_list); init_waitqueue_head(&kpc->wait_queue); mutex_init(&kpc->lock); /* The kernel is always index 0 */ kpc->name_count = 1; return 0; out_files: debugfs_remove_recursive(kpc->debugfs_root); out_workqueue: destroy_workqueue(kpc->workqueue); return -EINVAL; } static int __init kpc_init_module(void) { int out; global_kpc = kzalloc(sizeof(*global_kpc), GFP_KERNEL); if (!global_kpc) return -ENOMEM; out = kpc_init(global_kpc); if (out < 0) { kfree(global_kpc); global_kpc = NULL; } return out; } static void __exit kpc_exit_module(void) { kpc_clear(global_kpc); debugfs_remove_recursive(global_kpc->debugfs_root); unregister_module_notifier(&kpc_module_notifier_block); destroy_workqueue(global_kpc->workqueue); kfree(global_kpc); global_kpc = NULL; } module_init(kpc_init_module); module_exit(kpc_exit_module); MODULE_AUTHOR("Simon Kagstrom "); MODULE_DESCRIPTION("Code coverage through kprobes and debugfs"); MODULE_LICENSE("GPL"); kcov_25+dfsg.orig/src/main.cc000066400000000000000000000134261247015272500162410ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "merge-parser.hh" #include "writers/html-writer.hh" #include "writers/coveralls-writer.hh" #include "writers/cobertura-writer.hh" using namespace kcov; static IEngine *g_engine; static IOutputHandler *g_output; static ICollector *g_collector; static IReporter *g_reporter; static ISolibHandler *g_solibHandler; static IFilter *g_filter; static IFilter *g_dummyFilter; static void do_cleanup() { delete g_collector; delete g_output; delete g_reporter; delete g_solibHandler; // Before the engine since a SIGTERM is sent to the thread delete g_engine; delete g_filter; delete g_dummyFilter; } static void ctrlc(int sig) { // Forward the signal to the traced program g_engine->kill(sig); } static void daemonize(void) { pid_t child; int res; IConfiguration &conf = IConfiguration::getInstance(); std::string fifoName = conf.keyAsString("target-directory") + "/done.fifo"; unlink(fifoName.c_str()); res = mkfifo(fifoName.c_str(), 0600); panic_if(res < 0, "Can't create FIFO"); child = fork(); if (child < 0) { panic("Fork failed?\n"); } else if (child == 0) { child = fork(); if (child < 0) { panic("Fork failed?\n"); } else if (child > 0) { // Second parent exit(0); } } else { // Parent FILE *fp; char buf[255]; char *endp; char *p; unsigned long out; fp = fopen(fifoName.c_str(), "r"); panic_if (!fp, "Can't open FIFO"); p = fgets(buf, sizeof(buf), fp); panic_if (!p, "Can't read FIFO"); out = strtoul(p, &endp, 10); if (p == endp) out = 0; exit(out); } } // Return the number of metadata directories in the kcov output path unsigned int countMetadata() { IConfiguration &conf = IConfiguration::getInstance(); std::string base = conf.keyAsString("out-directory"); DIR *dir; struct dirent *de; dir = ::opendir(base.c_str()); if (!dir) return 0; unsigned int out = 0; // Count metadata directories for (de = ::readdir(dir); de; de = ::readdir(dir)) { std::string cur = base + de->d_name + "/metadata"; // ... except for the current coveree if (de->d_name == conf.keyAsString("binary-name")) continue; DIR *metadataDir; struct dirent *de2; metadataDir = ::opendir(cur.c_str()); if (!metadataDir) continue; // Count metadata files unsigned int datum = 0; for (de2 = ::readdir(metadataDir); de2; de2 = ::readdir(metadataDir)) { if (de2->d_name[0] == '.') continue; datum++; } out += !!datum; ::closedir(metadataDir); } ::closedir(dir); return out; } int main(int argc, const char *argv[]) { IConfiguration &conf = IConfiguration::getInstance(); if (!conf.parse(argc, argv)) return 1; std::string file = conf.keyAsString("binary-path") + conf.keyAsString("binary-name"); IFileParser *parser = IParserManager::getInstance().matchParser(file); if (!parser) { conf.printUsage(); return 1; } parser->addFile(file); // Match and create an engine IEngineFactory::IEngineCreator &engineCreator = IEngineFactory::getInstance().matchEngine(file); IEngine *engine = engineCreator.create(*parser); if (!engine) { conf.printUsage(); return 1; } IFilter &filter = IFilter::create(); IFilter &dummyFilter = IFilter::createDummy(); ICollector &collector = ICollector::create(*parser, *engine, filter); IReporter &reporter = IReporter::create(*parser, collector, filter); IOutputHandler &output = IOutputHandler::create(*parser, reporter, collector); ISolibHandler &solibHandler = createSolibHandler(*parser, collector); IConfiguration::RunMode_t runningMode = (IConfiguration::RunMode_t)conf.keyAsInt("running-mode"); // Register writers if (runningMode != IConfiguration::MODE_COLLECT_ONLY) { const std::string &base = output.getBaseDirectory(); const std::string &out = output.getOutDirectory(); IWriter &htmlWriter = createHtmlWriter(*parser, reporter, base, out, conf.keyAsString("binary-name")); IWriter &coberturaWriter = createCoberturaWriter(*parser, reporter, out + "/cobertura.xml"); IWriter &coverallsWriter = createCoverallsWriter(*parser, reporter); // The merge parser is both a parser, a writer and a collector (!) IMergeParser &mergeParser = createMergeParser(*parser, reporter, base, out, filter); IReporter &mergeReporter = IReporter::create(mergeParser, mergeParser, dummyFilter); IWriter &mergeHtmlWriter = createHtmlWriter(mergeParser, mergeReporter, base, base + "/kcov-merged", "[merged]", false); IWriter &mergeCoberturaWriter = createCoberturaWriter(mergeParser, mergeReporter, base + "kcov-merged/cobertura.xml"); (void)mkdir(fmt("%s/kcov-merged", base.c_str()).c_str(), 0755); reporter.registerListener(mergeParser); output.registerWriter(mergeParser); // Only one covered binary? No need for merging writers then if (countMetadata() > 0) { output.registerWriter(mergeHtmlWriter); output.registerWriter(mergeCoberturaWriter); } output.registerWriter(htmlWriter); output.registerWriter(coberturaWriter); output.registerWriter(coverallsWriter); } g_engine = engine; g_output = &output; g_reporter = &reporter; g_collector = &collector; g_solibHandler = &solibHandler; g_filter = &filter; g_dummyFilter = &dummyFilter; signal(SIGINT, ctrlc); signal(SIGTERM, ctrlc); if (conf.keyAsInt("daemonize-on-first-process-exit")) daemonize(); parser->setupParser(&filter); output.start(); int ret = 0; if (runningMode != IConfiguration::MODE_REPORT_ONLY) { ret = collector.run(file); } else { parser->parse(); } do_cleanup(); return ret; } kcov_25+dfsg.orig/src/merge-file-parser.cc000066400000000000000000000333171247015272500206240ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "merge-parser.hh" using namespace kcov; #define MERGE_MAGIC 0x4d6f6172 // "Moar" #define MERGE_VERSION 4 struct line_entry { uint32_t line; uint32_t address_start; uint32_t n_addresses; }; struct file_data { uint32_t magic; uint32_t version; uint32_t size; uint32_t checksum; uint64_t timestamp; uint32_t n_entries; uint32_t address_table_offset; uint32_t file_name_offset; struct line_entry entries[]; } __attribute__((packed)); // Unit test stuff namespace merge_parser { class marshal; class output; class input; } class MergeParser : public IMergeParser, public IFileParser::ILineListener, public IFileParser::IFileListener { public: friend class merge_parser::marshal; friend class merge_parser::output; friend class merge_parser::input; MergeParser(IFileParser &localParser, IReporter &reporter, const std::string &baseDirectory, const std::string &outputDirectory, IFilter &filter) : m_baseDirectory(baseDirectory), m_outputDirectory(outputDirectory), m_filter(filter) { reporter.registerListener(*this); localParser.registerFileListener(*this); localParser.registerLineListener(*this); } ~MergeParser() { for (FileByNameMap_t::iterator it = m_files.begin(); it != m_files.end(); ++it) { File *cur = it->second; delete cur; } m_files.clear(); } // From IFileParser virtual bool addFile(const std::string &filename, struct phdr_data_entry *phdr_data) { return true; } virtual void registerLineListener(IFileParser::ILineListener &listener) { m_lineListeners.push_back(&listener); } // Unused ... virtual void registerFileListener(IFileParser::IFileListener &listener) { } virtual bool parse() { return true; } virtual uint64_t getChecksum() { return 0; } std::string getParserType() { return "merge"; } enum IFileParser::PossibleHits maxPossibleHits() { return IFileParser::HITS_SINGLE; // Hits from multiple binaries } void setupParser(IFilter *filter) { } // ... to here virtual unsigned int matchParser(const std::string &filename, uint8_t *data, size_t dataSize) { return match_none; } // From IReporter::IListener void onAddress(uint64_t addr, unsigned long hits) { if (m_fileLineByAddress.find(addr) == m_fileLineByAddress.end()) { m_pendingHits[addr] = hits; return; } addr = m_fileLineByAddress[addr]; File *file = m_filesByAddress[addr]; if (!file) return; file->registerHits(addr, hits); for (CollectorListenerList_t::const_iterator it = m_collectorListeners.begin(); it != m_collectorListeners.end(); ++it) (*it)->onAddressHit(addr, hits); } // From IFileParser::ILineListener void onLine(const std::string &filename, unsigned int lineNr, uint64_t addr) { if (!m_filter.runFilters(filename)) { return; } // Nothing to do in that case if (!file_exists(filename)) return; File *file; file = m_files[filename]; if (!file) { file = new File(filename); m_files[filename] = file; } uint64_t addrHash = hashAddress(filename, lineNr, addr) & ~(1ULL << 63); m_fileLineByAddress[addr] = addrHash; // Mark as a local file file->setLocal(); file->addLine(lineNr, addrHash); // Record this for the collector hits m_filesByAddress[addrHash] = file; for (LineListenerList_t::const_iterator it = m_lineListeners.begin(); it != m_lineListeners.end(); ++it) (*it)->onLine(filename, lineNr, addrHash); /* * Visit pending addresses for this file/line. onAddress gets a non- * hashed address, so replicate that behavior here. */ AddrToHitsMap_t::iterator pend = m_pendingHits.find(addr); if (pend != m_pendingHits.end()) { onAddress(addr, pend->second); m_pendingHits.erase(addr); } } // From IFileParser::IFileListener void onFile(const IFileParser::File &file) { } // From IWriter void onStartup() { (void)mkdir(m_baseDirectory.c_str(), 0755); (void)mkdir(m_outputDirectory.c_str(), 0755); (void)mkdir(fmt("%s/metadata", m_outputDirectory.c_str()).c_str(), 0755); } void onStop() { parseStoredData(); /* Produce something like * * /tmp/kcov/calc/metadata/4f332bca * /tmp/kcov/calc/metadata/cd9932a1 * * For all the files we've covered. The output filename comes from a hash of * the input filename. */ for (FileByNameMap_t::const_iterator it = m_files.begin(); it != m_files.end(); ++it) { // Only marshal local files if (!it->second->m_local) continue; const struct file_data *fd = marshalFile(it->second->m_filename); if (!fd) continue; uint32_t crc = hash_block((const void *)it->second->m_filename.c_str(), it->second->m_filename.size()); std::string name = fmt("%08x", crc); write_file((const void *)fd, be_to_host(fd->size), "%s/metadata/%s", m_outputDirectory.c_str(), name.c_str() ); free((void *)fd); } } void write() { } // From ICollector virtual void registerListener(ICollector::IListener &listener) { m_collectorListeners.push_back(&listener); } virtual void registerEventTickListener(ICollector::IEventTickListener &listener) { } virtual int run(const std::string &filename) { // Not used panic("Should not call run here"); return 0; } private: uint64_t hashAddress(const std::string &filename, unsigned int lineNr, uint64_t addr) { // Convert address into a suitable format for the merge parser uint64_t addrHash = (uint64_t)hash_block(filename.c_str(), filename.size()) | ((uint64_t)lineNr << 32ULL); return addrHash; } void parseStoredData() { DIR *dir; struct dirent *de; dir = opendir(m_baseDirectory.c_str()); panic_if(!dir, "Can't open directory %s\n", m_baseDirectory.c_str()); // Unmarshal and parse all metadata for (de = readdir(dir); de; de = readdir(dir)) { std::string cur = m_baseDirectory + de->d_name; // ... except for the current coveree if (cur == m_outputDirectory) continue; parseDirectory(cur); } closedir(dir); } void parseDirectory(const std::string &dirName) { DIR *dir; struct dirent *de; std::string metadataDirName = dirName + "/metadata"; dir = opendir(metadataDirName.c_str()); // Can occur naturally if(!dir) return; // Read all metadata from the directory for (de = readdir(dir); de; de = readdir(dir)) parseOne(metadataDirName, de->d_name); closedir(dir); } void parseOne(const std::string &metadataDirName, const std::string &curFile) { size_t size; // Not as hash? if (!string_is_integer(curFile, 16)) return; struct file_data *fd = (struct file_data *)read_file(&size, "%s/%s", metadataDirName.c_str(), curFile.c_str()); if (!fd) return; if (size >= sizeof(struct file_data) && unMarshalFile(fd)) { parseFileData(fd); } free(fd); } void parseFileData(struct file_data *fd) { std::string filename((const char *)fd + fd->file_name_offset); // Do something File *file; file = m_files[filename]; if (!file) { file = new File(filename); m_files[filename] = file; } else { // Checksum doesn't match, ignore this file if (file->m_checksum != fd->checksum || file->m_fileTimestamp != fd->timestamp) return; } uint64_t *addrTable = (uint64_t *)((char *)fd + fd->address_table_offset); for (unsigned i = 0; i < fd->n_entries; i++) { uint32_t lineNr = fd->entries[i].line; for (unsigned ia = 0; ia < fd->entries[i].n_addresses; ia++) { uint64_t addr = addrTable[fd->entries[i].address_start + ia]; // Check if this was a hit (and remove the hit bit from the address) bool hit = (addr & (1ULL << 63)); addr &= ~(1ULL << 63); file->addLine(lineNr, addr); for (LineListenerList_t::const_iterator it = m_lineListeners.begin(); it != m_lineListeners.end(); ++it) (*it)->onLine(filename, lineNr, addr & ~(1ULL << 63)); // Register and report the hit if (hit) { file->registerHits(lineNr, 1); for (CollectorListenerList_t::const_iterator itC = m_collectorListeners.begin(); itC != m_collectorListeners.end(); ++itC) (*itC)->onAddressHit(addr, 1); } } } } const struct file_data *marshalFile(const std::string &filename) { File *file = m_files[filename]; if (!file) return NULL; uint32_t n_addrs = 0; for (LineAddrMap_t::const_iterator it = file->m_lines.begin(); it != file->m_lines.end(); ++it) { n_addrs += it->second.size(); } // Header + each line + the filename size_t size = sizeof(struct file_data) + file->m_lines.size() * sizeof(struct line_entry) + n_addrs * sizeof(uint64_t) + file->m_filename.size() + 1 + 8; // allow padding of the address table struct file_data *out = (struct file_data *)xmalloc(size); out->magic = to_be(MERGE_MAGIC); out->version = to_be(MERGE_VERSION); out->checksum = to_be(file->m_checksum); out->timestamp = to_be(file->m_fileTimestamp); out->size = to_be(size); out->n_entries = to_be(file->m_lines.size()); struct line_entry *p = out->entries; // Point to address table uint64_t *addrTable = (uint64_t *)((char *)p + sizeof(struct line_entry) * file->m_lines.size()); unsigned long addrTable_8 = (unsigned long)addrTable; uint32_t tableOffset = 0; // 64-bit align address table if ((addrTable_8 & 7) != 0) { addrTable_8 += 8 - (addrTable_8 & 7); addrTable = (uint64_t *)addrTable_8; } for (LineAddrMap_t::const_iterator it = file->m_lines.begin(); it != file->m_lines.end(); ++it) { uint32_t line = it->first; p->line = to_be(line); p->address_start = to_be(tableOffset); p->n_addresses = to_be(it->second.size()); for (AddrMap_t::const_iterator itAddr = it->second.begin(); itAddr != it->second.end(); ++itAddr) { uint64_t addr = itAddr->first; if (file->m_addrHits[addr]) addr |= (1ULL << 63); addrTable[tableOffset] = to_be(addr); tableOffset++; } p++; } uint32_t tableStart = (uint32_t)((char *)addrTable - (char *)out); out->address_table_offset = to_be(tableStart); char *p_name = (char *)out + tableStart + tableOffset * sizeof(uint64_t); // Allocated with the terminator above strcpy(p_name, file->m_filename.c_str()); out->file_name_offset = to_be(p_name - (char *)out); return out; } bool unMarshalFile(struct file_data *fd) { fd->magic = be_to_host(fd->magic); fd->version = be_to_host(fd->version); fd->checksum = be_to_host(fd->checksum); fd->timestamp = be_to_host(fd->timestamp); fd->size = be_to_host(fd->size); fd->n_entries = be_to_host(fd->n_entries); fd->file_name_offset = be_to_host(fd->file_name_offset); fd->address_table_offset = be_to_host(fd->address_table_offset); if (fd->magic != MERGE_MAGIC) return false; if (fd->version != MERGE_VERSION) return false; struct line_entry *p = fd->entries; // Unmarshal entries... for (unsigned i = 0; i < fd->n_entries; i++) { p->line = be_to_host(p->line); p->n_addresses = be_to_host(p->n_addresses); p->address_start = be_to_host(p->address_start); p++; } // ... and the address table uint64_t *addressTable = (uint64_t *)((char *)fd + fd->address_table_offset); for (unsigned i = 0; i < (fd->file_name_offset - fd->address_table_offset) / sizeof(uint64_t); i++) addressTable[i] = be_to_host(addressTable[i]); return true; } typedef std::unordered_map AddrMap_t; typedef std::unordered_map LineAddrMap_t; class File { public: File(const std::string &filename) : m_filename(filename), m_local(false) { void *data; size_t size; data = read_file(&size, "%s", filename.c_str()); panic_if(!data, "File %s exists, but can't be read???", filename.c_str()); m_checksum = hash_block(data, size); m_fileTimestamp = get_file_timestamp(filename.c_str()); free((void *)data); } void setLocal() { m_local = true; } void addLine(unsigned int lineNr, uint64_t addr) { m_lines[lineNr][addr]++; } void registerHits(uint64_t addr, unsigned int hits) { m_addrHits[addr] += hits; } std::string m_filename; uint64_t m_fileTimestamp; LineAddrMap_t m_lines; AddrMap_t m_addrHits; uint32_t m_checksum; bool m_local; }; typedef std::vector CollectorListenerList_t; typedef std::unordered_map FileByNameMap_t; typedef std::unordered_map FileByAddressMap_t; typedef std::unordered_map FileLineByAddress_t; typedef std::unordered_map AddrToHitsMap_t; typedef std::unordered_map AddressByFileLine_t; typedef std::vector LineListenerList_t; // All files in the current coverage session FileByNameMap_t m_files; FileByAddressMap_t m_filesByAddress; FileLineByAddress_t m_fileLineByAddress; AddrToHitsMap_t m_pendingHits; LineListenerList_t m_lineListeners; const std::string m_baseDirectory; const std::string m_outputDirectory; CollectorListenerList_t m_collectorListeners; IFilter &m_filter; }; namespace kcov { IMergeParser &createMergeParser(IFileParser &localParser, IReporter &reporter, const std::string &baseDirectory, const std::string &outputDirectory, IFilter &filter) { return *new MergeParser(localParser, reporter, baseDirectory, outputDirectory, filter); } } kcov_25+dfsg.orig/src/merge-parser.hh000066400000000000000000000006161247015272500177150ustar00rootroot00000000000000#pragma once #include #include namespace kcov { class IFileParser; class IMergeParser : public IFileParser, public IReporter::IListener, public IWriter, public ICollector { }; IMergeParser &createMergeParser(IFileParser &localParser, IReporter &reporter, const std::string &baseDirectory, const std::string &outputDirectory, IFilter &filter); } kcov_25+dfsg.orig/src/output-handler.cc000066400000000000000000000056411247015272500202700ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include namespace kcov { class OutputHandler : public IOutputHandler, public IFileParser::IFileListener, public ICollector::IEventTickListener { public: OutputHandler(IFileParser &parser, IReporter &reporter, ICollector &collector) { IConfiguration &conf = IConfiguration::getInstance(); m_baseDirectory = conf.keyAsString("out-directory"); m_outDirectory = conf.keyAsString("target-directory") + "/"; m_summaryDbFileName = m_outDirectory + "/summary.db"; m_outputInterval = conf.keyAsInt("output-interval"); m_lastTimestamp = get_ms_timestamp(); (void)mkdir(m_baseDirectory.c_str(), 0755); (void)mkdir(m_outDirectory.c_str(), 0755); parser.registerFileListener(*this); collector.registerEventTickListener(*this); } ~OutputHandler() { stop(); // Delete all writers for (WriterList_t::iterator it = m_writers.begin(); it != m_writers.end(); ++it) { IWriter *cur = *it; delete cur; } } const std::string &getBaseDirectory() { return m_baseDirectory; } const std::string &getOutDirectory() { return m_outDirectory; } void registerWriter(IWriter &writer) { m_writers.push_back(&writer); } // From IElf::IFileListener void onFile(const IFileParser::File &file) { } void start() { for (WriterList_t::const_iterator it = m_writers.begin(); it != m_writers.end(); ++it) (*it)->onStartup(); } void stop() { for (WriterList_t::const_iterator it = m_writers.begin(); it != m_writers.end(); ++it) (*it)->onStop(); // Produce output after stop if anyone yields new data in onStop() produce(); } void produce() { for (WriterList_t::const_iterator it = m_writers.begin(); it != m_writers.end(); ++it) (*it)->write(); } // From ICollector::IEventTickListener void onTick() { if (m_outputInterval == 0) return; if (get_ms_timestamp() - m_lastTimestamp >= m_outputInterval) { produce(); // Take a new timestamp since producing might take a long time m_lastTimestamp = get_ms_timestamp(); } } private: typedef std::vector WriterList_t; std::string m_outDirectory; std::string m_baseDirectory; std::string m_summaryDbFileName; WriterList_t m_writers; unsigned int m_outputInterval; uint64_t m_lastTimestamp; }; static OutputHandler *instance; IOutputHandler &IOutputHandler::create(IFileParser &parser, IReporter &reporter, ICollector &collector) { if (!instance) instance = new OutputHandler(parser, reporter, collector); return *instance; } IOutputHandler &IOutputHandler::getInstance() { return *instance; } } kcov_25+dfsg.orig/src/parser-manager.cc000066400000000000000000000021001247015272500202040ustar00rootroot00000000000000#include #include #include using namespace kcov; class ParserManager : public IParserManager { public: ParserManager() { } void registerParser(IFileParser &parser) { m_parsers.push_back(&parser); } IFileParser *matchParser(const std::string &fileName) { IFileParser *best = NULL; size_t sz; uint8_t *data = (uint8_t *)peek_file(&sz, "%s", fileName.c_str()); if (!data) return NULL; for (ParserList_t::const_iterator it = m_parsers.begin(); it != m_parsers.end(); ++it) { unsigned int myVal = (*it)->matchParser(fileName, data, sz); if (myVal == match_none) continue; if (!best) best = *it; unsigned int bestVal = best->matchParser(fileName, data, sz); if (myVal > bestVal) best = *it; } free((void *)data); return best; } private: typedef std::vector ParserList_t; ParserList_t m_parsers; }; static ParserManager *g_instance; IParserManager &IParserManager::getInstance() { if (!g_instance) g_instance = new ParserManager(); return *g_instance; } kcov_25+dfsg.orig/src/parsers/000077500000000000000000000000001247015272500164575ustar00rootroot00000000000000kcov_25+dfsg.orig/src/parsers/elf-parser.cc000066400000000000000000000337771247015272500210470ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #include using namespace kcov; enum SymbolType { SYM_NORMAL = 0, SYM_DYNAMIC = 1, }; class ElfInstance : public IFileParser { public: ElfInstance() { m_elf = NULL; m_filename = ""; m_checksum = 0; m_elfIs32Bit = true; m_isMainFile = true; m_initialized = false; m_filter = NULL; IParserManager::getInstance().registerParser(*this); } virtual ~ElfInstance() { } uint64_t getChecksum() { return m_checksum; } std::string getParserType() { return "ELF"; } bool elfIs64Bit() { return !m_elfIs32Bit; } void setupParser(IFilter *filter) { m_filter = filter; } enum IFileParser::PossibleHits maxPossibleHits() { return IFileParser::HITS_LIMITED; // Breakpoints are cleared after a hit } unsigned int matchParser(const std::string &filename, uint8_t *data, size_t dataSize) { Elf32_Ehdr *hdr = (Elf32_Ehdr *)data; if (memcmp(hdr->e_ident, ELFMAG, strlen(ELFMAG)) == 0) return match_perfect; return match_none; } bool addFile(const std::string &filename, struct phdr_data_entry *data) { if (!m_initialized) { /******* Swap debug source root with runtime source root *****/ m_origRoot = IConfiguration::getInstance().keyAsString("orig-path-prefix"); m_newRoot = IConfiguration::getInstance().keyAsString("new-path-prefix"); panic_if(elf_version(EV_CURRENT) == EV_NONE, "ELF version failed\n"); m_initialized = true; } m_filename = filename; m_buildId.clear(); m_curSegments.clear(); m_executableSegments.clear(); for (uint32_t i = 0; data && i < data->n_segments; i++) { struct phdr_data_segment *seg = &data->segments[i]; m_curSegments.push_back(Segment(seg->paddr, seg->vaddr, seg->size)); } for (FileListenerList_t::const_iterator it = m_fileListeners.begin(); it != m_fileListeners.end(); ++it) (*it)->onFile(File(m_filename, m_isMainFile ? IFileParser::FLG_NONE : IFileParser::FLG_TYPE_SOLIB, m_curSegments)); return checkFile(); } bool checkFile() { struct Elf *elf; bool out = true; int fd; fd = ::open(m_filename.c_str(), O_RDONLY, 0); if (fd < 0) { error("Cannot open %s\n", m_filename.c_str()); return false; } if (!(elf = elf_begin(fd, ELF_C_READ, NULL)) ) { error("elf_begin failed on %s\n", m_filename.c_str()); out = false; goto out_open; } if (elf_kind(elf) == ELF_K_NONE) out = false; if (m_isMainFile) { char *raw; size_t sz; raw = elf_getident(elf, &sz); if (raw && sz > EI_CLASS) { ICapabilities &capabilities = ICapabilities::getInstance(); m_elfIs32Bit = raw[EI_CLASS] == ELFCLASS32; if (elfIs64Bit() != machine_is_64bit()) capabilities.removeCapability("handle-solibs"); else capabilities.addCapability("handle-solibs"); } m_checksum = m_elfIs32Bit ? elf32_checksum(elf) : elf64_checksum(elf); } elf_end(elf); out_open: close(fd); return out; } bool parse() { struct stat st; if (lstat(m_filename.c_str(), &st) < 0) return 0; parseOneElf(); // Gcov data? if (IConfiguration::getInstance().keyAsInt("gcov") && !m_gcnoFiles.empty()) parseGcnoFiles(); else parseOneDwarf(); // After the first, all other are solibs m_isMainFile = false; return true; } void parseGcnoFiles() { for (FileList_t::const_iterator it = m_gcnoFiles.begin(); it != m_gcnoFiles.end(); ++it) { const std::string &cur = *it; parseOneGcno(cur); } } void parseOneGcno(const std::string &filename) { size_t sz; void *data; // The data is freed by the parser data = read_file(&sz, "%s", filename.c_str()); if (!data) return; // Parse this gcno file! GcnoParser parser((uint8_t *)data, sz); // Parsing error? if (!parser.parse()) { warning("Can't parse %s\n", filename.c_str()); return; } const GcnoParser::BasicBlockList_t &bbs = parser.getBasicBlocks(); for (GcnoParser::BasicBlockList_t::const_iterator it = bbs.begin(); it != bbs.end(); ++it) { const GcnoParser::BasicBlockMapping &cur = *it; // Report a generated address for (LineListenerList_t::const_iterator it = m_lineListeners.begin(); it != m_lineListeners.end(); ++it) (*it)->onLine(cur.m_file, cur.m_line, gcovGetAddress(cur.m_file, cur.m_function, cur.m_basicBlock, cur.m_index)); } } bool parseOneDwarf() { Dwarf_Off offset = 0; Dwarf_Off last_offset = 0; size_t hdr_size; Dwarf *dbg; int fd; fd = ::open(m_filename.c_str(), O_RDONLY, 0); if (fd < 0) { error("Cannot open %s\n", m_filename.c_str()); return false; } /* Initialize libdwarf */ dbg = dwarf_begin(fd, DWARF_C_READ); if (!dbg && m_buildId.length() > 0) { /* Look for separate debug info */ std::string debug_file = std::string("/usr/lib/debug/.build-id/" + m_buildId.substr(0,2) + "/" + m_buildId.substr(2, std::string::npos) + ".debug"); close(fd); fd = ::open(debug_file.c_str(), O_RDONLY, 0); if (fd < 0) { // Some shared libraries have neither symbols nor build-id files if (m_isMainFile) { warning("Cannot open %s", debug_file.c_str()); warning("kcov requires binaries built with -g/-ggdb or a build-id file."); } return false; } dbg = dwarf_begin(fd, DWARF_C_READ); } if (!dbg) { kcov_debug(ELF_MSG, "No debug symbols in %s.\n", m_filename.c_str()); close(fd); return false; } /* Iterate over the headers */ while (dwarf_nextcu(dbg, offset, &offset, &hdr_size, 0, 0, 0) == 0) { Dwarf_Lines* line_buffer; Dwarf_Files *file_buffer; size_t line_count; size_t file_count; Dwarf_Die die; unsigned int i; if (dwarf_offdie(dbg, last_offset + hdr_size, &die) == NULL) goto out_err; last_offset = offset; /* Get the source lines */ if (dwarf_getsrclines(&die, &line_buffer, &line_count) != 0) continue; /* And the files */ if (dwarf_getsrcfiles(&die, &file_buffer, &file_count) != 0) continue; const char *const *src_dirs; size_t ndirs = 0; /* Lookup the compilation path */ if (dwarf_getsrcdirs(file_buffer, &src_dirs, &ndirs) != 0) continue; if (ndirs == 0) continue; /* Store them */ for (i = 0; i < line_count; i++) { Dwarf_Line *line; int line_nr; const char* line_source; Dwarf_Word mtime, len; bool is_code; Dwarf_Addr addr; if ( !(line = dwarf_onesrcline(line_buffer, i)) ) goto out_err; if (dwarf_lineno(line, &line_nr) != 0) goto out_err; if (!(line_source = dwarf_linesrc(line, &mtime, &len)) ) goto out_err; if (dwarf_linebeginstatement(line, &is_code) != 0) goto out_err; if (dwarf_lineaddr(line, &addr) != 0) goto out_err; if (line_nr && is_code) { std::string full_file_path; std::string file_path = line_source; if (!addressIsValid(addr)) continue; /* Use the full compilation path unless the source already * has an absolute path */ std::string dir = src_dirs[0] == NULL ? "" : src_dirs[0]; full_file_path = dir_concat(dir, line_source); if (line_source[0] != '/') file_path = full_file_path; /******** replace the path information found in the debug symbols with *********/ /******** the value from in the newRoot variable. *********/ std::string rp; if (m_origRoot.length() > 0 && m_newRoot.length() > 0) { std::string dwarfPath = file_path; size_t dwIndex = dwarfPath.find(m_origRoot); if (dwIndex != std::string::npos) { dwarfPath.replace(dwIndex, m_origRoot.length(), m_newRoot); rp = get_real_path(dwarfPath); } } else { rp = get_real_path(file_path); } if (rp != "") { file_path = full_file_path = rp; } for (LineListenerList_t::const_iterator it = m_lineListeners.begin(); it != m_lineListeners.end(); ++it) (*it)->onLine(file_path, line_nr, adjustAddressBySegment(addr)); } } } out_err: /* Shutdown libdwarf */ if (dwarf_end(dbg) != 0) goto out_err; close(fd); return true; } bool parseOneElf() { Elf_Scn *scn = NULL; size_t shstrndx; bool ret = false; bool setupSegments = false; FileList_t gcdaFiles; // List of gcov data files scanned from .rodata bool doScanForGcda = IConfiguration::getInstance().keyAsInt("gcov"); int fd; unsigned int i; fd = ::open(m_filename.c_str(), O_RDONLY, 0); if (fd < 0) { error("Cannot open %s\n", m_filename.c_str()); return false; } if (!(m_elf = elf_begin(fd, ELF_C_READ, NULL)) ) { error("elf_begin failed on %s\n", m_filename.c_str()); goto out_open; } if (elf_getshdrstrndx(m_elf, &shstrndx) < 0) { error("elf_getshstrndx failed on %s\n", m_filename.c_str()); goto out_elf_begin; } setupSegments = m_curSegments.size() == 0; while ( (scn = elf_nextscn(m_elf, scn)) != NULL ) { uint64_t sh_type; uint64_t sh_addr; uint64_t sh_size; uint64_t sh_flags; uint64_t sh_name; uint64_t n_namesz; uint64_t n_descsz; uint64_t n_type; char *n_data; char *name; if (m_elfIs32Bit) { Elf32_Shdr *shdr32 = elf32_getshdr(scn); sh_type = shdr32->sh_type; sh_addr = shdr32->sh_addr; sh_size = shdr32->sh_size; sh_flags = shdr32->sh_flags; sh_name = shdr32->sh_name; } else { Elf64_Shdr *shdr64 = elf64_getshdr(scn); sh_type = shdr64->sh_type; sh_addr = shdr64->sh_addr; sh_size = shdr64->sh_size; sh_flags = shdr64->sh_flags; sh_name = shdr64->sh_name; } Elf_Data *data = elf_getdata(scn, NULL); name = elf_strptr(m_elf, shstrndx, sh_name); if(!data) { error("elf_getdata failed on section %s in %s\n", name, m_filename.c_str()); goto out_elf_begin; } // Parse rodata to find gcda files if (doScanForGcda && data->d_buf && strcmp(name, ".rodata") == 0) { const char *dataPtr = (const char *)data->d_buf; for (size_t i = 0; i < data->d_size - 5; i++) { const char *p = &dataPtr[i]; if (memcmp(p, (const void *)"gcda\0", 5) != 0) continue; const char *gcda = p; // Rewind to start of string while (gcda != dataPtr && *gcda != '\0') gcda--; // Rewound until start of rodata? if (gcda == dataPtr) continue; std::string file(gcda + 1); gcdaFiles.push_back(file); // Notify listeners that we have found gcda files for (FileListenerList_t::const_iterator it = m_fileListeners.begin(); it != m_fileListeners.end(); ++it) (*it)->onFile(File(file, IFileParser::FLG_TYPE_COVERAGE_DATA)); } } if (sh_type == SHT_NOTE && data->d_buf) { if (m_elfIs32Bit) { Elf32_Nhdr *nhdr32 = (Elf32_Nhdr *)data->d_buf; n_namesz = nhdr32->n_namesz; n_descsz = nhdr32->n_descsz; n_type = nhdr32->n_type; n_data = (char *)data->d_buf + sizeof (Elf32_Nhdr); } else { Elf64_Nhdr *nhdr64 = (Elf64_Nhdr *)data->d_buf; n_namesz = nhdr64->n_namesz; n_descsz = nhdr64->n_descsz; n_type = nhdr64->n_type; n_data = (char *)data->d_buf + sizeof (Elf64_Nhdr); } if (::strcmp(n_data, ELF_NOTE_GNU) == 0 && n_type == NT_GNU_BUILD_ID) { const char *hex_digits = "0123456789abcdef"; unsigned char *build_id; build_id = (unsigned char *)n_data + n_namesz; for (i = 0; i < n_descsz; i++) { m_buildId.push_back(hex_digits[(build_id[i] >> 4) & 0xf]); m_buildId.push_back(hex_digits[(build_id[i] >> 0) & 0xf]); } } } if ((sh_flags & (SHF_EXECINSTR | SHF_ALLOC)) != (SHF_EXECINSTR | SHF_ALLOC)) continue; // If we have segments already, we can safely skip this if (setupSegments) m_curSegments.push_back(Segment(sh_addr, sh_addr, sh_size)); m_executableSegments.push_back(Segment(sh_addr, sh_addr, sh_size)); } // If we have gcda files, try to find the corresponding gcno dittos for (FileList_t::iterator it = gcdaFiles.begin(); it != gcdaFiles.end(); ++it) { std::string &gcno = *it; // Modify in-place size_t sz = gcno.size(); // .gcda -> .gcno gcno[sz - 2] = 'n'; gcno[sz - 1] = 'o'; if (file_exists(gcno)) m_gcnoFiles.push_back(gcno); } elf_end(m_elf); if (!(m_elf = elf_begin(fd, ELF_C_READ, NULL)) ) { error("elf_begin failed on %s\n", m_filename.c_str()); goto out_open; } ret = true; out_elf_begin: elf_end(m_elf); out_open: close(fd); return ret; } void registerLineListener(ILineListener &listener) { m_lineListeners.push_back(&listener); } void registerFileListener(IFileListener &listener) { m_fileListeners.push_back(&listener); } private: typedef std::vector LineListenerList_t; typedef std::vector FileListenerList_t; typedef std::vector FileList_t; bool addressIsValid(uint64_t addr) { for (SegmentList_t::const_iterator it = m_executableSegments.begin(); it != m_executableSegments.end(); ++it) { if (it->addressIsWithinSegment(addr)) return true; } return false; } uint64_t adjustAddressBySegment(uint64_t addr) { for (SegmentList_t::const_iterator it = m_curSegments.begin(); it != m_curSegments.end(); ++it) { if (it->addressIsWithinSegment(addr)) { addr = it->adjustAddress(addr); break; } } return addr; } SegmentList_t m_curSegments; SegmentList_t m_executableSegments; FileList_t m_gcnoFiles; struct Elf *m_elf; bool m_elfIs32Bit; LineListenerList_t m_lineListeners; FileListenerList_t m_fileListeners; std::string m_filename; std::string m_buildId; bool m_isMainFile; uint64_t m_checksum; bool m_initialized; /***** Add strings to update path information. *******/ std::string m_origRoot; std::string m_newRoot; IFilter *m_filter; }; static ElfInstance g_instance; kcov_25+dfsg.orig/src/reporter.cc000066400000000000000000000375251247015272500171650ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include "swap-endian.hh" using namespace kcov; #define KCOV_MAGIC 0x6b636f76 /* "kcov" */ #define KCOV_DB_VERSION 5 struct marshalHeaderStruct { uint32_t magic; uint32_t db_version; uint64_t checksum; }; class Reporter : public IReporter, public IFileParser::ILineListener, public IFileParser::IFileListener, public ICollector::IListener, public IReporter::IListener { public: Reporter(IFileParser &fileParser, ICollector &collector, IFilter &filter) : m_fileParser(fileParser), m_collector(collector), m_filter(filter), m_maxPossibleHits(fileParser.maxPossibleHits()), m_unmarshallingDone(false) { m_fileParser.registerLineListener(*this); m_fileParser.registerFileListener(*this); m_collector.registerListener(*this); m_dbFileName = IConfiguration::getInstance().keyAsString("target-directory") + "/coverage.db"; } ~Reporter() { stop(); } void registerListener(IReporter::IListener &listener) { m_listeners.push_back(&listener); } bool fileIsIncluded(const std::string &file) { return m_filter.runFilters(file); } bool lineIsCode(const std::string &file, unsigned int lineNr) { // Not code if the file doesn't exist! if (m_files.find(file) == m_files.end()) return false; return m_files[file].lineIsCode(lineNr); } LineExecutionCount getLineExecutionCount(const std::string &file, unsigned int lineNr) { unsigned int hits = 0; unsigned int possibleHits = 0; FileMap_t::const_iterator it = m_files.find(file); if (it != m_files.end()) { const Line *line = it->second.getLine(lineNr); if (line) { hits = line->hits(); possibleHits = line->possibleHits(m_maxPossibleHits != IFileParser::HITS_UNLIMITED); } } return LineExecutionCount(hits, possibleHits); } ExecutionSummary getExecutionSummary() { unsigned int executedLines = 0; unsigned int nrLines = 0; // Summarize all files for (FileMap_t::const_iterator it = m_files.begin(); it != m_files.end(); ++it) { const std::string &fileName = it->first; const File &file = it->second; // Don't include non-existing files in summary if (!file_exists(fileName)) continue; // And not filtered ones either if (!m_filter.runFilters(fileName)) continue; executedLines += file.getExecutedLines(); nrLines += file.getNrLines(); } return ExecutionSummary(nrLines, executedLines); } void *marshal(size_t *szOut) { size_t sz = getMarshalSize(); void *start; uint8_t *p; start = malloc(sz); if (!start) return NULL; memset(start, 0, sz); p = marshalHeader((uint8_t *)start); // Marshal all lines in the files for (FileMap_t::const_iterator it = m_files.begin(); it != m_files.end(); ++it) { const File &cur = it->second; p = cur.marshal(p, *this); } *szOut = sz; return start; } bool unMarshal(void *data, size_t sz) { uint8_t *start = (uint8_t *)data; uint8_t *p = start; size_t n; p = unMarshalHeader(p); if (!p) return false; n = (sz - (p - start)) / getMarshalEntrySize(); for (size_t i = 0; i < n; i++) { uint64_t fileHash; uint64_t addr; uint64_t hits; uint64_t rangeIndex; p = Line::unMarshal(p, &addr, &hits, &fileHash, &rangeIndex); AddrToLineMap_t::iterator it = m_addrToLine.find(addr); if (!hits) continue; /* * Can't find this file. * * Typically because it's in a shared library, which hasn't been * loaded yet. */ if (fileHash != 0 && m_presentFiles.find(fileHash) == m_presentFiles.end()) { m_pendingFiles[fileHash].push_back(PendingFileAddress(addr, rangeIndex, hits)); continue; } /* Can't find this address. * * Either it doesn't exist in the binary, or it hasn't been parsed * yet, which will be the case for bash/python. Add it to pending * addresses if so. */ if (it == m_addrToLine.end()) { m_pendingHits[addr] = hits; continue; } Line *line = it->second; // Really an internal error, but let's not hang on corrupted data if (m_maxPossibleHits != IFileParser::HITS_UNLIMITED && hits > line->possibleHits(true)) hits = line->possibleHits(true); // Register all hits for this address reportAddress(addr, hits); } return true; } virtual void stop() { size_t sz; void *data = marshal(&sz); if (data) write_file(data, sz, "%s", m_dbFileName.c_str()); free(data); } private: size_t getMarshalEntrySize() { return 4 * sizeof(uint64_t); } size_t getMarshalSize() { size_t out = 0; // Sum all file sizes for (FileMap_t::const_iterator it = m_files.begin(); it != m_files.end(); ++it) { out += it->second.marshalSize(); } return out * getMarshalEntrySize() + sizeof(struct marshalHeaderStruct); } uint8_t *marshalHeader(uint8_t *p) { struct marshalHeaderStruct *hdr = (struct marshalHeaderStruct *)p; hdr->magic = to_be(KCOV_MAGIC); hdr->db_version = to_be(KCOV_DB_VERSION); hdr->checksum = to_be(m_fileParser.getChecksum()); return p + sizeof(struct marshalHeaderStruct); } uint8_t *unMarshalHeader(uint8_t *p) { struct marshalHeaderStruct *hdr = (struct marshalHeaderStruct *)p; if (be_to_host(hdr->magic) != KCOV_MAGIC) return NULL; if (be_to_host(hdr->db_version) != KCOV_DB_VERSION) return NULL; if (be_to_host(hdr->checksum) != m_fileParser.getChecksum()) return NULL; return p + sizeof(struct marshalHeaderStruct); } /* Called when the file is parsed */ void onLine(const std::string &file, unsigned int lineNr, uint64_t addr) { if (!m_filter.runFilters(file)) return; kcov_debug(INFO_MSG, "REPORT %s:%u at 0x%lx\n", file.c_str(), lineNr, (unsigned long)addr); Line *line = m_files[file].getLine(lineNr); if (!line) { line = new Line(file, lineNr); m_files[file].addLine(lineNr, line); } line->addAddress(addr); m_addrToLine[addr] = line; /* * If we have pending hits for this address, service * them here. */ AddrToHitsMap_t::iterator pend = m_pendingHits.find(addr); if (pend != m_pendingHits.end()) { reportAddress(addr, pend->second); m_pendingHits.erase(addr); } } // Called when a file is added (e.g., a shared library) void onFile(const IFileParser::File &file) { uint64_t fileHash = std::hash()(file.m_filename); // Add segments to the range list and mark file as present for (IFileParser::SegmentList_t::const_iterator it = file.m_segments.begin(); it != file.m_segments.end(); ++it) { m_addressRanges[AddressRange(it->getBase(), it->getSize())] = fileHash; m_presentFiles[fileHash].push_back(AddressRange(it->getBase(), it->getSize())); } // Only unmarshal once if (!m_unmarshallingDone) { void *data; size_t sz; data = read_file(&sz, "%s", m_dbFileName.c_str()); if (data && !unMarshal(data, sz)) kcov_debug(INFO_MSG, "Can't unmarshal %s\n", m_dbFileName.c_str()); m_unmarshallingDone = true; free(data); } // Report pending addresses for this file PendingFilesMap_t::const_iterator it = m_pendingFiles.find(fileHash); if (it != m_pendingFiles.end()) { const AddressRangeList_t &ranges = m_presentFiles[fileHash]; for (PendingHitsList_t::const_iterator fit = it->second.begin(); fit != it->second.end(); ++fit) { const PendingFileAddress &val = *fit; uint64_t addr = val.m_addr; unsigned long hits = val.m_hits; uint64_t index = val.m_index; if (index < ranges.size()) { const AddressRange &range = ranges[index]; addr = addr + range.getAddress(); } m_pendingHits[addr]= hits; } m_pendingFiles[fileHash].clear(); } } /* Called during runtime */ void reportAddress(uint64_t addr, unsigned long hits) { AddrToLineMap_t::iterator it = m_addrToLine.find(addr); if (it == m_addrToLine.end()) return; Line *line = it->second; kcov_debug(INFO_MSG, "REPORT hit at 0x%llx\n", (unsigned long long)addr); line->registerHit(addr, hits, m_maxPossibleHits != IFileParser::HITS_UNLIMITED); for (ListenerList_t::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) (*it)->onAddress(addr, hits); } // From ICollector::IListener void onAddressHit(uint64_t addr, unsigned long hits) { reportAddress(addr, hits); } // From IReporter::IListener - report recursively void onAddress(uint64_t addr, unsigned long hits) { } // Remove the base for an address (solibs) uint64_t addressToOffset(uint64_t addr) const { // Nothing to lookup if (m_addressRanges.empty()) return 0; AddressRangeMap_t::const_iterator it = m_addressRanges.lower_bound(addr); it--; if (it == m_addressRanges.end()) return addr; // Out of bounds? if (addr > it->first.getAddress() + it->first.getSize()) return addr; return addr - it->first.getAddress(); } int getAddressRangeIndex(uint64_t hash, uint64_t addr) const { const PresentFilesMap_t::const_iterator rit = m_presentFiles.find(hash); if (rit == m_presentFiles.end()) return 0; const AddressRangeList_t &ranges = rit->second; int i = 0; for (AddressRangeList_t::const_iterator it = ranges.begin(); it != ranges.end(); ++it, i++) { if (addr >= it->getAddress() && addr < it->getAddress() + it->getSize()) break; } // Not found? if (i == (int)ranges.size()) return 0; return i; } // Lookup the address within a range uint64_t getFileHashForAddress(uint64_t addr) const { // Nothing to lookup if (m_addressRanges.empty()) return 0; AddressRangeMap_t::const_iterator it = m_addressRanges.lower_bound(addr); it--; if (it == m_addressRanges.end()) return 0; return it->second; } class Line { public: typedef std::unordered_map AddrToHitsMap_t; Line(const std::string &file, unsigned int lineNr) { } void addAddress(uint64_t addr) { if (m_addrs.find(addr) == m_addrs.end()) m_addrs[addr] = 0; } void registerHit(uint64_t addr, unsigned long hits, bool singleShot) { if (singleShot) m_addrs[addr] = 1; else m_addrs[addr] += hits; } void clearHits() { for (AddrToHitsMap_t::iterator it = m_addrs.begin(); it != m_addrs.end(); ++it) it->second = 0; } unsigned int hits() const { unsigned int out = 0; for (AddrToHitsMap_t::const_iterator it = m_addrs.begin(); it != m_addrs.end(); ++it) out += it->second; return out; } unsigned int possibleHits(bool singleShot) const { if (singleShot) return m_addrs.size(); return 0; // Meaning any number of hits are possible } uint8_t *marshal(uint8_t *start, const Reporter &parent) { uint64_t *data = (uint64_t *)start; for (AddrToHitsMap_t::const_iterator it = m_addrs.begin(); it != m_addrs.end(); ++it) { // No hits? Ignore if so if (!it->second) continue; uint64_t hash = parent.getFileHashForAddress(it->first); uint64_t addr = it->first; uint64_t index = 0; // For solibs, we want only the offset if (hash != 0) { // Before addressToOffset since addr is changed index = parent.getAddressRangeIndex(hash, addr); addr = parent.addressToOffset(addr); } // Address, hash, index and number of hits *data++ = to_be(addr); *data++ = to_be(hash); *data++ = to_be(index); *data++ = to_be(it->second); } return (uint8_t *)data; } size_t marshalSize() const { unsigned int n = 0; for (AddrToHitsMap_t::const_iterator it = m_addrs.begin(); it != m_addrs.end(); ++it) { // No hits? Ignore if so if (!it->second) continue; n++; } // Address, file hash and number of hits (64-bit values) return n * sizeof(uint64_t) * 4; } static uint8_t *unMarshal(uint8_t *p, uint64_t *outAddr, uint64_t *outHits, uint64_t *outFileHash, uint64_t *outIndex) { uint64_t *data = (uint64_t *)p; *outAddr = be_to_host(*data++); *outFileHash = be_to_host(*data++); *outIndex = be_to_host(*data++); *outHits = be_to_host(*data++); return (uint8_t *)data; } private: AddrToHitsMap_t m_addrs; }; class File { public: File() : m_nrLines(0) { } ~File() { for (unsigned int i = 0; i < m_lines.size(); i++) { Line *cur = m_lines[i]; if (!cur) continue; delete cur; } } void addLine(unsigned int lineNr, Line *line) { // Resize the vector to fit this line if (lineNr >= m_lines.size()) m_lines.resize(lineNr + 1); m_lines[lineNr] = line; m_nrLines++; } Line *getLine(unsigned int lineNr) const { if (lineNr >= m_lines.size()) return NULL; return m_lines[lineNr]; } // Marshal all line data uint8_t *marshal(uint8_t *p, const Reporter &reporter) const { for (unsigned int i = 0; i < m_lines.size(); i++) { Line *cur = m_lines[i]; if (!cur) continue; p = cur->marshal(p, reporter); } return p; } size_t marshalSize() const { size_t out = 0; for (unsigned int i = 0; i < m_lines.size(); i++) { Line *cur = m_lines[i]; if (!cur) continue; out += cur->marshalSize(); } return out; } unsigned int getExecutedLines() const { unsigned int out = 0; for (unsigned int i = 0; i < m_lines.size(); i++) { Line *cur = m_lines[i]; if (!cur) continue; // Hits as zero or one (executed or not) out += !!cur->hits(); } return out; } unsigned int getNrLines() const { return m_nrLines; } bool lineIsCode(unsigned int lineNr) const { if (lineNr >= m_lines.size()) return false; return m_lines[lineNr] != NULL; } private: std::vector m_lines; unsigned int m_nrLines; }; /* * Helper class to store address ranges (base, size pairs). Used to convert * shared library addresses back to offsets. */ class AddressRange { public: AddressRange(uint64_t addr, size_t size = 1) : m_addr(addr), m_size(size) { } bool operator<(const uint64_t addr) const { return m_addr < addr; } bool operator<(const AddressRange &other) const { return m_addr < other.m_addr; } bool operator==(const AddressRange &other) const { return m_addr == other.m_addr && m_size == other.m_size; } uint64_t getAddress() const { return m_addr; } size_t getSize() const { return m_size; } private: uint64_t m_addr; size_t m_size; }; class PendingFileAddress { public: PendingFileAddress(uint64_t addr, uint64_t index, unsigned long hits) : m_addr(addr), m_index(index), m_hits(hits) { } uint64_t m_addr; uint64_t m_index; unsigned long m_hits; }; typedef std::unordered_map FileMap_t; typedef std::unordered_map AddrToLineMap_t; typedef std::unordered_map AddrToHitsMap_t; typedef std::vector ListenerList_t; typedef std::map AddressRangeMap_t; typedef std::vector AddressRangeList_t; typedef std::unordered_map PresentFilesMap_t; typedef std::vector PendingHitsList_t; // Address, hits typedef std::unordered_map PendingFilesMap_t; FileMap_t m_files; AddrToLineMap_t m_addrToLine; AddrToHitsMap_t m_pendingHits; ListenerList_t m_listeners; AddressRangeMap_t m_addressRanges; PresentFilesMap_t m_presentFiles; PendingFilesMap_t m_pendingFiles; IFileParser &m_fileParser; ICollector &m_collector; IFilter &m_filter; enum IFileParser::PossibleHits m_maxPossibleHits; bool m_unmarshallingDone; std::string m_dbFileName; }; IReporter &IReporter::create(IFileParser &parser, ICollector &collector, IFilter &filter) { return *new Reporter(parser, collector, filter); } kcov_25+dfsg.orig/src/solib-handler.cc000066400000000000000000000126421247015272500200370ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace kcov; extern GeneratedData __library_data; class SolibHandler : public ISolibHandler, ICollector::IEventTickListener { public: SolibHandler(IFileParser &parser, ICollector &collector) : m_ldPreloadString(NULL), m_envString(NULL), m_solibFd(-1), m_solibThreadValid(false), m_threadShouldExit(false), m_parser(&parser) { memset(&m_solibThread, 0, sizeof(m_solibThread)); // Only useful for ELF binaries if (parser.getParserType() == "ELF" && !IConfiguration::getInstance().keyAsInt("gcov")) { collector.registerEventTickListener(*this); startup(); } // Skip this very special library m_foundSolibs["libkcov_sowrapper.so"] = true; } virtual ~SolibHandler() { void *rv; m_threadShouldExit = true; if (m_solibPath != "") unlink(m_solibPath.c_str()); if (m_solibFd >= 0) close(m_solibFd); /* * First kill the solib thread if it's stuck in open (i.e., before * the tracee has started), then wait for it to terminate for maximum * niceness. * * Only do this if it has been started, naturally */ if (m_solibThreadValid) { pthread_cancel(m_solibThread); pthread_kill(m_solibThread, SIGTERM); pthread_join(m_solibThread, &rv); } } // From IEventTickListener void onTick() { checkSolibData(); } void startup() { std::string kcov_solib_pipe_path = IOutputHandler::getInstance().getOutDirectory() + "kcov-solib.pipe"; std::string kcov_solib_path = IOutputHandler::getInstance().getBaseDirectory() + "libkcov_sowrapper.so"; write_file(__library_data.data(), __library_data.size(), "%s", kcov_solib_path.c_str()); std::string kcov_solib_env = "KCOV_SOLIB_PATH=" + kcov_solib_pipe_path; unlink(kcov_solib_pipe_path.c_str()); (void)mkfifo(kcov_solib_pipe_path.c_str(), 0644); free(m_envString); m_envString = (char *)xmalloc(kcov_solib_env.size() + 1); strcpy(m_envString, kcov_solib_env.c_str()); std::string preloadEnv = std::string("LD_PRELOAD=" + kcov_solib_path).c_str(); free(m_ldPreloadString); m_ldPreloadString = (char *)xmalloc(preloadEnv.size() + 1); strcpy(m_ldPreloadString, preloadEnv.c_str()); if (IConfiguration::getInstance().keyAsInt("parse-solibs") && ICapabilities::getInstance().hasCapability("handle-solibs")) { if (file_exists(kcov_solib_path)) putenv(m_ldPreloadString); } putenv(m_envString); m_solibPath = kcov_solib_pipe_path; pthread_create(&m_solibThread, NULL, SolibHandler::threadStatic, (void *)this); } void solibThreadParse() { uint8_t buf[1024 * 1024]; m_solibFd = ::open(m_solibPath.c_str(), O_RDONLY); m_solibThreadValid = true; if (m_solibFd < 0) return; m_phdrListMutex.lock(); while (1) { int r = read(m_solibFd, buf, sizeof(buf)); // The destructor will close m_solibFd, so we'll exit here in that case if (r <= 0) break; panic_if ((unsigned)r >= sizeof(buf), "Too much solib data read"); struct phdr_data *p = phdr_data_unmarshal(buf); if (p) { size_t sz = sizeof(struct phdr_data) + p->n_entries * sizeof(struct phdr_data_entry); struct phdr_data *cpy = (struct phdr_data*)xmalloc(sz); memcpy(cpy, p, sz); m_phdrs.push_back(cpy); } m_solibDataReadSemaphore.notify(); } close(m_solibFd); m_phdrListMutex.unlock(); m_solibDataReadSemaphore.notify(); } void solibThreadMain() { while (1) { if (m_threadShouldExit) break; solibThreadParse(); } } // Wrapper for ptrace static void *threadStatic(void *pThis) { SolibHandler *p = (SolibHandler *)pThis; p->solibThreadMain(); return NULL; } void checkSolibData() { struct phdr_data *p = NULL; if (!m_parser) return; m_phdrListMutex.lock(); if (!m_phdrs.empty()) { p = m_phdrs.front(); m_phdrs.pop_front(); } m_phdrListMutex.unlock(); if (!p) return; for (unsigned int i = 0; i < p->n_entries; i++) { struct phdr_data_entry *cur = &p->entries[i]; if (strlen(cur->name) == 0) continue; if (m_foundSolibs.find(cur->name) != m_foundSolibs.end()) continue; m_parser->addFile(cur->name, cur); m_parser->parse(); m_foundSolibs[cur->name] = true; } free(p); } //private: typedef std::list PhdrList_t; typedef std::unordered_map FoundSolibsMap_t; std::string m_solibPath; char *m_ldPreloadString; char *m_envString; int m_solibFd; bool m_solibThreadValid; bool m_threadShouldExit; pthread_t m_solibThread; Semaphore m_solibDataReadSemaphore; PhdrList_t m_phdrs; FoundSolibsMap_t m_foundSolibs; std::mutex m_phdrListMutex; IFileParser *m_parser; }; static SolibHandler *g_handler; ISolibHandler &kcov::createSolibHandler(IFileParser &parser, ICollector &collector) { // OK, not very nicely encapsulated... g_handler = new SolibHandler(parser, collector); return *g_handler; } void kcov::blockUntilSolibDataRead() { // If it has been started if (g_handler->m_solibThreadValid) g_handler->m_solibDataReadSemaphore.wait(); } kcov_25+dfsg.orig/src/solib-parser/000077500000000000000000000000001247015272500174025ustar00rootroot00000000000000kcov_25+dfsg.orig/src/solib-parser/lib.c000066400000000000000000000041051247015272500203140ustar00rootroot00000000000000#ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include static struct phdr_data *phdr_data; static int phdrCallback(struct dl_phdr_info *info, size_t size, void *data) { phdr_data_add(phdr_data, info); return 0; } static int phdrSizeCallback(struct dl_phdr_info *info, size_t size, void *data) { size_t *ps = (size_t *)data; *ps += sizeof(struct phdr_data_entry); return 0; } static void parse_solibs(void) { char *kcov_solib_path; void *p; ssize_t written; size_t allocSize; size_t sz; int fd; kcov_solib_path = getenv("KCOV_SOLIB_PATH"); if (!kcov_solib_path) return; allocSize = sizeof(struct phdr_data); dl_iterate_phdr(phdrSizeCallback, &allocSize); phdr_data = phdr_data_new(allocSize); if (!phdr_data) { fprintf(stderr, "kcov-solib: Can't allocate %zu bytes\n", allocSize); return; } dl_iterate_phdr(phdrCallback, NULL); p = phdr_data_marshal(phdr_data, &sz); fd = open(kcov_solib_path, O_WRONLY); if (fd < 0) { fprintf(stderr, "kcov-solib: Can't open %s\n", kcov_solib_path); return; } written = write(fd, p, sz); if (written != sz) fprintf(stderr, "kcov-solib: Can't write to solib FIFO (%zu)\n", written); phdr_data_free(p); close(fd); } static void force_breakpoint(void) { asm volatile( #if defined(__i386__) || defined(__x86_64__) "int3\n" #elif defined(__powerpc__) ".long 0x7fe00008\n" /* trap instruction */ #elif defined(__arm__) ".long 0xfedeffe7\n" /* undefined insn */ #else # error Unsupported architecture #endif ); } static void *(*orig_dlopen)(const char *, int); void *dlopen(const char *filename, int flag) { void *out; if (!orig_dlopen) orig_dlopen = dlsym(RTLD_NEXT, "dlopen"); out = orig_dlopen(filename, flag); parse_solibs(); force_breakpoint(); return out; } void __attribute__((constructor))kcov_solib_at_startup(void) { parse_solibs(); force_breakpoint(); } kcov_25+dfsg.orig/src/solib-parser/phdr_data.c000066400000000000000000000035641247015272500215040ustar00rootroot00000000000000#ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include "phdr_data.h" #include #include #include #include #include #define KCOV_MAGIC 0x6b636f76 /* "kcov" */ #define KCOV_SOLIB_VERSION 2 static uint8_t data_area[4 * 1024 * 1024]; struct phdr_data *phdr_data_new(size_t allocSize) { struct phdr_data *p = (struct phdr_data *)data_area; if (allocSize > sizeof(data_area)) return NULL; p->magic = KCOV_MAGIC; p->version = KCOV_SOLIB_VERSION; p->n_entries = 0; return p; } void phdr_data_free(struct phdr_data *p) { } void phdr_data_add(struct phdr_data *p, struct dl_phdr_info *info) { uint32_t n = p->n_entries + 1; uint32_t curEntry = p->n_entries; int phdr; struct phdr_data_entry *cur = &p->entries[curEntry]; memset(cur, 0, sizeof(*cur)); strncpy(cur->name, info->dlpi_name, sizeof(cur->name) - 1); cur->n_segments = 0; for (phdr = 0; phdr < info->dlpi_phnum; phdr++) { const ElfW(Phdr) *curHdr = &info->dlpi_phdr[phdr]; struct phdr_data_segment *seg = &cur->segments[cur->n_segments]; if (curHdr->p_type != PT_LOAD) continue; if (cur->n_segments >= sizeof(cur->segments) / sizeof(cur->segments[0])) { fprintf(stderr, "Too many segments\n"); return; } seg->paddr = curHdr->p_paddr; seg->vaddr = info->dlpi_addr + curHdr->p_vaddr; seg->size = curHdr->p_memsz; cur->n_segments++; } p->n_entries = n; } void *phdr_data_marshal(struct phdr_data *p, size_t *out_sz) { *out_sz = sizeof(struct phdr_data) + sizeof(struct phdr_data_entry) * p->n_entries; return (void *)p; } struct phdr_data *phdr_data_unmarshal(void *p) { struct phdr_data *out = (struct phdr_data *)p; if (out->magic != KCOV_MAGIC) return NULL; if (out->version != KCOV_SOLIB_VERSION) return NULL; // Silly amounts of entries mean broken data if (out->n_entries > 0x10000) return NULL; return out; } kcov_25+dfsg.orig/src/utils.cc000066400000000000000000000264561247015272500164640ustar00rootroot00000000000000 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int g_kcov_debug_mask = STATUS_MSG; static void* (*mocked_read_callback)(size_t* out_size, const char* path); static int (*mocked_write_callback)(const void* data, size_t size, const char* path); static bool (*mocked_file_exists_callback)(const std::string &path); static uint64_t (*mocked_get_file_timestamp_callback)(const std::string &path); void msleep(uint64_t ms) { struct timespec ts; uint64_t ns = ms * 1000 * 1000; ts.tv_sec = ns / (1000 * 1000 * 1000); ts.tv_nsec = ns % (1000 * 1000 * 1000); nanosleep(&ts, NULL); } static void *read_file_int(size_t *out_size, uint64_t timeout, const char *path) { uint8_t *data = NULL; int fd; size_t pos = 0; const size_t chunk = 1024; fd_set rfds; struct timeval tv; int ret; int n; if (mocked_read_callback) return mocked_read_callback(out_size, path); fd = open(path, O_RDONLY | O_NONBLOCK); if (fd < 0 && (errno == ENXIO || errno == EWOULDBLOCK)) { msleep(timeout); fd = open(path, O_RDONLY | O_NONBLOCK); } if (fd < 0) return NULL; tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 10; FD_ZERO(&rfds); FD_SET(fd, &rfds); do { ret = select(fd + 1, &rfds, NULL, NULL, &tv); if (ret == -1) { close(fd); free(data); return NULL; } else if (ret == 0) { // Timeout close(fd); free(data); return NULL; } data = (uint8_t *)xrealloc(data, pos + chunk); memset(data + pos, 0, chunk); n = read(fd, data + pos, chunk); if (n < 0) { close(fd); free(data); return NULL; } pos += n; } while (n == (int)chunk); *out_size = pos; close(fd); return data; } void *read_file(size_t *out_size, const char *fmt, ...) { char path[2048]; va_list ap; int r; /* Create the filename */ va_start(ap, fmt); r = vsnprintf(path, 2048, fmt, ap); va_end(ap); panic_if (r >= 2048, "Too long string!"); return read_file_int(out_size, 0, path); } void *peek_file(size_t *out_size, const char *fmt, ...) { char path[2048]; va_list ap; int r; /* Create the filename */ va_start(ap, fmt); r = vsnprintf(path, 2048, fmt, ap); va_end(ap); panic_if (r >= 2048, "Too long string!"); // Read a little bit of the file const size_t to_read = 512; char *out = static_cast(xmalloc(to_read)); std::ifstream str(path); if (str.is_open()) { str.read(out,to_read); // If successful (all desired characters) or // a non-zero number of characters were read, then succeed if (str || str.gcount() > 0) { *out_size = str.gcount(); } else { // No characters read, so fail free(out); out = NULL; } str.close(); } else { free(out); out = NULL; } return out; } static int write_file_int(const void *data, size_t len, uint64_t timeout, const char *path) { int fd; fd_set wfds; struct timeval tv; int ret = 0; tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 10; fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); if (fd < 0 && (errno == ENXIO || errno == EWOULDBLOCK)) { msleep(timeout); fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); if (fd < 0 && (errno == ENXIO || errno == EWOULDBLOCK)) return -2; } if (fd < 0) return fd; FD_ZERO(&wfds); FD_SET(fd, &wfds); ret = select(fd + 1, NULL, &wfds, NULL, &tv); if (ret == -1) { close(fd); return ret; } else if (ret == 0) { // Timeout close(fd); return -2; } if (write(fd, data, len) != (int)len) return -3; close(fd); return 0; } int write_file(const void *data, size_t len, const char *fmt, ...) { char path[2048]; va_list ap; /* Create the filename */ va_start(ap, fmt); vsnprintf(path, 2048, fmt, ap); va_end(ap); if (mocked_write_callback) return mocked_write_callback(data, len, path); return write_file_int(data, len, 0, path); } std::string dir_concat(const std::string &dir, const std::string &filename) { if (dir == "") return filename; return dir + "/" + filename; } const char *get_home(void) { return getenv("HOME"); } bool file_readable(FILE *fp, unsigned int ms) { fd_set rfds; struct timeval tv; int rv; int fd = fileno(fp); FD_ZERO(&rfds); FD_SET(fd, &rfds); tv.tv_sec = ms / 1000; tv.tv_usec = (ms % 1000) * 1000; rv = select(fd + 1, &rfds, NULL, NULL, &tv); return rv > 0; } static std::unordered_map statCache; bool file_exists(const std::string &path) { if (mocked_file_exists_callback) return mocked_file_exists_callback(path); bool out; if (statCache.find(path) == statCache.end()) { struct stat st; out = lstat(path.c_str(), &st) == 0; statCache[path] = out; } else { out = statCache[path]; } return out; } void mock_read_file(void *(*callback)(size_t *out_size, const char *path)) { mocked_read_callback = callback; } void mock_write_file(int (*callback)(const void *data, size_t size, const char *path)) { mocked_write_callback = callback; } void mock_file_exists(bool (*callback)(const std::string &path)) { mocked_file_exists_callback = callback; } void mock_get_file_timestamp(uint64_t (*callback)(const std::string &path)) { mocked_get_file_timestamp_callback = callback; } uint64_t get_file_timestamp(const std::string &path) { if (mocked_get_file_timestamp_callback) return mocked_get_file_timestamp_callback(path); struct stat st; // "Fail" if (lstat(path.c_str(), &st) != 0) return 0; return st.st_mtim.tv_sec; } static void read_write(FILE *dst, FILE *src) { char buf[1024]; while (!feof(src)) { int n = fread(buf, sizeof(char), sizeof(buf), src); fwrite(buf, sizeof(char), n, dst); } } int concat_files(const char *dst_name, const char *file_a, const char *file_b) { FILE *dst; FILE *s1, *s2; int ret = -1; dst = fopen(dst_name, "w"); if (!dst) return -1; s1 = fopen(file_a, "r"); if (!s1) goto out_dst; s2 = fopen(file_b, "r"); if (!s2) goto out_s1; read_write(dst, s1); read_write(dst, s2); fclose(s2); out_s1: fclose(s1); out_dst: fclose(dst); return ret; } unsigned long get_aligned(unsigned long addr) { return (addr / sizeof(long)) * sizeof(long); } unsigned long get_aligned_4b(unsigned long addr) { return addr & ~3; } std::string fmt(const char *fmt, ...) { char buf[4096]; va_list ap; int res; va_start(ap, fmt); res = vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); panic_if(res >= (int)sizeof(buf), "Buffer overflow"); return std::string(buf); } int kcov_get_current_cpu(void) { return sched_getcpu(); } void kcov_tie_process_to_cpu(pid_t pid, int cpu) { // Switching CPU while running will cause icache // conflicts. So let's just forbid that. cpu_set_t *set = CPU_ALLOC(1); panic_if (!set, "Can't allocate CPU set!\n"); CPU_ZERO_S(CPU_ALLOC_SIZE(1), set); CPU_SET(cpu, set); panic_if (sched_setaffinity(pid, CPU_ALLOC_SIZE(1), set) < 0, "Can't set CPU affinity. Coincident won't work"); CPU_FREE(set); } void mdelay(unsigned int ms) { struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = ms * 1000 * 1000; nanosleep(&ts, NULL); } uint64_t get_ms_timestamp(void) { static uint64_t first; struct timeval tv; gettimeofday(&tv, NULL); if (first == 0) first = (tv.tv_sec * 1000000ULL + tv.tv_usec) / 1000; return ((tv.tv_sec * 1000000ULL + tv.tv_usec) / 1000) - first; } bool machine_is_64bit(void) { return sizeof(unsigned long) == 8; } // http://stackoverflow.com/questions/236129/how-to-split-a-string-in-c static std::vector &split(const std::string &s, char delim, std::vector &elems) { std::stringstream ss(s); std::string item; while (std::getline(ss, item, delim)) { elems.push_back(item); } return elems; } std::vector split_string(const std::string &s, const char *delims) { std::vector elems; split(s, *delims, elems); return elems; } std::string trim_string(const std::string &strIn) { std::string str = strIn; size_t endpos = str.find_last_not_of(" \t\n\r"); if (std::string::npos != endpos) str = str.substr( 0, endpos+1 ); // trim leading spaces size_t startpos = str.find_first_not_of(" \t"); if (std::string::npos != startpos) str = str.substr( startpos ); else str = ""; return str; } // Cache for ::realpath - it's apparently one of the reasons why kcov is slow typedef std::unordered_map PathMap_t; static PathMap_t realPathCache; const std::string &get_real_path(const std::string &path) { PathMap_t::const_iterator it = realPathCache.find(path); if (it != realPathCache.end()) return it->second; char *rp = NULL; rp = ::realpath(path.c_str(), NULL); if (!rp) return path; realPathCache[path] = rp; free(rp); return realPathCache[path]; } bool string_is_integer(const std::string &str, unsigned base) { size_t pos; try { stoull(str, &pos, base); } catch(std::invalid_argument &e) { return false; } catch(std::out_of_range &e) { return false; } return pos == str.size(); } int64_t string_to_integer(const std::string &str, unsigned base) { size_t pos; return (int64_t)stoull(str, &pos, base); } static char *escape_helper(char *dst, const char *what) { int len = strlen(what); strcpy(dst, what); return dst + len; } std::string escape_html(const std::string &str) { const char *s = str.c_str(); char buf[4096]; char *dst = buf; size_t len = strlen(s); bool truncated = false; size_t i; // Truncate long lines (or entries) if (len > 512) { len = 512; truncated = true; } memset(buf, 0, sizeof(buf)); for (i = 0; i < len; i++) { char c = s[i]; switch (c) { case '<': dst = escape_helper(dst, "<"); break; case '>': dst = escape_helper(dst, ">"); break; case '&': dst = escape_helper(dst, "&"); break; case '\"': dst = escape_helper(dst, """); break; case '\'': dst = escape_helper(dst, "'"); break; case '/': dst = escape_helper(dst, "/"); break; case '\\': dst = escape_helper(dst, "\"); break; case '\n': case '\r': dst = escape_helper(dst, " "); break; default: *dst = c; dst++; break; } } if (truncated) return std::string(buf) + "..."; return std::string(buf); } std::string escape_json(const std::string &str) { const std::string escapeChars = "\"\\\t'"; size_t n_escapes = 0; for (unsigned i = 0; i < str.size(); i++) { for (unsigned int n = 0; n < escapeChars.size(); n++) { if (str[i] == escapeChars[n]) n_escapes++; } } if (n_escapes == 0) return str; std::string out; out.resize(str.size() + n_escapes); unsigned cur = 0; for (unsigned i = 0; i < str.size(); i++) { out[cur] = str[i]; // Special-case tabs if (str[i] == '\t') { out[cur] = '\\'; cur++; out[cur] = 't'; cur++; continue; } // Quote quotes and backslashes for (unsigned int n = 0; n < escapeChars.size(); n++) { if (str[i] == escapeChars[n]) { out[cur] = '\\'; cur++; out[cur] = str[i]; } } cur++; } return out; } uint32_t hash_block(const void *buf, size_t len) { return crc32(0, (const Bytef *)buf, len); } kcov_25+dfsg.orig/src/writers/000077500000000000000000000000001247015272500164775ustar00rootroot00000000000000kcov_25+dfsg.orig/src/writers/cobertura-writer.cc000066400000000000000000000100671247015272500223120ustar00rootroot00000000000000namespace std { class type_info; } #include #include #include #include #include #include #include #include #include #include #include "writer-base.hh" using namespace kcov; class CoberturaWriter : public WriterBase { public: CoberturaWriter(IFileParser &parser, IReporter &reporter, const std::string &outFile) : WriterBase(parser, reporter), m_outFile(outFile), m_maxPossibleHits(parser.maxPossibleHits()) { } void onStartup() { } void onStop() { } void write() { std::ofstream out(m_outFile); // Output directory not writable? if (!out.is_open()) return; unsigned int nTotalExecutedLines = 0; unsigned int nTotalCodeLines = 0; setupCommonPaths(); for (FileMap_t::const_iterator it = m_files.begin(); it != m_files.end(); ++it) { File *file = it->second; nTotalCodeLines += file->m_codeLines; nTotalExecutedLines += file->m_executedLines; } out << getHeader(nTotalCodeLines, nTotalExecutedLines); for (FileMap_t::const_iterator it = m_files.begin(); it != m_files.end(); ++it) { File *file = it->second; out << writeOne(file); } out << getFooter(); } private: const std::string mangleFileName(const std::string &name) { std::string out = name; for (size_t pos = 0; pos < name.size(); pos++) { if (out[pos] == '.' || out[pos] == '-') out[pos] = '_'; } return out; } std::string writeOne(File *file) { std::string out; std::string mangledName = mangleFileName(file->m_fileName); unsigned int nExecutedLines = 0; unsigned int nCodeLines = 0; for (unsigned int n = 1; n < file->m_lastLineNr; n++) { if (!m_reporter.lineIsCode(file->m_name, n)) continue; IReporter::LineExecutionCount cnt = m_reporter.getLineExecutionCount(file->m_name, n); nExecutedLines += !!cnt.m_hits; nCodeLines++; unsigned int hits = cnt.m_hits; if (hits && m_maxPossibleHits == IFileParser::HITS_SINGLE) hits = 1; out = out + " \n"; // Update the execution count file->m_executedLines = nExecutedLines; file->m_codeLines = nCodeLines; } if (nCodeLines == 0) nCodeLines = 1; std::string filename = file->m_name; size_t pos = filename.find(m_commonPath); if (pos != std::string::npos && filename.size() > m_commonPath.size()) filename = filename.substr(m_commonPath.size() + 1); out = " \n" + " \n" + out + " \n" " \n"; return out; } std::string getHeader(unsigned int nCodeLines, unsigned int nExecutedLines) { time_t t; struct tm *tm; char date_buf[80]; t = time(NULL); tm = localtime(&t); strftime(date_buf, sizeof(date_buf), "%s", tm); if (nCodeLines == 0) nCodeLines = 1; std::string lineRate = fmt("%.3f", nExecutedLines / (float)nCodeLines); return "\n" "\n" "\n" " \n" " " + m_commonPath + "/\n" " \n" " \n" " \n" " \n" ; } const std::string getFooter() { return " \n" " \n" " \n" "\n"; } std::string m_outFile; IFileParser::PossibleHits m_maxPossibleHits; }; namespace kcov { IWriter &createCoberturaWriter(IFileParser &parser, IReporter &reporter, const std::string &outFile) { return *new CoberturaWriter(parser, reporter, outFile); } } kcov_25+dfsg.orig/src/writers/cobertura-writer.hh000066400000000000000000000003061247015272500223170ustar00rootroot00000000000000#pragma once namespace kcov { class IFileParser; class IReporter; class IOutputHandler; IWriter &createCoberturaWriter(IFileParser &elf, IReporter &reporter, const std::string &outFile); } kcov_25+dfsg.orig/src/writers/coveralls-writer.cc000066400000000000000000000113471247015272500223200ustar00rootroot00000000000000/* * Writer for the "coveralls.io" web service. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "writer-base.hh" using namespace kcov; class CurlConnectionHandler { public: CurlConnectionHandler() : m_headerlist(NULL) { static const char buf[] = "Expect:"; // Workaround proxy problems m_headerlist = curl_slist_append(m_headerlist, buf); curl_global_init(CURL_GLOBAL_ALL); m_curl = curl_easy_init(); // Setup curl to read from memory curl_easy_setopt(m_curl, CURLOPT_URL, "https://coveralls.io/api/v1/jobs"); curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, curlWriteFuncStatic); curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, (void *)this); curl_easy_setopt(m_curl, CURLOPT_USERAGENT, "libcurl-agent/1.0"); curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, m_headerlist); // Proxy issues } ~CurlConnectionHandler() { curl_slist_free_all(m_headerlist); curl_easy_cleanup(m_curl); curl_global_cleanup(); } // Send fileName and data to the remote server bool talk(const std::string &fileName) { m_writtenData = ""; struct curl_httppost *formpost = NULL; struct curl_httppost *lastptr = NULL; CURLcode res; curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "json_file", CURLFORM_FILE, fileName.c_str(), CURLFORM_END); curl_easy_setopt(m_curl, CURLOPT_HTTPPOST, formpost); res = curl_easy_perform(m_curl); curl_formfree(formpost); if (res != CURLE_OK) return false; if (m_writtenData.find("Job #") == std::string::npos) { warning("coveralls write failed: %s\n", m_writtenData.c_str()); return false; } return true; } private: size_t curlWritefunc(void *ptr, size_t size, size_t nmemb) { const char *p = (char *)ptr; m_writtenData.append(p, size * nmemb); return size * nmemb; } static size_t curlWriteFuncStatic(void *ptr, size_t size, size_t nmemb, void *priv) { if (!priv) return 0; return ((CurlConnectionHandler *)priv)->curlWritefunc(ptr, size, nmemb); } CURL *m_curl; std::string m_writtenData; struct curl_slist *m_headerlist; }; static CurlConnectionHandler *g_curl; class CoverallsWriter : public WriterBase { public: CoverallsWriter(IFileParser &parser, IReporter &reporter) : WriterBase(parser, reporter), m_doWrite(false) { } void onStartup() { } void onStop() { m_doWrite = true; } void write() { // Write once at the end only if (!m_doWrite) return; IConfiguration &conf = IConfiguration::getInstance(); const std::string &id = conf.keyAsString("coveralls-id"); // No token? Skip output then if (id == "") return; std::string outFile = conf.keyAsString("target-directory") + "/coveralls.out"; // Output file with coveralls json data std::ofstream out(outFile); // Output directory not writable? if (!out.is_open()) return; out << "{\n"; if (isRepoToken(id)) { out << " \"repo_token\": \"" + id + "\",\n"; } else { out << " \"service_name\": \"travis-ci\",\n"; out << " \"service_job_id\": \"" + id + "\",\n"; } out << " \"source_files\": [\n"; setupCommonPaths(); unsigned int filesLeft = m_files.size(); for (FileMap_t::const_iterator it = m_files.begin(); it != m_files.end(); ++it) { File *file = it->second; std::string fileName; // Strip away the common path (unless this is the only file) if (m_commonPath != file->m_name) fileName = file->m_name.substr(m_commonPath.size() + 1); else fileName = file->m_fileName; out << " {\n"; out << " \"name\": \"" + escape_json(fileName) + "\",\n"; // Use hash as source file out << fmt(" \"source\": \"0x%08lx\",\n", (unsigned long)file->m_crc); out << " \"coverage\": ["; // And coverage for (unsigned int n = 1; n < file->m_lastLineNr; n++) { if (!m_reporter.lineIsCode(file->m_name, n)) { out << "null"; } else { IReporter::LineExecutionCount cnt = m_reporter.getLineExecutionCount(file->m_name, n); out << cnt.m_hits; } if (n != file->m_lastLineNr - 1) out << ","; } out << "]\n"; // Add comma or not on the last run filesLeft--; if (filesLeft > 0) out << " },\n"; else out << " }\n"; } out << " ]\n"; out << "}\n"; out.close(); // Create singleton if (!g_curl) g_curl = new CurlConnectionHandler(); g_curl->talk(outFile); } private: bool isRepoToken(const std::string &str) { return str.size() >= 32; } bool m_doWrite; }; namespace kcov { IWriter &createCoverallsWriter(IFileParser &parser, IReporter &reporter) { return *new CoverallsWriter(parser, reporter); } } kcov_25+dfsg.orig/src/writers/coveralls-writer.hh000066400000000000000000000002231247015272500223210ustar00rootroot00000000000000#pragma once namespace kcov { class IFileParser; class IReporter; IWriter &createCoverallsWriter(IFileParser &parser, IReporter &reporter); } kcov_25+dfsg.orig/src/writers/html-writer.cc000066400000000000000000000261671247015272500213000ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "writer-base.hh" using namespace kcov; // Generated extern GeneratedData css_text_data; extern GeneratedData icon_amber_data; extern GeneratedData icon_glass_data; extern GeneratedData source_file_text_data; extern GeneratedData index_text_data; extern GeneratedData handlebars_text_data; extern GeneratedData kcov_text_data; extern GeneratedData jquery_text_data; extern GeneratedData tablesorter_text_data; extern GeneratedData tablesorter_widgets_text_data; extern GeneratedData tablesorter_theme_text_data; class HtmlWriter : public WriterBase { public: HtmlWriter(IFileParser &parser, IReporter &reporter, const std::string &indexDirectory, const std::string &outDirectory, const std::string &name, bool includeInTotals) : WriterBase(parser, reporter), m_outDirectory(outDirectory + "/"), m_indexDirectory(indexDirectory + "/"), m_summaryDbFileName(outDirectory + "/summary.db"), m_name(name), m_includeInTotals(includeInTotals), m_maxPossibleHits(parser.maxPossibleHits()) { } void onStop() { } private: void writeOne(File *file) { std::string jsonOutName = m_outDirectory + "/" + file->m_jsonOutFileName; std::string htmlOutName = m_outDirectory + "/" + file->m_outFileName; unsigned int nExecutedLines = 0; unsigned int nCodeLines = 0; // Out-file for JSON data std::ofstream outJson(jsonOutName); // ... and HTML data std::ofstream outHtml(htmlOutName); outJson << "var data = {lines:[\n"; // Produce each line in the file for (unsigned int n = 1; n < file->m_lastLineNr; n++) { const std::string &line = file->m_lineMap[n]; outJson << fmt( "{\"lineNum\":\"%5u\"," "\"line\":\"%s\"", n, escape_json(line).c_str() ); if (m_reporter.lineIsCode(file->m_name, n)) { IReporter::LineExecutionCount cnt = m_reporter.getLineExecutionCount(file->m_name, n); std::string lineClass = "lineNoCov"; if (m_maxPossibleHits == IFileParser::HITS_UNLIMITED || m_maxPossibleHits == IFileParser::HITS_SINGLE) { if (cnt.m_hits) lineClass = "lineCov"; } else { // One or multiple for a line if (cnt.m_hits == cnt.m_possibleHits) lineClass = "lineCov"; else if (cnt.m_hits) lineClass = "linePartCov"; } outJson << fmt( ",\"class\":\"%s\"," "\"hits\":\"%u\",", lineClass.c_str(), cnt.m_hits); if (m_maxPossibleHits != IFileParser::HITS_SINGLE) outJson << fmt("\"possible_hits\":\"%u\",", cnt.m_possibleHits); nExecutedLines += !!cnt.m_hits; nCodeLines++; } outJson << "},\n"; // Update the execution count file->m_executedLines = nExecutedLines; file->m_codeLines = nCodeLines; } // Add the header outJson << "]};\n"; outJson << getHeader(nCodeLines, nExecutedLines); outJson << "var merged_data = [];\n"; // Produce HTML out-file outHtml << fmt( "\n", file->m_jsonOutFileName.c_str()); outHtml.write((const char *)source_file_text_data.data(), source_file_text_data.size()); } void writeIndex() { IConfiguration &conf = IConfiguration::getInstance(); unsigned int nTotalExecutedLines = 0; unsigned int nTotalCodeLines = 0; // Out-file for JSON data std::ofstream outJson(m_outDirectory + "index.json"); outJson << "var data = {files:[\n"; // Not really json, but anyway for (FileMap_t::const_iterator it = m_files.begin(); it != m_files.end(); ++it) { File *file = it->second; unsigned int nExecutedLines = file->m_executedLines; unsigned int nCodeLines = file->m_codeLines; nTotalCodeLines += nCodeLines; nTotalExecutedLines += nExecutedLines; std::string listName = file->m_name; size_t pos = listName.find(m_commonPath); unsigned int stripLevel = IConfiguration::getInstance().keyAsInt("path-strip-level"); if (pos != std::string::npos && m_commonPath.size() != 0 && stripLevel != ~0U) { std::string pathToRemove = m_commonPath; for (unsigned int i = 0; i < stripLevel; i++) { size_t slashPos = pathToRemove.rfind("/"); if (slashPos == std::string::npos) break; pathToRemove = pathToRemove.substr(0, slashPos); } std::string prefix = "[...]"; if (pathToRemove == "") prefix = ""; listName = prefix + listName.substr(pathToRemove.size()); } outJson << getIndexHeader(file->m_outFileName, file->m_fileName, listName, nCodeLines, nExecutedLines); } // Add the header outJson << fmt("]};\n" "var percent_low = %d;" "var percent_high = %d;" "\n" "var header = {" " \"command\" : \"%s\"," " \"date\" : \"%s\"," " \"instrumented\" : %d," " \"covered\" : %d," "};" "\n", conf.keyAsInt("low-limit"), conf.keyAsInt("high-limit"), escape_json(conf.keyAsString("binary-name")).c_str(), getDateNow().c_str(), nTotalCodeLines, nTotalExecutedLines ) + "var merged_data = [];\n"; // Produce HTML outfile std::ofstream outHtml(m_outDirectory + "index.html"); outHtml.write((const char *)index_text_data.data(), index_text_data.size()); // Produce a summary IReporter::ExecutionSummary summary = m_reporter.getExecutionSummary(); summary.m_includeInTotals = m_includeInTotals; size_t sz; void *data = marshalSummary(summary, m_name, &sz); if (data) write_file(data, sz, "%s", m_summaryDbFileName.c_str()); free(data); } void writeGlobalIndex() { DIR *dir; struct dirent *de; std::string idx = m_indexDirectory.c_str(); std::string s; unsigned int nTotalExecutedLines = 0; unsigned int nTotalCodeLines = 0; dir = opendir(idx.c_str()); panic_if(!dir, "Can't open directory %s\n", idx.c_str()); std::ofstream outJson(m_indexDirectory + "index.json"); outJson << "var data = {files:[\n"; std::string merged; for (de = readdir(dir); de; de = readdir(dir)) { std::string cur = idx + de->d_name + "/summary.db"; if (!file_exists(cur)) continue; size_t sz; void *data = read_file(&sz, "%s", cur.c_str()); if (!data) continue; IReporter::ExecutionSummary summary; std::string name; bool res = unMarshalSummary(data, sz, summary, name); free(data); if (!res) continue; // Skip entries (merged ones) that shouldn't be included in the totals if (summary.m_includeInTotals) { nTotalCodeLines += summary.m_lines; nTotalExecutedLines += summary.m_executedLines; } std::string datum = getIndexHeader(fmt("%s/index.html", de->d_name), name, name, summary.m_lines, summary.m_executedLines); if (name == "[merged]") merged += datum; else outJson << datum; } // Add the header outJson << "], merged_files:[" + merged + "]};\n" + getHeader(nTotalCodeLines, nTotalExecutedLines); // Produce HTML outfile std::ofstream outHtml(m_indexDirectory + "index.html"); outHtml.write((const char *)index_text_data.data(), index_text_data.size()); closedir(dir); } void write() { for (FileMap_t::const_iterator it = m_files.begin(); it != m_files.end(); ++it) writeOne(it->second); setupCommonPaths(); writeIndex(); if (m_includeInTotals) writeGlobalIndex(); } std::string getHeader(unsigned int lines, unsigned int executedLines) { IConfiguration &conf = IConfiguration::getInstance(); return fmt( "var percent_low = %d;" "var percent_high = %d;" "\n" "var header = {" " \"command\" : \"%s\"," " \"date\" : \"%s\"," " \"instrumented\" : %d," " \"covered\" : %d," "};" "\n", conf.keyAsInt("low-limit"), conf.keyAsInt("high-limit"), escape_json(conf.keyAsString("binary-name")).c_str(), getDateNow().c_str(), lines, executedLines); } // Return a header for index-type JSON files std::string getIndexHeader(const std::string &linkName, const std::string titleName, const std::string summaryName, unsigned int lines, unsigned int executedLines) { double percent = 0; if (lines != 0) percent = (executedLines / (double)lines) * 100; return fmt( "{\"link\":\"%s\"," "\"title\":\"%s\"," "\"summary_name\":\"%s\"," "\"covered_class\":\"%s\"," "\"covered\":\"%.1f\"," "\"covered_lines\":\"%d\"," "\"uncovered_lines\":\"%d\"," "\"total_lines\" : \"%d\"},\n", linkName.c_str(), titleName.c_str(), summaryName.c_str(), colorFromPercent(percent).c_str(), percent, executedLines, lines - executedLines, lines); } std::string colorFromPercent(double percent) { IConfiguration &conf = IConfiguration::getInstance(); if (percent >= conf.keyAsInt("high-limit")) return "lineCov"; else if (percent > conf.keyAsInt("low-limit")) return "linePartCov"; return "lineNoCov"; } std::string getDateNow() { time_t t; struct tm *tm; char date_buf[128]; t = time(NULL); tm = localtime(&t); strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", tm); return std::string(date_buf); } void writeHelperFiles(std::string dir) { write_file(icon_amber_data.data(), icon_amber_data.size(), "%s/amber.png", dir.c_str()); write_file(icon_glass_data.data(), icon_glass_data.size(), "%s/glass.png", dir.c_str()); write_file(css_text_data.data(), css_text_data.size(), "%s/bcov.css", dir.c_str()); (void)mkdir(fmt("%s/data", dir.c_str()).c_str(), 0755); (void)mkdir(fmt("%s/data/js", dir.c_str()).c_str(), 0755); write_file(icon_amber_data.data(), icon_amber_data.size(), "%s/data/amber.png", dir.c_str()); write_file(icon_glass_data.data(), icon_glass_data.size(), "%s/data/glass.png", dir.c_str()); write_file(css_text_data.data(), css_text_data.size(), "%s/data/bcov.css", dir.c_str()); write_file(handlebars_text_data.data(), handlebars_text_data.size(), "%s/data/js/handlebars.js", dir.c_str()); write_file(kcov_text_data.data(), kcov_text_data.size(), "%s/data/js/kcov.js", dir.c_str()); write_file(jquery_text_data.data(), jquery_text_data.size(), "%s/data/js/jquery.min.js", dir.c_str()); write_file(tablesorter_text_data.data(), tablesorter_text_data.size(), "%s/data/js/tablesorter.min.js", dir.c_str()); write_file(tablesorter_widgets_text_data.data(), tablesorter_widgets_text_data.size(), "%s/data/js/jquery.tablesorter.widgets.min.js", dir.c_str()); write_file(tablesorter_theme_text_data.data(), tablesorter_theme_text_data.size(), "%s/data/tablesorter-theme.css", dir.c_str()); } void onStartup() { writeHelperFiles(m_indexDirectory); writeHelperFiles(m_outDirectory); } std::string m_outDirectory; std::string m_indexDirectory; std::string m_summaryDbFileName; std::string m_name; bool m_includeInTotals; enum IFileParser::PossibleHits m_maxPossibleHits; }; namespace kcov { IWriter &createHtmlWriter(IFileParser &parser, IReporter &reporter, const std::string &indexDirectory, const std::string &outDirectory, const std::string &name, bool includeInTotals) { return *new HtmlWriter(parser, reporter, indexDirectory, outDirectory, name, includeInTotals); } } kcov_25+dfsg.orig/src/writers/html-writer.hh000066400000000000000000000004501247015272500212750ustar00rootroot00000000000000#pragma once namespace kcov { class IFileParser; class IReporter; class IOutputHandler; IWriter &createHtmlWriter(IFileParser &elf, IReporter &reporter, const std::string &indexDirectory, const std::string &outDirectory, const std::string &name, bool includeInTotals = true); } kcov_25+dfsg.orig/src/writers/writer-base.cc000066400000000000000000000072321247015272500212360ustar00rootroot00000000000000#include "writer-base.hh" #include #include #include using namespace kcov; #define SUMMARY_MAGIC 0x456d696c #define SUMMARY_VERSION 2 struct summaryStruct { uint32_t magic; uint32_t version; uint32_t includeInTotals; uint32_t nLines; uint32_t nExecutedLines; char name[256]; }; WriterBase::WriterBase(IFileParser &parser, IReporter &reporter) : m_fileParser(parser), m_reporter(reporter), m_commonPath("not set") { m_fileParser.registerLineListener(*this); } WriterBase::~WriterBase() { for (FileMap_t::iterator it = m_files.begin(); it != m_files.end(); ++it) { File *cur = it->second; delete cur; } m_files.clear(); } WriterBase::File::File(const std::string &filename) : m_name(filename), m_codeLines(0), m_executedLines(0), m_lastLineNr(0) { size_t pos = m_name.rfind('/'); if (pos != std::string::npos) m_fileName = m_name.substr(pos + 1, std::string::npos); else m_fileName = m_name; // Make this name unique (we might have several files with the same name) m_crc = hash_block(filename.c_str(), filename.size()); readFile(filename); m_outFileName = fmt("%s.%x.html", m_fileName.c_str(), m_crc); m_jsonOutFileName = fmt("%s.%x.json", m_fileName.c_str(), m_crc); } void WriterBase::File::readFile(const std::string &filename) { FILE *fp = fopen(filename.c_str(), "r"); unsigned int lineNr = 1; panic_if(!fp, "Can't open %s", filename.c_str()); while (1) { char *lineptr = NULL; ssize_t res; size_t n; res = getline(&lineptr, &n, fp); if (res < 0) break; std::string s(lineptr); s.erase(s.find_last_not_of(" \n\r\t")+1); m_lineMap[lineNr] = s; free((void *)lineptr); lineNr++; } m_lastLineNr = lineNr; fclose(fp); } void WriterBase::onLine(const std::string &file, unsigned int lineNr, uint64_t addr) { if (!m_reporter.fileIsIncluded(file)) return; if (m_files.find(file) != m_files.end()) return; if (!file_exists(file)) return; m_files[file] = new File(file); } void *WriterBase::marshalSummary(IReporter::ExecutionSummary &summary, const std::string &name, size_t *sz) { struct summaryStruct *p; p = (struct summaryStruct *)xmalloc(sizeof(struct summaryStruct)); memset(p, 0, sizeof(*p)); p->magic = to_be(SUMMARY_MAGIC); p->version = to_be(SUMMARY_VERSION); p->includeInTotals = to_be(summary.m_includeInTotals); p->nLines = to_be(summary.m_lines); p->nExecutedLines = to_be(summary.m_executedLines); strncpy(p->name, name.c_str(), sizeof(p->name) - 1); *sz = sizeof(*p); return (void *)p; } bool WriterBase::unMarshalSummary(void *data, size_t sz, IReporter::ExecutionSummary &summary, std::string &name) { struct summaryStruct *p = (struct summaryStruct *)data; if (sz != sizeof(*p)) return false; if (be_to_host(p->magic) != SUMMARY_MAGIC) return false; if (be_to_host(p->version) != SUMMARY_VERSION) return false; summary.m_lines = be_to_host(p->nLines); summary.m_executedLines = be_to_host(p->nExecutedLines); summary.m_includeInTotals = be_to_host(p->includeInTotals); name = std::string(p->name); return true; } void WriterBase::setupCommonPaths() { for (FileMap_t::const_iterator it = m_files.begin(); it != m_files.end(); ++it) { File *file = it->second; if (m_commonPath == "not set") m_commonPath = file->m_name; /* Already matching? */ if (file->m_name.find(m_commonPath) == 0) continue; while (1) { size_t pos = m_commonPath.rfind('/'); if (pos == std::string::npos) break; m_commonPath = m_commonPath.substr(0, pos); if (file->m_name.find(m_commonPath) == 0) break; } } } kcov_25+dfsg.orig/src/writers/writer-base.hh000066400000000000000000000024551247015272500212520ustar00rootroot00000000000000#pragma once #include #include #include #include #include namespace kcov { class IFileParser; class IReporter; class WriterBase : public IFileParser::ILineListener, public IWriter { protected: WriterBase(IFileParser &elf, IReporter &reporter); ~WriterBase(); class File { public: typedef std::unordered_map LineMap_t; File(const std::string &filename); std::string m_name; std::string m_fileName; std::string m_outFileName; std::string m_jsonOutFileName; uint32_t m_crc; LineMap_t m_lineMap; unsigned int m_codeLines; unsigned int m_executedLines; unsigned int m_lastLineNr; private: void readFile(const std::string &filename); }; typedef std::unordered_map FileMap_t; /* Called when the ELF is parsed */ void onLine(const std::string &file, unsigned int lineNr, uint64_t addr); void *marshalSummary(IReporter::ExecutionSummary &summary, const std::string &name, size_t *sz); bool unMarshalSummary(void *data, size_t sz, IReporter::ExecutionSummary &summary, std::string &name); void setupCommonPaths(); IFileParser &m_fileParser; IReporter &m_reporter; FileMap_t m_files; std::string m_commonPath; }; } kcov_25+dfsg.orig/tests/000077500000000000000000000000001247015272500153535ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/CMakeLists.txt000066400000000000000000000063751247015272500201260ustar00rootroot00000000000000cmake_minimum_required (VERSION 2.6) project (kcov-tests) enable_language(ASM) include_directories( ${CMAKE_BINARY_DIR} ../src/include ) set (CMAKE_CXX_FLAGS "-std=c++0x -Wall -D_GLIBCXX_USE_NANOSLEEP -DKCOV_LIBRARY_PREFIX=${KCOV_LIBRARY_PREFIX}") add_custom_command (OUTPUT multi-fork-generated.c COMMAND ${CMAKE_SOURCE_DIR}/multi-fork/generate-functions.py ${CMAKE_SOURCE_DIR}/multi-fork/code-template.c 1024 > ${CMAKE_BINARY_DIR}/multi-fork-generated.c DEPENDS ${CMAKE_SOURCE_DIR}/multi-fork/generate-functions.py ) add_custom_target(multi-fork-generated ALL DEPENDS multi-fork-generated.c ) set_property(SOURCE multi-fork/test-multi-fork.c APPEND PROPERTY OBJECT_DEPENDS ${CMAKE_BINARY_DIR}/multi-fork-generated.c) set (main_tests_SRCS main.cc subdir/file.c subdir2/file2.c ) set (argv_dependent_SRCS argv-dependent.c ) set (fork_SRCS fork/fork.c ) set (fork_no_wait_SRCS fork/fork-no-wait.c ) set (signals_SRCS signals/test-signals.c ) set (multi_fork_SRCS multi-fork/test-multi-fork.c ) set (recursive-ptrace_SRCS recursive-ptrace/main.cc ) set (shared_library_test_SRCS shared-library/main.c ) set (test_popen_SRCS popen/test-popen.c ) set (global_constructors_SRCS global-constructors/test-global-ctors.cc ) set (daemon_SRCS daemon/test-daemon.cc ) add_library(shared_library SHARED shared-library/big-symbol.S shared-library/solib.c ) set(CMAKE_BUILD_TYPE distribution) set(CMAKE_C_FLAGS_DISTRIBUTION "-g") set(CMAKE_CXX_FLAGS_DISTRIBUTION "-g") find_library(shared_library_LIBRARY NAMES shared_library PATHS ${CMAKE_CURRENT_BINARY_DIR} ) add_executable(main-tests ${main_tests_SRCS}) add_executable(recursive-ptrace ${recursive-ptrace_SRCS}) add_executable(fork ${fork_SRCS}) add_executable(fork_no_wait ${fork_no_wait_SRCS}) add_executable(signals ${signals_SRCS}) add_executable(multi_fork ${multi_fork_SRCS}) add_executable(shared_library_test ${shared_library_test_SRCS}) add_executable(argv_dependent ${argv_dependent_SRCS}) add_executable(test_popen ${test_popen_SRCS}) add_executable(global-constructors ${global_constructors_SRCS}) add_executable(test_daemon ${daemon_SRCS}) add_executable(multi_1 merge-tests/file.c merge-tests/main_1.c) add_executable(multi_2 merge-tests/file.c merge-tests/main_2.c) add_executable(setpgid-kill setpgid-kill/setpgid-kill-main.cc ../src/utils.cc) add_executable(issue31 daemon/test-issue31.cc) add_executable(dlopen dlopen/dlopen.cc dlopen/dlopen-main.cc) add_executable(main-tests-gcov ${main_tests_SRCS}) set_target_properties(main-tests-gcov PROPERTIES COMPILE_FLAGS "--coverage") set_target_properties(main-tests-gcov PROPERTIES LINK_FLAGS "--coverage") target_link_libraries(dlopen dl) add_custom_target (illegal-insn ALL COMMAND ${CMAKE_C_COMPILER} -nostdlib ${CMAKE_CURRENT_SOURCE_DIR}/assembly/illegal-insn.S -o ${CMAKE_BINARY_DIR}/illegal-insn ) add_custom_target (fork-32 ALL COMMAND ${CMAKE_C_COMPILER} -g -m32 ${CMAKE_CURRENT_SOURCE_DIR}/fork/fork.c -o ${CMAKE_BINARY_DIR}/fork-32 ) target_link_libraries(shared_library_test shared_library) target_link_libraries(global-constructors shared_library) target_link_libraries(multi_fork m) target_link_libraries(setpgid-kill z) target_link_libraries(issue31 pthread) add_custom_target(tests-stripped ALL COMMAND strip -o tests-stripped main-tests) kcov_25+dfsg.orig/tests/argv-dependent.c000066400000000000000000000004021247015272500204160ustar00rootroot00000000000000#include static int first(int a) { printf("first\n"); return a + 1; } static int second(int a) { printf("second\n"); return a + 2; } int main(int argc, const char *argv[]) { if (argc == 1) first(argc); else second(argc); return 0; } kcov_25+dfsg.orig/tests/assembly/000077500000000000000000000000001247015272500171725ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/assembly/illegal-insn.S000066400000000000000000000000551247015272500216740ustar00rootroot00000000000000.globl _start _start: .byte 0xff, 0xff ret kcov_25+dfsg.orig/tests/bash/000077500000000000000000000000001247015272500162705ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/bash/first-dir/000077500000000000000000000000001247015272500201735ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/bash/first-dir/a.sh000066400000000000000000000001521247015272500207450ustar00rootroot00000000000000#!/bin/sh echo A first if [ $1 = "hej" ] ; then source `dirname $0`/c.sh fi source `dirname $0`/b.sh kcov_25+dfsg.orig/tests/bash/first-dir/b.sh000066400000000000000000000000271247015272500207470ustar00rootroot00000000000000#!/bin/sh echo hejsan kcov_25+dfsg.orig/tests/bash/first-dir/c.sh000066400000000000000000000000271247015272500207500ustar00rootroot00000000000000#!/bin/sh echo C now kcov_25+dfsg.orig/tests/bash/other.sh000066400000000000000000000001361247015272500177450ustar00rootroot00000000000000#!/bin/sh echo "This is another shell script" if [ $1 -eq 5 ] ; then echo "Kalle anka" fi kcov_25+dfsg.orig/tests/bash/second-dir/000077500000000000000000000000001247015272500203175ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/bash/second-dir/a.sh000066400000000000000000000001531247015272500210720ustar00rootroot00000000000000#!/bin/sh echo A second if [ $1 = "hej" ] ; then source `dirname $0`/c.sh fi source `dirname $0`/b.sh kcov_25+dfsg.orig/tests/bash/second-dir/b.sh000066400000000000000000000000271247015272500210730ustar00rootroot00000000000000#!/bin/sh echo hejsan kcov_25+dfsg.orig/tests/bash/second-dir/c.sh000066400000000000000000000000271247015272500210740ustar00rootroot00000000000000#!/bin/sh echo C now kcov_25+dfsg.orig/tests/bash/sh-shebang.sh000077500000000000000000000001101247015272500206360ustar00rootroot00000000000000#!/bin/sh # This do not shows up in coverage report echo "foo from sh" kcov_25+dfsg.orig/tests/bash/shell-main000077500000000000000000000020601247015272500202450ustar00rootroot00000000000000#!/bin/sh function fn0() { echo "fn0 $1" } fn1() # Other fn syntax { echo "fn1: Are we really executing this?" } function fn2 { # Yet another echo fn2 } function fn3 { # Yet yet another echo fn3 } fn0 if [ $# -eq 1 ] ; then fn1 fi A=5 while [ $A -gt 0 ] ; do A=$(($A - 1)) echo $A done # Comment case $# in 1) echo "1 args" ;; 2) echo "2 args" ;;# Comment 'sorry mister') echo "This is very much dead code!" ;; "SORRY") echo "and more of that here" ;; *) echo "else args $#" ;; esac for idx in 1 \ 2 \ 3 \ 4 ; do echo "IDX: $idx" done cat <&1 exec 4>&2 exec 2>>$1 exec 1>>$1 echo hopp `dirname $0`/short-test.sh # Issue #50: This breaks cat< #include #include #include void daemonize(void) { pid_t child; child = fork(); if (child < 0) { perror("Fork failed?\n"); exit(3); } else if (child == 0) { child = fork(); if (child < 0) { perror("Fork failed?\n"); exit(3); } else if (child > 0) { // Second parent exit(1); } else { freopen( "/dev/null", "r", stdin); freopen( "/dev/null", "w", stdout); freopen( "/dev/null", "w", stderr); // Daemonized child, just exit after a while sleep(5); exit(4); } } else { // First parent exit(2); } } int main(int argc, const char *argv[]) { daemonize(); return 0; } kcov_25+dfsg.orig/tests/daemon/test-issue31.cc000066400000000000000000000010011247015272500213660ustar00rootroot00000000000000#include #include #include #include #include void* thread_main(void *) { std::ofstream ofs("thread.log"); int i = 0; while(true) { ofs << ++i << std::endl; sleep(1); } return NULL; } int main() { pthread_t t; int thread_code = pthread_create(&t, NULL, thread_main, NULL); if (thread_code < 0) return 1; std::ofstream ofs("main.log"); int i = 0; while(true) { ofs << ++i << std::endl; sleep(1); } return 0; } kcov_25+dfsg.orig/tests/daemon/test-script.sh000077500000000000000000000001661247015272500214410ustar00rootroot00000000000000#!/bin/bash kcov=$1 out=$2 prg=$3 $prg & pid=$! $kcov --pid=$pid $out $prg & kcov_pid=$! sleep 1.2 kill $kcov_pid kcov_25+dfsg.orig/tests/dlopen/000077500000000000000000000000001247015272500166345ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/dlopen/dlopen-main.cc000066400000000000000000000002461247015272500213500ustar00rootroot00000000000000#include #include #include extern void do_dlopen(); int main(int argc, const char *argv[]) { do_dlopen(); sleep(1); return 0; } kcov_25+dfsg.orig/tests/dlopen/dlopen.cc000066400000000000000000000006531247015272500204300ustar00rootroot00000000000000#include #include #include void do_dlopen() { void *handle; int (*sym)(int); handle = dlopen("libshared_library.so", RTLD_LAZY); if (!handle) { printf("Can't dlopen\n"); exit(1); } dlerror(); sym = (int (*)(int))dlsym(handle, "vobb"); if (!sym) { printf("No symbol\n"); dlclose(handle); exit(1); } int a = sym(5); printf("from shared lib: %d\n", a); dlclose(handle); } kcov_25+dfsg.orig/tests/fork/000077500000000000000000000000001247015272500163145ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/fork/fork-no-wait.c000066400000000000000000000010441247015272500207740ustar00rootroot00000000000000#include #include #include #include #include #include #include int main(int argc, const char *argv[]) { pid_t child; child = fork(); if (child < 0) { fprintf(stderr, "fork failed!\n"); return -1; } if (child > 0) printf("Parent %d. Will exit\n", getpid()); else { printf("In child, waiting\n"); sleep(1); printf("Wait done, parent should be finished by now\n"); } /* No wait for children */ return 4; // To test non-0/-1/1 exit codes } kcov_25+dfsg.orig/tests/fork/fork.c000066400000000000000000000013701247015272500174220ustar00rootroot00000000000000#include #include #include #include #include // Space added // ... so that I don't have to change the tests :-) static void mibb(void) { printf("Mibb\n"); } int main(int argc, const char *argv[]) { pid_t child; int status; child = fork(); if (child < 0) { fprintf(stderr, "fork failed!\n"); return -1; } if (child == 0) printf("Parent %d\n", getpid()); else { pid_t grand_child = fork(); if (grand_child < 0) fprintf(stderr, "fork gc failed\n"); else if (grand_child == 0) { printf("Grand child %d\n", getpid()); mibb(); } else printf("Child %d\n", getpid()); wait(&status); } mibb(); printf("Waiting in %d\n", getpid()); wait(&status); return 0; } kcov_25+dfsg.orig/tests/global-constructors/000077500000000000000000000000001247015272500213615ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/global-constructors/test-global-ctors.cc000066400000000000000000000003001247015272500252260ustar00rootroot00000000000000#include #include std::string glob = "Kalle"; int main(int argc, const char *argv[]) { if (argc > 1) glob = "Manne"; printf("XXX: %s\n", glob.c_str()); return 0; } kcov_25+dfsg.orig/tests/include/000077500000000000000000000000001247015272500167765ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/include/header.h000066400000000000000000000002461247015272500204010ustar00rootroot00000000000000#ifndef HEADER_H #define HEADER_H #if defined(__cplusplus) extern "C" { #endif void tjoho(int a); void tjoho2(int a); #if defined(__cplusplus) }; #endif #endif kcov_25+dfsg.orig/tests/main.cc000066400000000000000000000003771247015272500166150ustar00rootroot00000000000000#include #include "include/header.h" class Test { public: Test() { printf("Constructor\n"); } void hello() { printf("Hello\n"); } }; static Test g_test; int main(int argc, const char *argv[]) { tjoho(1); tjoho2(0); return 0; } kcov_25+dfsg.orig/tests/merge-tests/000077500000000000000000000000001247015272500176125ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/merge-tests/file.c000066400000000000000000000000771247015272500207010ustar00rootroot00000000000000int vobb(void) { return 15; } int mibb(void) { return 27; } kcov_25+dfsg.orig/tests/merge-tests/main_1.c000066400000000000000000000002421247015272500211200ustar00rootroot00000000000000#include extern int vobb(void); int main(int argc, const char *argv[]) { int v = 0; if (argc > 1) v = vobb(); printf("First: V is %d\n", v); } kcov_25+dfsg.orig/tests/merge-tests/main_2.c000066400000000000000000000002431247015272500211220ustar00rootroot00000000000000#include extern int mibb(void); int main(int argc, const char *argv[]) { int v = 0; if (argc > 1) v = mibb(); printf("Second: V is %d\n", v); } kcov_25+dfsg.orig/tests/multi-fork/000077500000000000000000000000001247015272500174445ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/multi-fork/code-template.c000066400000000000000000000012371247015272500223360ustar00rootroot00000000000000 volatile double out = 199992.5; out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); out = out + sqrt(out); return out; kcov_25+dfsg.orig/tests/multi-fork/generate-functions.py000077500000000000000000000010401247015272500236140ustar00rootroot00000000000000#!/usr/bin/env python import sys def generate(idx, template): print "double fn%d(void) {" % (idx) print "%s" % (template) print "}" print "" def generate_table(n): print "const unsigned n_functions = %d;" % (n) print "double (*functions[])(void) = {" for i in range(0, n): print " fn%d," % (i) print "};" if __name__ == "__main__": fileName = sys.argv[1] f = open(fileName) template = f.read() f.close() n = int(sys.argv[2]) print "#include " for i in range(0, n): generate(i, template) generate_table(n)kcov_25+dfsg.orig/tests/multi-fork/test-multi-fork.c000066400000000000000000000012111247015272500226510ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "multi-fork-generated.c" int main(int argc, const char *argv[]) { size_t sz = 16 * 1024 * 1024; unsigned i; size_t n = n_functions; for (i = 0; i < n; i++) { pid_t child; child = fork(); if (child < 0) { fprintf(stderr, "fork failed!\n"); return 1; } if (child == 0) { // Child functions[i](); exit(0); } } while (1) { int status; int res; res = wait(&status); // No more children if (res == -1) break; } return 0; } kcov_25+dfsg.orig/tests/popen/000077500000000000000000000000001247015272500164745ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/popen/test-popen.c000066400000000000000000000016431247015272500207420ustar00rootroot00000000000000#include #include #include #include #include #include int test_popen(void) { void *data = NULL; size_t chunk = 1024; size_t alloc_sz = chunk; size_t sz = 0; FILE *fp; fp = popen("/bin/ls -l /", "r"); if (!fp) { fprintf(stderr, "popen failed\n"); return 1; } /* Read in the entire file */ while (!feof(fp)) { size_t ret; data = realloc(data, alloc_sz); if (!data) { fprintf(stderr, "realloc %zu failed\n", alloc_sz); break; } ret = fread((uint8_t *)data + sz, 1, chunk, fp); sz += ret; /* Reached the end */ if (ret < chunk) break; alloc_sz += chunk; } free(data); pclose(fp); return 0; } int main(int argc, const char *argv[]) { unsigned i; for (i = 0; i < 30; i++) { printf("Round %u\n", i); if (test_popen() != 0) { printf("FAIL\n"); exit(0); } } printf("popen OK\n"); return 0; } kcov_25+dfsg.orig/tests/python/000077500000000000000000000000001247015272500166745ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/python/main000077500000000000000000000010421247015272500175430ustar00rootroot00000000000000#!/usr/bin/env python import sys import second def fn(): print("hello!") return def loop_fn(): for i in range(0,10): print("NR %d" % (i)) if __name__ == "__main__": if len(sys.argv) < 2: print("Need at least one arg") sys.exit(1) fn() print("in main, but will return soon") second.second_fn(int(sys.argv[1])) try: if int(sys.argv[1]) > 10: raise Exception("metallgutten") except: print("Got an exception") loop_fn() sys.exit(int(sys.argv[1])) kcov_25+dfsg.orig/tests/python/second.py000066400000000000000000000021321247015272500205170ustar00rootroot00000000000000def second_fn(arg): if arg > 5: # Comment here, but valid line print("Yep") # This is a comment print("Hopp") kalle_anka() arne_anka(arg) def kalle_anka(): ''' satt pa en planka ''' pass def arne_anka(arg): b = """ satt inte pa en planka """ print(b) jens_anka(arg) jens_anka(9) def viktor_anka(): """Single-line multi-line string""" return 0 def jens_anka(kalle): v = """Single-line multi-line string""" if kalle > 5: print("Hej") elif kalle > 128: print("Commodore 128") else: print("Hopp") sven_anka() def sven_anka(): 'single-quoted line' jerker_anka() def mats_anka(): "single-quoted line" pass dict = { 2 : [ 1, 2, 3], 3 : [ 4, 5, 6 ], 5 : { 9 : 1, }, 6 : { }, 7 : { 1 : 2 }, 8 : [ 1, 2 ] } def jerker_anka(): try: f = open("/tmp/a") except: print("Exception") finally: print("Finally finally!") kcov_25+dfsg.orig/tests/python/short-test.py000077500000000000000000000001711247015272500213640ustar00rootroot00000000000000#!/usr/bin/python import sys def doSomething(): print 'Hello world' if __name__ == '__main__': doSomething() kcov_25+dfsg.orig/tests/python/unittest/000077500000000000000000000000001247015272500205535ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/python/unittest/testdriver000077500000000000000000000010131247015272500226670ustar00rootroot00000000000000#!/usr/bin/python import sys import unittest class testcase (unittest.TestCase): def setUp(self): print('in setUp()') self.unitundertest = unitundertest.testme() def testMethod1(self): print('in testcase.testMethod1()') expected=4 result= self.unitundertest.testmethod1(2,2) self.assertEqual(expected,result) if __name__ == '__main__': sys.path.append('.') try: import unitundertest except: print('exception') unittest.main() kcov_25+dfsg.orig/tests/python/unittest/unitundertest.py000066400000000000000000000002601247015272500240400ustar00rootroot00000000000000#!/usr/bin/python import sys class testme: def __init__(self): print('ctor') def testmethod1(self,a,b): print('in testmethod1()') return a+b kcov_25+dfsg.orig/tests/recursive-ptrace/000077500000000000000000000000001247015272500206365ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/recursive-ptrace/main.cc000066400000000000000000000020071247015272500220700ustar00rootroot00000000000000#include #include #include #include #include #include #include int forkAndAttach() { pid_t child, who; int status; child = fork(); if (child < 0) { fprintf(stderr, "fork failed!\n"); return -1; } if (child == 0) { int res; /* We're in the child, set me as traced */ res = ptrace(PTRACE_TRACEME, 0, 0, 0); if (res < 0) { fprintf(stderr, "Can't set me as ptraced\n"); return -1; } ::kill(getpid(), SIGSTOP); return 0; } /* Wait for the initial stop */ who = waitpid(child, &status, 0); if (who < 0) { fprintf(stderr, "waitpid failed\n"); return -1; } if (!WIFSTOPPED(status)) { fprintf(stderr, "Child hasn't stopped: %x\n", status); return -1; } ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACECLONE | PTRACE_O_TRACEFORK); return child; } int main(int argc, const char *argv[]) { int res = forkAndAttach(); if (res >= 0) printf("OK!\n"); else printf("NOK!\n"); return res >= 0; } kcov_25+dfsg.orig/tests/robot-framework/000077500000000000000000000000001247015272500204735ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/robot-framework/kcov-tests.txt000066400000000000000000001103151247015272500233370ustar00rootroot00000000000000*** Settings *** Library OperatingSystem *** Variables *** ${kcov} %{WORKSPACE}/build/src/kcov ${out} %{WORKSPACE}/kcov ${kcov_path} %{WORKSPACE} ${xml_lookup} ${kcov_path}/tests/tools/lookup-xml-node.py *** Test Cases *** too-few-arguments ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} ${out} Should be equal as integers ${rc} 1 wrong-arguments ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} --abcd=efg ${out} tests-stripped Should be equal as integers ${rc} 1 illegal-insn ${output}= Run ${kcov} ${out} ./illegal-insn Should Contain ${output} Illegal instructions are unit test ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ../unit-tests/ut fork-no-wait Run rm -rf ${out}/fork_no_wait ${no_kcov_rc}= Run and return RC ./fork_no_wait ${rc}= Run and return RC ${kcov} ${out} ./fork_no_wait Should be equal as integers ${rc} ${no_kcov_rc} ${output}= Run ${xml_lookup} ${out}/fork_no_wait/cobertura.xml fork_no_wait_c 20 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/fork_no_wait/cobertura.xml fork_no_wait_c 22 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/fork_no_wait/cobertura.xml fork_no_wait_c 24 Should Contain ${output} 1 fork Run rm -rf ${out}/fork ${no_kcov_rc}= Run and return RC ./fork ${rc}= Run and return RC ${kcov} ${out} ./fork Should be equal as integers ${rc} ${no_kcov_rc} ${output}= Run ${xml_lookup} ${out}/fork/cobertura.xml fork_c 21 Should Contain ${output} 0 ${output}= Run ${xml_lookup} ${out}/fork/cobertura.xml fork_c 26 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/fork/cobertura.xml fork_c 34 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/fork/cobertura.xml fork_c 37 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/fork/cobertura.xml fork_c 46 Should Contain ${output} 1 fork-32 Run rm -rf ${out}/fork-32 ${no_kcov_rc}= Run and return RC ./fork-32 ${rc}= Run and return RC ${kcov} ${out} ./fork-32 Should be equal as integers ${rc} ${no_kcov_rc} ${output}= Run ${xml_lookup} ${out}/fork-32/cobertura.xml fork_c 21 Should Contain ${output} 0 ${output}= Run ${xml_lookup} ${out}/fork-32/cobertura.xml fork_c 26 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/fork-32/cobertura.xml fork_c 34 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/fork-32/cobertura.xml fork_c 37 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/fork-32/cobertura.xml fork_c 46 Should Contain ${output} 1 shared-library [Tags] not_ready Run rm -rf ${out}/shared_library_test ${no_kcov_rc}= Run and return RC ./shared_library_test ${rc}= Run and return RC ${kcov} ${out} ./shared_library_test Should be equal as integers ${rc} ${no_kcov_rc} ${output}= Run ${xml_lookup} ${out}/shared_library_test/cobertura.xml main_c 9 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/shared_library_test/cobertura.xml solib_c 5 Should Contain ${output} 1 shared-library-skip Run rm -rf ${out}/shared_library_test ${rc}= Run and return RC ${kcov} --skip-solibs ${out} ./shared_library_test ${output}= Run ${xml_lookup} ${out}/shared_library_test/cobertura.xml main_c 9 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/shared_library_test/cobertura.xml solib_c 5 Should Contain ${output} nocode shared-library-filter-out Run rm -rf ${out}/shared_library_test ${no_kcov_rc}= Run and return RC ./shared_library_test ${rc}= Run and return RC ${kcov} --exclude-pattern=solib ${out} ./shared_library_test Should be equal as integers ${rc} ${no_kcov_rc} ${output}= Run ${xml_lookup} ${out}/shared_library_test/cobertura.xml main_c 9 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/shared_library_test/cobertura.xml solib_c 5 Should Contain ${output} nocode shared-library-accumulate [Tags] not_ready Run rm -rf ${out}/shared_library_test ${rc}= Run and return RC ${kcov} ${out} ./shared_library_test 5 ${output}= Run ${xml_lookup} ${out}/shared_library_test/cobertura.xml main_c 9 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/shared_library_test/cobertura.xml solib_c 5 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/shared_library_test/cobertura.xml solib_c 10 Should Contain ${output} 1 ${rc}= Run and return RC ${kcov} ${out} ./shared_library_test ${output}= Run ${xml_lookup} ${out}/shared_library_test/cobertura.xml solib_c 10 Should Contain ${output} 1 dlopen [Tags] not_ready Run rm -rf ${out}/dlopen ${no_kcov_rc}= Run and return RC ./dlopen ${rc}= Run and return RC ${kcov} ${out} ./dlopen Should be equal as integers ${rc} ${no_kcov_rc} ${output}= Run ${xml_lookup} ${out}/dlopen/cobertura.xml dlopen_cc 11 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/dlopen/cobertura.xml dlopen_cc 12 Should Contain ${output} 0 ${output}= Run ${xml_lookup} ${out}/dlopen/cobertura.xml solib_c 5 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/dlopen/cobertura.xml solib_c 12 Should Contain ${output} 0 dlopen-in-ignored-source-file [Tags] not_ready Run rm -rf ${out}/dlopen ${no_kcov_rc}= Run and return RC ./dlopen ${rc}= Run and return RC ${kcov} --exclude-pattern=dlopen.cc ${out} ./dlopen Should be equal as integers ${rc} ${no_kcov_rc} ${output}= Run ${xml_lookup} ${out}/dlopen/cobertura.xml dlopen_main_cc 10 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/dlopen/cobertura.xml dlopen_cc 11 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/dlopen/cobertura.xml solib_c 5 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/dlopen/cobertura.xml solib_c 12 Should Contain ${output} 0 include-exclude-pattern Run rm -rf ${out}/main-tests ${rc}= Run and return RC ${kcov} --exclude-pattern=.cc ${out} ./main-tests ${output}= Run ${xml_lookup} ${out}/main-tests/cobertura.xml main_cc 9 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/main-tests/cobertura.xml file_c 6 Should Contain ${output} 1 ${rc}= Run and return RC ${kcov} --include-pattern=subdir2 ${out} ./main-tests ${output}= Run ${xml_lookup} ${out}/main-tests/cobertura.xml main_cc 9 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/main-tests/cobertura.xml file_c 6 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/main-tests/cobertura.xml file2_c 6 Should Contain ${output} 1 ${rc}= Run and return RC ${kcov} --include-pattern=subdir ${out} ./main-tests ${output}= Run ${xml_lookup} ${out}/main-tests/cobertura.xml main_cc 9 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/main-tests/cobertura.xml file_c 6 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/main-tests/cobertura.xml file2_c 6 Should Contain ${output} 1 ${rc}= Run and return RC ${kcov} --include-pattern=subdir --exclude-pattern=file2 ${out} ./main-tests ${output}= Run ${xml_lookup} ${out}/main-tests/cobertura.xml main_cc 9 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/main-tests/cobertura.xml file_c 6 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/main-tests/cobertura.xml file2_c 6 Should Contain ${output} nocode main-test Run rm -rf ${out}/main-tests ${no_kcov_rc}= Run and return RC ./main-tests ${rc}= Run and return RC ${kcov} ${out} ./main-tests Should be equal as integers ${rc} ${no_kcov_rc} ${output}= Run ${xml_lookup} ${out}/main-tests/cobertura.xml main_cc 9 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/main-tests/cobertura.xml main_cc 14 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/main-tests/cobertura.xml main_cc 18 Should Be True ${output} >= 1 ${output}= Run ${xml_lookup} ${out}/main-tests/cobertura.xml main_cc 25 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/main-tests/cobertura.xml file_c 6 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/main-tests/cobertura.xml file2_c 7 Should Contain ${output} 0 main-test-gcov [Tags] not_ready Run rm -rf ${out}/main-tests-gcov ${no_kcov_rc}= Run and return RC ./main-tests-gcov ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} --gcov ${out} ./main-tests-gcov Should be equal as integers ${rc} ${no_kcov_rc} ${output}= Run ${xml_lookup} ${out}/main-tests-gcov/cobertura.xml main_cc 9 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/main-tests-gcov/cobertura.xml main_cc 14 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/main-tests-gcov/cobertura.xml main_cc 18 Should Be True ${output} >= 1 ${output}= Run ${xml_lookup} ${out}/main-tests-gcov/cobertura.xml main_cc 25 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/main-tests-gcov/cobertura.xml file_c 6 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/main-tests-gcov/cobertura.xml file2_c 7 Should Contain ${output} 0 gcov-tests-tricky [Tags] not_ready Run rm -rf ${out}/main-tests-gcov ${no_kcov_rc}= Run and return RC ./main-tests-gcov ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} --gcov ${out} ./main-tests-gcov ${output}= Run ${xml_lookup} ${out}/main-tests-gcov/cobertura.xml main_cc 20 Should Contain ${output} nocode accumulate-data Run rm -rf ${out}/argv_dependent/ ${rc}= Run and return RC ${kcov} ${out} ./argv_dependent Should be equal as integers ${rc} 0 ${output}= Run ${xml_lookup} ${out}/argv_dependent/cobertura.xml argv_dependent_c 5 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/argv_dependent/cobertura.xml argv_dependent_c 11 Should Contain ${output} 0 ${rc}= Run and return RC ${kcov} ${out} ./argv_dependent a Should be equal as integers ${rc} 0 ${output}= Run ${xml_lookup} ${out}/argv_dependent/cobertura.xml argv_dependent_c 11 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/argv_dependent/cobertura.xml argv_dependent_c 5 Should Contain ${output} 1 popen-test Run rm -rf ${out}/test_popen ${no_kcov_rc}= Run and return RC ./test_popen ${rc}= Run and return RC ${kcov} ${out} ./test_popen Should be equal as integers ${rc} ${no_kcov_rc} ${output}= Run ${kcov} ${out} ./test_popen Should Contain ${output} popen OK global-constructors-test Run rm -rf ${out}/global-constructors ${no_kcov_rc}= Run and return RC ./global-constructors ${rc}= Run and return RC ${kcov} ${out} ./global-constructors Should be equal as integers ${rc} ${no_kcov_rc} ${output}= Run ${xml_lookup} ${out}/global-constructors/cobertura.xml test_global_ctors_cc 4 Should Be True ${output} >= 1 daemon-wait-for-last-child-test Run rm -rf ${out}/test_daemon ${no_kcov_rc}= Run and return RC ./test_daemon Should be equal as integers ${no_kcov_rc} 2 ${rc}= Run and return RC ${kcov} ${out} ./test_daemon Should be equal as integers ${rc} 4 ${output}= Run ${xml_lookup} ${out}/test_daemon/cobertura.xml test_daemon_cc 31 Should Contain ${output} 1 daemon-no-wait-for-last-child-test [Tags] not_ready Run rm -rf ${out}/test_daemon/cobertura.xml ${no_kcov_rc}= Run and return RC ./test_daemon ${rc}= Run and return RC ${kcov} --exit-first-process ${out} ./test_daemon Should be equal as integers ${no_kcov_rc} ${rc} ${output}= Run ${xml_lookup} ${out}/test_daemon/cobertura.xml test_daemon_cc 31 Should Contain ${output} 0 Run sleep 5 ${output}= Run ${xml_lookup} ${out}/test_daemon/cobertura.xml test_daemon_cc 31 Should Contain ${output} 1 signals Run rm -rf ${out}/signals ${no_kcov_rc}= Run and return RC ./signals hup ${rc}= Run and return RC ${kcov} ${out} ./signals hup Should be equal as integers ${no_kcov_rc} ${rc} ${output}= Run ${xml_lookup} ${out}/signals/cobertura.xml test_signals_c 14 ${no_kcov_rc}= Run and return RC ./signals int ${rc}= Run and return RC ${kcov} ${out} ./signals int Should be equal as integers ${no_kcov_rc} ${rc} ${output}= Run ${xml_lookup} ${out}/signals/cobertura.xml test_signals_c 19 ${no_kcov_rc}= Run and return RC ./signals quit ${rc}= Run and return RC ${kcov} ${out} ./signals quit Should be equal as integers ${no_kcov_rc} ${rc} ${output}= Run ${xml_lookup} ${out}/signals/cobertura.xml test_signals_c 24 ${no_kcov_rc}= Run and return RC ./signals abrt ${rc}= Run and return RC ${kcov} ${out} ./signals abrt Should be equal as integers ${no_kcov_rc} ${rc} ${output}= Run ${xml_lookup} ${out}/signals/cobertura.xml test_signals_c 39 ${no_kcov_rc}= Run and return RC ./signals segv ${rc}= Run and return RC ${kcov} ${out} ./signals segv Should be equal as integers ${no_kcov_rc} ${rc} ${output}= Run ${xml_lookup} ${out}/signals/cobertura.xml test_signals_c 69 ${no_kcov_rc}= Run and return RC ./signals term ${rc}= Run and return RC ${kcov} ${out} ./signals term Should be equal as integers ${no_kcov_rc} ${rc} ${output}= Run ${xml_lookup} ${out}/signals/cobertura.xml test_signals_c 89 signals-self Run rm -rf ${out}/signals/ ${output}= Run ${kcov} ${out} ./signals segv self Should Contain ${output} kcov: Process exited with signal 11 ${output}= Run ${kcov} ${out} ./signals abrt self Should Contain ${output} kcov: Process exited with signal 6 collect-only-and-report-only Run rm -rf ${out}/main-tests/ ${no_kcov_rc}= Run and return RC ./main-tests ${rc}= Run and return RC ${kcov} --collect-only ${out} ./main-tests Should be equal as integers ${rc} ${no_kcov_rc} ${rc}= Run and return RC test -e ${out}/main-tests/cobertura.xml Should be equal as integers ${rc} 1 ${rc}= Run and return RC ${kcov} --collect-only ${out} ./main-tests Should be equal as integers ${rc} 0 ${output}= Run ${xml_lookup} ${out}/main-tests/cobertura.xml main_cc 9 Should Contain ${output} 1 setpgid Run rm -rf ${out} ${output}= Run ${kcov_path}/tests/setpgid-kill/test-script.sh ./setpgid-kill Should Contain ${output} SUCCESS ${output}= Run ${kcov_path}/tests/setpgid-kill/test-script.sh ${kcov} ${out} ./setpgid-kill Should Contain ${output} SUCCESS # Issue 31 attach-process-with-threads Run rm -rf ${out} ${output}= Run ${kcov_path}/tests/daemon/test-script.sh ${kcov} ${out} ./issue31 ${output}= Run ${xml_lookup} ${out}/issue31/cobertura.xml test_issue31_cc 28 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/issue31/cobertura.xml test_issue31_cc 11 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/issue31/cobertura.xml test_issue31_cc 8 Should Contain ${output} 0 python-exit-status Run rm -rf ${out}/main ${no_kcov_rc}= Run and return RC python ${kcov_path}/tests/python/main 5 ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} ${out} ${kcov_path}/tests/python/main 5 Should be equal as integers ${rc} ${no_kcov_rc} python-unittest Run rm -rf ${out}/testdriver ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} ${out} ${kcov_path}/tests/python/unittest/testdriver ${output}= Run ${xml_lookup} ${out}/testdriver/cobertura.xml testdriver 14 Should Contain ${output} 1 python-short-file Run rm -rf ${out}/short-test.py ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} ${out} ${kcov_path}/tests/python/short-test.py Should be equal as integers ${rc} 0 ${output}= Run ${xml_lookup} ${out}/short-test.py/cobertura.xml short_test_py 6 Should Contain ${output} 1 python-coverage Run rm -rf ${out}/main ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} ${out} ${kcov_path}/tests/python/main 5 ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml main 10 Should Contain ${output} 2 ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml main 17 Should Contain ${output} 0 ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml main 22 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml second_py 2 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml second_py 4 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml second_py 11 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml second_py 31 Should Contain ${output} 0 ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml second_py 38 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml second_py 39 Should Contain ${output} 0 ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml second_py 40 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml second_py 41 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml second_py 56 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml second_py 60 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml second_py 65 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml second_py 77 Should Contain ${output} nocode python-accumulate-data Run rm -rf ${out}/main ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} ${out} ${kcov_path}/tests/python/main 5 ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml main 16 Should Contain ${output} 0 ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml main 19 Should Contain ${output} 1 ${rc}= Run and return RC ${kcov} ${out} ${kcov_path}/tests/python/main ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml main 16 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml main 19 Should Contain ${output} 1 python-coverage-python3 Run rm -rf ${out}/main ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} --python-parser=python3 ${out} ${kcov_path}/tests/python/main 5 ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml main 10 Should Contain ${output} 2 ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml main 17 Should Contain ${output} 0 ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml main 22 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml second_py 2 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml second_py 4 Should Contain ${output} nocode python-tricky-single-line-string-assignment [Tags] not_ready Run rm -rf ${out}/main ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} ${out} ${kcov_path}/tests/python/main 5 ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml second_py 34 Should Contain ${output} 1 python-tricky-single-dict-assignment [Tags] not_ready Run rm -rf ${out}/main ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} ${out} ${kcov_path}/tests/python/main 5 ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml second_py 57 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml second_py 61 Should Contain ${output} 1 #python-select-parser # Run rm -rf ${out}/main # ${rc}= Run and return RC ${kcov} --python-parser=${kcov_path}/tests/tools/dummy-python.sh ${out} ${kcov_path}/tests/python/main 5 # Should be equal as integers ${rc} 99 # Not sure why this fails in some settings (works in F20 at least) bash-exit-status [Tags] not_ready Run rm -rf ${out}/main ${no_kcov_rc}= Run and return RC ${kcov_path}/tests/bash/shell-main 5 ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} ${out} ${kcov_path}/tests/bash/shell-main 5 Should be equal as integers ${rc} ${no_kcov_rc} bash-coverage Run rm -rf ${out}/shell-main ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} ${out} ${kcov_path}/tests/bash/shell-main 5 ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 3 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 4 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 13 Should Contain ${output} 0 ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 26 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 30 Should Contain ${output} 5 ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 34 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 38 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml other_sh 5 Should Contain ${output} 1 ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} ${out} ${kcov_path}/tests/bash/shell-main ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml short_test_sh 5 Should Contain ${output} 11 bash-short-file Run rm -rf ${out}/short-test.sh ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} ${out} ${kcov_path}/tests/bash/short-test.sh Should be equal as integers ${rc} 0 ${output}= Run ${xml_lookup} ${out}/short-test.sh/cobertura.xml short_test_sh 5 Should Contain ${output} 11 bash-coverage-heredoc-backslashes Run rm -rf ${out} ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} ${out} ${kcov_path}/tests/bash/shell-main 5 ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 52 Should Contain ${output} 4 ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 53 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 55 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 59 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 61 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 62 Should Contain ${output} nocode bash-issue-44-heredoc-special-cases Run rm -rf ${out}/shell-main ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} ${out} ${kcov_path}/tests/bash/shell-main ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 77 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 83 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 88 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 93 Should Contain ${output} 1 bash-coverage-tricky Run rm -rf ${out}/shell-main ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} ${out} ${kcov_path}/tests/bash/shell-main 5 ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 36 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 44 Should Contain ${output} nocode ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 7 Should Contain ${output} nocode bash-honor-signal Run rm -rf ${out} ${output}= Run ${kcov_path}/tests/setpgid-kill/test-script.sh ${kcov} ${out} ${kcov_path}/tests/bash/trap.sh Should Contain ${output} SUCCESS ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml trap_sh 5 Should Contain ${output} 1 bash-accumulate-data Run rm -rf ${out} ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} ${out} ${kcov_path}/tests/bash/unitundertest.sh 1 ${output}= Run ${xml_lookup} ${out}/unitundertest.sh/cobertura.xml unitundertest_sh 6 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/unitundertest.sh/cobertura.xml unitundertest_sh 16 Should Contain ${output} 0 ${rc}= Run and return RC ${kcov} ${out} ${kcov_path}/tests/bash/unitundertest.sh 2 ${output}= Run ${xml_lookup} ${out}/unitundertest.sh/cobertura.xml unitundertest_sh 16 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/unitundertest.sh/cobertura.xml unitundertest_sh 6 Should Contain ${output} 1 bash-accumulate-changed-data Run rm -rf ${out} ${rc}= Run and return RC cp ${kcov_path}/tests/bash/shell-main . ${rc}= Run and return RC cp ${kcov_path}/tests/bash/other.sh . ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} ${out} ./shell-main 5 9 ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 40 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 67 Should Contain ${output} 2 ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml other_sh 6 Should Contain ${output} 2 Run echo 'echo arne-anka' >> shell-main ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} ${out} ./shell-main 5 ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 40 Should Contain ${output} 0 ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml shell_main 67 Should Contain ${output} 0 ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml other_sh 6 Should Contain ${output} 3 bash-merge-data-issue-38 Run rm -rf ${out} ${rc}= Run and return RC ${kcov} ${out} ./dlopen ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} ${out} ${kcov_path}/tests/bash/unitundertest.sh 1 ${output}= Run ${xml_lookup} ${out}/unitundertest.sh/cobertura.xml unitundertest_sh 6 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/unitundertest.sh/cobertura.xml unitundertest_sh 16 Should Contain ${output} 0 ${rc}= Run and return RC ${kcov} ${out} ${kcov_path}/tests/bash/unitundertest.sh 2 ${output}= Run ${xml_lookup} ${out}/unitundertest.sh/cobertura.xml unitundertest_sh 16 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/unitundertest.sh/cobertura.xml unitundertest_sh 6 Should Contain ${output} 1 ${rc}= Run and return RC ${kcov} ${out} ${kcov_path}/tests/bash/unitundertest.sh all ${output}= Run ${xml_lookup} ${out}/unitundertest.sh/cobertura.xml unitundertest_sh 36 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/kcov-merged/cobertura.xml unitundertest_sh 30 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/kcov-merged/cobertura.xml unitundertest_sh 33 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/kcov-merged/cobertura.xml unitundertest_sh 36 Should Contain ${output} 1 bash-output-crc Run rm -rf ${out}/a.sh ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} ${out} ${kcov_path}/tests/bash/first-dir/a.sh ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} ${out} ${kcov_path}/tests/bash/second-dir/a.sh ${output}= Run ls -l ${out}/a.sh/a.*.json | wc -l Should Contain ${output} 2 ${output}= Run ls -l ${out}/a.sh/b.*.json | wc -l Should Contain ${output} 2 bash-sh-shebang [Tags] not_ready Run rm -rf ${out}/shell-main ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} ${out} ${kcov_path}/tests/bash/shell-main 5 ${output}= Run ${xml_lookup} ${out}/shell-main/cobertura.xml sh_shebang_sh 4 Should Contain ${output} 1 merge-basic Run rm -rf ${out} ${rc}= Run and return RC ${kcov} --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov ${kcov} ${out} ${kcov_path}/tests/python/main 5 ${rc}= Run and return RC ${kcov} ${out} ./main-tests ${output}= Run ${xml_lookup} ${out}/main/cobertura.xml main 10 Should Contain ${output} 2 ${output}= Run ${xml_lookup} ${out}/main-tests/cobertura.xml main_cc 9 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/kcov-merged/cobertura.xml main 10 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/kcov-merged/cobertura.xml main_cc 9 Should Contain ${output} 1 merge-same-file-in-multiple-binaries Run rm -rf ${out} ${rc}= Run and return RC ${kcov} ${out} ./multi_1 ${output}= Run ${xml_lookup} ${out}/multi_1/cobertura.xml main_1_c 9 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/multi_1/cobertura.xml main_1_c 10 ${output}= Run ${xml_lookup} ${out}/multi_1/cobertura.xml file_c 3 Should Contain ${output} 0 Should Contain ${output} 0 ${rc}= Run and return RC ${kcov} ${out} ./multi_2 ${output}= Run ${xml_lookup} ${out}/kcov-merged/cobertura.xml main_2_c 9 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/kcov-merged/cobertura.xml main_2_c 9 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/kcov-merged/cobertura.xml file_c 3 Should Contain ${output} 0 ${output}= Run ${xml_lookup} ${out}/kcov-merged/cobertura.xml file_c 8 Should Contain ${output} 0 ${rc}= Run and return RC ${kcov} ${out} ./multi_2 1 ${output}= Run ${xml_lookup} ${out}/kcov-merged/cobertura.xml file_c 8 Should Contain ${output} 1 ${rc}= Run and return RC ${kcov} ${out} ./multi_1 1 ${output}= Run ${xml_lookup} ${out}/kcov-merged/cobertura.xml file_c 3 Should Contain ${output} 1 merge-changed-file Run rm -rf ${out} file.c file.o main_1.o main_2.o ${rc}= Run and return RC cp ${kcov_path}/tests/merge-tests/file.c . Should be equal as integers ${rc} 0 ${rc}= Run and return RC gcc -c -g -o file.o file.c ${rc}= Run and return RC gcc -c -g -o main_1.o ${kcov_path}/tests/merge-tests/main_1.c ${rc}= Run and return RC gcc -o multi_3 main_1.o file.o ${rc}= Run and return RC ${kcov} ${out} ./multi_3 9 ${output}= Run ${xml_lookup} ${out}/multi_3/cobertura.xml file_c 3 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/multi_3/cobertura.xml file_c 8 Should Contain ${output} 0 ${output}= Run ${xml_lookup} ${out}/multi_3/cobertura.xml file_c 10 Should Contain ${output} nocode ${rc}= Run and return RC echo "int kalle(void) { return 129; }" >> file.c ${rc}= Run and return RC gcc -c -g -o file.o file.c ${rc}= Run and return RC gcc -c -g -o main_2.o ${kcov_path}/tests/merge-tests/main_2.c ${rc}= Run and return RC gcc -o multi_4 main_2.o file.o ${rc}= Run and return RC ${kcov} ${out} ./multi_4 9 ${output}= Run ${xml_lookup} ${out}/kcov-merged/cobertura.xml file_c 3 Should Contain ${output} 0 ${output}= Run ${xml_lookup} ${out}/kcov-merged/cobertura.xml file_c 8 Should Contain ${output} 1 ${output}= Run ${xml_lookup} ${out}/kcov-merged/cobertura.xml file_c 10 Should Contain ${output} 0 kcov_25+dfsg.orig/tests/setpgid-kill/000077500000000000000000000000001247015272500177435ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/setpgid-kill/setpgid-kill-main.cc000066400000000000000000000011231247015272500235610ustar00rootroot00000000000000#include #include #include int main (int argc, char** argv) { printf("pid: %d\n", getpid()); printf("pgid: %d\n", getpgid(0)); printf("calling setpgid...\n"); const int result = setpgid(0, 0); printf("setpgid result: %d\n", result); int pid = getpid(); printf("pid: %d\n", pid); printf("pgid: %d\n", getpgid(0)); std::string s = fmt("%d\n", pid); write_file(s.c_str(), s.size(), "/tmp/setpgid.pid"); // Long sleep just to allow kcov to clean up stuff sleep(20); printf("After sleep\n"); return 0; } kcov_25+dfsg.orig/tests/setpgid-kill/test-script.sh000077500000000000000000000002401247015272500225570ustar00rootroot00000000000000#!/bin/bash prg=$* $prg & sleep 1 kill -TERM %1 sleep 2 pid=`cat /tmp/setpgid.pid` if [ -e /proc/$pid/cmdline ] ; then echo "FAIL" else echo "SUCCESS" fi kcov_25+dfsg.orig/tests/shared-library/000077500000000000000000000000001247015272500202635ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/shared-library/big-symbol.S000066400000000000000000000001571247015272500224560ustar00rootroot00000000000000.text .globl very_big_symbol very_big_symbol: .fill 4096, 1, 0x90 #if defined(__PPC__) blr #else ret #endif kcov_25+dfsg.orig/tests/shared-library/main.c000066400000000000000000000002101247015272500213440ustar00rootroot00000000000000#include extern int vobb(int a); int main(int argc, const char *argv[]) { printf("In main\n"); vobb(argc); return 0; } kcov_25+dfsg.orig/tests/shared-library/solib.c000066400000000000000000000003301247015272500215330ustar00rootroot00000000000000#include int vobb(int a) { printf("In shared library\n"); a = a + 1; if (a > 2) return 1; return 0; } void this_function_should_not_be_called(void) { printf("If it is, something is wrong\n"); } kcov_25+dfsg.orig/tests/signals/000077500000000000000000000000001247015272500170135ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/signals/test-signals.c000066400000000000000000000070051247015272500215760ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include void catch_hup(int sig) { printf("HUP\n"); } void catch_int(int sig) { printf("INT\n"); } void catch_quit(int sig) { printf("QUIT\n"); } void catch_ill(int sig) { printf("ILL\n"); } void catch_trap(int sig) { printf("TRAP\n"); } void catch_abrt(int sig) { printf("ABRT\n"); } void catch_bus(int sig) { printf("BUS\n"); } void catch_fpe(int sig) { printf("FPE\n"); } void catch_kill(int sig) { printf("KILL\n"); } void catch_usr1(int sig) { printf("USR1\n"); } void catch_segv(int sig) { printf("SEGV\n"); } void catch_usr2(int sig) { printf("USR2\n"); } void catch_pipe(int sig) { printf("PIPE\n"); } void catch_alarm(int sig) { printf("ALARM\n"); } void catch_term(int sig) { printf("TERM\n"); } void catch_stkflt(int sig) { printf("STKFLT\n"); } void catch_chld(int sig) { printf("CHLD\n"); } void catch_cont(int sig) { printf("CONT\n"); } void catch_stop(int sig) { printf("STOP\n"); } void catch_stp(int sig) { printf("STP\n"); } void catch_ttin(int sig) { printf("TTIN\n"); } void catch_ttou(int sig) { printf("TTOU\n"); } void catch_urg(int sig) { printf("URG\n"); } void catch_xcpu(int sig) { printf("XCPU\n"); } void catch_xfsz(int sig) { printf("XFSZ\n"); } void catch_vtalrm(int sig) { printf("VTALRM\n"); } void catch_prof(int sig) { printf("PROF\n"); } void catch_winch(int sig) { printf("WINCH\n"); } void catch_gio(int sig) { printf("GIO\n"); } void catch_pwr(int sig) { printf("PWR\n"); } void catch_sys(int sig) { printf("SYS\n"); } static const char *handler_names[] = { "zero has no handler", "hup", "int", "quit", "ill", "trap", "abrt", "bus", "fpe", "kill", "usr1", "segv", "usr2", "pipe", "alarm", "term", "stkflt", "chld", "cont", "stop", "stp", "ttin", "ttou", "urg", "xcpu", "xfsz", "vtalrm", "prof", "winch", "gio", "pwr", "sys", }; static void (*handler_table[])(int) = { NULL, catch_hup, //SIGHUP catch_int, //SIGINT catch_quit, //SIGQUIT catch_ill, //SIGILL catch_trap, //SIGTRAP catch_abrt, //SIGABRT catch_bus, //SIGBUS catch_fpe, //SIGFPE catch_kill, //SIGKILL catch_usr1, //SIGUSR1 catch_segv, //SIGSEGV catch_usr2, //SIGUSR2 catch_pipe, //SIGPIPE catch_alarm,//SIGALRM catch_term, //SIGTERM catch_stkflt,//SIGSTKFLT catch_chld, //SIGCHLD catch_cont, //SIGCONT catch_stop, //SIGSTOP catch_stp, //SIGTSTP catch_ttin, //SIGTTIN catch_ttou, //SIGTTOU catch_urg, //SIGURG catch_xcpu, //SIGXCPU catch_xfsz, //SIGXFSZ catch_vtalrm,//SIGVTALRM catch_prof, //SIGPROF catch_winch,//SIGWINCH catch_gio, //SIGIO catch_pwr, //SIGPWR catch_sys, //SIGSYS }; int main(int argc, const char *argv[]) { pid_t child; int status; int sig; unsigned self = 0; size_t n_handlers = sizeof(handler_names) / sizeof(handler_names[0]); if (argc <= 1) { printf("Usage: xxx [self]\n"); exit(1); } if (argc == 3) self = 1; for (sig = 0; sig < n_handlers; sig++) { if (strcmp(argv[1], handler_names[sig]) == 0) break; } if (sig == n_handlers) { printf("Unknown handler %s\n", argv[1]); exit(1); } child = fork(); if (child < 0) { fprintf(stderr, "fork failed!\n"); return -1; } if (child == 0) { signal(sig, handler_table[sig]); sleep(2); return 0; } else { // Parent sleep(1); if (self) kill(getpid(), sig); else kill(child, sig); } wait(&status); return 0; } kcov_25+dfsg.orig/tests/subdir/000077500000000000000000000000001247015272500166435ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/subdir/file.c000066400000000000000000000001441247015272500177250ustar00rootroot00000000000000#include #include "../include/header.h" void tjoho(int a) { if (a) printf("Hej\n"); } kcov_25+dfsg.orig/tests/subdir2/000077500000000000000000000000001247015272500167255ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/subdir2/file.c000066400000000000000000000001451247015272500200100ustar00rootroot00000000000000#include #include "../include/header.h" void tjoho2(int a) { if (a) printf("Hej\n"); } kcov_25+dfsg.orig/tests/subdir2/file2.c000066400000000000000000000001451247015272500200720ustar00rootroot00000000000000#include #include "../include/header.h" void tjoho2(int a) { if (a) printf("Hej\n"); } kcov_25+dfsg.orig/tests/test-module/000077500000000000000000000000001247015272500176155ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/test-module/Makefile000066400000000000000000000003351247015272500212560ustar00rootroot00000000000000KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) CFLAGS_test_module.o += -g3 obj-m += test_module.o default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules clean: rm -f *.o *.ko *.order *.symvers *.mod.c kcov_25+dfsg.orig/tests/test-module/test_module.c000066400000000000000000000025671247015272500223170ustar00rootroot00000000000000/* * * Copyright (C) 2012 Simon Kagstrom * * Author: Simon Kagstrom * * Most of the stuff comes from Jonathan Corbets seqfile example here: * * http://lwn.net/Articles/22359/ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive * for more details. */ #include #include #include #include #include #include static int hz_show(struct seq_file *m, void *v) { seq_printf(m, "%d\n", HZ); return 0; } static int hz_open(struct inode *inode, struct file *file) { return single_open(file, hz_show, NULL); } static const struct file_operations ct_file_ops = { .owner = THIS_MODULE, .open = hz_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int __init test_init(void) { struct proc_dir_entry *entry; printk(KERN_INFO "initing test module!\n"); entry = create_proc_entry("test_module", 0, NULL); if (entry) entry->proc_fops = &ct_file_ops; return 0; } static void __exit test_exit(void) { remove_proc_entry("test_module", NULL); } module_init(test_init); module_exit(test_exit); MODULE_AUTHOR("Simon Kagstrom "); MODULE_DESCRIPTION("Test"); MODULE_LICENSE("GPL"); kcov_25+dfsg.orig/tests/tools/000077500000000000000000000000001247015272500165135ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/tools/dummy-python.sh000077500000000000000000000000231247015272500215170ustar00rootroot00000000000000#!/bin/sh exit 99 kcov_25+dfsg.orig/tests/tools/lookup-xml-node.py000077500000000000000000000024771247015272500221340ustar00rootroot00000000000000#!/usr/bin/env python import sys import xml.dom.minidom #all these imports are standard on most modern python implementations def readFile(name): f = open(name,'r') data = f.read() f.close() return data def lookupClassName(dom, name): tags = dom.getElementsByTagName('class') for tag in tags: nameAttr = tag.attributes["name"] if nameAttr.value == name: return tag return None def lookupHitsByLine(classTag, lineNr): tags = classTag.getElementsByTagName('line') for tag in tags: numberAttr = tag.attributes["number"] if int(numberAttr.value) == int(lineNr): hitsAttr = tag.attributes["hits"] return int(hitsAttr.value) return None def parse(data): #parse the xml you got from the file dom = xml.dom.minidom.parseString(data) return dom if __name__ == "__main__": if len(sys.argv) < 4: print "Usage: lookup-class-line " sys.exit(1) fileName = sys.argv[2] line = int(sys.argv[3]) data = readFile(sys.argv[1]) dom = parse(data) fileTag = lookupClassName(dom, fileName) if fileTag != None: hits = lookupHitsByLine(fileTag, line) if hits != None: print hits sys.exit(0) print "nocode" kcov_25+dfsg.orig/tests/unit-tests/000077500000000000000000000000001247015272500174725ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/unit-tests/CMakeLists.txt000066400000000000000000000064671247015272500222470ustar00rootroot00000000000000cmake_minimum_required (VERSION 2.6) project (kcov-ut) set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../../cmake) find_package (LibCRPCUT REQUIRED) find_package (LibElf REQUIRED) find_package (Elfutils REQUIRED) # ==================================== # project name and version # ==================================== project (unit-test) set (TGT ut) set (${TGT}_SRCS ../../src/capabilities.cc ../../src/collector.cc ../../src/engine-factory.cc ../../src/gcov.cc ../../src/output-handler.cc ../../src/parsers/elf-parser.cc ../../src/parser-manager.cc ../../src/utils.cc ../../src/writers/cobertura-writer.cc ../../src/writers/html-writer.cc ../../src/writers/writer-base.cc main.cc tests-collector.cc tests-configuration.cc tests-elf.cc tests-filter.cc tests-merge-parser.cc tests-reporter.cc tests-utils.cc tests-writer.cc ) set (CMAKE_BUILD_TYPE debug) set (CMAKE_CXX_FLAGS "-std=c++1y -Wall -D_GLIBCXX_USE_NANOSLEEP") add_custom_command( OUTPUT html-data-files.cc COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/../../src/bin-to-c-source.py ${CMAKE_CURRENT_SOURCE_DIR}/../../data/bcov.css css_text ${CMAKE_CURRENT_SOURCE_DIR}/../../data/amber.png icon_amber ${CMAKE_CURRENT_SOURCE_DIR}/../../data/glass.png icon_glass ${CMAKE_CURRENT_SOURCE_DIR}/../../data/source-file.html source_file_text ${CMAKE_CURRENT_SOURCE_DIR}/../../data/index.html index_text ${CMAKE_CURRENT_SOURCE_DIR}/../../data/js/handlebars.js handlebars_text ${CMAKE_CURRENT_SOURCE_DIR}/../../data/js/kcov.js kcov_text ${CMAKE_CURRENT_SOURCE_DIR}/../../data/js/jquery.min.js jquery_text ${CMAKE_CURRENT_SOURCE_DIR}/../../data/js/jquery.tablesorter.min.js tablesorter_text ${CMAKE_CURRENT_SOURCE_DIR}/../../data/js/jquery.tablesorter.widgets.min.js tablesorter_widgets_text ${CMAKE_CURRENT_SOURCE_DIR}/../../data/tablesorter-theme.css tablesorter_theme_text > html-data-files.cc DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../../data/bcov.css ${CMAKE_CURRENT_SOURCE_DIR}/../../data/amber.png ${CMAKE_CURRENT_SOURCE_DIR}/../../data/glass.png ${CMAKE_CURRENT_SOURCE_DIR}/../../data/source-file.html ${CMAKE_CURRENT_SOURCE_DIR}/../../data/index.html ${CMAKE_CURRENT_SOURCE_DIR}/../../data/js/handlebars.js ${CMAKE_CURRENT_SOURCE_DIR}/../../data/js/kcov.js ${CMAKE_CURRENT_SOURCE_DIR}/../../data/js/jquery.min.js ${CMAKE_CURRENT_SOURCE_DIR}/../../data/js/jquery.tablesorter.min.js ${CMAKE_CURRENT_SOURCE_DIR}/../../data/js/jquery.tablesorter.widgets.min.js ${CMAKE_CURRENT_SOURCE_DIR}/../../data/tablesorter-theme.css ${CMAKE_CURRENT_SOURCE_DIR}/../../src/bin-to-c-source.py ) include_directories( ../../src/include/ ${LIBCRPCUT_INCLUDE_DIRS} ${LIBELF_INCLUDE_DIRS} ${LIBDW_INCLUDE_DIRS} ) link_directories (/home/ska/local/lib) add_executable (${TGT} ${${TGT}_SRCS} html-data-files.cc) target_link_libraries(${TGT} ${LIBCRPCUT_LIBRARIES} ${LIBDW_LIBRARIES} ${LIBELF_LIBRARIES} dl z pthread) add_executable (same-name-test ../main.cc ../subdir/file.c ../subdir2/file.c ) add_executable (test-binary test-source.c second-source.c) SET_TARGET_PROPERTIES(test-binary PROPERTIES COMPILE_FLAGS "-nostdinc") SET_TARGET_PROPERTIES(test-binary PROPERTIES LINK_FLAGS "-nostdlib") kcov_25+dfsg.orig/tests/unit-tests/main.cc000066400000000000000000000002271247015272500207260ustar00rootroot00000000000000#include "test.hh" #include "mocks/mock-engine.hh" using namespace kcov; int main(int argc, const char *argv[]) { return crpcut::run(argc, argv); } kcov_25+dfsg.orig/tests/unit-tests/mocks/000077500000000000000000000000001247015272500206065ustar00rootroot00000000000000kcov_25+dfsg.orig/tests/unit-tests/mocks/mock-collector.hh000066400000000000000000000010031247015272500240360ustar00rootroot00000000000000#pragma once #include "../test.hh" #include #include class MockCollector : public kcov::ICollector { public: MAKE_MOCK1(registerListener, void(kcov::ICollector::IListener &listener)); MAKE_MOCK1(registerEventTickListener, void(kcov::ICollector::IEventTickListener &listener)); MAKE_MOCK0(prepare, int()); MAKE_MOCK1(run, int(const std::string &)); MAKE_MOCK0(stop, void()); void mockRegisterListener(IListener &listener) { m_listener = &listener; } IListener *m_listener; }; kcov_25+dfsg.orig/tests/unit-tests/mocks/mock-engine.hh000066400000000000000000000007361247015272500233310ustar00rootroot00000000000000#pragma once #include "../test.hh" #include using namespace kcov; class MockEngine : public IEngine { public: MockEngine() { } MAKE_MOCK1(registerBreakpoint, int(unsigned long addr)); MAKE_MOCK2(start, bool(IEventListener &listener, const std::string &executable)); MAKE_MOCK0(continueExecution, bool()); MAKE_MOCK1(kill, void(int)); unsigned int matchFile(const std::string &filename, uint8_t *data, size_t dataSize) { return match_perfect; } }; kcov_25+dfsg.orig/tests/unit-tests/mocks/mock-reporter.hh000066400000000000000000000013441247015272500237220ustar00rootroot00000000000000#pragma once #include namespace kcov { class MockReporter : public IReporter { public: MAKE_MOCK1(fileIsIncluded, bool(const std::string &file)); MAKE_MOCK2(lineIsCode, bool(const std::string &file, unsigned int lineNr)); MAKE_MOCK2(getLineExecutionCount, LineExecutionCount(const std::string &file, unsigned int lineNr)); MAKE_MOCK0(getExecutionSummary, ExecutionSummary()); MAKE_MOCK1(registerListener, void(kcov::IReporter::IListener &listener)); MAKE_MOCK1(marshal, void *(size_t *szOut)); MAKE_MOCK2(unMarshal, bool(void *data, size_t sz)); MAKE_MOCK0(stop, void()); void *mockMarshal(size_t *outSz) { void *out = malloc(32); *outSz = 32; return out; } }; } kcov_25+dfsg.orig/tests/unit-tests/second-source.c000066400000000000000000000002021247015272500224010ustar00rootroot00000000000000int mibb(int a) { // "Hej" and 'hej' means HTML escaping! \ is another escape if (a < 5 && a > 3) return 0; return a - 1; } kcov_25+dfsg.orig/tests/unit-tests/test-source.c000066400000000000000000000004021247015272500221070ustar00rootroot00000000000000// Small dummy test program to keep the addresses few! int kalle(int a, int b) { if (a == 15) return 1; // Should be two blocks if (a == 10 && b == 19) return 2; return 0; } void _start(int a, int b) { if (kalle(a, b)) return; kalle(9, 5); } kcov_25+dfsg.orig/tests/unit-tests/test.hh000066400000000000000000000001421247015272500207670ustar00rootroot00000000000000#pragma once #include "trompeloeil/trompeloeil.hpp" #include using trompeloeil::_; kcov_25+dfsg.orig/tests/unit-tests/tests-collector.cc000066400000000000000000000027141247015272500231330ustar00rootroot00000000000000#include "test.hh" #include "mocks/mock-engine.hh" #include #include #include #include #include using namespace kcov; class MockCollectorListener : public ICollector::IListener { public: virtual ~MockCollectorListener() { } MAKE_MOCK2(onAddressHit, void(unsigned long addr, unsigned long hits)); }; DISABLED_TEST(collector) { MockEngine engine; MockCollectorListener listener; IFileParser *elf; char filename[1024]; bool res; sprintf(filename, "%s/test-binary", crpcut::get_start_dir()); elf = IParserManager::getInstance().matchParser(filename); ASSERT_TRUE(elf); ICollector &collector = ICollector::create(*elf, engine, IFilter::create()); collector.registerListener(listener); REQUIRE_CALL(engine, registerBreakpoint(_)) .TIMES(AT_LEAST(1)) .RETURN(0) ; res = elf->parse(); ASSERT_TRUE(res); int v; REQUIRE_CALL(engine, start(_,_)) .TIMES(1) .RETURN(true) ; IEngine::Event evExit, evOnce; evExit.addr = 0; evExit.type = ev_exit; evExit.data = 1; evOnce.addr = 1; evOnce.type = ev_breakpoint; evOnce.data = 1; REQUIRE_CALL(listener, onAddressHit(1, _)) .TIMES(1) ; v = collector.run(filename); ASSERT_EQ(v, evExit.data); evOnce.type = ev_error; // Test error REQUIRE_CALL(engine, start(_,_)) .TIMES(1) .RETURN(0) ; REQUIRE_CALL(engine, continueExecution()) .TIMES(1) .RETURN(true) ; v = collector.run(filename); ASSERT_EQ(v, -1); } kcov_25+dfsg.orig/tests/unit-tests/tests-configuration.cc000066400000000000000000000100651247015272500240120ustar00rootroot00000000000000#include "test.hh" #include #include #include "../../src/configuration.cc" static bool runParse(std::string args) { char *p = strdup(args.c_str()); char *cur; std::list argList; argList.push_back(std::string(crpcut::get_start_dir()) + "/test-binary"); cur = strtok(p, " "); while (cur) { argList.push_back(std::string(cur)); cur = strtok(NULL, " "); } free((void *)p); const char **argv = (const char **)malloc(argList.size() * sizeof(const char*)); int i = 0; for (std::list::iterator it = argList.begin(); it != argList.end(); it++) { // Yes, these are memory leaks. argv[i] = strdup((*it).c_str()); i++; } return IConfiguration::getInstance().parse(argList.size(), argv); } TESTSUITE(configuration) { TEST(basic) { std::string filename = std::string(crpcut::get_start_dir()) + "/test-binary"; Configuration *conf = &(Configuration &)Configuration::getInstance(); ASSERT_TRUE(conf); bool res = runParse(fmt("/tmp/vobb %s tjena", filename.c_str())); ASSERT_TRUE(res); const char **a = conf->getArgv(); ASSERT_TRUE(filename == a[0]); ASSERT_TRUE(std::string("tjena") == a[1]); ASSERT_TRUE(conf->keyAsString("binary-name") == "test-binary"); ASSERT_TRUE(conf->keyAsString("binary-path") == std::string(crpcut::get_start_dir()) + "/"); res = runParse("-h"); ASSERT_FALSE(res); res = runParse("-s vbb"); ASSERT_FALSE(res); ASSERT_TRUE(conf->keyAsInt("low-limit") == 25); ASSERT_TRUE(conf->keyAsInt("high-limit")== 75U); res = runParse(fmt("-l 30,60 /tmp/vobb %s", filename.c_str())); ASSERT_TRUE(res); ASSERT_TRUE(conf->keyAsInt("low-limit")== 30); ASSERT_TRUE(conf->keyAsInt("high-limit") == 60); res = runParse(fmt("-l 20 /tmp/vobb %s", filename.c_str())); ASSERT_TRUE(!res); res = runParse(fmt("-l 40,50,90 /tmp/vobb %s", filename.c_str())); ASSERT_TRUE(!res); res = runParse(fmt("-l 35,hej /tmp/vobb %s", filename.c_str())); ASSERT_TRUE(!res); res = runParse(fmt("-l yo,89 /tmp/vobb %s", filename.c_str())); ASSERT_TRUE(!res); res = runParse(fmt("/tmp/vobb %s", filename.c_str())); ASSERT_TRUE(res); res = runParse(fmt("/tmp/vobb %s", filename.c_str())); ASSERT_TRUE(res); res = runParse(fmt("--path-strip-level=ejNummer /tmp/vobb, %s", filename.c_str())); ASSERT_FALSE(res); res = runParse(fmt("--path-strip-level=3 /tmp/vobb, %s", filename.c_str())); ASSERT_TRUE(res); res = runParse(fmt("--include-path=/a,/b/c --exclude-pattern=d/e/f /tmp/vobb %s", filename.c_str())); ASSERT_TRUE(res); ASSERT_TRUE(conf->keyAsList("include-path").size() == 2U); ASSERT_TRUE(conf->keyAsList("include-path")[0] == "/a"); ASSERT_TRUE(conf->keyAsList("include-path")[1] == "/b/c"); ASSERT_TRUE(conf->keyAsList("exclude-pattern").size() == 1U); ASSERT_TRUE(conf->keyAsList("exclude-pattern")[0] == "d/e/f"); res = runParse(fmt("--include-path=~/a /tmp/vobb %s", filename.c_str())); ASSERT_TRUE(res); ASSERT_TRUE(conf->keyAsList("include-path").size() == 1U); ASSERT_TRUE(conf->keyAsList("include-path")[0] == fmt("%s/a", get_home())); ASSERT_TRUE(conf->keyAsInt("attach-pid") == 0U); res = runParse(fmt("-p ejNummer /tmp/vobb %s", filename.c_str())); ASSERT_FALSE(res); res = runParse(fmt("-p 10 /tmp/vobb %s", filename.c_str())); ASSERT_TRUE(res); res = runParse(fmt("--pid=100 /tmp/vobb %s", filename.c_str())); ASSERT_TRUE(res); ASSERT_TRUE(conf->keyAsInt("attach-pid") == 100U); ASSERT_TRUE(g_kcov_debug_mask == STATUS_MSG); res = runParse(fmt("--debug=7 /tmp/vobb %s", filename.c_str())); ASSERT_TRUE(res); ASSERT_TRUE(g_kcov_debug_mask == 7); res = runParse(fmt("--exclude-pattern=a /tmp/vobb %s", filename.c_str())); ASSERT_TRUE(res); auto ep = conf->keyAsList("exclude-pattern"); ASSERT_TRUE(ep.size() == 1); ASSERT_TRUE(ep[0] == "a"); res = runParse(fmt("--include-pattern=a,b,c /tmp/vobb %s", filename.c_str())); ASSERT_TRUE(res); ep = conf->keyAsList("include-pattern"); ASSERT_TRUE(ep.size() == 3); ASSERT_TRUE(ep[0] == "a"); ASSERT_TRUE(ep[1] == "b"); ASSERT_TRUE(ep[2] == "c"); } } kcov_25+dfsg.orig/tests/unit-tests/tests-elf.cc000066400000000000000000000026711247015272500217150ustar00rootroot00000000000000#include "test.hh" #include #include #include #include #include using namespace kcov; class FunctionListener : public IFileParser::ILineListener { public: virtual void onLine(const std::string &file, unsigned int lineNr, unsigned long addr) { m_lineMap[constructString(file, lineNr)]++; } static std::string constructString(const std::string &file, int nr) { const char *p = file.c_str(); char *c_str = (char *)xmalloc(file.size() + 20); const char *name = strrchr(p, '/'); ASSERT_TRUE(name); sprintf(c_str, "%s:%d", name, nr); std::string out(c_str); free(c_str); return out; } std::map m_lineMap; }; TEST(elf, DEADLINE_REALTIME_MS(30000)) { FunctionListener listener; char filename[1024]; bool res; IFileParser *elf = IParserManager::getInstance().matchParser("not-found"); ASSERT_TRUE(!elf); sprintf(filename, "%s/Makefile", crpcut::get_start_dir()); elf = IParserManager::getInstance().matchParser(filename); ASSERT_TRUE(!elf); sprintf(filename, "%s/test-binary", crpcut::get_start_dir()); elf = IParserManager::getInstance().matchParser(filename); ASSERT_TRUE(elf); elf->registerLineListener(listener); res = elf->addFile(filename); ASSERT_TRUE(res == true); res = elf->parse(); ASSERT_TRUE(res == true); std::string str = FunctionListener::constructString("/test-source.c", 8); ASSERT_TRUE(listener.m_lineMap[str] > 0); } kcov_25+dfsg.orig/tests/unit-tests/tests-filter.cc000066400000000000000000000041421247015272500224270ustar00rootroot00000000000000#include "test.hh" #include #include "../../src/filter.cc" using namespace kcov; TEST(filter) { std::string filename = std::string(crpcut::get_start_dir()) + "/test-binary"; IConfiguration &conf = IConfiguration::getInstance(); Filter &filter = (Filter &)IFilter::create(); bool res; const char *argv[] = {NULL, "/tmp/vobb", filename.c_str(), "tjena"}; res = conf.parse(4, argv); ASSERT_TRUE(res); filter.setup(); res = filter.runFilters(""); ASSERT_TRUE(res); res = filter.runFilters(filename); ASSERT_TRUE(res); const char *argv2[] = {NULL, "--include-pattern=test-bin", "/tmp/vobb", filename.c_str(), "tjena"}; res = conf.parse(5, argv2); ASSERT_TRUE(res); filter.setup(); res = filter.runFilters(filename); ASSERT_TRUE(res); res = filter.runFilters("ingenting"); ASSERT_FALSE(res); const char *argv3[] = {NULL, "--exclude-pattern=hej,hopp", "--include-pattern=bin", "/tmp/vobb", filename.c_str(), "tjena"}; res = conf.parse(5, argv3); ASSERT_TRUE(res); filter.setup(); res = filter.runFilters("binary"); ASSERT_TRUE(res); res = filter.runFilters("hopp/binary"); ASSERT_FALSE(res); res = filter.runFilters("hej/binary"); ASSERT_FALSE(res); res = filter.runFilters("varken-eller"); ASSERT_FALSE(res); std::string ip = std::string("--include-path=") + crpcut::get_start_dir(); const char *argv4[] = {NULL, ip.c_str(), "/tmp/vobb", filename.c_str(), "tjena"}; res = conf.parse(5, argv4); ASSERT_TRUE(res); filter.setup(); res = filter.runFilters(crpcut::get_start_dir()); ASSERT_TRUE(res); res = filter.runFilters(filename); ASSERT_TRUE(res); res = filter.runFilters("hejsan-hoppsan"); ASSERT_FALSE(res); std::string ep = std::string("--exclude-path=") + crpcut::get_start_dir(); const char *argv5[] = {NULL, ep.c_str(), "/tmp/vobb", filename.c_str(), "tjena"}; res = conf.parse(5, argv5); ASSERT_TRUE(res); filter.setup(); res = filter.runFilters(crpcut::get_start_dir()); ASSERT_FALSE(res); res = filter.runFilters(std::string(crpcut::get_start_dir()) + "/svenne"); ASSERT_FALSE(res); res = filter.runFilters("/tmp"); ASSERT_TRUE(res); } kcov_25+dfsg.orig/tests/unit-tests/tests-merge-parser.cc000066400000000000000000000202361247015272500235350ustar00rootroot00000000000000#include "test.hh" #include "mocks/mock-reporter.hh" #include "../../src/merge-file-parser.cc" #include #include #include class MockParser : public IFileParser { public: MAKE_MOCK2(addFile, bool(const std::string &filename, struct phdr_data_entry *phdr_data)); MAKE_MOCK1(registerLineListener, void(ILineListener &listener)); MAKE_MOCK1(registerFileListener, void(IFileListener &listener)); MAKE_MOCK0(parse, bool()); MAKE_MOCK0(getChecksum, uint64_t()); MAKE_MOCK0(getParserType, std::string()); MAKE_MOCK1(setupParser, void(IFilter *)); MAKE_MOCK0(maxPossibleHits, enum IFileParser::PossibleHits()); MAKE_MOCK3(matchParser, unsigned int(const std::string &filename, uint8_t *mock_data, size_t dataSize)); }; static bool mocked_file_exists(const std::string &path) { return true; } static std::vector mock_data; static void *mocked_read_file(size_t *out_size, const char *path) { *out_size = mock_data.size(); void *p = xmalloc(*out_size); memcpy(p, mock_data.data(), *out_size); return p; } static std::unordered_map path_to_data; static int mocked_write_file(const void *data, size_t size, const char *path) { path_to_data[path] = std::string((const char *)data); return 0; } static uint64_t mocked_ts; static uint64_t mocked_get_timestamp(const std::string &filename) { return mocked_ts; } class LineListenerFixture : public IFileParser::ILineListener { public: virtual ~LineListenerFixture() { } void onLine(const std::string &file, unsigned int lineNr, unsigned long addr) { m_lineToAddr[lineNr] = addr; } protected: std::unordered_map m_lineToAddr; }; class AddressListenerFixture : public IReporter::IListener, public ICollector::IListener { public: virtual ~AddressListenerFixture() { } void onAddress(uint64_t addr, unsigned long hits) { m_addrToHits[addr] = hits; } void onAddressHit(uint64_t addr, unsigned long hits) { m_breakpointToHits[addr] = hits; } protected: std::unordered_map m_addrToHits; std::unordered_map m_breakpointToHits; }; TESTSUITE(merge_parser) { TEST(marshal) { MockParser mockParser; MockReporter reporter; auto &filter = IFilter::create(); REQUIRE_CALL(mockParser, registerFileListener(_)) .TIMES(2) ; REQUIRE_CALL(mockParser, registerLineListener(_)) .TIMES(2) ; mock_data = {'a', '\n', 'b', '\n', '\0'}; mocked_ts = 1; MergeParser parser(mockParser, reporter, "/tmp", "/tmp/kalle", filter); mock_file_exists(mocked_file_exists); mock_read_file(mocked_read_file); mock_get_file_timestamp(mocked_get_timestamp); parser.onLine("a", 1, 2); parser.onLine("a", 1, 3); // Two addresses on the same line parser.onLine("a", 2, 3); parser.onLine("c", 2, 5); const struct file_data *p; // Does not exist p = parser.marshalFile("b"); ASSERT_TRUE(!p); // But this one does p = parser.marshalFile("a"); ASSERT_TRUE(p); ASSERT_TRUE(be_to_host(p->magic) == MERGE_MAGIC); ASSERT_TRUE(be_to_host(p->version) == MERGE_VERSION); ASSERT_TRUE(be_to_host(p->n_entries) == 2); char *name = ((char *)p) + be_to_host(p->file_name_offset); ASSERT_TRUE(std::string(name) == "a"); ASSERT_TRUE(name + strlen(name) + 1 - (char *)p == be_to_host(p->size)); ASSERT_TRUE(be_to_host(p->entries[0].line) == 1); ASSERT_TRUE(be_to_host(p->entries[0].n_addresses) == 2); uint64_t *addressTable = (uint64_t *)((char *)p + be_to_host(p->address_table_offset)); uint32_t start = be_to_host(p->entries[0].address_start); ASSERT_TRUE(be_to_host(addressTable[start]) == parser.hashAddress("a", 1, 2)); ASSERT_TRUE(be_to_host(addressTable[start + 1]) == parser.hashAddress("a", 1, 3) + 1); // No output MergeParser parser2(mockParser, reporter, "/tmp", "/tmp/kalle", filter); parser2.onLine("c", 4, 1); // New timestamp for the "a" file mocked_ts = 2; parser2.onLine("a", 1, 2); const struct file_data *p2; // Test that the checksum changes on new TS p2 = parser2.marshalFile("a"); ASSERT_TRUE(p2); ASSERT_TRUE(p->checksum == p2->checksum); ASSERT_TRUE(p->timestamp != p2->timestamp); // ... but is the same with the old TS p = parser.marshalFile("c"); p2 = parser2.marshalFile("c"); ASSERT_TRUE(p); ASSERT_TRUE(p2); // Should be the same ASSERT_TRUE(p->checksum == p2->checksum); ASSERT_TRUE(p->timestamp == p2->timestamp); // Same timestamp, different data parser.onLine("d", 9, 1); mock_data = {'a', '\n', 'b', 'c', '\n', '\0'}; parser2.onLine("d", 9, 1); p = parser.marshalFile("d"); p2 = parser2.marshalFile("d"); ASSERT_TRUE(p); ASSERT_TRUE(p2); ASSERT_TRUE(p->timestamp == p2->timestamp); ASSERT_TRUE(p->checksum != p2->checksum); } TEST(output) { auto &filter = IFilter::create(); MockParser mockParser; MockReporter reporter; REQUIRE_CALL(mockParser, registerFileListener(_)) .TIMES(1) ; REQUIRE_CALL(mockParser, registerLineListener(_)) .TIMES(1) ; mock_data = {'a', '\n', 'b', '\n', '\0'}; mocked_ts = 1; MergeParser parser(mockParser, reporter, "/tmp", "/tmp/kalle", filter); mock_file_exists(mocked_file_exists); mock_read_file(mocked_read_file); mock_write_file(mocked_write_file); mock_get_file_timestamp(mocked_get_timestamp); parser.onLine("a", 1, 2); parser.onLine("b", 2, 3); parser.onStop(); std::string p0 = parser.m_outputDirectory + "/metadata/" + fmt("%08x", hash_block("a", 1)); std::string p1 = parser.m_outputDirectory + "/metadata/" + fmt("%08x", hash_block("b", 1)); ASSERT_TRUE(path_to_data.find(p0) != path_to_data.end()); ASSERT_TRUE(path_to_data.find(p1) != path_to_data.end()); } TEST(input, LineListenerFixture, AddressListenerFixture) { auto &filter = IFilter::create(); MockParser mockParser; MockReporter reporter; REQUIRE_CALL(mockParser, registerFileListener(_)) .TIMES(2) ; REQUIRE_CALL(mockParser, registerLineListener(_)) .TIMES(2) ; mock_data = {'a', '\n', 'b', '\n', '\0'}; mocked_ts = 1; MergeParser parser(mockParser, reporter, "/tmp", "/tmp/kalle", filter); mock_file_exists(mocked_file_exists); mock_read_file(mocked_read_file); mock_write_file(mocked_write_file); mock_get_file_timestamp(mocked_get_timestamp); // Register the collector address listener parser.registerListener(*this); parser.onLine("a", 1, 2); parser.onLine("a", 3, 9); parser.onLine("a", 4, 72); parser.onLine("b", 2, 3); ASSERT_TRUE(m_breakpointToHits[parser.hashAddress("b", 2, 3)] == 0); parser.onAddress(3, 2); ASSERT_TRUE(m_breakpointToHits[parser.hashAddress("b", 2, 3)] == 2); // Register afterwards to get everything on parse below... parser.registerLineListener(*this); // Non-hash file parser.parseOne("/tmp/kalle/df/", "some-other-file"); // Non-existing parser.parseOne("/tmp/kalle/df/", "12345678"); // Existing (but fake, anyway) parser.parseOne("/tmp/kalle/df/", fmt("0x%08x", parser.m_files["a"]->m_checksum)); const struct file_data *p; p = parser.marshalFile("a"); ASSERT_TRUE(p); MergeParser parser2(mockParser, reporter, "/tmp", "/tmp/kalle", filter); parser2.registerLineListener(*this); parser2.registerListener(*this); // Valid file and data ASSERT_TRUE(m_lineToAddr.size() == 0); ASSERT_TRUE(m_breakpointToHits[parser.hashAddress("a", 4, 72)] == 0); // See to it that we "know of" file "a" parser2.onLine("a", 4, 72); // Mark all entries as executed in the table (should yield hits below) uint64_t *table = (uint64_t*)((const char *)p + be_to_host(p->address_table_offset)); for (unsigned int i = 0; i < 4; i++) table[i] = to_be((be_to_host(table[i]) | (1ULL << 63))); mock_data.clear(); uint8_t *b = (uint8_t *)p; for (unsigned int i = 0; i < be_to_host(p->size); i++) mock_data.push_back(b[i]); parser2.parseOne("/tmp/kalle/df/", fmt("0x%08x", parser.m_files["a"]->m_checksum)); ASSERT_TRUE(m_lineToAddr.size() == 3); ASSERT_TRUE(m_lineToAddr[1] == parser.hashAddress("a", 1, 2)); ASSERT_TRUE(m_breakpointToHits[parser.hashAddress("a", 4, 72)] == 1); free((void *)p); } } kcov_25+dfsg.orig/tests/unit-tests/tests-reporter.cc000066400000000000000000000073421247015272500230110ustar00rootroot00000000000000#include "test.hh" #include #include #include #include #include #include #include #include "../../src/reporter.cc" #include "mocks/mock-collector.hh" using namespace kcov; class ElfListener : public IFileParser::ILineListener { public: virtual ~ElfListener() { } void onLine(const std::string &file, unsigned int lineNr, unsigned long addr) { // Just store the lastest to have something m_lineToAddr[lineNr] = addr; if (m_file == "") m_file = file; } std::string m_file; std::unordered_map m_lineToAddr; }; TEST(reporter) { ElfListener elfListener; IFileParser *elf; bool res; char filename[1024]; sprintf(filename, "%s/test-binary", crpcut::get_start_dir()); elf = IParserManager::getInstance().matchParser(filename); ASSERT_TRUE(elf); elf->registerLineListener(elfListener); MockCollector collector; REQUIRE_CALL(collector, registerListener(_)) .TIMES(1) .LR_SIDE_EFFECT(collector.mockRegisterListener(_1)) ; Reporter &reporter = (Reporter &)IReporter::create(*elf, collector, IFilter::create()); IReporter::ExecutionSummary summary = reporter.getExecutionSummary(); ASSERT_TRUE(summary.m_lines == 0U); ASSERT_TRUE(summary.m_executedLines == 0U); res = elf->addFile(filename); ASSERT_TRUE(res); res = elf->parse(); ASSERT_TRUE(res); summary = reporter.getExecutionSummary(); ASSERT_TRUE(summary.m_lines == 17U); // Executable lines ASSERT_TRUE(summary.m_executedLines == 0U); // Test something which doesn't exist collector.m_listener->onAddressHit(100, 1); summary = reporter.getExecutionSummary(); ASSERT_TRUE(summary.m_executedLines == 0U); // See test-source.c IReporter::LineExecutionCount lc = reporter.getLineExecutionCount(elfListener.m_file.c_str(), 19); ASSERT_TRUE(lc.m_hits == 0U); ASSERT_TRUE(lc.m_possibleHits == 1U); ASSERT_TRUE(elfListener.m_lineToAddr[8]); // and something which does collector.m_listener->onAddressHit(elfListener.m_lineToAddr[19], 1); lc = reporter.getLineExecutionCount(elfListener.m_file.c_str(), 19); ASSERT_TRUE(lc.m_hits == 1U); ASSERT_TRUE(lc.m_possibleHits == 1U); // Once again (should not happen except on marshalling - this should // not count up the number of hits) collector.m_listener->onAddressHit(elfListener.m_lineToAddr[19], 1); lc = reporter.getLineExecutionCount(elfListener.m_file.c_str(), 19); ASSERT_TRUE(lc.m_hits == 1U); summary = reporter.getExecutionSummary(); ASSERT_TRUE(summary.m_executedLines == 1U); res = reporter.lineIsCode(elfListener.m_file.c_str(), 19); ASSERT_TRUE(res == true); res = reporter.lineIsCode(elfListener.m_file.c_str(), 13); ASSERT_TRUE(res == false); // Test marshal and unmarshal collector.m_listener->onAddressHit(elfListener.m_lineToAddr[16], 1); summary = reporter.getExecutionSummary(); ASSERT_TRUE(summary.m_executedLines == 2U); size_t sz; void *data = reporter.marshal(&sz); ASSERT_TRUE(sz >= 0U); ASSERT_TRUE(data); // Happened after, not part of the marshalling collector.m_listener->onAddressHit(elfListener.m_lineToAddr[17], 1); summary = reporter.getExecutionSummary(); ASSERT_TRUE(summary.m_executedLines == 3U); res = reporter.unMarshal(data, sz); ASSERT_TRUE(res); lc = reporter.getLineExecutionCount(elfListener.m_file.c_str(), 16); ASSERT_TRUE(lc.m_hits == 1U); ASSERT_TRUE(lc.m_possibleHits == 1U); struct marshalHeaderStruct *hdr = (struct marshalHeaderStruct *)data; hdr->checksum++; res = reporter.unMarshal(data, sz); ASSERT_FALSE(res); hdr->checksum--; hdr->db_version++; res = reporter.unMarshal(data, sz); ASSERT_FALSE(res); hdr->db_version--; hdr->magic++; res = reporter.unMarshal(data, sz); ASSERT_FALSE(res); free(data); } kcov_25+dfsg.orig/tests/unit-tests/tests-utils.cc000066400000000000000000000015541247015272500223060ustar00rootroot00000000000000#include "test.hh" #include #include TESTSUITE(utils) { TEST(escapeHtml) { char buf[8192]; memset(buf, '&', sizeof(buf)); buf[sizeof(buf) - 1] = '\0'; std::string s = escape_html(buf); // Should have been truncated ASSERT_TRUE(s.size() < 4096); ASSERT_TRUE(s.find("&") != std::string::npos); } TEST(escapeJson) { std::string a = "var kalle=1;"; std::string b = "var kalle=\"1\";"; std::string c = "var a=\"\\hej\";"; std::string d = "var kalle=\tX;"; std::string e = "var kalle='X';"; std::string s; s = escape_json(a); ASSERT_TRUE(s == a); s = escape_json(b); ASSERT_TRUE(s == "var kalle=\\\"1\\\";"); s = escape_json(c); ASSERT_TRUE(s == "var a=\\\"\\\\hej\\\";"); s = escape_json(d); ASSERT_TRUE(s == "var kalle=\\tX;"); s = escape_json(e); ASSERT_TRUE(s == "var kalle=\\'X\\';"); } } kcov_25+dfsg.orig/tests/unit-tests/tests-writer.cc000066400000000000000000000112671247015272500224640ustar00rootroot00000000000000#include "test.hh" #include #include #include #include #include #include #include #include #include #include #include #include "../../src/writers/html-writer.hh" #include "../../src/writers/cobertura-writer.hh" #include "mocks/mock-collector.hh" #include "mocks/mock-reporter.hh" using namespace kcov; static int filePatternInDir(const char *name, const char *pattern) { DIR *d = opendir(name); ASSERT_TRUE(d); struct dirent *de = readdir(d); int out = 0; while (de) { if (strstr(de->d_name, pattern)) out++; de = readdir(d); } return out; } TEST(writer, DEADLINE_REALTIME_MS(20000)) { IFileParser *elf; bool res; char filename[1024]; MockReporter reporter; IReporter::LineExecutionCount def(0, 1); IReporter::LineExecutionCount partial(1, 2); IReporter::LineExecutionCount full(3, 3); IReporter::ExecutionSummary summary(17, 4); std::string outDir = (std::string(crpcut::get_start_dir()) + "/kcov-writer"); system(fmt("rm -rf %s", outDir.c_str()).c_str()); sprintf(filename, "%s/test-binary", crpcut::get_start_dir()); elf = IParserManager::getInstance().matchParser(filename); ASSERT_TRUE(elf); const char *argv[] = {NULL, outDir.c_str(), filename}; IConfiguration &conf = IConfiguration::getInstance(); res = conf.parse(3, argv); ASSERT_TRUE(res); REQUIRE_CALL(reporter, lineIsCode(_,_)) .TIMES(AT_LEAST(1)) .RETURN((true)) ; REQUIRE_CALL(reporter, fileIsIncluded(_)) .TIMES(AT_LEAST(1)) .RETURN((true)) ; REQUIRE_CALL(reporter, lineIsCode(_,7)) .TIMES(AT_LEAST(4)) // Both files .RETURN((false)) ; REQUIRE_CALL(reporter, getLineExecutionCount(_,_)) .TIMES(AT_LEAST(1)) .RETURN((def)) ; REQUIRE_CALL(reporter, getLineExecutionCount(_,8)) .TIMES(AT_LEAST(3)) .RETURN((partial)) ; REQUIRE_CALL(reporter, getLineExecutionCount(_,11)) .TIMES(AT_LEAST(2)) .RETURN((full)) ; REQUIRE_CALL(reporter, getExecutionSummary()) .TIMES(AT_LEAST(3)) .RETURN((summary)) ; MockCollector collector; IOutputHandler &output = IOutputHandler::create(*elf, reporter, collector); IWriter &writer = createHtmlWriter(*elf, reporter, output.getBaseDirectory(), output.getOutDirectory(), "kalle"); IWriter &coberturaWriter = createCoberturaWriter(*elf, reporter, output.getOutDirectory() + "/cobertura.xml"); output.registerWriter(writer); output.registerWriter(coberturaWriter); res = elf->addFile(filename); ASSERT_TRUE(res == true); res = elf->parse(); ASSERT_TRUE(res == true); output.start(); output.produce(); REQUIRE_CALL(reporter, marshal(_)) .TIMES(AT_LEAST(1)) .LR_RETURN(reporter.mockMarshal(_1)) ; output.produce(); ASSERT_TRUE(filePatternInDir((outDir + "/test-binary").c_str(), "test-source.c") >= 1); ASSERT_TRUE(file_exists((outDir + "/test-binary/cobertura.xml").c_str())); output.start(); delete &output; // UGLY! } TEST(writerSameName, DEADLINE_REALTIME_MS(20000)) { IFileParser *elf; bool res; char filename[1024]; MockReporter reporter; IReporter::LineExecutionCount def(0, 1); IReporter::LineExecutionCount partial(1, 2); IReporter::LineExecutionCount full(3, 3); IReporter::ExecutionSummary summary(17, 4); std::string outDir = (std::string(crpcut::get_start_dir()) + "/kcov-writerSameName"); system(fmt("rm -rf %s", outDir.c_str()).c_str()); sprintf(filename, "%s/same-name-test", crpcut::get_start_dir()); elf = IParserManager::getInstance().matchParser(filename); ASSERT_TRUE(elf); const char *argv[] = {NULL, outDir.c_str(), filename}; IConfiguration &conf = IConfiguration::getInstance(); res = conf.parse(3, argv); ASSERT_TRUE(res); REQUIRE_CALL(reporter, lineIsCode(_,_)) .TIMES(AT_LEAST(1)) .RETURN((true)) ; REQUIRE_CALL(reporter, fileIsIncluded(_)) .TIMES(AT_LEAST(1)) .RETURN((true)) ; REQUIRE_CALL(reporter, getLineExecutionCount(_,_)) .TIMES(AT_LEAST(1)) .RETURN((def)) ; REQUIRE_CALL(reporter, getExecutionSummary()) .TIMES(AT_LEAST(2)) .RETURN((summary)) ; MockCollector collector; IOutputHandler &output = IOutputHandler::create(*elf, reporter, collector); IWriter &writer = createHtmlWriter(*elf, reporter, output.getBaseDirectory(), output.getOutDirectory(), "anka"); output.registerWriter(writer); res = elf->addFile(filename); ASSERT_TRUE(res == true); res = elf->parse(); ASSERT_TRUE(res == true); output.start(); output.produce(); REQUIRE_CALL(reporter, marshal(_)) .TIMES(AT_LEAST(1)) .LR_RETURN(reporter.mockMarshal(_1)) ; output.produce(); int cnt = filePatternInDir((outDir + "/same-name-test").c_str(), "html"); ASSERT_TRUE(cnt == 4); // index.html + 3 source files delete &output; // UGLY! } kcov_25+dfsg.orig/tools/000077500000000000000000000000001247015272500153515ustar00rootroot00000000000000kcov_25+dfsg.orig/tools/CMakeLists.txt000066400000000000000000000031711247015272500201130ustar00rootroot00000000000000cmake_minimum_required (VERSION 2.6) project (kcov-tools) set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../cmake) find_package (LibElf REQUIRED) find_package (Elfutils REQUIRED) find_package (PkgConfig REQUIRED) pkg_check_modules(LIBZ REQUIRED zlib) # ==================================== # project name and version # ==================================== set (LINE2ADDR line2addr) set (${LINE2ADDR}_SRCS ../src/capabilities.cc ../src/configuration.cc ../src/filter.cc ../src/gcov.cc ../src/parsers/elf-parser.cc ../src/parser-manager.cc ../src/utils.cc line2addr.cc ) set (CMAKE_CXX_FLAGS "-std=c++0x -g -Wall -D_GLIBCXX_USE_NANOSLEEP -DKCOV_LIBRARY_PREFIX=${KCOV_LIBRARY_PREFIX}") include_directories( ../src/include/ ${LIBELF_INCLUDE_DIRS} ${LIBDW_INCLUDE_DIRS} ${LIBZ_INCLUDE_DIRS} ) # Reference: http://www.cmake.org/Wiki/CMake_RPATH_handling if(SPECIFY_RPATH) set (CMAKE_SKIP_BUILD_RPATH FALSE) set (CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) set (CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) # the RPATH to be used when installing, but only if it's not a system directory LIST(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir) IF("${isSystemDir}" STREQUAL "-1") SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") ENDIF("${isSystemDir}" STREQUAL "-1") endif(SPECIFY_RPATH) add_executable (${LINE2ADDR} ${${LINE2ADDR}_SRCS}) target_link_libraries(${LINE2ADDR} ${LIBDW_LIBRARIES} ${LIBELF_LIBRARIES} stdc++ dl pthread m ${LIBZ_LIBRARIES}) file ( GLOB kcov-merge kcov-merge ) install (PROGRAMS ${kcov-merge} DESTINATION bin ) kcov_25+dfsg.orig/tools/line2addr.cc000066400000000000000000000025571247015272500175350ustar00rootroot00000000000000#include #include #include using namespace kcov; class Listener : public IFileParser::ILineListener { public: Listener(IFileParser &parser, const std::string &filePattern, int lineNr) : m_filePattern(filePattern), m_lineNr(lineNr) { parser.registerLineListener(*this); } virtual ~Listener() { } void onLine(const std::string &file, unsigned int lineNr, uint64_t addr) { if (file.find(m_filePattern) != std::string::npos) { if (m_lineNr < 0 || lineNr == (unsigned int)m_lineNr) report(addr); } } private: void report(unsigned long addr) { printf("0x%lx\n", addr); } const std::string m_filePattern; int m_lineNr; }; int main(int argc, const char *argv[]) { if (argc < 3) { fprintf(stderr, "Usage: line2addr in-file file-pattern [line-nr]\n"); return 1; } std::string file(argv[1]); std::string fileName(argv[2]); int lineNr = -1; if (argc >= 4) { if (!string_is_integer(argv[3])) { fprintf(stderr, "line-nr argument (%s) must be integer\n", argv[3]); exit(1); } lineNr = string_to_integer(argv[3]); } IFileParser *parser = IParserManager::getInstance().matchParser(file); if (!parser) { fprintf(stderr, "Can't match parser for %s\n", file.c_str()); return 1; } Listener listener(*parser, fileName, lineNr); parser->addFile(file); return parser->parse() ? 0 : 1; } kcov_25+dfsg.orig/travis/000077500000000000000000000000001247015272500155215ustar00rootroot00000000000000kcov_25+dfsg.orig/travis/Makefile000066400000000000000000000056111247015272500171640ustar00rootroot00000000000000#!/usr/bin/make chroot=/tmp/32-bit-chroot kcov_deps=libdw-dev libelf-dev elfutils libcurl4-openssl-dev python-pip python3 cmake .PHONY: prepare_environment build_reference: mkdir -p reference/src reference/build cd reference/ && git clone https://github.com/SimonKagstrom/kcov.git cd reference/kcov && git checkout v23 cd reference/build && cmake ../kcov make -C reference/build/ cp reference/build/src/kcov /tmp/kcov-reference build: mkdir -p build build-tests build-tools cd build && cmake .. make -C build cd build-tools && cmake ../tools make -C build-tools sudo make -C build install cd build-tests && cmake ../tests make -C build-tests build_gcc: build_reference sudo rm -rf ${chroot}/tmp/kcov/build ${chroot}/tmp/kcov/build-tests sudo mkdir -p ${chroot}/tmp/kcov/build ${chroot}/tmp/kcov/build-tests ${chroot}/tmp/kcov/build-tools sudo i386 chroot ${chroot} sh -c "cd /tmp/kcov/build && cmake .." sudo i386 chroot ${chroot} sh -c "make -C /tmp/kcov/build" sudo i386 chroot ${chroot} sh -c "make -C /tmp/kcov/build install" sudo i386 chroot ${chroot} sh -c "cd /tmp/kcov/build-tools && cmake ../tools" sudo i386 chroot ${chroot} sh -c "make -C /tmp/kcov/build-tools" sudo i386 chroot ${chroot} sh -c "cd /tmp/kcov/build-tests && cmake ../tests" sudo i386 chroot ${chroot} sh -c "make -C /tmp/kcov/build-tests" run-performance: build_gcc travis/perf-test.sh build/src/kcov . build >/dev/null /usr/bin/time -f "%e" travis/perf-test.sh build/src/kcov . build >/dev/null rm -rf /tmp/kcov-perf travis/perf-test.sh /tmp/kcov-reference . build >/dev/null /usr/bin/time -f "%e" travis/perf-test.sh /tmp/kcov-reference . build >/dev/null run-tests-gcc: run-performance sudo i386 chroot ${chroot} sh -c "cd /tmp/kcov/build-tests && WORKSPACE=/tmp/kcov/ pybot --noncritical not_ready ../tests/robot-framework/kcov-tests.txt" cd build-tests && WORKSPACE=`pwd`/../ pybot --noncritical not_ready ../tests/robot-framework/kcov-tests.txt build/src/kcov --coveralls-id=$(TRAVIS_JOB_ID) --include-pattern=kcov --exclude-pattern=helper.cc,library.cc,html-data-files.cc /tmp/kcov-kcov build/src/kcov || true run-tests-clang: cd build-tests && WORKSPACE=`pwd`/../ pybot --noncritical not_ready ../tests/robot-framework/kcov-tests.txt build_clang: run-tests: build run-tests-${CC} prepare_linux: sudo apt-get update -qq sudo apt-get install -y ${kcov_deps} gcc-multilib debootstrap git time sudo pip install robotframework prepare_gcc: sudo i386 debootstrap --arch=i386 --components=main,universe precise $(chroot) sudo i386 chroot "${chroot}" apt-get update sudo i386 chroot "${chroot}" apt-get install -y build-essential sudo i386 chroot "${chroot}" apt-get install -y ${kcov_deps} sudo i386 chroot "${chroot}" pip install robotframework sudo cp -r "$(shell pwd)" "${chroot}/tmp/" sudo i386 chroot "${chroot}" sh -c "mount -t proc procfs /proc" prepare_clang: prepare_environment: prepare_linux prepare_${CC} kcov_25+dfsg.orig/travis/perf-test.sh000077500000000000000000000006531247015272500177750ustar00rootroot00000000000000#!/bin/sh if [ $# -ne 3 ] ; then echo "Usage: perf-test.sh " exit 1 fi KCOV=$1 SRC=$2 BUILD=$3 $KCOV /tmp/kcov-perf $BUILD/src/kcov $KCOV /tmp/kcov-perf $SRC/tests/python/main 5 $KCOV /tmp/kcov-perf $SRC/tests/bash/shell-main 5 $KCOV /tmp/kcov-perf $BUILD/src/kcov /tmp/kcov-perf $SRC/tests/python/main 5 $KCOV /tmp/kcov-perf $BUILD/src/kcov /tmp/kcov-perf $SRC/tests/bash/shell-main 5 exit 0