pax_global_header00006660000000000000000000000064120136350020014502gustar00rootroot0000000000000052 comment=979dbe10d53681f6f692550792da4be51fb50f6a kcov-11/000077500000000000000000000000001201363500200122275ustar00rootroot00000000000000kcov-11/.gitignore000066400000000000000000000000141201363500200142120ustar00rootroot00000000000000*.o *~ kcov kcov-11/CMakeLists.txt000066400000000000000000000023421201363500200147700ustar00rootroot00000000000000cmake_minimum_required (VERSION 2.6) # ==================================== # project name and version # ==================================== project (kcov) set (PROJECT_VERSION_MAJOR 11) set (PROJECT_VERSION_MINOR 0) set (PROJECT_VERSION_PATCH 0) set (PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") 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) # ==================================== # 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-11/COPYING000066400000000000000000000431101201363500200132610ustar00rootroot00000000000000 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-11/CPack.local.cmake000066400000000000000000000015721201363500200153100ustar00rootroot00000000000000# ==================================== # 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-11/ChangeLog000066400000000000000000000122621201363500200140040ustar00rootroot00000000000000Kcov (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-11/INSTALL000066400000000000000000000022171201363500200132620ustar00rootroot00000000000000Building Kcov ============= You need development headers and libraries for libstdc++ and elfutils to build 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-11/README000066400000000000000000000010451201363500200131070ustar00rootroot00000000000000Kcov is a code coverage tester based on Bcov (http://bcov.sf.net). Kcov, like Bcov, uses DWARF debugging information to make it possible to collect coverage information without special compiler switches. Simple usage: kcov /path/to/outdir executable [args for the executable] /path/to/outdir will contain lcov-style HTML output generated continously while the application runs. kcov is written by Simon Kagstrom and more information can be found at the web page, http://simonkagstrom.github.com/kcov/index.html kcov-11/cmake/000077500000000000000000000000001201363500200133075ustar00rootroot00000000000000kcov-11/cmake/FindGMock.cmake000066400000000000000000000106761201363500200161240ustar00rootroot00000000000000# Locate the Google C++ Mocking Framework. # (This file is almost an identical copy of the original FindGTest.cmake file, # feel free to use it as it is or modify it for your own needs.) # # # Defines the following variables: # # GMOCK_FOUND - Found the Google Testing framework # GMOCK_INCLUDE_DIRS - Include directories # # Also defines the library variables below as normal # variables. These contain debug/optimized keywords when # a debugging library is found. # # GMOCK_BOTH_LIBRARIES - Both libgmock & libgmock-main # GMOCK_LIBRARIES - libgmock # GMOCK_MAIN_LIBRARIES - libgmock-main # # Accepts the following variables as input: # # GMOCK_ROOT - (as a CMake or environment variable) # The root directory of the gmock install prefix # # GMOCK_MSVC_SEARCH - If compiling with MSVC, this variable can be set to # "MD" or "MT" to enable searching a gmock build tree # (defaults: "MD") # #----------------------- # Example Usage: # # find_package(GMock REQUIRED) # include_directories(${GMOCK_INCLUDE_DIRS}) # # add_executable(foo foo.cc) # target_link_libraries(foo ${GMOCK_BOTH_LIBRARIES}) # #============================================================================= # This file is released under the MIT licence: # # Copyright (c) 2011 Matej Svec # # 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. #============================================================================= function(_gmock_append_debugs _endvar _library) if(${_library} AND ${_library}_DEBUG) set(_output optimized ${${_library}} debug ${${_library}_DEBUG}) else() set(_output ${${_library}}) endif() set(${_endvar} ${_output} PARENT_SCOPE) endfunction() function(_gmock_find_library _name) find_library(${_name} NAMES ${ARGN} HINTS $ENV{GMOCK_ROOT} ${GMOCK_ROOT} PATH_SUFFIXES ${_gmock_libpath_suffixes} ) mark_as_advanced(${_name}) endfunction() if(NOT DEFINED GMOCK_MSVC_SEARCH) set(GMOCK_MSVC_SEARCH MD) endif() set(_gmock_libpath_suffixes lib) if(MSVC) if(GMOCK_MSVC_SEARCH STREQUAL "MD") list(APPEND _gmock_libpath_suffixes msvc/gmock-md/Debug msvc/gmock-md/Release) elseif(GMOCK_MSVC_SEARCH STREQUAL "MT") list(APPEND _gmock_libpath_suffixes msvc/gmock/Debug msvc/gmock/Release) endif() endif() find_path(GMOCK_INCLUDE_DIR gmock/gmock.h HINTS $ENV{GMOCK_ROOT}/include ${GMOCK_ROOT}/include ) mark_as_advanced(GMOCK_INCLUDE_DIR) if(MSVC AND GMOCK_MSVC_SEARCH STREQUAL "MD") # The provided /MD project files for Google Mock add -md suffixes to the # library names. _gmock_find_library(GMOCK_LIBRARY gmock-md gmock) _gmock_find_library(GMOCK_LIBRARY_DEBUG gmock-mdd gmockd) _gmock_find_library(GMOCK_MAIN_LIBRARY gmock_main-md gmock_main) _gmock_find_library(GMOCK_MAIN_LIBRARY_DEBUG gmock_main-mdd gmock_maind) else() _gmock_find_library(GMOCK_LIBRARY gmock) _gmock_find_library(GMOCK_LIBRARY_DEBUG gmockd) _gmock_find_library(GMOCK_MAIN_LIBRARY gmock_main) _gmock_find_library(GMOCK_MAIN_LIBRARY_DEBUG gmock_maind) endif() FIND_PACKAGE_HANDLE_STANDARD_ARGS(GMock DEFAULT_MSG GMOCK_LIBRARY GMOCK_INCLUDE_DIR GMOCK_MAIN_LIBRARY) if(GMOCK_FOUND) set(GMOCK_INCLUDE_DIRS ${GMOCK_INCLUDE_DIR}) _gmock_append_debugs(GMOCK_LIBRARIES GMOCK_LIBRARY) _gmock_append_debugs(GMOCK_MAIN_LIBRARIES GMOCK_MAIN_LIBRARY) set(GMOCK_BOTH_LIBRARIES ${GMOCK_LIBRARIES} ${GMOCK_MAIN_LIBRARIES}) endif() kcov-11/cmake/FindGTest.cmake000066400000000000000000000125361201363500200161470ustar00rootroot00000000000000# Locate the Google C++ Testing Framework. # # Defines the following variables: # # GTEST_FOUND - Found the Google Testing framework # GTEST_INCLUDE_DIRS - Include directories # # Also defines the library variables below as normal # variables. These contain debug/optimized keywords when # a debugging library is found. # # GTEST_BOTH_LIBRARIES - Both libgtest & libgtest-main # GTEST_LIBRARIES - libgtest # GTEST_MAIN_LIBRARIES - libgtest-main # # Accepts the following variables as input: # # GTEST_ROOT - (as a CMake or environment variable) # The root directory of the gtest install prefix # # GTEST_MSVC_SEARCH - If compiling with MSVC, this variable can be set to # "MD" or "MT" to enable searching a GTest build tree # (defaults: "MD") # #----------------------- # Example Usage: # # enable_testing() # find_package(GTest REQUIRED) # include_directories(${GTEST_INCLUDE_DIRS}) # # add_executable(foo foo.cc) # target_link_libraries(foo ${GTEST_BOTH_LIBRARIES}) # # add_test(AllTestsInFoo foo) # #----------------------- # # If you would like each Google test to show up in CTest as # a test you may use the following macro. # NOTE: It will slow down your tests by running an executable # for each test and test fixture. You will also have to rerun # CMake after adding or removing tests or test fixtures. # # GTEST_ADD_TESTS(executable extra_args ARGN) # executable = The path to the test executable # extra_args = Pass a list of extra arguments to be passed to # executable enclosed in quotes (or "" for none) # ARGN = A list of source files to search for tests & test # fixtures. # # Example: # set(FooTestArgs --foo 1 --bar 2) # add_executable(FooTest FooUnitTest.cc) # GTEST_ADD_TESTS(FooTest "${FooTestArgs}" FooUnitTest.cc) #============================================================================= # Copyright 2009 Kitware, Inc. # Copyright 2009 Philip Lowman # Copyright 2009 Daniel Blezek # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) # # Thanks to Daniel Blezek for the GTEST_ADD_TESTS code function(GTEST_ADD_TESTS executable extra_args) if(NOT ARGN) message(FATAL_ERROR "Missing ARGN: Read the documentation for GTEST_ADD_TESTS") endif() foreach(source ${ARGN}) file(READ "${source}" contents) string(REGEX MATCHALL "TEST_?F?\\(([A-Za-z_0-9 ,]+)\\)" found_tests ${contents}) foreach(hit ${found_tests}) string(REGEX REPLACE ".*\\( *([A-Za-z_0-9]+), *([A-Za-z_0-9]+) *\\).*" "\\1.\\2" test_name ${hit}) add_test(${test_name} ${executable} --gtest_filter=${test_name} ${extra_args}) endforeach() endforeach() endfunction() function(_gtest_append_debugs _endvar _library) if(${_library} AND ${_library}_DEBUG) set(_output optimized ${${_library}} debug ${${_library}_DEBUG}) else() set(_output ${${_library}}) endif() set(${_endvar} ${_output} PARENT_SCOPE) endfunction() function(_gtest_find_library _name) find_library(${_name} NAMES ${ARGN} HINTS $ENV{GTEST_ROOT} ${GTEST_ROOT} PATH_SUFFIXES ${_gtest_libpath_suffixes} ) mark_as_advanced(${_name}) endfunction() # if(NOT DEFINED GTEST_MSVC_SEARCH) set(GTEST_MSVC_SEARCH MD) endif() set(_gtest_libpath_suffixes lib) if(MSVC) if(GTEST_MSVC_SEARCH STREQUAL "MD") list(APPEND _gtest_libpath_suffixes msvc/gtest-md/Debug msvc/gtest-md/Release) elseif(GTEST_MSVC_SEARCH STREQUAL "MT") list(APPEND _gtest_libpath_suffixes msvc/gtest/Debug msvc/gtest/Release) endif() endif() find_path(GTEST_INCLUDE_DIR gtest/gtest.h HINTS $ENV{GTEST_ROOT}/include ${GTEST_ROOT}/include ) mark_as_advanced(GTEST_INCLUDE_DIR) if(MSVC AND GTEST_MSVC_SEARCH STREQUAL "MD") # The provided /MD project files for Google Test add -md suffixes to the # library names. _gtest_find_library(GTEST_LIBRARY gtest-md gtest) _gtest_find_library(GTEST_LIBRARY_DEBUG gtest-mdd gtestd) _gtest_find_library(GTEST_MAIN_LIBRARY gtest_main-md gtest_main) _gtest_find_library(GTEST_MAIN_LIBRARY_DEBUG gtest_main-mdd gtest_maind) else() _gtest_find_library(GTEST_LIBRARY gtest) _gtest_find_library(GTEST_LIBRARY_DEBUG gtestd) _gtest_find_library(GTEST_MAIN_LIBRARY gtest_main) _gtest_find_library(GTEST_MAIN_LIBRARY_DEBUG gtest_maind) endif() FIND_PACKAGE_HANDLE_STANDARD_ARGS(GTest DEFAULT_MSG GTEST_LIBRARY GTEST_INCLUDE_DIR GTEST_MAIN_LIBRARY) if(GTEST_FOUND) set(GTEST_INCLUDE_DIRS ${GTEST_INCLUDE_DIR}) _gtest_append_debugs(GTEST_LIBRARIES GTEST_LIBRARY) _gtest_append_debugs(GTEST_MAIN_LIBRARIES GTEST_MAIN_LIBRARY) set(GTEST_BOTH_LIBRARIES ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES}) endif() kcov-11/cmake/FindLibCRPCUT.cmake000066400000000000000000000027661201363500200165540ustar00rootroot00000000000000# - 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-11/cmake/FindLibDwarf.cmake000066400000000000000000000032031201363500200166020ustar00rootroot00000000000000# - 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 (LIBDWARF_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(LibDwarf DEFAULT_MSG LIBDWARF_LIBRARIES LIBDWARF_INCLUDE_DIRS) mark_as_advanced(LIBDW_INCLUDE_DIR DWARF_INCLUDE_DIR) mark_as_advanced(LIBDWARF_INCLUDE_DIRS LIBDWARF_LIBRARIES) kcov-11/cmake/FindLibElf.cmake000066400000000000000000000025451201363500200162550ustar00rootroot00000000000000# - 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-11/cmake/FindLibUDIS86.cmake000066400000000000000000000026651201363500200164740ustar00rootroot00000000000000# - 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-11/doc/000077500000000000000000000000001201363500200127745ustar00rootroot00000000000000kcov-11/doc/CMakeLists.txt000066400000000000000000000001261201363500200155330ustar00rootroot00000000000000file (GLOB man_files_1 *.1) install (FILES ${man_files_1} ${INSTALL_MAN_PATH}/man1) kcov-11/doc/kcov.1000066400000000000000000000066611201363500200140310ustar00rootroot00000000000000.\" 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 using DWARF debugging information .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 based on bcov by Thomas Neumann. It allows collecting code coverage information from executables without special command-line arguments, 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\-s\fP, \fB\-\-sort\-type\fP=\fItype\fP How to sort files: f[ilename] (default), p[ercent], u[ncovered lines], l[ines] .TP \fB\-l\fP, \fB\-\-limits\fP=\fIlow,high\fP Setup limits for low/high coverage (default: 16,50). .TP \fB\-t\fP, \fB\-\-title\fP=\fItitle\fP Set the web page title to title. .TP \fB\-\-path\-strip\-level\fP=\fIN\fP Number of path levels to show for common paths (default: 2). .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 patterns to include in the report. .TP \fB\-\-exclude\-pattern\fP=\fIP1\fP[\fI,P2\fP...] Comma-separated list of patterns to exclude from the report. .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 .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. .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-11/src/000077500000000000000000000000001201363500200130165ustar00rootroot00000000000000kcov-11/src/CMakeLists.txt000066400000000000000000000025501201363500200155600ustar00rootroot00000000000000cmake_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 (LibDwarf REQUIRED) # ==================================== # project name and version # ==================================== project (kcov) set (TGT kcov) set (SOLIB kcov_sowrapper) set (${SOLIB}_SRCS solib-parser/phdr_data.c solib-parser/lib.c ) set (${TGT}_SRCS cobertura-writer.cc collector.cc configuration.cc elf.cc filter.cc html-writer.cc main.cc output-handler.cc ptrace.cc reporter.cc solib-parser/phdr_data.c utils.cc writer-base.cc ) set (KCOV_LIBRARY_PREFIX "/tmp") set (CMAKE_CXX_FLAGS "-std=c++0x -Wall -D_GLIBCXX_USE_NANOSLEEP -DKCOV_LIBRARY_PREFIX=${KCOV_LIBRARY_PREFIX}") include_directories( include/ ${LIBELF_INCLUDE_DIRS} ${LIBDWARF_INCLUDE_DIRS} ) link_directories (/home/ska/local/lib) add_library (${SOLIB} SHARED ${${SOLIB}_SRCS}) add_custom_command( OUTPUT library.c COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/bin-to-c-source.py lib${SOLIB}.so > library.c DEPENDS ${SOLIB} ) add_executable (${TGT} ${${TGT}_SRCS} library.c) target_link_libraries(${TGT} ${LIBDWARF_LIBRARIES} ${LIBELF_LIBRARIES} dl pthread) install (TARGETS ${PROJECT_NAME} ${INSTALL_TARGETS_PATH}) kcov-11/src/bin-to-c-source.py000077500000000000000000000007401201363500200163020ustar00rootroot00000000000000#!/usr/bin/env python import sys, struct def generate(data_in): print "#include " print "#include " print "size_t __library_data_size = 0x%x;" % (len(data)) print "uint8_t __library_data[] = {" 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 "};" if __name__ == "__main__": f = open(sys.argv[1]) data = f.read() f.close() generate(data) kcov-11/src/cobertura-writer.cc000066400000000000000000000073721201363500200166360ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "writer-base.hh" using namespace kcov; class CoberturaWriter : public WriterBase { public: CoberturaWriter(IElf &elf, IReporter &reporter, IOutputHandler &output) : WriterBase(elf, reporter, output), m_output(output) { } void onStartup() { } void onStop() { } void write() { std::string str; unsigned int nTotalExecutedLines = 0; unsigned int nTotalCodeLines = 0; setupCommonPaths(); m_fileMutex.lock(); for (FileMap_t::iterator it = m_files.begin(); it != m_files.end(); it++) { File *file = it->second; str = str + writeOne(file); nTotalCodeLines += file->m_codeLines; nTotalExecutedLines += file->m_executedLines; } m_fileMutex.unlock(); str = getHeader(nTotalCodeLines, nTotalExecutedLines) + str + getFooter(); write_file((void *)str.c_str(), str.size(), (m_output.getOutDirectory() + "cobertura.xml").c_str()); } private: std::string mangleFileName(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++) { std::string line = file->m_lineMap[n]; if (!m_reporter.lineIsCode(file->m_name.c_str(), n)) continue; IReporter::LineExecutionCount cnt = m_reporter.getLineExecutionCount(file->m_name.c_str(), n); nExecutedLines += !!cnt.m_hits; nCodeLines++; 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" ; } std::string getFooter() { return " \n" " \n" " \n" "\n"; } IOutputHandler &m_output; }; namespace kcov { IWriter &createCoberturaWriter(IElf &elf, IReporter &reporter, IOutputHandler &output) { return *new CoberturaWriter(elf, reporter, output); } } kcov-11/src/cobertura-writer.hh000066400000000000000000000002611201363500200166360ustar00rootroot00000000000000#pragma once namespace kcov { class IElf; class IReporter; class IOutputHandler; IWriter &createCoberturaWriter(IElf &elf, IReporter &reporter, IOutputHandler &output); } kcov-11/src/collector.cc000066400000000000000000000034311201363500200153140ustar00rootroot00000000000000#include #include #include #include #include #include #include using namespace kcov; class Collector : public ICollector, public IElf::ILineListener { public: Collector(IElf &elf) : m_elf(elf), m_engine(IEngine::getInstance()) { m_elf.registerLineListener(*this); } void registerListener(ICollector::IListener &listener) { m_listeners.push_back(&listener); } int run() { if (!m_engine.start(m_elf.getFilename())) { error("Can't start/attach to %s", m_elf.getFilename()); return -1; } // This will set all breakpoints m_elf.parse(); while (1) { IEngine::Event ev; ev = m_engine.continueExecution(); switch (ev.type) { case ev_error: case ev_syscall: case ev_crash: fprintf(stderr, "Process exited with %s\n", m_engine.eventToName(ev).c_str()); return -1; case ev_exit: return ev.data; case ev_breakpoint: for (ListenerList_t::iterator it = m_listeners.begin(); it != m_listeners.end(); it++) (*it)->onBreakpoint(ev.addr); // Disable this breakpoint m_engine.clearBreakpoint(ev.data); break; default: error("Unknown event %d", ev.type); return -1; } } /* Should never get here */ panic("Unreachable code\n"); return 0; } virtual void stop() { } private: void onLine(const char *file, unsigned int lineNr, unsigned long addr) { if (addr == 0) return; m_engine.setBreakpoint(addr); m_addrs[addr]++; } typedef std::unordered_map AddrMap_t; typedef std::list ListenerList_t; AddrMap_t m_addrs; IElf &m_elf; IEngine &m_engine; ListenerList_t m_listeners; }; ICollector &ICollector::create(IElf *elf) { return *new Collector(*elf); } kcov-11/src/configuration.cc000066400000000000000000000204511201363500200161760ustar00rootroot00000000000000#include #include #include #include #include #include #include #include using namespace kcov; class Configuration : public IConfiguration { public: Configuration() { m_outDirectory = ""; m_binaryName = ""; m_lowLimit = 25; m_highLimit = 75; m_pathStripLevel = 2; m_ptracePid = 0; m_sortType = FILENAME; } 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" " -s, --sort-type=type how to sort files: f[ilename] (default), p[ercent],\n" " u[ncovered-lines], l[ines]\n" " -l, --limits=low,high setup limits for low/high coverage (default %u,%u)\n" " -t, --title=title title for the coverage (default: filename)\n" " --path-strip-level=num path levels to show for common paths (default: %u)\n" " --include-path=path comma-separated paths to include in the report\n" " --exclude-path=path comma-separated paths to exclude from the report\n" " --include-pattern=pat comma-separated path patterns to include in the report\n" " --exclude-pattern=pat comma-separated path patterns to exclude from the report\n" "\n" "Examples:\n" " kcov /tmp/frodo ./frodo # Check coverage for ./frodo\n" " kcov --pid=1000 /tmp/frodo # Check coverage for PID 1000\n" " kcov --include-pattern=/src/frodo/ /tmp/frodo ./frodo # Only include files\n" " # including /src/frodo\n" "", m_lowLimit, m_highLimit, m_pathStripLevel); 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'}, {"sort-type", required_argument, 0, 's'}, {"limits", required_argument, 0, 'l'}, {"title", required_argument, 0, 't'}, {"path-strip-level", required_argument, 0, 'S'}, {"exclude-pattern", required_argument, 0, 'x'}, {"include-pattern", required_argument, 0, 'i'}, {"exclude-path", required_argument, 0, 'X'}, {"include-path", required_argument, 0, 'I'}, {"debug", required_argument, 0, 'D'}, /*{"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; m_onlyIncludePath.clear(); m_onlyIncludePattern.clear(); m_excludePath.clear(); m_excludePattern.clear(); /* 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 (file_is_elf(argv[lastArg])) break; } /* 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': return usage(); case 'p': if (!isInteger(std::string(optarg))) return usage(); m_ptracePid = stoul(std::string(optarg)); extraNeeded = 1; break; case 's': if (*optarg == 'p' || *optarg == 'P') m_sortType = PERCENTAGE; else if (*optarg == 'u' || *optarg == 'U') m_sortType = UNCOVERED_LINES; else if (*optarg == 'l' || *optarg == 'L') m_sortType = FILE_LENGTH; else if (*optarg != 'f' && *optarg != 'F') return usage(); break; case 't': m_title = optarg; break; case 'S': if (!isInteger(std::string(optarg))) return usage(); m_pathStripLevel = stoul(std::string(optarg)); if (m_pathStripLevel == 0) m_pathStripLevel = ~0; break; case 'D': if (!isInteger(std::string(optarg))) return usage(); g_kcov_debug_mask = stoul(std::string(optarg)); break; case 'i': m_onlyIncludePattern = getCommaSeparatedList(std::string(optarg)); break; case 'x': m_excludePattern = getCommaSeparatedList(std::string(optarg)); break; case 'I': m_onlyIncludePath = getCommaSeparatedList(std::string(optarg)); expandPath(m_onlyIncludePath); break; case 'X': m_excludePath = getCommaSeparatedList(std::string(optarg)); expandPath(m_excludePath); 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(); m_lowLimit = stoul(vec[0]); m_highLimit = stoul(vec[1]); break; } default: error("Unrecognized option: -%c\n", optopt); return usage(); } } afterOpts = optind; /* When tracing by PID, the filename is optional */ if (argc < afterOpts + extraNeeded) return usage(); m_outDirectory = argv[afterOpts]; if (m_outDirectory[m_outDirectory.size() - 1] != '/') m_outDirectory += "/"; if (argc >= afterOpts + 2) { StringPair_t tmp = splitPath(argv[afterOpts + 1]); m_binaryPath = tmp.first; m_binaryName = tmp.second; } m_programArgs = &argv[afterOpts + 1]; return true; } std::string &getOutDirectory() { return m_outDirectory; } std::string &getBinaryName() { return m_binaryName; } std::string &getBinaryPath() { return m_binaryPath; } unsigned int getAttachPid() { return m_ptracePid; } unsigned int getLowLimit() { return m_lowLimit; } unsigned int getHighLimit() { return m_highLimit; } const char **getArgv() { return m_programArgs; } enum SortType getSortType() { return m_sortType; } std::map &getExcludePattern() { return m_excludePattern; } std::map &getOnlyIncludePattern() { return m_onlyIncludePattern; } std::map &getOnlyIncludePath() { return m_onlyIncludePath; } std::map &getExcludePath() { return m_excludePath; } unsigned int getPathStripLevel() { return m_pathStripLevel; } // "private", but we ignore that in the unit test typedef std::map StrVecMap_t; typedef std::pair StringPair_t; 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->second; if (s[0] == '~') s = get_home() + s.substr(1, s.size()); it->second = s; } } StrVecMap_t getCommaSeparatedList(std::string str) { StrVecMap_t out; if (str.find(',') == std::string::npos) { out[0] = str; return out; } size_t pos, lastPos; unsigned int n = 0; 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[n++] = cur; lastPos = pos; } out[n] = str.substr(lastPos + 1, 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; } unsigned int m_lowLimit; unsigned int m_highLimit; unsigned int m_pathStripLevel; enum SortType m_sortType; unsigned int m_ptracePid; std::string m_outDirectory; std::string m_binaryName; std::string m_binaryPath; const char **m_programArgs; std::string m_title; StrVecMap_t m_excludePattern; StrVecMap_t m_onlyIncludePattern; StrVecMap_t m_excludePath; StrVecMap_t m_onlyIncludePath; }; IConfiguration & IConfiguration::getInstance() { static Configuration *g_instance; if (!g_instance) g_instance = new Configuration(); return *g_instance; } kcov-11/src/elf.cc000066400000000000000000000211211201363500200140700ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include using namespace kcov; enum SymbolType { SYM_NORMAL = 0, SYM_DYNAMIC = 1, }; class Elf : public IElf { public: Elf() : m_filter(IFilter::getInstance()) { m_elf = NULL; m_filename = NULL; m_checksum = 0; m_elfIs32Bit = true; m_isMainFile = true; } virtual ~Elf() { free((void *)m_filename); } const char *getFilename() { return m_filename; } uint64_t getChecksum() { return m_checksum; } bool addFile(const char *filename, struct phdr_data_entry *data = 0) { free((void *)m_filename); m_filename = strdup(filename); 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)); } return checkFile(); } bool checkFile() { Elf *elf; bool out = true; int fd; fd = ::open(m_filename, O_RDONLY, 0); if (fd < 0) { error("Cannot open %s\n", m_filename); return false; } if (!(elf = elf_begin(fd, ELF_C_READ, NULL)) ) { error("elf_begin failed on %s\n", m_filename); out = false; goto out_open; } if (elf_kind(elf) == ELF_K_NONE) out = false; elf_end(elf); out_open: close(fd); return out; } bool parse() { struct stat st; if (lstat(m_filename, &st) < 0) return 0; if (m_isMainFile) m_checksum = ((uint64_t)st.st_mtim.tv_sec << 32) | ((uint64_t)st.st_mtim.tv_nsec); parseOneElf(); parseOneDwarf(); for (FileListenerList_t::iterator it = m_fileListeners.begin(); it != m_fileListeners.end(); it++) { IFileListener *cur = *it; cur->onFile(m_filename, !m_isMainFile); } // After the first, all other are solibs m_isMainFile = false; return true; } bool parseOneDwarf() { Dwarf_Off offset = 0; Dwarf_Off last_offset = 0; size_t hdr_size; Dwarf *dbg; int fd; fd = ::open(m_filename, O_RDONLY, 0); if (fd < 0) { error("Cannot open %s\n", m_filename); return false; } /* Initialize libdwarf */ dbg = dwarf_begin(fd, DWARF_C_READ); if (!dbg) { kcov_debug(ELF_MSG, "No debug symbols in %s.\n", m_filename); 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; /* 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) { const char *const *src_dirs; const char *full_file_path; const char *file_path = line_source; size_t ndirs = 0; /* Lookup the compilation path */ if (dwarf_getsrcdirs(file_buffer, &src_dirs, &ndirs) != 0) continue; if (ndirs == 0) continue; if (!addressIsValid(addr)) continue; /* Use the full compilation path unless the source already * has an absolute path */ full_file_path = dir_concat(src_dirs[0], line_source); if (line_source[0] != '/') file_path = full_file_path; char *rp = ::realpath(file_path, NULL); if (rp) { free((void *)full_file_path); file_path = full_file_path = rp; } if (m_filter.runFilters(file_path) == true) { for (LineListenerList_t::iterator it = m_lineListeners.begin(); it != m_lineListeners.end(); it++) (*it)->onLine(file_path, line_nr, adjustAddressBySegment(addr)); } free((void *)full_file_path); } } } 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; size_t sz; char *raw; int fd; fd = ::open(m_filename, O_RDONLY, 0); if (fd < 0) { error("Cannot open %s\n", m_filename); return false; } if (!(m_elf = elf_begin(fd, ELF_C_READ, NULL)) ) { error("elf_begin failed on %s\n", m_filename); goto out_open; } raw = elf_getident(m_elf, &sz); if (raw && sz > EI_CLASS) m_elfIs32Bit = raw[EI_CLASS] == ELFCLASS32; if (elf_getshdrstrndx(m_elf, &shstrndx) < 0) { error("elf_getshstrndx failed on %s\n", m_filename); goto out_elf_begin; } setupSegments = m_curSegments.size() == 0; while ( (scn = elf_nextscn(m_elf, scn)) != NULL ) { uint64_t sh_addr; uint64_t sh_size; uint64_t sh_flags; uint64_t sh_name; char *name; if (m_elfIs32Bit) { Elf32_Shdr *shdr32 = elf32_getshdr(scn); 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_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); goto out_elf_begin; } 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)); } elf_end(m_elf); if (!(m_elf = elf_begin(fd, ELF_C_READ, NULL)) ) { error("elf_begin failed on %s\n", m_filename); 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: class Segment { public: Segment(uint64_t paddr, uint64_t vaddr, uint64_t size) : m_paddr(paddr), m_vaddr(vaddr), m_size(size) { } uint64_t m_paddr; uint64_t m_vaddr; size_t m_size; }; typedef std::list SegmentList_t; typedef std::list LineListenerList_t; typedef std::list FileListenerList_t; bool addressIsValid(uint64_t addr) { for (SegmentList_t::iterator it = m_executableSegments.begin(); it != m_executableSegments.end(); it++) { Segment cur = *it; if (addr >= cur.m_paddr && addr < cur.m_paddr + cur.m_size) { return true; } } return false; } uint64_t adjustAddressBySegment(uint64_t addr) { for (SegmentList_t::iterator it = m_curSegments.begin(); it != m_curSegments.end(); it++) { Segment cur = *it; if (addr >= cur.m_paddr && addr < cur.m_paddr + cur.m_size) { addr = (addr - cur.m_paddr + cur.m_vaddr); break; } } return addr; } SegmentList_t m_curSegments; SegmentList_t m_executableSegments; IFilter &m_filter; Elf *m_elf; bool m_elfIs32Bit; LineListenerList_t m_lineListeners; FileListenerList_t m_fileListeners; const char *m_filename; bool m_isMainFile; uint64_t m_checksum; }; static Elf *g_instance; IElf *IElf::open(const char *filename) { static bool initialized = false; if (!initialized) { panic_if(elf_version(EV_CURRENT) == EV_NONE, "ELF version failed\n"); initialized = true; } g_instance = new Elf(); if (g_instance->addFile(filename) == false) { delete g_instance; g_instance = NULL; return NULL; } return g_instance; } IElf &IElf::getInstance() { panic_if (!g_instance, "ELF object not yet created"); return *g_instance; } kcov-11/src/filter.cc000066400000000000000000000065241201363500200146210ustar00rootroot00000000000000#include #include #include #include #include using namespace kcov; 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(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().getOnlyIncludePattern()), m_excludePatterns(IConfiguration::getInstance().getExcludePattern()) { } 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 (std::map::iterator it = m_includePatterns.begin(); it != m_includePatterns.end(); it++) { std::string &pattern = it->second; if (file.find(pattern) != std::string::npos) out = true; } for (std::map::iterator it = m_excludePatterns.begin(); it != m_excludePatterns.end(); it++) { std::string &pattern = it->second; if (file.find(pattern) != std::string::npos) out = false; } return out; } private: std::map &m_includePatterns; std::map &m_excludePatterns; }; class PathHandler { public: PathHandler() : m_includePaths(IConfiguration::getInstance().getOnlyIncludePath()), m_excludePaths(IConfiguration::getInstance().getExcludePath()) { } bool isSetup() { return !(m_includePaths.size() == 0 && m_excludePaths.size() == 0); } bool includeFile(std::string file) { if (m_includePaths.size() == 0 && m_excludePaths.size() == 0) return true; bool out = true; if (m_includePaths.size() != 0) out = false; char *path = realpath(file.c_str(), NULL); // Can't show something which doesn't exist, so filter it out if (!path) return false; std::string pathStr(path); free(path); for (std::map::iterator it = m_includePaths.begin(); it != m_includePaths.end(); it++) { std::string &pathPattern = it->second; if (pathStr.find(pathPattern) == 0) out = true; } for (std::map::iterator it = m_excludePaths.begin(); it != m_excludePaths.end(); it++) { std::string &pathPattern = it->second; if (pathStr.find(pathPattern) == 0) out = false; } return out; } private: std::map &m_includePaths; std::map &m_excludePaths; }; PatternHandler *m_patternHandler; PathHandler *m_pathHandler; }; IFilter &IFilter::getInstance() { static Filter *g_instance; if (!g_instance) g_instance = new Filter(); return *g_instance; } kcov-11/src/html-writer.cc000066400000000000000000000524651201363500200156170ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include "writer-base.hh" using namespace kcov; static const uint8_t icon_ruby[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x25, 0xdb, 0x56, 0xca, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xd2, 0x07, 0x11, 0x0f, 0x18, 0x10, 0x5d, 0x57, 0x34, 0x6e, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, 0x12, 0x01, 0xd2, 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x06, 0x50, 0x4c, 0x54, 0x45, 0xff, 0x35, 0x2f, 0x00, 0x00, 0x00, 0xd0, 0x33, 0x9a, 0x9d, 0x00, 0x00, 0x00, 0x0a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0xe5, 0x27, 0xde, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const uint8_t icon_amber[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x25, 0xdb, 0x56, 0xca, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xd2, 0x07, 0x11, 0x0f, 0x28, 0x04, 0x98, 0xcb, 0xd6, 0xe0, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, 0x12, 0x01, 0xd2, 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x06, 0x50, 0x4c, 0x54, 0x45, 0xff, 0xe0, 0x50, 0x00, 0x00, 0x00, 0xa2, 0x7a, 0xda, 0x7e, 0x00, 0x00, 0x00, 0x0a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0xe5, 0x27, 0xde, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const uint8_t icon_emerald[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x25, 0xdb, 0x56, 0xca, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xd2, 0x07, 0x11, 0x0f, 0x22, 0x2b, 0xc9, 0xf5, 0x03, 0x33, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, 0x12, 0x01, 0xd2, 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x06, 0x50, 0x4c, 0x54, 0x45, 0x1b, 0xea, 0x59, 0x0a, 0x0a, 0x0a, 0x0f, 0xba, 0x50, 0x83, 0x00, 0x00, 0x00, 0x0a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0xe5, 0x27, 0xde, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const uint8_t icon_snow[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x25, 0xdb, 0x56, 0xca, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xd2, 0x07, 0x11, 0x0f, 0x1e, 0x1d, 0x75, 0xbc, 0xef, 0x55, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, 0x12, 0x01, 0xd2, 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x06, 0x50, 0x4c, 0x54, 0x45, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x55, 0xc2, 0xd3, 0x7e, 0x00, 0x00, 0x00, 0x0a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0xe5, 0x27, 0xde, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const uint8_t icon_glass[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x25, 0xdb, 0x56, 0xca, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x06, 0x50, 0x4c, 0x54, 0x45, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x55, 0xc2, 0xd3, 0x7e, 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, 0x00, 0x01, 0x62, 0x4b, 0x47, 0x44, 0x00, 0x88, 0x05, 0x1d, 0x48, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, 0x12, 0x01, 0xd2, 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xd2, 0x07, 0x13, 0x0f, 0x08, 0x19, 0xc4, 0x40, 0x56, 0x10, 0x00, 0x00, 0x00, 0x0a, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x63, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x48, 0xaf, 0xa4, 0x71, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const char css_text[] = "/* Based upon the lcov CSS style, style files can be reused */\n" "body { color: #000000; background-color: #FFFFFF; }\n" "a:link { color: #284FA8; text-decoration: underline; }\n" "a:visited { color: #00CB40; text-decoration: underline; }\n" "a:active { color: #FF0040; text-decoration: underline; }\n" "td.title { text-align: center; padding-bottom: 10px; font-size: 20pt; font-weight: bold; }\n" "td.ruler { background-color: #6688D4; }\n" "td.headerItem { text-align: right; padding-right: 6px; font-family: sans-serif; font-weight: bold; }\n" "td.headerValue { text-align: left; color: #284FA8; font-family: sans-serif; font-weight: bold; }\n" "td.versionInfo { text-align: center; padding-top: 2px; }\n" "pre.source { font-family: monospace; white-space: pre; }\n" "span.lineNum { background-color: #EFE383; }\n" "span.lineCov { background-color: #CAD7FE; }\n" "span.linePartCov { background-color: #FFEA20; }\n" "span.lineNoCov { background-color: #FF6230; }\n" "td.tableHead { text-align: center; color: #FFFFFF; background-color: #6688D4; font-family: sans-serif; font-size: 120%; font-weight: bold; }\n" "td.coverFile { text-align: left; padding-left: 10px; padding-right: 20px; color: #284FA8; background-color: #DAE7FE; font-family: monospace; }\n" "td.coverBar { padding-left: 10px; padding-right: 10px; background-color: #DAE7FE; }\n" "td.coverBarOutline { background-color: #000000; }\n" "td.coverPerHi { text-align: right; padding-left: 10px; padding-right: 10px; background-color: #A7FC9D; font-weight: bold; }\n" "td.coverPerLeftMed { text-align: left; padding-left: 10px; padding-right: 10px; background-color: #FFEA20; font-weight: bold; }\n" "td.coverPerLeftLo { text-align: left; padding-left: 10px; padding-right: 10px; background-color: #FF0000; font-weight: bold; }\n" "td.coverPerLeftHi { text-align: left; padding-left: 10px; padding-right: 10px; background-color: #A7FC9D; font-weight: bold; }\n" "td.coverNumHi { text-align: right; padding-left: 10px; padding-right: 10px; background-color: #A7FC9D; }\n" "td.coverPerMed { text-align: right; padding-left: 10px; padding-right: 10px; background-color: #FFEA20; font-weight: bold; }\n" "td.coverNumMed { text-align: right; padding-left: 10px; padding-right: 10px; background-color: #FFEA20; }\n" "td.coverPerLo { text-align: right; padding-left: 10px; padding-right: 10px; background-color: #FF0000; font-weight: bold; }\n" "td.coverNumLo { text-align: right; padding-left: 10px; padding-right: 10px; background-color: #FF0000; }\n"; class HtmlWriter : public WriterBase { public: HtmlWriter(IElf &elf, IReporter &reporter, IOutputHandler &output) : WriterBase(elf, reporter, output), m_stop(false) { m_indexDirectory = output.getBaseDirectory(); m_outDirectory = output.getOutDirectory(); m_summaryDbFileName = m_outDirectory + "summary.db"; } void onStop() { } private: void writeOne(File *file) { std::string outName = m_outDirectory + "/" + file->m_outFileName; unsigned int nExecutedLines = 0; unsigned int nCodeLines = 0; std::string s = "
\n";
		for (unsigned int n = 1; n < file->m_lastLineNr; n++) {
			std::string line = file->m_lineMap[n];

			s += "" + fmt("%5u", n) + "";

			if (!m_reporter.lineIsCode(file->m_name.c_str(), n)) {
				s += "              : " + escapeHtml(line) + "\n";
				continue;
			}

			IReporter::LineExecutionCount cnt =
					m_reporter.getLineExecutionCount(file->m_name.c_str(), n);

			std::string lineClass = "lineNoCov";

			nExecutedLines += !!cnt.m_hits;
			nCodeLines++;

			if (cnt.m_hits == cnt.m_possibleHits)
				lineClass = "lineCov";
			else if (cnt.m_hits)
				lineClass = "linePartCov";

			s += "    " +
					fmt("%3u", cnt.m_hits) + "  / " + fmt("%3u", cnt.m_possibleHits) + ": " +
					escapeHtml(line) +
					"\n";
		}
		s += "
