Skip to content

Commit

Permalink
Issue SimonKagstrom#92: system-mode: Implement system mode recording …
Browse files Browse the repository at this point in the history
…via dyninst

Instruments binaries using dyninst.
  • Loading branch information
SimonKagstrom committed Apr 18, 2017
1 parent ac63ebf commit e43715b
Show file tree
Hide file tree
Showing 16 changed files with 1,525 additions and 14 deletions.
5 changes: 4 additions & 1 deletion INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@ and (optional) binutils and libiberty to build kcov. Note that elfutils is
found in multiple variants, and at least in RH/Centos/Fedora you'll need
elfutils-devel and *not* elfutils-libelf-devel.

On Linux, if [dyninst](http://www.dyninst.org) is present, kcov will also be
able to do full-system instrumentation.

Ubuntu
------
Install binutils-dev libcurl4-openssl-dev zlib1g-dev libdw-dev libiberty-dev


Fedora / Centos / RHEL
----------------------
Install elfutils-libelf-devel libcurl-devel binutils-devel elfutils-devel
Install elfutils-libelf-devel libcurl-devel binutils-devel elfutils-devel dyninst-devel

Mac OS X
--------
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ after_success:

the -s /path/to/outdir part can be skipped if kcov produces output in the current directory.

Full-system instrumentation
---------------------------
Using [dyninst](http://www.dyninst.org), Kcov can instrument all binaries with very low overhead for embedded systems.
Refer to the [full system instrumentation](doc/full-system-instrumentation.md) documentation for details.

More information
----------------
kcov is written by Simon Kagstrom <[email protected]> and more
Expand Down
58 changes: 58 additions & 0 deletions cmake/FindDyninst.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# - Try to find dyninst
# Once done this will define
#
# DYNINST_FOUND - system has dyninst
# DYNINST_INCLUDE_DIRS - the dyninst include directory
# DYNINST_LIBRARIES - Link these to use dyninst
# DYNINST_DEFINITIONS - Compiler switches required for using dyninst
#

if (DYNINST_LIBRARIES AND DYNINST_INCLUDE_DIRS)
set (Dyninst_FIND_QUIETLY TRUE)
endif (DYNINST_LIBRARIES AND DYNINST_INCLUDE_DIRS)

find_path (DYNINST_INCLUDE_DIR
NAMES
BPatch.h
PATHS
/usr/include/dyninst
/usr/local/include/dyninst
/opt/local/include/dyninst
/sw/include/dyninst
/sw/include/dyninst
/usr/local/include
ENV CPATH) # PATH and INCLUDE will also work
if (DYNINST_INCLUDE_DIR)
set (DYNINST_INCLUDE_DIRS ${DYNINST_INCLUDE_DIR})
endif (DYNINST_INCLUDE_DIR)

find_library (DYNINST_LIBRARIES
NAMES
dyninstAPI
PATHS
/usr/lib
/usr/lib64
/usr/local/lib
/opt/local/lib
/usr/local/lib64
/opt/local/lib64
/sw/lib
/usr/lib/dyninst
/usr/lib64/dyninst
/usr/local/lib/dyninst
/opt/local/lib/dyninst
/usr/local/lib64/dyninst
/opt/local/lib64/dyninst
ENV LIBRARY_PATH # PATH and LIB will also work
ENV LD_LIBRARY_PATH)

include (FindPackageHandleStandardArgs)


# handle the QUIETLY and REQUIRED arguments and set DYNINST_FOUND to TRUE
# if all listed variables are TRUE
FIND_PACKAGE_HANDLE_STANDARD_ARGS(DYNINST DEFAULT_MSG
DYNINST_LIBRARIES
DYNINST_INCLUDE_DIR)

mark_as_advanced(DYNINST_INCLUDE_DIR DYNINST_LIBRARIES)
22 changes: 22 additions & 0 deletions doc/full-system-instrumentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
## *Full system instrumentation with kcov*
Using [dyninst](http://www.dyninst.org), Kcov can instrument all binaries with very low overhead for embedded systems.

Instrumenting binaries for a target system
------------------------------------------
If your binaries (with debug symbols) are in e.g., sysroot, you can instrument binaries using

```
kcov-system --system-record /tmp/out-sysroot sysroot
```

After this finishes, build your filesystem (squashfs etc) using the instrumented binaries
and install and run on your target.


Creating a report from the collected data
-----------------------------------------
Copy `/tmp/kcov-data` from your target system and run

```
kcov-system --system-report /tmp/kcov /tmp/kcov-data
```
6 changes: 6 additions & 0 deletions doc/kcov.1
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ executed as /bin/bash). Does not work well on some systems, so the default is no
.TP
\fB\-\-replace\-src\-path\fP=\fIP1\fP:\fIP2\fP
Replace source file path P1 with P2, if found.
.TP
\fB\-\-system\-record
Perform full-system instrumentation on a sysroot, outputting patched binaries which collect coverage data
.TP
\fB\-\-system\-report
Produce coverage output for a full-system coverage run.
.RE
.SH EXAMPLES
.PP
Expand Down
87 changes: 86 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
${CMAKE_CURRENT_SOURCE_DIR}/../../cmake)
find_package (Threads)
find_package (Bfd)
find_package (Dyninst)

include(TargetArch)
target_architecture(CMAKE_TARGET_ARCHITECTURES)
Expand Down Expand Up @@ -51,6 +52,9 @@ set (DISASSEMBLER_SRCS
parsers/dummy-disassembler.cc
)

set (DYNINST_SRCS
)

set (HAS_LIBBFD "0")

if("${CMAKE_TARGET_ARCHITECTURES}" STREQUAL "i386" OR "${CMAKE_TARGET_ARCHITECTURES}" STREQUAL "x86_64")
Expand All @@ -67,6 +71,13 @@ if("${CMAKE_TARGET_ARCHITECTURES}" STREQUAL "i386" OR "${CMAKE_TARGET_ARCHITECTU
endif()
endif()


if(DYNINST_FOUND)
set (DYNINST_SRCS
engines/dyninst-engine.cc
)
endif()

set (coveralls_SRCS writers/coveralls-writer.cc)

if ("${KCOV_STATIC_BUILD}" STREQUAL "1")
Expand All @@ -84,6 +95,7 @@ set (ELF_SRCS
set (MACHO_SRCS
)
set (SOLIB_generated )
set (DYNINST_SOLIB_generated )

# Linux-specific sources
if (${CMAKE_SYSTEM_NAME} MATCHES Linux)
Expand Down Expand Up @@ -131,6 +143,7 @@ set (${KCOV}_SRCS
configuration.cc
engine-factory.cc
engines/bash-engine.cc
engines/dyninst-file-format.cc
engines/gcov-engine.cc
engines/python-engine.cc
filter.cc
Expand All @@ -141,7 +154,7 @@ set (${KCOV}_SRCS
${DISASSEMBLER_SRCS}
parser-manager.cc
reporter.cc
source-file-cache.cc
source-file-cache.cc
utils.cc
writers/cobertura-writer.cc
writers/json-writer.cc
Expand Down Expand Up @@ -170,6 +183,51 @@ set (${KCOV}_SRCS
include/phdr_data.h
)

# Should be some better way of doing this...
set (KCOV_DYNINST_SRCS
capabilities.cc
collector.cc
configuration.cc
dummy-solib-handler.cc
engine-factory.cc
engines/bash-engine.cc
engines/dyninst-engine.cc
engines/dyninst-file-format.cc
engines/gcov-engine.cc
engines/python-engine.cc
filter.cc
gcov.cc
main.cc
merge-file-parser.cc
output-handler.cc
parser-manager.cc
reporter.cc
source-file-cache.cc
utils.cc
writers/cobertura-writer.cc
writers/json-writer.cc
${coveralls_SRCS}
writers/html-writer.cc
writers/sonarqube-xml-writer.cc
writers/writer-base.cc
include/capabilities.hh
include/gcov.hh
include/reporter.hh
include/collector.hh
include/generated-data-base.hh
include/solib-handler.hh
include/configuration.hh
include/lineid.hh
include/swap-endian.hh
include/engine.hh
include/manager.hh
include/utils.hh
include/file-parser.hh
include/output-handler.hh
include/writer.hh
include/filter.hh
include/phdr_data.h
)

set (KCOV_LIBRARY_PREFIX "/tmp")

Expand All @@ -182,6 +240,7 @@ include_directories(
${LIBDW_INCLUDE_DIRS}
${LIBZ_INCLUDE_DIRS}
${LIBCURL_INCLUDE_DIRS}
${DYNINST_INCLUDE_DIRS}
)

link_directories (/home/ska/local/lib)
Expand All @@ -197,6 +256,12 @@ add_custom_command(
DEPENDS ${SOLIB} ${CMAKE_CURRENT_SOURCE_DIR}/bin-to-c-source.py
)

add_custom_command(
OUTPUT dyninst-binary-library.cc
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/bin-to-c-source.py libkcov-binary-dyninst.so __dyninst_binary_library > dyninst-binary-library.cc
DEPENDS kcov-binary-dyninst ${CMAKE_CURRENT_SOURCE_DIR}/bin-to-c-source.py
)

add_custom_command(
OUTPUT bash-redirector-library.cc
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/bin-to-c-source.py $<TARGET_FILE:bash_execve_redirector> bash_redirector_library > bash-redirector-library.cc
Expand Down Expand Up @@ -278,5 +343,25 @@ target_link_libraries(${KCOV}
${LLDB_LIBRARY}
)

if(DYNINST_FOUND)
add_library (kcov-binary-dyninst SHARED engines/dyninst-binary-lib.cc engines/dyninst-file-format.cc utils.cc)
set_target_properties(kcov-binary-dyninst PROPERTIES SUFFIX ".so")

add_executable (kcov-system ${KCOV_DYNINST_SRCS} ${SOLIB_generated} bash-redirector-library.cc dyninst-binary-library.cc python-helper.cc bash-helper.cc html-data-files.cc version.c)

target_link_libraries(kcov-system
${DYNINST_LIBRARIES}
${LIBELF_LIBRARIES}
stdc++
${LIBCURL_LIBRARIES}
m
${DISASSEMBLER_LIBRARIES}
${LIBZ_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
dl
${LLDB_LIBRARY}
)
endif()


install (TARGETS ${PROJECT_NAME} ${INSTALL_TARGETS_PATH})
22 changes: 21 additions & 1 deletion src/configuration.cc
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,11 @@ class Configuration : public IConfiguration
" # including /src/frodo\n"
" kcov --collect-only /tmp/kcov ./frodo # Collect coverage, don't report\n"
" kcov --report-only /tmp/kcov ./frodo # Report coverage collected above\n"
" kcov --merge /tmp/out /tmp/dir1 /tmp/dir2 # Merge the dir1/dir2 reports\n"
" kcov --merge /tmp/out /tmp/dir1 /tmp/dir2 # Merge the dir1/dir2 reports\n"
" kcov --system-record /tmp/out-dir sysroot # Perform full-system in-\n"
" strumentation for sysroot\n"
" kcov --system-report /tmp/data-dir # Report all data from a full-\n"
" system run.\n"
"",
keyAsInt("low-limit"), keyAsInt("high-limit"),
uncommonOptions().c_str());
Expand Down Expand Up @@ -138,6 +142,8 @@ class Configuration : public IConfiguration
{"python-parser", required_argument, 0, 'P'},
{"bash-parser", required_argument, 0, 'B'},
{"bash-method", required_argument, 0, '4'},
{"system-record", no_argument, 0, '8'},
{"system-report", no_argument, 0, '9'},
{"verify", no_argument, 0, 'V'},
{"version", no_argument, 0, 'v'},
{"uncommon-options", no_argument, 0, 'U'},
Expand Down Expand Up @@ -371,6 +377,12 @@ class Configuration : public IConfiguration
case 'm':
setKey("running-mode", IConfiguration::MODE_MERGE_ONLY);
break;
case '8': // Full system record
setKey("running-mode", IConfiguration::MODE_SYSTEM_RECORD);
break;
case '9': // Full system report
setKey("running-mode", IConfiguration::MODE_SYSTEM_REPORT);
break;
case 'l': {
StrVecMap_t vec = getCommaSeparatedList(std::string(optarg));

Expand Down Expand Up @@ -537,6 +549,8 @@ class Configuration : public IConfiguration
setKey("merged-name", "[merged]");
setKey("css-file", "");
setKey("lldb-use-raw-breakpoint-writes", 0);
setKey("system-mode-write-file", "");
setKey("system-mode-read-results-file", 0);
}


Expand Down Expand Up @@ -585,6 +599,10 @@ class Configuration : public IConfiguration
setKey(key, stoul(std::string(value)));
else if (key == "high-limit")
setKey(key, stoul(std::string(value)));
else if (key == "system-mode-write-file")
setKey(key, std::string(value));
else if (key == "system-mode-read-results-file")
setKey(key, stoul(std::string(value)));
else if (key == "bash-use-basic-parser")
setKey(key, stoul(std::string(value)));
else if (key == "lldb-use-raw-breakpoint-writes")
Expand Down Expand Up @@ -622,6 +640,8 @@ class Configuration : public IConfiguration
"\n"
" --gcov use gcov parser instead of DWARF debugging info\n"
" --clang use Clang Sanitizer-coverage parser\n"
" --system-record perform full-system instrumentation\n"
" --system-report report full-system coverage data\n"
" --skip-solibs don't parse shared libraries (default: parse solibs)\n"
" --exit-first-process exit when the first process exits, i.e., honor the\n"
" behavior of daemons (default: wait until last)\n"
Expand Down
Loading

0 comments on commit e43715b

Please sign in to comment.