Skip to content

Commit

Permalink
Fix cgreen-devs#251 nm parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
fnadeau committed Jun 7, 2021
1 parent 7f8e6c3 commit c2cf634
Show file tree
Hide file tree
Showing 9 changed files with 365 additions and 46 deletions.
17 changes: 14 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
language: c

os:
- linux
- osx
- freebsd
- windows

# Build matrix
compiler:
- gcc
Expand All @@ -20,8 +26,13 @@ before_install:
- if [[ $CC == gcc ]] ; then export CXX=g++ ; else export CXX=clang++ ; fi
- $CC --version
- $CXX --version
- gem install coveralls-lcov
- if [ "$CC" = "gcc" ]; then export WITH_GCOV=ON; else WITH_GCOV=OFF; fi
- |-
case $TRAVIS_OS_NAME in
linux)
gem install coveralls-lcov
if [ "$CC" = "gcc" ]; then export WITH_GCOV=ON; else WITH_GCOV=OFF; fi
;;
esac
script:
- mkdir -p build
Expand All @@ -31,7 +42,7 @@ script:
- ctest --output-on-failure

after_success:
- if [ "$CC" = "gcc" ];
- if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$CC" = "gcc" ];
then
lcov -d tests -d src -d tools -base-directory .. -c -o coverage.info;
lcov --remove coverage.info '/usr/*' -o coverage.info;
Expand Down
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,30 @@ You can also clone the repository or download the source zip from [GitHub](http:
You need the [CMake](http://www.cmake.org) build system.
Most standard C/C++ compilers should work. GCC definitely does.

Perl, diff, find and sed are quired to run unit-tests. Most distro will have
those already installed.

Valgrind is required to run ``make valgrind``.

In the root directory run ``make``. That will configure and build the
library and the `cgreen-runner`, both supporting both C and C++. See
also the documentation.
also the documentation. The later will only be build if build requirements are met.

Here are some example of how to build using docker for various distro:

```
# debian build
$ docker run --rm -v $PWD:/cgreen debian bash -c \
"apt update && apt -y install git cmake gcc g++ && cd /cgreen && make unit test"
# Fedora
$ docker run -t --rm -v $PWD:/cgreen fedora bash -c \
"/usr/bin/yum -y install cmake gcc g++ git diffutils findutils && cd /cgreen && make unit test && make clean"
# OpenSUSE
$ docker run --rm -v $PWD:/cgreen opensuse/tumbleweed bash -c \
'/usr/bin/zypper refresh && /usr/bin/zypper --non-interactive install git cmake gcc gcc-c++ git diffutils findutils && cd /cgreen && make unit test && make clean'
```

## Using It

Expand Down
3 changes: 2 additions & 1 deletion tools/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ set(RUNNER_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/runner.c
${CMAKE_CURRENT_SOURCE_DIR}/discoverer.c
${CMAKE_CURRENT_SOURCE_DIR}/test_item.c
${CMAKE_CURRENT_SOURCE_DIR}/io.c
${CMAKE_CURRENT_SOURCE_DIR}/dl_adapter.c
)
set_source_files_properties(${RUNNER_SRCS} PROPERTIES LANGUAGE C)

Expand All @@ -16,6 +16,7 @@ cmake_define_relative_file_paths ("${RUNNER_SRCS}")

add_executable(cgreen-runner ${RUNNER_SRCS})
target_link_libraries(cgreen-runner ${CGREEN_SHARED_LIBRARY} ${CMAKE_DL_LIBS})
target_compile_definitions(cgreen-runner PUBLIC -D_GNU_SOURCE)

install(TARGETS cgreen-runner
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
Expand Down
114 changes: 75 additions & 39 deletions tools/discoverer.c
Original file line number Diff line number Diff line change
@@ -1,72 +1,108 @@
#include "discoverer.h"

#include "io.h"

#include <dlfcn.h>
#include <link.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>

#include <cgreen/internal/unit_implementation.h>

#include "../src/utils.h"


#include "dl_adapter.h"
#include "test_item.h"

#ifdef UNITTESTING
int printf_unittesting(const char *fmt, ...);
#ifdef printf
#undef printf
#endif
#define printf printf_unittesting
#endif

static const char *cgreen_spec_start_of(const char *line) {
return strstr(line, CGREEN_SPEC_PREFIX CGREEN_SEPARATOR);
}

static bool contains_cgreen_spec(const char *line) {
return cgreen_spec_start_of(line) != NULL;
static bool starts_with_cgreen_spec(const char *line) {
return cgreen_spec_start_of(line) == line;
}

static bool is_definition(const char *line) {
return strstr(line, " D ") != NULL;
}

static bool complete_line_read(char line[]) {
return line[strlen(line)-1] == '\n';
}


static void strip_newline_from(char *name) {
if (name[strlen(name)-1] == '\n')
name[strlen(name)-1] = '\0';
}

static void add_all_tests_from(FILE *nm_output_pipe, CgreenVector *tests, bool verbose) {
char line[1000];
int length = read_line(nm_output_pipe, line, sizeof(line)-1);
while (length > -1) { /* TODO: >0 ? */
if (!complete_line_read(line))
PANIC("Too long line in nm output");
if (contains_cgreen_spec(line) && is_definition(line)) {
strip_newline_from(line);
TestItem *test_item = create_test_item_from(cgreen_spec_start_of(line));
static void add_all_tests_from(ElfW(Sym) *symbols, char *symbol_str, long symbol_size, CgreenVector *tests, bool verbose) {
int size = symbol_str - (char *)symbols;
for (int k = 0; k < size / symbol_size; ++k)
{
ElfW(Sym) * sym = &symbols[k];
// If sym is function
if ((ELF64_ST_TYPE(symbols[k].st_info) == STT_OBJECT)
&& starts_with_cgreen_spec(&symbol_str[sym->st_name]))
{
TestItem *test_item = create_test_item_from(cgreen_spec_start_of(&symbol_str[sym->st_name]));
if (verbose)
printf("Discovered %s:%s (%s)\n", test_item->context_name, test_item->test_name,
test_item->specification_name);
cgreen_vector_add(tests, test_item);
}
length = read_line(nm_output_pipe, line, sizeof(line)-1);
}
}

/* Read in the dynamic symbols. */
static ElfW(Sym) *get_symbols_table(struct link_map *map, long *symbol_size, char **symbol_str)
{
ElfW(Sym) *symtab = NULL;
*symbol_size = 0;
for (ElfW(Dyn) *section = map->l_ld; section->d_tag != DT_NULL; ++section)
{
if (section->d_tag == DT_SYMTAB)
{
symtab = (ElfW(Sym) *)section->d_un.d_ptr;
}
else if (section->d_tag == DT_STRTAB)
{
*symbol_str = (char*)section->d_un.d_ptr;
}
else if (section->d_tag == DT_SYMENT)
{
*symbol_size = section->d_un.d_val;
}
}
return symtab;
}

CgreenVector *discover_tests_in(const char *filename, bool verbose) {
FILE *library = open_file(filename, "r");
if (library == NULL)
void *handle = dl_adapter_open(filename, RTLD_LAZY | RTLD_GLOBAL);
if (!handle) {
if (verbose)
printf("%s: dl_adapter_open failed\n", filename);
return NULL;
}

struct link_map *map = NULL;
int loadInforResult = dl_adapter_info(handle, RTLD_DI_LINKMAP, &map);
if (loadInforResult == -1) {
if (verbose)
printf("%s: dl_atapter_info failed\n", filename);
dl_adapter_close(handle);
return NULL;
close_file(library);
}

ElfW(Sym) *symbols = NULL;
char *symbol_str = NULL;
long symbol_size = 0;
symbols = get_symbols_table(handle, &symbol_size, &symbol_str);

char nm_command[1000];
sprintf(nm_command, "/usr/bin/nm '%s'", filename);
FILE *nm_output_pipe = open_process(nm_command, "r");
if (nm_output_pipe == NULL)
if ((symbols == NULL) || (symbol_str == NULL) || (symbol_size <= 0)) {
if (verbose)
printf("%s: couldn't find any symbol\n", filename);
dl_adapter_close(handle);
return NULL;
}

CgreenVector *tests = create_cgreen_vector((GenericDestructor)&destroy_test_item);
add_all_tests_from(nm_output_pipe, tests, verbose);
close_process(nm_output_pipe);
add_all_tests_from(symbols, symbol_str, symbol_size, tests, verbose);

dl_adapter_close(handle);

return tests;
}
21 changes: 21 additions & 0 deletions tools/dl_adapter.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include "dl_adapter.h"

#include <dlfcn.h>
#include <link.h>
#include <limits.h>
#include <sys/mman.h>

void *dl_adapter_open(const char *filename, int flag)
{
return dlopen(filename, flag);
}

int dl_adapter_close(void *handle)
{
return dlclose(handle);
}

int dl_adapter_info(void *handle, int request, void *p)
{
return dlinfo(handle, request, p);
}
17 changes: 17 additions & 0 deletions tools/dl_adapter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#ifndef DL_ADAPTER_H
#define DL_ADAPTER_H

#include <dlfcn.h>

#ifdef UNITTESTING
#define dl_adapter_open dl_adapter_open_unittesting
#define dl_adapter_close dl_adapter_close_unittesting
#define dl_adapter_info dl_adapter_info_unittesting
#endif

void *dl_adapter_open(const char *filename, int flag);
int dl_adapter_close(void *handle);

int dl_adapter_info(void *handle, int request, void *p);

#endif
19 changes: 19 additions & 0 deletions tools/dl_adapter.mocks
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#include "dl_adapter.h"

#include <cgreen/cgreen.h>
#include <cgreen/mocks.h>

/* Adapter to dl */
void *dl_adapter_open(const char *filename, int flag) {
return (void *) mock(filename, flag);
}

int dl_adapter_close(void *handle)
{
return (int) mock(handle);
}

int dl_adapter_info(void *handle, int request, void *p)
{
return (int) mock(handle, request, p);
}
46 changes: 44 additions & 2 deletions tools/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,49 @@ set(CGREEN_RUNNER_TESTS_LIBRARY
set(RUNNER_TESTS_SRCS
runnerTests.c
../discoverer.c
../io.c
../dl_adapter.c
../test_item.c)
add_library(${CGREEN_RUNNER_TESTS_LIBRARY} SHARED ${RUNNER_TESTS_SRCS})

target_link_libraries(${CGREEN_RUNNER_TESTS_LIBRARY} ${CGREEN_SHARED_LIBRARY} ${CMAKE_DL_LIBS})
target_compile_definitions(${CGREEN_RUNNER_TESTS_LIBRARY} PUBLIC -D_GNU_SOURCE)
target_link_libraries(${CGREEN_RUNNER_TESTS_LIBRARY}
${CGREEN_SHARED_LIBRARY}
${CMAKE_DL_LIBS}
${LIBBFD_LIBRARIES})


set(DISCOVERER_ACCEPTANCE_TESTS_LIBRARY
discoverer_acceptance_tests
CACHE INTERNAL "discoverer acceptance tests"
)
set(DISCOVERER_ACCEPTANCE_TESTS_SRCS
../discoverer_acceptance_tests.c
../discoverer.c
../dl_adapter.c
../test_item.c)
add_library(${DISCOVERER_ACCEPTANCE_TESTS_LIBRARY} SHARED ${DISCOVERER_ACCEPTANCE_TESTS_SRCS})

target_compile_definitions(${DISCOVERER_ACCEPTANCE_TESTS_LIBRARY} PUBLIC -D_GNU_SOURCE)
target_link_libraries(${DISCOVERER_ACCEPTANCE_TESTS_LIBRARY}
${CGREEN_SHARED_LIBRARY}
${CMAKE_DL_LIBS}
${LIBBFD_LIBRARIES})

set(DISCOVERER_UNIT_TESTS_LIBRARY
discoverer_unit_tests
CACHE INTERNAL "discoverer unit tests"
)
set(DISCOVERER_UNIT_TESTS_SRCS
discoverer_unit_tests.c
../discoverer.c)
add_library(${DISCOVERER_UNIT_TESTS_LIBRARY} SHARED ${DISCOVERER_UNIT_TESTS_SRCS})

target_compile_definitions(${DISCOVERER_UNIT_TESTS_LIBRARY} PUBLIC UNITTESTING -D_GNU_SOURCE)
target_link_libraries(${DISCOVERER_UNIT_TESTS_LIBRARY} ${CMAKE_DL_LIBS})

SET(CGREEN_RUNNER_TESTS_LIBRARY "$<TARGET_FILE_DIR:cgreen_runner_tests>/$<TARGET_FILE_NAME:cgreen_runner_tests>")
SET(DISCOVERER_ACCEPTANCE_TESTS_LIBRARY "$<TARGET_FILE_DIR:discoverer_acceptance_tests>/$<TARGET_FILE_NAME:discoverer_acceptance_tests>")
SET(DISCOVERER_UNIT_TESTS_LIBRARY "$<TARGET_FILE_DIR:discoverer_unit_tests>/$<TARGET_FILE_NAME:discoverer_unit_tests>")

# Due to some (of many) CMake irregularities to reference the test libraries
# we can't just use its CMake name variable, but have to look it up with
Expand Down Expand Up @@ -58,3 +94,9 @@ macro_add_test(NAME cgreen_runner_with_xml_reporter

macro_add_test(NAME cgreen_runner_multiple_libraries
COMMAND cgreen-runner ${CGREEN_RUNNER_TESTS_LIBRARY} ${CGREEN_RUNNER_TESTS_LIBRARY} ${CGREEN_RUNNER_TESTS_LIBRARY})

macro_add_test(NAME discoverer_acceptance_tests
COMMAND cgreen-runner ${DISCOVERER_ACCEPTANCE_TESTS_LIBRARY})

macro_add_test(NAME discoverer_unit_tests
COMMAND cgreen-runner ${DISCOVERER_UNIT_TESTS_LIBRARY})
Loading

0 comments on commit c2cf634

Please sign in to comment.