\n"; s = getHeader(nCodeLines, nExecutedLines) + s + getFooter(file); write_file((void *)s.c_str(), s.size(), outName.c_str()); // Update the execution count file->m_executedLines = nExecutedLines; file->m_codeLines = nCodeLines; } void writeIndex() { std::string s; unsigned int nTotalExecutedLines = 0; unsigned int nTotalCodeLines = 0; std::multimap fileListByPercent; std::multimap fileListByUncoveredLines; std::multimap fileListByFileLength; std::map fileListByName; m_fileMutex.lock(); for (FileMap_t::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; double percent = 0; if (nCodeLines != 0) percent = ((double)nExecutedLines / (double)nCodeLines) * 100; nTotalCodeLines += nCodeLines; nTotalExecutedLines += nExecutedLines; std::string coverPer = strFromPercentage(percent); std::string listName = file->m_name; size_t pos = listName.find(m_commonPath); unsigned int stripLevel = IConfiguration::getInstance().getPathStripLevel(); 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()); } std::string cur = " \n" " m_outFileName + "\" title=\"" + file->m_fileName + "\">" + listName + "\n" " \n" "
" + constructBar(percent) + "
\n" " \n" " " + fmt("%.1f", percent) + " %\n" " " + fmt("%u", nExecutedLines) + " / " + fmt("%u", nCodeLines) + " lines\n" " \n"; fileListByName[file->m_name] = cur; fileListByPercent.insert(std::pair(percent, cur)); fileListByUncoveredLines.insert(std::pair(nCodeLines - nExecutedLines, cur)); fileListByFileLength.insert(std::pair(nCodeLines, cur)); } m_fileMutex.unlock(); if (IConfiguration::getInstance().getSortType() == IConfiguration::PERCENTAGE) { for (std::multimap::iterator it = fileListByPercent.begin(); it != fileListByPercent.end(); it++) s = s + it->second; } else if (IConfiguration::getInstance().getSortType() == IConfiguration::UNCOVERED_LINES) { for (std::multimap::iterator it = fileListByUncoveredLines.begin(); it != fileListByUncoveredLines.end(); it++) s = it->second + s; // Sort backwards } else if (IConfiguration::getInstance().getSortType() == IConfiguration::FILE_LENGTH) { for (std::multimap::iterator it = fileListByFileLength.begin(); it != fileListByFileLength.end(); it++) s = it->second + s; // Ditto } else { for (std::map::iterator it = fileListByName.begin(); it != fileListByName.end(); it++) s = s + it->second; } s = getHeader(nTotalCodeLines, nTotalExecutedLines) + getIndexHeader() + s + getFooter(NULL); write_file((void *)s.c_str(), s.size(), (m_outDirectory + "index.html").c_str()); // Produce a summary IReporter::ExecutionSummary summary = m_reporter.getExecutionSummary(); size_t sz; void *data = marshalSummary(summary, IConfiguration::getInstance().getBinaryName(), &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()); for (de = readdir(dir); de; de = readdir(dir)) { std::string cur = idx + de->d_name + "/summary.db"; if (!file_exists(cur.c_str())) 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; double percent = 0; if (summary.m_lines != 0) percent = (summary.m_executedLines / (double)summary.m_lines) * 100; nTotalCodeLines += summary.m_lines; nTotalExecutedLines += summary.m_executedLines; std::string coverPer = strFromPercentage(percent); s += " \n" " d_name) + "/index.html\" title=\"" + name + "\">" + name + "\n" " \n" "
" + constructBar(percent) + "
\n" " \n" " " + fmt("%.1f", percent) + " %\n" " " + fmt("%u", summary.m_executedLines) + " / " + fmt("%u", summary.m_lines) + " lines\n" " \n"; } s = getHeader(nTotalCodeLines, nTotalExecutedLines, false) + getIndexHeader() + s + getFooter(NULL); write_file((void *)s.c_str(), s.size(), (m_indexDirectory + "index.html").c_str()); } void write() { m_fileMutex.lock(); for (FileMap_t::iterator it = m_files.begin(); it != m_files.end(); it++) { File *file = it->second; writeOne(file); } m_fileMutex.unlock(); setupCommonPaths(); writeIndex(); writeGlobalIndex(); } std::string strFromPercentage(double percent) { IConfiguration &conf = IConfiguration::getInstance(); std::string coverPer = "Med"; if (percent >= conf.getHighLimit()) coverPer = "Hi"; else if (percent < conf.getLowLimit()) coverPer = "Lo"; return coverPer; } std::string getIndexHeader() { return std::string( "
\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" ); } std::string getFooter(File *file) { return std::string( "

FilenameCoverage
\n" " \n" " \n" "
\"\"/
Generated by: Kcov (based on bcov)
\n" "
\n" "\n" "\n" ); } std::string getHeader(unsigned int nCodeLines, unsigned int nExecutedLines, bool includeCommand = true) { IConfiguration &conf = IConfiguration::getInstance(); std::string percentage_text = "Lo"; double percentage = 0; char date_buf[80]; time_t t; struct tm *tm; if (nCodeLines != 0) percentage = ((double)nExecutedLines / (double)nCodeLines) * 100; if (percentage > conf.getLowLimit() && percentage < conf.getHighLimit()) percentage_text = "Med"; else if (percentage >= conf.getHighLimit()) percentage_text = "Hi"; t = time(NULL); tm = localtime(&t); strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", tm); std::string date(date_buf); std::string instrumentedLines = fmt("%u", nCodeLines); std::string lines = fmt("%u", nExecutedLines); std::string covered = fmt("%.1f", percentage); std::string commandStr = ""; if (includeCommand) commandStr = " Command:\n" " " + escapeHtml(conf.getBinaryName()) + "\n"; return std::string( "\n" "\n" "\n" " Coverage - " + escapeHtml(conf.getBinaryName()) + "\n" " \n" "\n" "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" "
Coverage Report
\"\"/
\n" " \n" " \n" + commandStr + " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "
Date:" + escapeHtml(date) + "Instrumented lines:" + instrumentedLines + "
Code covered:" + covered + "%Executed lines:" + lines + "
\n" "
\"\"/
\n" ); } std::string constructBar(double percent) { const char* color = "ruby.png"; char buf[1024]; int width = (int)(percent+0.5); IConfiguration &conf = IConfiguration::getInstance(); if (percent >= conf.getHighLimit()) color = "emerald.png"; else if (percent > conf.getLowLimit()) color = "amber.png"; else if (percent <= 1) color = "snow.png"; xsnprintf(buf, sizeof(buf), "\"%.1f%%\"/\"%.1f%%\"/", color, width, percent, 100 - width, percent); return std::string(buf); } char *escapeHelper(char *dst, const char *what) { int len = strlen(what); strcpy(dst, what); return dst + len; } std::string escapeHtml(std::string &str) { const char *s = str.c_str(); char buf[4096]; char *dst = buf; size_t len = strlen(s); size_t i; memset(buf, 0, sizeof(buf)); for (i = 0; i < len; i++) { char c = s[i]; switch (c) { case '<': dst = escapeHelper(dst, "<"); break; case '>': dst = escapeHelper(dst, ">"); break; case '&': dst = escapeHelper(dst, "&"); break; case '\"': dst = escapeHelper(dst, """); break; case '\'': dst = escapeHelper(dst, "'"); break; case '/': dst = escapeHelper(dst, "/"); break; case '\\': dst = escapeHelper(dst, "\"); break; case '\n': case '\r': dst = escapeHelper(dst, " "); break; default: *dst = c; dst++; break; } } return std::string(buf); } void writeHelperFiles(std::string dir) { write_file(icon_ruby, sizeof(icon_ruby), "%s/ruby.png", dir.c_str()); write_file(icon_amber, sizeof(icon_amber), "%s/amber.png", dir.c_str()); write_file(icon_emerald, sizeof(icon_emerald), "%s/emerald.png", dir.c_str()); write_file(icon_snow, sizeof(icon_snow), "%s/snow.png", dir.c_str()); write_file(icon_glass, sizeof(icon_glass), "%s/glass.png", dir.c_str()); write_file(css_text, sizeof(css_text), "%s/bcov.css", dir.c_str()); } void onStartup() { writeHelperFiles(m_indexDirectory); writeHelperFiles(m_outDirectory); } bool m_stop; std::string m_outDirectory; std::string m_indexDirectory; std::string m_summaryDbFileName; }; namespace kcov { IWriter &createHtmlWriter(IElf &elf, IReporter &reporter, IOutputHandler &output) { return *new HtmlWriter(elf, reporter, output); } } kcov-11/src/html-writer.hh000066400000000000000000000002541201363500200156160ustar00rootroot00000000000000#pragma once namespace kcov { class IElf; class IReporter; class IOutputHandler; IWriter &createHtmlWriter(IElf &elf, IReporter &reporter, IOutputHandler &output); } kcov-11/src/include/000077500000000000000000000000001201363500200144415ustar00rootroot00000000000000kcov-11/src/include/collector.hh000066400000000000000000000005441201363500200167530ustar00rootroot00000000000000#pragma once namespace kcov { class IElf; class ICollector { public: class IListener { public: virtual void onBreakpoint(unsigned long addr) = 0; }; static ICollector &create(IElf *elf); virtual void registerListener(IListener &listener) = 0; virtual int run() = 0; virtual void stop() = 0; virtual ~ICollector() {}; }; } kcov-11/src/include/configuration.hh000066400000000000000000000020411201363500200176260ustar00rootroot00000000000000#pragma once #include #include namespace kcov { class IConfiguration { public: enum SortType { PERCENTAGE, FILENAME, FILE_LENGTH, UNCOVERED_LINES }; virtual ~IConfiguration() {} virtual void printUsage() = 0; virtual std::string &getOutDirectory() = 0; virtual std::string &getBinaryName() = 0; virtual std::string &getBinaryPath() = 0; virtual enum SortType getSortType() = 0; virtual unsigned int getAttachPid() = 0; virtual unsigned int getLowLimit() = 0; virtual unsigned int getHighLimit() = 0; virtual unsigned int getPathStripLevel() = 0; virtual const char **getArgv() = 0; virtual std::map &getExcludePattern() = 0; virtual std::map &getOnlyIncludePattern() = 0; virtual std::map &getOnlyIncludePath() = 0; virtual std::map &getExcludePath() = 0; virtual bool parse(unsigned int argc, const char *argv[]) = 0; static IConfiguration &getInstance(); }; } kcov-11/src/include/elf.hh000066400000000000000000000014351201363500200155330ustar00rootroot00000000000000#pragma once #include #include struct phdr_data_entry; namespace kcov { class IElf { public: class ILineListener { public: virtual void onLine(const char *file, unsigned int lineNr, unsigned long addr) = 0; }; class IFileListener { public: virtual void onFile(const char *file, bool isSolib) = 0; }; static IElf *open(const char *filename); static IElf &getInstance(); virtual ~IElf() {} virtual bool addFile(const char *filename, struct phdr_data_entry *phdr_data = 0) = 0; virtual const char *getFilename() = 0; virtual void registerLineListener(ILineListener &listener) = 0; virtual void registerFileListener(IFileListener &listener) = 0; virtual bool parse() = 0; virtual uint64_t getChecksum() = 0; }; } kcov-11/src/include/engine.hh000066400000000000000000000021701201363500200162270ustar00rootroot00000000000000#pragma once #include #include #include namespace kcov { enum event_type { ev_error = -1, ev_breakpoint = 1, ev_syscall = 2, ev_crash = 3, ev_exit = 4, }; class IEngine { public: class Event { public: enum event_type type; int data; // Typically the breakpoint unsigned long addr; }; static IEngine &getInstance(); 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 setBreakpoint(unsigned long addr) = 0; virtual bool clearBreakpoint(int id) = 0; virtual void clearAllBreakpoints() = 0; /** * For a new process and attach to it with ptrace * * @return true if OK, false otherwise */ virtual bool start(const char *executable) = 0; /** * Continue execution until next breakpoint. * * @return the event the execution stopped at. */ virtual const Event continueExecution() = 0; virtual std::string eventToName(Event ev) = 0; virtual void kill() = 0; }; } kcov-11/src/include/filter.hh000066400000000000000000000004301201363500200162440ustar00rootroot00000000000000#pragma once #include namespace kcov { class IFilter { public: class Handler { public: virtual bool includeFile(std::string file) = 0; }; virtual bool runFilters(std::string file) = 0; static IFilter &getInstance(); virtual ~IFilter() {} }; } kcov-11/src/include/output-handler.hh000066400000000000000000000007101201363500200177330ustar00rootroot00000000000000#pragma once #include namespace kcov { class IWriter; class IReporter; class IOutputHandler { public: virtual ~IOutputHandler() {} virtual void registerWriter(IWriter &writer) = 0; virtual void start() = 0; virtual void stop() = 0; virtual std::string getBaseDirectory() = 0; virtual std::string getOutDirectory() = 0; static IOutputHandler &create(IReporter &reporter); static IOutputHandler &getInstance(); }; } kcov-11/src/include/phdr_data.h000066400000000000000000000013051201363500200165370ustar00rootroot00000000000000#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[8]; }; 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(void); 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); struct phdr_data *phdr_data_unmarshal(void *p); #ifdef __cplusplus } #endif kcov-11/src/include/reporter.hh000066400000000000000000000021021201363500200166170ustar00rootroot00000000000000#pragma once #include namespace kcov { class IElf; class ICollector; 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) { } ExecutionSummary(unsigned int lines, unsigned int executedLines) : m_lines(lines), m_executedLines(executedLines) { } unsigned int m_lines; unsigned int m_executedLines; }; virtual ~IReporter() {} virtual bool lineIsCode(const char *file, unsigned int lineNr) = 0; virtual LineExecutionCount getLineExecutionCount(const char *file, unsigned int lineNr) = 0; virtual ExecutionSummary getExecutionSummary() = 0; virtual void *marshal(size_t *szOut) = 0; virtual bool unMarshal(void *data, size_t sz) = 0; virtual void stop() = 0; static IReporter &create(IElf &elf, ICollector &collector); }; } kcov-11/src/include/utils.hh000066400000000000000000000043001201363500200161170ustar00rootroot00000000000000#pragma once #include #include #include #include #include #define error(x...) do \ { \ fprintf(stderr, "Error: "); \ fprintf(stderr, x); \ fprintf(stderr, "\n"); \ } while(0) #define warning(x...) do \ { \ fprintf(stderr, "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, PTRACE_MSG = 2, ELF_MSG = 4, BP_MSG = 8, }; 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; } extern int file_is_elf(const char *filename); static inline void *xmalloc(size_t sz) { void *out = malloc(sz); panic_if(!out, "malloc failed"); memset(out, 0, sz); return out; } extern int write_file(const void *data, size_t len, const char *fmt, ...); extern void *read_file(size_t *out_size, const char *fmt, ...); extern const char *dir_concat(const char *dir, const char *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 int file_exists(const char *path); extern int concat_files(const char *dst, const char *file_a, const char *file_b); extern const char *get_home(); unsigned long get_aligned(unsigned long addr); unsigned long get_aligned_4b(unsigned long addr); std::string fmt(const char *fmt, ...); 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); kcov-11/src/include/writer.hh000066400000000000000000000003611201363500200162760ustar00rootroot00000000000000#pragma once namespace kcov { class IElf; class IReporter; class IWriter { public: virtual ~IWriter() {} virtual void onStartup() = 0; virtual void onStop() = 0; virtual void write() = 0; virtual void stop() = 0; }; } kcov-11/src/kernel/000077500000000000000000000000001201363500200142765ustar00rootroot00000000000000kcov-11/src/kernel/Makefile000066400000000000000000000003171201363500200157370ustar00rootroot00000000000000KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) CFLAGS_kmod.o += -g3 obj-m += kmod.o default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules clean: rm -f *.o *.ko *.order *.symvers *.mod.c kcov-11/src/kernel/kmod.c000066400000000000000000000210111201363500200153670ustar00rootroot00000000000000/* * lib/kprobe-coverage.c * * Copyright (C) 2012 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 struct kprobe_coverage { struct dentry *debugfs_root; wait_queue_head_t wq; /* Each coverage entry is on exactly one of these lists */ struct list_head pending_list; struct list_head hit_list; spinlock_t list_lock; }; struct kprobe_coverage_entry { struct kprobe_coverage *parent; unsigned long base_addr; const char *module_name; struct kprobe kp; struct work_struct work; struct list_head lh; }; 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 */ schedule_work(&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 = entry->parent; /* Move from pending list to the hit list */ spin_lock(&kpc->list_lock); list_del(&entry->lh); list_add_tail(&entry->lh, &kpc->hit_list); unregister_kprobe(&entry->kp); spin_unlock(&kpc->list_lock); /* Wake up the listener */ wake_up(&kpc->wq); } static void kpc_free_entry(struct kprobe_coverage_entry *entry) { vfree(entry->module_name); vfree(entry); } static void kpc_new_entry(struct kprobe_coverage *kpc, const char *module_name, unsigned long base_addr, unsigned long where) { struct kprobe_coverage_entry *out; char *name; int err; name = vmalloc(strlen(module_name) + 1); if (!name) return; out = vmalloc(sizeof(*out)); if (!out) { vfree(name); return; } memset(out, 0, sizeof(*out)); strcpy(name, module_name); name[strlen(module_name)] = '\0'; out->module_name = name; out->base_addr = base_addr; out->parent = kpc; out->kp.addr = (void *)(base_addr + where); out->kp.pre_handler = kpc_pre_handler; INIT_WORK(&out->work, kpc_probe_work); if ( (err = register_kprobe(&out->kp)) < 0) goto err; spin_lock(&kpc->list_lock); list_add(&out->lh, &kpc->pending_list); spin_unlock(&kpc->list_lock); if (enable_kprobe(&out->kp) < 0) { unregister_kprobe(&out->kp); goto err; } return; err: kpc_free_entry(out); } static void kpc_add_probe(struct kprobe_coverage *kpc, const char *module_name, unsigned long where) { static char cur_module[MODULE_NAME_LEN]; static unsigned long cur_base_addr; /* Cache the module data to avoid having to iterate through all * the modules each time. */ if (module_name == NULL) { /* The kernel itself */ strcpy(cur_module, ""); cur_base_addr = 0; } else if (strcmp(cur_module, module_name) != 0) { struct module *module; preempt_disable(); module = find_module(module_name); if (module) { strncpy(cur_module, module->name, MODULE_NAME_LEN); cur_base_addr = (unsigned long)module->module_core; } preempt_enable(); if (!module) return; } /* If we are unlucky we might end up with stale data here if the * module has been removed. However, it just means that the kprobe * insertion will fail or that we instrument the wrong module, i.e., * generating confusing but harmless data. */ kpc_new_entry(kpc, cur_module, cur_base_addr, where); } static void kpc_clear_list(struct kprobe_coverage *kpc, struct list_head *list) { struct list_head *iter; list_for_each(iter, list) { struct kprobe_coverage_entry *entry; entry = (struct kprobe_coverage_entry *)container_of(iter, struct kprobe_coverage_entry, lh); unregister_kprobe(&entry->kp); kpc_free_entry(entry); } } static void kpc_clear(struct kprobe_coverage *kpc) { /* Free everything on the two lists */ spin_lock(&kpc->list_lock); kpc_clear_list(kpc, &kpc->hit_list); kpc_clear_list(kpc, &kpc->pending_list); INIT_LIST_HEAD(&kpc->pending_list); INIT_LIST_HEAD(&kpc->hit_list); spin_unlock(&kpc->list_lock); } static void *kpc_unlink_next(struct kprobe_coverage *kpc) { struct kprobe_coverage_entry *entry; int err; /* Wait for something to arrive on the hit list, abort on signal */ err = wait_event_interruptible(kpc->wq, !list_empty(&kpc->hit_list)); if (err < 0) return NULL; spin_lock(&kpc->list_lock); entry = list_first_entry(&kpc->hit_list, struct kprobe_coverage_entry, lh); list_del(&entry->lh); spin_unlock(&kpc->list_lock); return entry; } static int kpc_control_open(struct inode *inode, struct file *file) { file->private_data = inode->i_private; /* kpc */ return 0; } static int kpc_show_open(struct inode *inode, struct file *file) { file->private_data = inode->i_private; return 0; } static ssize_t kpc_show_read(struct file *file, char __user *user_buf, size_t count, loff_t *off) { struct kprobe_coverage *kpc = file->private_data; struct kprobe_coverage_entry *entry; unsigned long addr; char *buf; size_t buf_size; int n = -ENOMEM; /* Wait for an entry */ entry = kpc_unlink_next(kpc); if (!entry) return 0; buf_size = strlen(entry->module_name) + 24; buf = vmalloc(buf_size); if (!buf) goto out; /* Write it out to the user buffer */ memset(buf, 0, buf_size); addr = (unsigned long)entry->kp.addr - entry->base_addr; n = snprintf(buf, buf_size, "%s:0x%lx\n", entry->module_name, addr); if (copy_to_user(user_buf + *off, buf, n)) n = -EFAULT; out: vfree(buf); kpc_free_entry(entry); return n; } 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 to_copy; ssize_t out = 0; char *line; char *buf; char *p; buf = (char *)__get_free_page(GFP_KERNEL); if (!buf) return -ENOMEM; /* Assure it's NULL-terminated */ to_copy = min(count, (size_t)PAGE_SIZE - 1); memset(buf, 0, PAGE_SIZE); memcpy(buf, user_buf, to_copy); if (copy_from_user(buf, user_buf, out)) { free_page((unsigned long) 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); } free_page((unsigned long)buf); *off += out; return out; } 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 = kpc_show_read, }; static int __init kpc_init_one(struct kprobe_coverage *kpc) { /* 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"); return -ENODEV; } 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; INIT_LIST_HEAD(&kpc->pending_list); INIT_LIST_HEAD(&kpc->hit_list); init_waitqueue_head(&kpc->wq); spin_lock_init(&kpc->list_lock); return 0; out_files: debugfs_remove_recursive(kpc->debugfs_root); return -EINVAL; } static struct kprobe_coverage *global_kpc; static int __init kpc_init(void) { int out; global_kpc = vmalloc(sizeof(*global_kpc)); if (!global_kpc) return -ENOMEM; memset(global_kpc, 0, sizeof(*global_kpc)); out = kpc_init_one(global_kpc); if (out < 0) vfree(global_kpc); return out; } static void __exit kpc_exit(void) { debugfs_remove_recursive(global_kpc->debugfs_root); kpc_clear(global_kpc); vfree(global_kpc); global_kpc = NULL; } module_init(kpc_init); module_exit(kpc_exit); MODULE_AUTHOR("Simon Kagstrom "); MODULE_DESCRIPTION("Code coverage through kprobes and debugfs"); MODULE_LICENSE("GPL"); kcov-11/src/library-binary.h000066400000000000000000000001731201363500200161160ustar00rootroot00000000000000#pragma once #include #include extern size_t __library_data_size; extern uint8_t __library_data[]; kcov-11/src/main.cc000066400000000000000000000026571201363500200142630ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "html-writer.hh" #include "cobertura-writer.hh" using namespace kcov; static IOutputHandler *g_output; static ICollector *g_collector; static IReporter *g_reporter; static void ctrlc(int sig) { g_collector->stop(); g_reporter->stop(); g_output->stop(); IEngine::getInstance().kill(); exit(0); } int main(int argc, const char *argv[]) { IConfiguration &conf = IConfiguration::getInstance(); if (!conf.parse(argc, argv)) return 1; std::string file = conf.getBinaryPath() + conf.getBinaryName(); IElf *elf = IElf::open(file.c_str()); if (!elf) { conf.printUsage(); return 1; } ICollector &collector = ICollector::create(elf); IReporter &reporter = IReporter::create(*elf, collector); IOutputHandler &output = IOutputHandler::create(reporter); // Register writers IWriter &htmlWriter = createHtmlWriter(*elf, reporter, output); IWriter &coberturaWriter = createCoberturaWriter(*elf, reporter, output); output.registerWriter(htmlWriter); output.registerWriter(coberturaWriter); g_output = &output; g_reporter = &reporter; g_collector = &collector; signal(SIGINT, ctrlc); signal(SIGTERM, ctrlc); output.start(); int ret = collector.run(); output.stop(); IEngine::getInstance().kill(); return ret; } kcov-11/src/output-handler.cc000066400000000000000000000067051201363500200163100ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace kcov { class OutputHandler : public IOutputHandler, IElf::IFileListener { public: OutputHandler(IReporter &reporter) : m_reporter(reporter) { IConfiguration &conf = IConfiguration::getInstance(); m_baseDirectory = conf.getOutDirectory(); m_outDirectory = m_baseDirectory + conf.getBinaryName() + "/"; m_dbFileName = m_outDirectory + "coverage.db"; m_summaryDbFileName = m_outDirectory + "summary.db"; m_threadStopped = false; m_stop = false; mkdir(m_baseDirectory.c_str(), 0755); mkdir(m_outDirectory.c_str(), 0755); IElf::getInstance().registerFileListener(*this); } std::string getBaseDirectory() { return m_baseDirectory; } std::string getOutDirectory() { return m_outDirectory; } void registerWriter(IWriter &writer) { m_writers.push_back(&writer); } // From IElf::IFileListener void onFile(const char *file, bool isSolib) { // Only unmarshal the main file if (isSolib) return; size_t sz; void *data = read_file(&sz, m_dbFileName.c_str()); if (data) { if (!m_reporter.unMarshal(data, sz)) kcov_debug(INFO_MSG, "Can't unmarshal %s\n", m_dbFileName.c_str()); } free(data); } void start() { for (WriterList_t::iterator it = m_writers.begin(); it != m_writers.end(); it++) (*it)->onStartup(); panic_if (pthread_create(&m_thread, NULL, threadMainStatic, this) < 0, "Can't create thread"); } void stop() { m_stop = true; for (WriterList_t::iterator it = m_writers.begin(); it != m_writers.end(); it++) (*it)->stop(); // Wait for half a second for (unsigned int i = 0; i < 50; i++) { if (m_threadStopped) break; mdelay(10); } for (WriterList_t::iterator it = m_writers.begin(); it != m_writers.end(); it++) (*it)->write(); size_t sz; void *data = m_reporter.marshal(&sz); if (data) write_file(data, sz, m_dbFileName.c_str()); free(data); for (WriterList_t::iterator it = m_writers.begin(); it != m_writers.end(); it++) (*it)->onStop(); } private: void threadMain() { sigset_t set; sigemptyset(&set); sigaddset(&set, SIGINT); sigaddset(&set, SIGTERM); // Block INT and TERM to avoid Ctrl-C ending up in this thread sigprocmask(SIG_BLOCK, &set, NULL); while (!m_stop) { for (WriterList_t::iterator it = m_writers.begin(); it != m_writers.end(); it++) (*it)->write(); sleep(1); } m_threadStopped = true; } static void *threadMainStatic(void *pThis) { OutputHandler *p = (OutputHandler *)pThis; p->threadMain(); return NULL; } typedef std::list WriterList_t; IReporter &m_reporter; std::string m_outDirectory; std::string m_baseDirectory; std::string m_dbFileName; std::string m_summaryDbFileName; WriterList_t m_writers; pthread_t m_thread; bool m_stop; bool m_threadStopped; }; static OutputHandler *instance; IOutputHandler &IOutputHandler::create(IReporter &reporter) { if (!instance) instance = new OutputHandler(reporter); return *instance; } IOutputHandler &IOutputHandler::getInstance() { return *instance; } } kcov-11/src/ptrace.cc000066400000000000000000000273411201363500200146120ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "library-binary.h" using namespace kcov; #define str(s) #s #define xstr(s) str(s) static unsigned long arch_getPcFromRegs(struct user_regs_struct *regs) { unsigned long out; #if defined(__i386__) out = regs->eip - 1; #elif defined(__x86_64__) out = regs->rip - 1; #else # error Unsupported architecture #endif return out; } static void arch_adjustPcAfterBreakpoint(struct user_regs_struct *regs) { #if defined(__i386__) regs->eip--; #elif defined(__x86_64__) regs->rip--; #else # error Unsupported architecture #endif } class Ptrace : public IEngine { public: Ptrace() : m_solibValid(false), m_solibFd(-1), m_ldPreloadString(NULL), m_envString(NULL) { m_breakpointId = 0; m_parentCpu = kcov_get_current_cpu(); kcov_tie_process_to_cpu(getpid(), m_parentCpu); } bool readMemory(uint8_t *dst, unsigned long addr, size_t bytes) { unsigned long aligned = getAligned(addr); unsigned long offs = addr - aligned; unsigned long shift = 8 * offs; unsigned long data = ptrace(PTRACE_PEEKTEXT, m_activeChild, aligned, 0); panic_if(bytes != 1, "Can only read 1 byte now"); *dst = (data & (0xffULL << shift)) >> shift; return true; } bool readProcessMemory(uint8_t *dst, void *start, size_t bytes) { panic_if (bytes != sizeof(unsigned long), "Can only read a word at a time now"); panic_if ((unsigned long)start & (sizeof(unsigned long) - 1), "Address must be aligned"); unsigned long data = ptrace(PTRACE_PEEKTEXT, m_activeChild, start, 0); memcpy(dst, &data, bytes); return true; } bool start(const char *executable) { m_breakpointToAddrMap.clear(); m_addrToBreakpointMap.clear(); m_instructionMap.clear(); /* Basic check first */ if (access(executable, X_OK) != 0) return false; 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, __library_data_size, kcov_solib_path.c_str()); std::string kcov_solib_env = "KCOV_SOLIB_PATH=" + kcov_solib_pipe_path; unlink(kcov_solib_pipe_path.c_str()); 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 (file_exists(kcov_solib_path.c_str())) putenv(m_ldPreloadString); putenv(m_envString); m_solibFd = open(kcov_solib_pipe_path.c_str(), O_RDONLY | O_NONBLOCK); unsigned int pid = IConfiguration::getInstance().getAttachPid(); bool res = false; if (pid != 0) res = attachPid(pid); else res = forkChild(executable); return res; } int setBreakpoint(unsigned long addr) { uint8_t data; int id; // There already? if (m_addrToBreakpointMap.find(addr) != m_addrToBreakpointMap.end()) return m_addrToBreakpointMap[addr]; if (readMemory(&data, addr, 1) == false) return -1; id = m_breakpointId++; m_breakpointToAddrMap[id] = addr; m_addrToBreakpointMap[addr] = id; m_instructionMap[addr] = data; kcov_debug(BP_MSG, "BP set at 0x%lx\n", addr); // Set the breakpoint writeByte(m_activeChild, addr, 0xcc); return id; } void clearAllBreakpoints() { for (breakpointToAddrMap_t::iterator it = m_breakpointToAddrMap.begin(); it != m_breakpointToAddrMap.end(); it++) clearBreakpoint(it->first); m_addrToBreakpointMap.clear(); m_addrToBreakpointMap.clear(); m_instructionMap.clear(); } bool clearBreakpoint(int id) { if (m_breakpointToAddrMap.find(id) == m_breakpointToAddrMap.end()) return false; unsigned long addr = m_breakpointToAddrMap[id]; panic_if(m_addrToBreakpointMap.find(addr) == m_addrToBreakpointMap.end(), "Breakpoint id, but no addr-to-id map!"); panic_if(m_instructionMap.find(addr) == m_instructionMap.end(), "Breakpoint found, but no instruction data at that point!"); // Clear the actual breakpoint instruction writeByte(m_activeChild, addr, m_instructionMap[addr]); return true; } void singleStep() { struct user_regs_struct regs; ptrace(PTRACE_GETREGS, m_activeChild, 0, ®s); // Step back one instruction arch_adjustPcAfterBreakpoint(®s); ptrace(PTRACE_SETREGS, m_activeChild, 0, ®s); } void checkSolibData() { if (m_solibValid) return; int r = read(m_solibFd, m_solibData, sizeof(m_solibData)); if (r <= 0) return; panic_if ((unsigned)r >= sizeof(m_solibData), "Too much solib data read"); struct phdr_data *p = phdr_data_unmarshal(m_solibData); 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; // Skip this very special library if (strstr(cur->name, "libkcov_sowrapper.so")) continue; IElf &elf = IElf::getInstance(); elf.addFile(cur->name, cur); elf.parse(); } m_solibValid = true; } const Event continueExecution() { Event out; int status; int who; int res; // Assume error out.type = ev_exit; out.data = -1; checkSolibData(); kcov_debug(PTRACE_MSG, "PT continuing %d\n", m_activeChild); res = ptrace(PTRACE_CONT, m_activeChild, 0, 0); if (res < 0) { kcov_debug(PTRACE_MSG, "PT error for %d: %d\n", m_activeChild, res); return out; } while (1) { who = waitpid(-1, &status, __WALL); if (who == -1) return out; m_children[who] = 1; m_activeChild = who; out.addr = getPc(m_activeChild); kcov_debug(PTRACE_MSG, "PT stopped 0x%08x\n", status); // A signal? if (WIFSTOPPED(status)) { int sig = WSTOPSIG(status); // A trap? if (sig == SIGTRAP) { out.type = ev_breakpoint; out.data = -1; // Breakpoint id addrToBreakpointMap_t::iterator it = m_addrToBreakpointMap.find(out.addr); if (it != m_addrToBreakpointMap.end()) out.data = it->second; kcov_debug(PTRACE_MSG, "PT BP at 0x%lx:%d\n", out.addr, out.data); if (out.data != -1) singleStep(); return out; } else if (sig == SIGSEGV || sig == SIGTERM || sig == SIGFPE || sig == SIGBUS || sig == SIGILL) { out.type = ev_crash; out.data = sig; return out; } else if ((status >> 16) == PTRACE_EVENT_CLONE || (status >> 16) == PTRACE_EVENT_FORK) { sig = 0; } else if (sig == SIGSTOP) { out.type = ev_breakpoint; out.data = -1; return out; } // No, deliver it directly kcov_debug(PTRACE_MSG, "PT signal %d at 0x%lx for %d\n", WSTOPSIG(status), out.addr, m_activeChild); ptrace(PTRACE_CONT, m_activeChild, 0, sig); continue; } // Thread died? if (WIFSIGNALED(status) || WIFEXITED(status)) { m_children.erase(who); if (m_children.size() == 0) { Event tmp = continueExecution(); out.type = ev_exit; out.data = WEXITSTATUS(status); if (tmp.type != ev_error) continue; return out; } continue; } out.type = ev_error; return out; } } std::string eventToName(Event ev) { switch (ev.type) { case ev_breakpoint: return std::string("breakpoint at 0x%lx", ev.addr); case ev_exit: return fmt("exit code %d", ev.data); case ev_crash: { 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 == 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"); } void kill() { ptrace(PTRACE_KILL, m_activeChild, 0, 0); ptrace(PTRACE_DETACH, m_activeChild, 0, 0); } private: 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; } execv(executable, argv); /* Exec failed */ return false; } /* Fork error? */ if (child < 0) { perror("fork"); return false; } m_child = m_activeChild = 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(PTRACE_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) { m_activeChild = pid; errno = 0; ptrace(PTRACE_ATTACH, m_activeChild, 0, 0); if (errno) { 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; } unsigned long getPcFromRegs(struct user_regs_struct *regs) { return arch_getPcFromRegs(regs); } unsigned long getPc(int pid) { struct user_regs_struct regs; memset(®s, 0, sizeof(regs)); ptrace(PTRACE_GETREGS, pid, 0, ®s); return getPcFromRegs(®s); } // Assume x86 with single-byte breakpoint instructions for now... void writeByte(int pid, unsigned long addr, uint8_t byte) { unsigned long aligned = getAligned(addr); unsigned long offs = addr - aligned; unsigned long shift = 8 * offs; unsigned long data = byte; unsigned long old_data; unsigned long val; old_data = ptrace(PTRACE_PEEKTEXT, pid, aligned, 0); val = (old_data & ~(0xffULL << shift)) | ((data & 0xff) << shift); ptrace(PTRACE_POKETEXT, pid, aligned, val); } unsigned long getAligned(unsigned long addr) { return (addr / sizeof(unsigned long)) * sizeof(unsigned long); } typedef std::unordered_map breakpointToAddrMap_t; typedef std::unordered_map addrToBreakpointMap_t; typedef std::unordered_map instructionMap_t; typedef std::unordered_map ChildMap_t; int m_breakpointId; instructionMap_t m_instructionMap; breakpointToAddrMap_t m_breakpointToAddrMap; addrToBreakpointMap_t m_addrToBreakpointMap; pid_t m_activeChild; pid_t m_child; ChildMap_t m_children; bool m_solibValid; int m_solibFd; uint8_t m_solibData[128 * 1024]; char *m_ldPreloadString; char *m_envString; int m_parentCpu; }; IEngine &IEngine::getInstance() { static Ptrace *instance; if (!instance) instance = new Ptrace(); return *instance; } kcov-11/src/reporter.cc000066400000000000000000000156161201363500200152000ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include using namespace kcov; #define KCOV_MAGIC 0x6b636f76 /* "kcov" */ #define KCOV_DB_VERSION 1 struct marshalHeaderStruct { uint32_t magic; uint32_t db_version; uint64_t checksum; }; class Reporter : public IReporter, public IElf::ILineListener, public ICollector::IListener { public: Reporter(IElf &elf, ICollector &collector) : m_elf(elf), m_collector(collector), m_filter(IFilter::getInstance()) { m_elf.registerLineListener(*this); m_collector.registerListener(*this); } bool lineIsCode(const char *file, unsigned int lineNr) { bool out; m_mutex.lock(); out = m_lines.find(LineId(file, lineNr)) != m_lines.end(); m_mutex.unlock(); return out; } LineExecutionCount getLineExecutionCount(const char *file, unsigned int lineNr) { unsigned int hits = 0; unsigned int possibleHits = 0; m_mutex.lock(); LineMap_t::iterator it = m_lines.find(LineId(file, lineNr)); if (it != m_lines.end()) { Line *line = it->second; hits = line->hits(); possibleHits = line->possibleHits(); } m_mutex.unlock(); return LineExecutionCount(hits, possibleHits); } ExecutionSummary getExecutionSummary() { unsigned int executedLines = 0; unsigned int nrLines = 0; m_mutex.lock(); for (LineMap_t::iterator it = m_lines.begin(); it != m_lines.end(); it++) { Line *cur = it->second; if (!m_filter.runFilters(cur->m_file)) continue; executedLines += !!cur->hits(); nrLines++; } m_mutex.unlock(); 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); m_mutex.lock(); for (LineMap_t::iterator it = m_lines.begin(); it != m_lines.end(); it++) { Line *cur = it->second; p = cur->marshal(p); } m_mutex.unlock(); *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(); m_mutex.lock(); // Should already be 0, but anyway for (AddrToLineMap_t::iterator it = m_addrToLine.begin(); it != m_addrToLine.end(); it++) it->second->clearHits(); for (size_t i = 0; i < n; i++) { unsigned long addr; unsigned int hits; p = Line::unMarshal(p, &addr, &hits); Line *line = m_addrToLine[addr]; if (!line) continue; if (!hits) continue; // Really an internal error, but let's not hang on corrupted data if (hits > line->possibleHits()) hits = line->possibleHits(); // Register all hits for this address while (hits--) line->registerHit(addr); } m_mutex.unlock(); return true; } virtual void stop() { // Otherwise the writer thread can hang here m_mutex.unlock(); } private: size_t getMarshalEntrySize() { return 2 * sizeof(uint64_t); } size_t getMarshalSize() { size_t out = 0; for (LineMap_t::iterator it = m_lines.begin(); it != m_lines.end(); it++) { Line *cur = it->second; out += cur->m_addrs.size(); } return out * getMarshalEntrySize() + sizeof(struct marshalHeaderStruct); } uint8_t *marshalHeader(uint8_t *p) { struct marshalHeaderStruct *hdr = (struct marshalHeaderStruct *)p; hdr->magic = KCOV_MAGIC; hdr->db_version = KCOV_DB_VERSION; hdr->checksum = m_elf.getChecksum(); return p + sizeof(struct marshalHeaderStruct); } uint8_t *unMarshalHeader(uint8_t *p) { struct marshalHeaderStruct *hdr = (struct marshalHeaderStruct *)p; if (hdr->magic != KCOV_MAGIC) return NULL; if (hdr->db_version != KCOV_DB_VERSION) return NULL; if (hdr->checksum != m_elf.getChecksum()) return NULL; return p + sizeof(struct marshalHeaderStruct); } /* Called when the ELF is parsed */ void onLine(const char *file, unsigned int lineNr, unsigned long addr) { LineId key(file, lineNr); m_mutex.lock(); LineMap_t::iterator it = m_lines.find(key); Line *line; if (it == m_lines.end()) { if (!file_exists(file)) goto out; line = new Line(key); m_lines[key] = line; } else { line = it->second; } line->addAddress(addr); m_addrToLine[addr] = line; out: m_mutex.unlock(); } /* Called during runtime */ void onBreakpoint(unsigned long addr) { m_mutex.lock(); AddrToLineMap_t::iterator it = m_addrToLine.find(addr); if (it != m_addrToLine.end()) { Line *line = it->second; line->registerHit(addr); } m_mutex.unlock(); } class LineId { public: LineId(const char *fileName, int nr) : m_file(fileName), m_lineNr(nr) { } bool operator==(const LineId &other) const { return (m_file == other.m_file) && (m_lineNr == other.m_lineNr); } std::string m_file; unsigned int m_lineNr; }; class LineIdHash { public: size_t operator()(const LineId &obj) const { return std::hash()(obj.m_file) ^ std::hash()(obj.m_lineNr); } }; class Line { public: typedef std::unordered_map AddrToHitsMap_t; Line(LineId id) : m_file(id.m_file), m_lineNr(id.m_lineNr) { } void addAddress(unsigned long addr) { m_addrs[addr] = 0; } unsigned int registerHit(unsigned long addr) { unsigned int out = !m_addrs[addr]; m_addrs[addr] = 1; return out; } void clearHits() { for (AddrToHitsMap_t::iterator it = m_addrs.begin(); it != m_addrs.end(); it++) it->second = 0; } unsigned int hits() { unsigned int out = 0; for (AddrToHitsMap_t::iterator it = m_addrs.begin(); it != m_addrs.end(); it++) out += it->second; return out; } unsigned int possibleHits() { return m_addrs.size(); } uint8_t *marshal(uint8_t *start) { uint64_t *data = (uint64_t *)start; for (AddrToHitsMap_t::iterator it = m_addrs.begin(); it != m_addrs.end(); it++) { // Address and number of hits *data++ = (uint64_t)it->first; *data++ = (uint64_t)it->second; } return (uint8_t *)data; } static uint8_t *unMarshal(uint8_t *p, unsigned long *outAddr, unsigned int *outHits) { uint64_t *data = (uint64_t *)p; *outAddr = *data++; *outHits = *data++; return (uint8_t *)data; } std::string m_file; unsigned int m_lineNr; AddrToHitsMap_t m_addrs; }; typedef std::unordered_map LineMap_t; typedef std::unordered_map AddrToLineMap_t; LineMap_t m_lines; AddrToLineMap_t m_addrToLine; std::mutex m_mutex; // Protects m_lines, m_addrToLine IElf &m_elf; ICollector &m_collector; IFilter &m_filter; }; IReporter &IReporter::create(IElf &elf, ICollector &collector) { return *new Reporter(elf, collector); } kcov-11/src/solib-parser/000077500000000000000000000000001201363500200154205ustar00rootroot00000000000000kcov-11/src/solib-parser/lib.c000066400000000000000000000021201201363500200163250ustar00rootroot00000000000000#ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #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; } void __attribute__((constructor))at_startup(void) { char *kcov_solib_path; void *p; ssize_t written; size_t sz; int fd; kcov_solib_path = getenv("KCOV_SOLIB_PATH"); if (!kcov_solib_path) return; fd = open(kcov_solib_path, O_WRONLY); if (fd < 0) { fprintf(stderr, "kcov-solib: Can't open %s\n", kcov_solib_path); return; } phdr_data = phdr_data_new(); dl_iterate_phdr(phdrCallback, NULL); p = phdr_data_marshal(phdr_data, &sz); written = write(fd, p, sz); if (written != sz) fprintf(stderr, "kcov-solib: Can't write to solib FIFO (%d)\n", written); free(p); close(fd); // Clear from the environment putenv("KCOV_SOLIB_PATH"); } kcov-11/src/solib-parser/phdr_data.c000066400000000000000000000035101201363500200175110ustar00rootroot00000000000000#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 struct phdr_data *phdr_data_new(void) { struct phdr_data *p = malloc(sizeof(struct phdr_data)); p->magic = KCOV_MAGIC; p->version = KCOV_SOLIB_VERSION; p->n_entries = 0; return p; } void phdr_data_add(struct phdr_data **p_in, struct dl_phdr_info *info) { struct phdr_data *p = *p_in; uint32_t n = p->n_entries + 1; uint32_t curEntry = p->n_entries; int phdr; p = realloc(p, sizeof(struct phdr_data) + n * sizeof(struct phdr_data_entry)); if (!p) { fprintf(stderr, "Can't allocate phdr data"); return; } 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; *p_in = p; } 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; return out; } kcov-11/src/utils.cc000066400000000000000000000070321201363500200144670ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include "utils.hh" int g_kcov_debug_mask; extern int file_is_elf(const char *filename) { Elf32_Ehdr hdr; int ret = 0; int fd; fd = open(filename, O_RDONLY); if (fd < 0) return ret; /* Compare the header with the ELF magic */ if (read(fd, &hdr, sizeof(hdr)) == sizeof(hdr)) ret = memcmp(hdr.e_ident, ELFMAG, strlen(ELFMAG)) == 0; close(fd); return ret; } void *read_file(size_t *out_size, const char *fmt, ...) { struct stat buf; char path[2048]; va_list ap; void *data; size_t size; FILE *f; 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!"); if (lstat(path, &buf) < 0) return NULL; size = buf.st_size; data = xmalloc(size + 2); /* NULL-terminate, if used as string */ f = fopen(path, "r"); if (!f) { free(data); return NULL; } if (fread(data, 1, size, f) != size) { free(data); data = NULL; } fclose(f); *out_size = size; return data; } int write_file(const void *data, size_t len, const char *fmt, ...) { char path[2048]; va_list ap; FILE *fp; int ret = 0; /* Create the filename */ va_start(ap, fmt); vsnprintf(path, 2048, fmt, ap); va_end(ap); fp = fopen(path, "w"); if (!fp) return -1; if (fwrite(data, sizeof(uint8_t), len, fp) != len) ret = -1; fclose(fp); return ret; } const char *dir_concat(const char *dir, const char *filename) { size_t len; char *out; if (dir == NULL) return xstrdup(filename); len = strlen(dir) + strlen(filename) + 4; out = (char *)xmalloc(len); xsnprintf(out, len, "%s/%s", dir, filename); return out; } const char *get_home(void) { return getenv("HOME"); } int file_exists(const char *path) { struct stat st; return lstat(path, &st) == 0; } 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); } kcov-11/src/writer-base.cc000066400000000000000000000056621201363500200155620ustar00rootroot00000000000000#include "writer-base.hh" #include #include using namespace kcov; struct summaryStruct { uint32_t nLines; uint32_t nExecutedLines; char name[256]; }; int WriterBase::File::fileNameCount; WriterBase::WriterBase(IElf &elf, IReporter &reporter, IOutputHandler &output) : m_elf(elf), m_reporter(reporter) { m_commonPath = "not set"; m_elf.registerLineListener(*this); } WriterBase::File::File(const char *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_outFileName = fmt("%s.%d.html", m_fileName.c_str(), fileNameCount); fileNameCount++; readFile(filename); } void WriterBase::File::readFile(const char *filename) { FILE *fp = fopen(filename, "r"); unsigned int lineNr = 1; panic_if(!fp, "Can't open %s", filename); while (1) { char *lineptr = NULL; ssize_t res; size_t n; res = getline(&lineptr, &n, fp); if (res < 0) break; m_lineMap[lineNr] = std::string(lineptr); free((void *)lineptr); lineNr++; } m_lastLineNr = lineNr; fclose(fp); } void WriterBase::onLine(const char *file, unsigned int lineNr, unsigned long addr) { m_fileMutex.lock(); if (m_files.find(std::string(file)) != m_files.end()) goto out; if (!file_exists(file)) goto out; m_files[std::string(file)] = new File(file); out: m_fileMutex.unlock(); } void *WriterBase::marshalSummary(IReporter::ExecutionSummary &summary, std::string &name, size_t *sz) { struct summaryStruct *p; p = (struct summaryStruct *)xmalloc(sizeof(struct summaryStruct)); memset(p, 0, sizeof(*p)); p->nLines = summary.m_lines; p->nExecutedLines = 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; summary.m_lines = p->nLines; summary.m_executedLines = p->nExecutedLines; name = std::string(p->name); return true; } void WriterBase::setupCommonPaths() { m_fileMutex.lock(); for (FileMap_t::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; } } m_fileMutex.unlock(); } void WriterBase::stop() { // Called from the main process, which must release this on stop. m_fileMutex.unlock(); } kcov-11/src/writer-base.hh000066400000000000000000000024551201363500200155710ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include namespace kcov { class IElf; class IReporter; class IOutputHandler; class WriterBase : public IElf::ILineListener, public IWriter { protected: WriterBase(IElf &elf, IReporter &reporter, IOutputHandler &output); class File { public: typedef std::unordered_map LineMap_t; File(const char *filename); std::string m_name; std::string m_fileName; std::string m_outFileName; LineMap_t m_lineMap; unsigned int m_codeLines; unsigned int m_executedLines; unsigned int m_lastLineNr; static int fileNameCount; private: void readFile(const char *filename); }; typedef std::unordered_map FileMap_t; /* Called when the ELF is parsed */ void onLine(const char *file, unsigned int lineNr, unsigned long addr); void *marshalSummary(IReporter::ExecutionSummary &summary, std::string &name, size_t *sz); bool unMarshalSummary(void *data, size_t sz, IReporter::ExecutionSummary &summary, std::string &name); void setupCommonPaths(); void stop(); IElf &m_elf; IReporter &m_reporter; FileMap_t m_files; std::mutex m_fileMutex; std::string m_commonPath; }; } kcov-11/tests/000077500000000000000000000000001201363500200133715ustar00rootroot00000000000000kcov-11/tests/CMakeLists.txt000066400000000000000000000022241201363500200161310ustar00rootroot00000000000000cmake_minimum_required (VERSION 2.6) project (kcov-tests) set (main_tests_SRCS main.cc subdir/file.c subdir2/file.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 (recursive-ptrace_SRCS recursive-ptrace/main.cc ) set (shared_library_test_SRCS shared-library/main.c ) set (test_popen_SRCS popen/test-popen.c ) add_library(shared_library SHARED 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(shared_library_test ${shared_library_test_SRCS}) add_executable(argv_dependent ${argv_dependent_SRCS}) add_executable(test_popen ${test_popen_SRCS}) target_link_libraries(shared_library_test shared_library) add_custom_target(tests-stripped ALL COMMAND strip -o tests-stripped main-tests) kcov-11/tests/argv-dependent.c000066400000000000000000000004021201363500200164340ustar00rootroot00000000000000#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-11/tests/fork/000077500000000000000000000000001201363500200143325ustar00rootroot00000000000000kcov-11/tests/fork/fork-no-wait.c000066400000000000000000000010441201363500200170120ustar00rootroot00000000000000#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-11/tests/fork/fork.c000066400000000000000000000013401201363500200154350ustar00rootroot00000000000000#include #include #include #include #include #include #include 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-11/tests/include/000077500000000000000000000000001201363500200150145ustar00rootroot00000000000000kcov-11/tests/include/header.h000066400000000000000000000002461201363500200164170ustar00rootroot00000000000000#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-11/tests/main.cc000066400000000000000000000003771201363500200146330ustar00rootroot00000000000000#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-11/tests/popen/000077500000000000000000000000001201363500200145125ustar00rootroot00000000000000kcov-11/tests/popen/test-popen.c000066400000000000000000000016421201363500200167570ustar00rootroot00000000000000#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 %u 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-11/tests/recursive-ptrace/000077500000000000000000000000001201363500200166545ustar00rootroot00000000000000kcov-11/tests/recursive-ptrace/main.cc000066400000000000000000000020071201363500200201060ustar00rootroot00000000000000#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-11/tests/robot-framework/000077500000000000000000000000001201363500200165115ustar00rootroot00000000000000kcov-11/tests/robot-framework/kcov-tests.txt000066400000000000000000000112231201363500200213530ustar00rootroot00000000000000*** 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} ${out} Should be equal as integers ${rc} 1 wrong-arguments ${rc}= Run and return RC ${kcov} --abcd=efg ${out} tests-stripped Should be equal as integers ${rc} 1 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 ${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 shared-library ${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-filter-out ${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 main-test ${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 Contain ${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 file_c 7 Should Contain ${output} 0 accumulate-data ${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 ${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 kcov-11/tests/shared-library/000077500000000000000000000000001201363500200163015ustar00rootroot00000000000000kcov-11/tests/shared-library/main.c000066400000000000000000000002101201363500200173620ustar00rootroot00000000000000#include extern int vobb(int a); int main(int argc, const char *argv[]) { printf("In main\n"); vobb(argc); return 0; } kcov-11/tests/shared-library/solib.c000066400000000000000000000001301201363500200175470ustar00rootroot00000000000000#include int vobb(int a) { printf("In shared library\n"); return a + 1; } kcov-11/tests/subdir/000077500000000000000000000000001201363500200146615ustar00rootroot00000000000000kcov-11/tests/subdir/file.c000066400000000000000000000001441201363500200157430ustar00rootroot00000000000000#include #include "../include/header.h" void tjoho(int a) { if (a) printf("Hej\n"); } kcov-11/tests/subdir2/000077500000000000000000000000001201363500200147435ustar00rootroot00000000000000kcov-11/tests/subdir2/file.c000066400000000000000000000001451201363500200160260ustar00rootroot00000000000000#include #include "../include/header.h" void tjoho2(int a) { if (a) printf("Hej\n"); } kcov-11/tests/test-module/000077500000000000000000000000001201363500200156335ustar00rootroot00000000000000kcov-11/tests/test-module/Makefile000066400000000000000000000003351201363500200172740ustar00rootroot00000000000000KDIR := /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-11/tests/test-module/test_module.c000066400000000000000000000045311201363500200203260ustar00rootroot00000000000000/* * * 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 /* * The sequence iterator functions. We simply use the count of the * next line as our internal position. */ static void *ct_seq_start(struct seq_file *s, loff_t *pos) { loff_t *spos = kmalloc(sizeof(loff_t), GFP_KERNEL); if (! spos) return NULL; *spos = *pos; return spos; } static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos) { loff_t *spos = (loff_t *) v; *pos = ++(*spos); return spos; } static void ct_seq_stop(struct seq_file *s, void *v) { kfree (v); } /* * The show function. */ static int ct_seq_show(struct seq_file *s, void *v) { loff_t *spos = (loff_t *) v; seq_printf(s, "%Ld\n", *spos); return 0; } /* * Tie them all together into a set of seq_operations. */ static struct seq_operations ct_seq_ops = { .start = ct_seq_start, .next = ct_seq_next, .stop = ct_seq_stop, .show = ct_seq_show }; /* * Time to set up the file operations for our /proc file. In this case, * all we need is an open function which sets up the sequence ops. */ static int ct_open(struct inode *inode, struct file *file) { return seq_open(file, &ct_seq_ops); }; /* * The file operations structure contains our open function along with * set of the canned seq_ ops. */ static struct file_operations ct_file_ops = { .owner = THIS_MODULE, .open = ct_open, .read = seq_read, .llseek = seq_lseek, .release = seq_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) { printk(KERN_INFO "Exiting test module!\n"); } module_init(test_init); module_exit(test_exit); MODULE_AUTHOR("Simon Kagstrom "); MODULE_DESCRIPTION("Test"); MODULE_LICENSE("GPL"); kcov-11/tests/tools/000077500000000000000000000000001201363500200145315ustar00rootroot00000000000000kcov-11/tests/tools/lookup-xml-node.py000077500000000000000000000024771201363500200201520ustar00rootroot00000000000000#!/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-11/tests/unit-tests/000077500000000000000000000000001201363500200155105ustar00rootroot00000000000000kcov-11/tests/unit-tests/CMakeLists.txt000066400000000000000000000031521201363500200202510ustar00rootroot00000000000000cmake_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 (LibDwarf REQUIRED) find_package (GTest REQUIRED) find_package (GMock REQUIRED) # ==================================== # project name and version # ==================================== project (unit-test) set (TGT ut) set (${TGT}_SRCS ../../src/cobertura-writer.cc ../../src/collector.cc ../../src/elf.cc ../../src/output-handler.cc ../../src/reporter.cc ../../src/utils.cc ../../src/html-writer.cc ../../src/writer-base.cc main.cc tests-collector.cc tests-elf.cc tests-filter.cc tests-reporter.cc tests-writer.cc tests-configuration.cc ) set (CMAKE_BUILD_TYPE debug) set (CMAKE_CXX_FLAGS "-std=c++0x -Wall -D_GLIBCXX_USE_NANOSLEEP") include_directories( ../../src/include/ ${GMOCK_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS} ${LIBCRPCUT_INCLUDE_DIRS} ${LIBELF_INCLUDE_DIRS} ${LIBDWARF_INCLUDE_DIRS} ) link_directories (/home/ska/local/lib) add_executable (${TGT} ${${TGT}_SRCS}) target_link_libraries(${TGT} ${LIBCRPCUT_LIBRARIES} ${GTEST_BOTH_LIBRARIES} ${GMOCK_BOTH_LIBRARIES} ${LIBDWARF_LIBRARIES} ${LIBELF_LIBRARIES} dl 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-11/tests/unit-tests/main.cc000066400000000000000000000004371201363500200167470ustar00rootroot00000000000000#include "test.hh" #include "mocks/mock-engine.hh" using namespace kcov; IEngine &IEngine::getInstance() { static MockEngine *instance; if (!instance) instance = new MockEngine(); return *instance; } int main(int argc, const char *argv[]) { return crpcut::run(argc, argv); } kcov-11/tests/unit-tests/mocks/000077500000000000000000000000001201363500200166245ustar00rootroot00000000000000kcov-11/tests/unit-tests/mocks/mock-engine.hh000066400000000000000000000007341201363500200213450ustar00rootroot00000000000000#pragma once #include "../test.hh" #include using namespace kcov; class MockEngine : public IEngine { public: MockEngine() { } MOCK_METHOD1(setBreakpoint, int(unsigned long addr)); MOCK_METHOD1(clearBreakpoint, bool(int id)); MOCK_METHOD0(clearAllBreakpoints, void()); MOCK_METHOD1(start, bool(const char *executable)); MOCK_METHOD0(continueExecution, const Event()); MOCK_METHOD1(eventToName, std::string(Event ev)); MOCK_METHOD0(kill, void()); }; kcov-11/tests/unit-tests/second-source.c000066400000000000000000000002021201363500200204170ustar00rootroot00000000000000int mibb(int a) { // "Hej" and 'hej' means HTML escaping! \ is another escape if (a < 5 && a > 3) return 0; return a - 1; } kcov-11/tests/unit-tests/test-source.c000066400000000000000000000004021201363500200201250ustar00rootroot00000000000000// 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-11/tests/unit-tests/test.hh000066400000000000000000000001601201363500200170050ustar00rootroot00000000000000#pragma once #include #include #include using namespace testing; kcov-11/tests/unit-tests/tests-collector.cc000066400000000000000000000031161201363500200211460ustar00rootroot00000000000000#include "test.hh" #include "mocks/mock-engine.hh" #include #include #include #include using namespace kcov; class MockCollectorListener : public ICollector::IListener { public: MOCK_METHOD1(onBreakpoint, void(unsigned long addr)); }; TEST(collector) { MockEngine &engine = (MockEngine &)IEngine::getInstance(); MockCollectorListener listener; IElf *elf; char filename[1024]; bool res; sprintf(filename, "%s/test-binary", crpcut::get_start_dir()); elf = IElf::open(filename); ASSERT_TRUE(elf); ICollector &collector = ICollector::create(elf); collector.registerListener(listener); EXPECT_CALL(engine, setBreakpoint(_)) .Times(AtLeast(1)) ; res = elf->parse(); ASSERT_TRUE(res); int v; EXPECT_CALL(engine, start(_)) .Times(Exactly(1)) .WillRepeatedly(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; EXPECT_CALL(listener, onBreakpoint(1)) .Times(Exactly(1)) ; EXPECT_CALL(engine, continueExecution()) .Times(Exactly(2)) .WillOnce(Return(evOnce)) .WillRepeatedly(Return(evExit)) ; EXPECT_CALL(engine, clearBreakpoint(evOnce.data)) .Times(Exactly(1)) ; v = collector.run(); ASSERT_TRUE(v == evExit.data); evOnce.type = ev_error; // Test error EXPECT_CALL(engine, start(_)) .Times(Exactly(1)) .WillRepeatedly(Return(0)) ; EXPECT_CALL(engine, continueExecution()) .Times(Exactly(1)) .WillRepeatedly(Return(evOnce)) ; v = collector.run(); ASSERT_TRUE(v == -1); } kcov-11/tests/unit-tests/tests-configuration.cc000066400000000000000000000073651201363500200220410ustar00rootroot00000000000000#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); } TEST(configuration) { 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->getBinaryName() == "test-binary"); ASSERT_TRUE(conf->getBinaryPath() == std::string(crpcut::get_start_dir()) + "/"); res = runParse("-h"); ASSERT_FALSE(res); res = runParse("-s vbb"); ASSERT_FALSE(res); ASSERT_TRUE(conf->getSortType() == IConfiguration::FILENAME); ASSERT_TRUE(conf->m_lowLimit == 25U); ASSERT_TRUE(conf->m_highLimit == 75U); res = runParse(fmt("-l 30,60 /tmp/vobb --sort-type=p %s", filename.c_str())); ASSERT_TRUE(res); ASSERT_TRUE(conf->getSortType() == IConfiguration::PERCENTAGE); ASSERT_TRUE(conf->m_lowLimit == 30U); ASSERT_TRUE(conf->m_highLimit == 60U); res = runParse(fmt("-l 20 /tmp/vobb --sort-type=p %s", filename.c_str())); ASSERT_TRUE(!res); res = runParse(fmt("-l 40,50,90 /tmp/vobb --sort-type=p %s", filename.c_str())); ASSERT_TRUE(!res); res = runParse(fmt("-l 35,hej /tmp/vobb --sort-type=p %s", filename.c_str())); ASSERT_TRUE(!res); res = runParse(fmt("-l yo,89 /tmp/vobb --sort-type=p %s", filename.c_str())); ASSERT_TRUE(!res); res = runParse(fmt("/tmp/vobb --sort-type=u %s", filename.c_str())); ASSERT_TRUE(res); ASSERT_TRUE(conf->getSortType() == IConfiguration::UNCOVERED_LINES); res = runParse(fmt("/tmp/vobb --sort-type=l %s", filename.c_str())); ASSERT_TRUE(res); ASSERT_TRUE(conf->getSortType() == IConfiguration::FILE_LENGTH); 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->getOnlyIncludePath().size() == 2U); ASSERT_TRUE(conf->getOnlyIncludePath()[0] == "/a"); ASSERT_TRUE(conf->getOnlyIncludePath()[1] == "/b/c"); ASSERT_TRUE(conf->getExcludePattern().size() == 1U); ASSERT_TRUE(conf->getExcludePattern()[0] == "d/e/f"); res = runParse(fmt("--include-path=~/a /tmp/vobb %s", filename.c_str())); ASSERT_TRUE(res); ASSERT_TRUE(conf->getOnlyIncludePath().size() == 1U); ASSERT_TRUE(conf->getOnlyIncludePath()[0] == fmt("%s/a", get_home())); ASSERT_TRUE(conf->getAttachPid() == 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->getAttachPid() == 100U); ASSERT_TRUE(g_kcov_debug_mask == 0); res = runParse(fmt("--debug=7 /tmp/vobb %s", filename.c_str())); ASSERT_TRUE(res); ASSERT_TRUE(g_kcov_debug_mask == 7); } kcov-11/tests/unit-tests/tests-elf.cc000066400000000000000000000023221201363500200177240ustar00rootroot00000000000000#include "test.hh" #include #include #include #include using namespace kcov; class FunctionListener : public IElf::ILineListener { public: virtual void onLine(const char *file, unsigned int lineNr, unsigned long addr) { m_lineMap[constructString(file, lineNr)]++; } static std::string constructString(const char *file, int nr) { char *c_str = (char *)xmalloc(strlen(file) + 20); const char *name = strrchr(file, '/'); 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; IElf *elf = IElf::open("not-found"); ASSERT_TRUE(!elf); sprintf(filename, "%s/Makefile", crpcut::get_start_dir()); elf = IElf::open(filename); ASSERT_TRUE(!elf); sprintf(filename, "%s/test-binary", crpcut::get_start_dir()); elf = IElf::open(filename); ASSERT_TRUE(elf); elf->registerLineListener(listener); res = elf->parse(); ASSERT_TRUE(res == true); std::string str = FunctionListener::constructString("/test-source.c", 8); ASSERT_TRUE(listener.m_lineMap[str] > 0); } kcov-11/tests/unit-tests/tests-filter.cc000066400000000000000000000041471201363500200204520ustar00rootroot00000000000000#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::getInstance(); 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-11/tests/unit-tests/tests-reporter.cc000066400000000000000000000102541201363500200210230ustar00rootroot00000000000000#include "test.hh" #include #include #include #include #include #include using namespace kcov; #define KCOV_MAGIC 0x6b636f76 /* "kcov" */ #define KCOV_DB_VERSION 1 struct marshalHeaderStruct { uint32_t magic; uint32_t db_version; uint64_t checksum; }; class MockCollector : public ICollector { public: MOCK_METHOD1(registerListener, void(IListener &listener)); MOCK_METHOD0(prepare, int()); MOCK_METHOD0(run, int()); MOCK_METHOD0(stop, void()); void mockRegisterListener(IListener &listener) { m_listener = &listener; } IListener *m_listener; }; class ElfListener : public IElf::ILineListener { public: void onLine(const char *file, unsigned int lineNr, unsigned long addr) { // Just store the lastest to have something m_lineToAddr[lineNr] = addr; if (m_file == "") m_file = std::string(file); } std::string m_file; std::unordered_map m_lineToAddr; }; TEST(reporter) { ElfListener elfListener; IElf *elf; bool res; char filename[1024]; sprintf(filename, "%s/test-binary", crpcut::get_start_dir()); elf = IElf::open(filename); ASSERT_TRUE(elf); elf->registerLineListener(elfListener); MockCollector collector; EXPECT_CALL(collector, registerListener(_)) .Times(Exactly(1)) .WillRepeatedly(Invoke(&collector, &MockCollector::mockRegisterListener)) ; IReporter &reporter = IReporter::create(*elf, collector); IReporter::ExecutionSummary summary = reporter.getExecutionSummary(); ASSERT_TRUE(summary.m_lines == 0U); ASSERT_TRUE(summary.m_executedLines == 0U); 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->onBreakpoint(100); 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->onBreakpoint(elfListener.m_lineToAddr[19]); 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->onBreakpoint(elfListener.m_lineToAddr[19]); 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->onBreakpoint(elfListener.m_lineToAddr[16]); 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->onBreakpoint(elfListener.m_lineToAddr[17]); 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); // Unmarshal resets this summary = reporter.getExecutionSummary(); ASSERT_TRUE(summary.m_executedLines == 2U); lc = reporter.getLineExecutionCount(elfListener.m_file.c_str(), 17); ASSERT_TRUE(lc.m_hits == 0U); free(data); } kcov-11/tests/unit-tests/tests-writer.cc000066400000000000000000000113711201363500200204760ustar00rootroot00000000000000#include "test.hh" #include #include #include #include #include #include #include #include #include #include #include #include "../../src/html-writer.hh" #include "../../src/cobertura-writer.hh" using namespace kcov; class MockReporter : public IReporter { public: MOCK_METHOD2(lineIsCode, bool(const char *file, unsigned int lineNr)); MOCK_METHOD2(getLineExecutionCount, LineExecutionCount(const char *file, unsigned int lineNr)); MOCK_METHOD0(getExecutionSummary, ExecutionSummary()); MOCK_METHOD1(marshal, void *(size_t *szOut)); MOCK_METHOD2(unMarshal, bool(void *data, size_t sz)); MOCK_METHOD0(stop, void()); void *mockMarshal(size_t *outSz) { void *out = malloc(32); *outSz = 32; return out; } }; 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)) { IElf *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"); sprintf(filename, "%s/test-binary", crpcut::get_start_dir()); elf = IElf::open(filename); ASSERT_TRUE(elf); const char *argv[] = {NULL, outDir.c_str(), filename}; IConfiguration &conf = IConfiguration::getInstance(); res = conf.parse(3, argv); ASSERT_TRUE(res); EXPECT_CALL(reporter, lineIsCode(_,_)) .Times(AtLeast(1)) .WillRepeatedly(Return(true)) ; EXPECT_CALL(reporter, lineIsCode(_,7)) .Times(AtLeast(4)) // Both files .WillRepeatedly(Return(false)) ; EXPECT_CALL(reporter, getLineExecutionCount(_,_)) .Times(AtLeast(1)) .WillRepeatedly(Return(def)) ; EXPECT_CALL(reporter, getLineExecutionCount(_,8)) .Times(AtLeast(3)) .WillRepeatedly(Return(partial)) ; EXPECT_CALL(reporter, getLineExecutionCount(_,11)) .Times(AtLeast(2)) .WillRepeatedly(Return(full)) ; EXPECT_CALL(reporter, getExecutionSummary()) .Times(AtLeast(3)) .WillRepeatedly(Return(summary)) ; IOutputHandler &output = IOutputHandler::create(reporter); IWriter &writer = createHtmlWriter(*elf, reporter, output); IWriter &coberturaWriter = createCoberturaWriter(*elf, reporter, output); output.registerWriter(writer); output.registerWriter(coberturaWriter); EXPECT_CALL(reporter, unMarshal(_,_)) .Times(Exactly(1)) .WillOnce(Return(true)) ; res = elf->parse(); ASSERT_TRUE(res == true); output.start(); std::this_thread::sleep_for(std::chrono::milliseconds(500)); EXPECT_CALL(reporter, marshal(_)) .Times(AtLeast(1)) .WillRepeatedly(Invoke(&reporter, &MockReporter::mockMarshal)) ; output.stop(); 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(); std::this_thread::sleep_for(std::chrono::milliseconds(500)); output.stop(); } TEST(writerSameName, DEADLINE_REALTIME_MS(20000)) { IElf *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"); sprintf(filename, "%s/same-name-test", crpcut::get_start_dir()); elf = IElf::open(filename); ASSERT_TRUE(elf); const char *argv[] = {NULL, outDir.c_str(), filename}; IConfiguration &conf = IConfiguration::getInstance(); res = conf.parse(3, argv); ASSERT_TRUE(res); EXPECT_CALL(reporter, lineIsCode(_,_)) .Times(AtLeast(1)) .WillRepeatedly(Return(true)) ; EXPECT_CALL(reporter, getLineExecutionCount(_,_)) .Times(AtLeast(1)) .WillRepeatedly(Return(def)) ; EXPECT_CALL(reporter, getExecutionSummary()) .Times(AtLeast(2)) .WillRepeatedly(Return(summary)) ; IOutputHandler &output = IOutputHandler::create(reporter); IWriter &writer = createHtmlWriter(*elf, reporter, output); output.registerWriter(writer); res = elf->parse(); ASSERT_TRUE(res == true); output.start(); std::this_thread::sleep_for(std::chrono::milliseconds(500)); EXPECT_CALL(reporter, marshal(_)) .Times(AtLeast(1)) .WillRepeatedly(Invoke(&reporter, &MockReporter::mockMarshal)) ; output.stop(); int cnt = filePatternInDir((outDir + "/same-name-test").c_str(), "html"); ASSERT_TRUE(cnt == 4); // index.html + 3 source files }