Skip to content

Commit

Permalink
test: Update unittest data when it is not set
Browse files Browse the repository at this point in the history
On RISC-V, at least some GCC doesn't set the uftrace test data in
the uftrace.unit_test section.  I'm not sure what's the problem
exactly but we can handle that by filling the symbol and string from
the binary.

Fixed: #1833
Signed-off-by: Namhyung Kim <[email protected]>
  • Loading branch information
namhyung committed Dec 21, 2023
1 parent ad86221 commit 96f2ea1
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 8 deletions.
60 changes: 58 additions & 2 deletions tests/unittest.c
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,49 @@ static int sort_tests(const void *tc1, const void *tc2)
return strcmp(test1->name, test2->name);
}

static bool free_test_names;

/*
* RISC-V GCC doesn't set uftrace_unit_test data in the uftrace.test section
* (#1833). So it should read the unittest binary to find the symbol table
* and fill the data manually.
*
* Note that the unit test functions are global functions start with "func_"
* prefix. Other (global) functions should not start with that.
*/
static void update_test_cases(struct uftrace_unit_test *tcases, int num,
struct uftrace_elf_data *elf)
{
struct uftrace_elf_iter iter;
int len = strlen(TEST_FUNC_PREFIX_STR);
int i = 0;

printf("update test cases from binary\n");
elf_for_each_shdr(elf, &iter) {
if (iter.shdr.sh_type != SHT_SYMTAB)
continue;
elf_for_each_symbol(elf, &iter) {
typeof(iter.sym) *sym = &iter.sym;
const char *name = elf_get_name(elf, &iter, sym->st_name);

if (elf_symbol_type(sym) != STT_FUNC || elf_symbol_bind(sym) != STB_GLOBAL)
continue;
if (strncmp(name, TEST_FUNC_PREFIX_STR, len))
continue;
tcases[i].func = (void *)sym->st_value + load_base;
tcases[i].name = strdup(name + len);
i++;
}
/*
* It applied the base address already, make sure not to do it
* twice.
*/
load_base = 0;
free_test_names = true;
break;
}
}

static int setup_unit_test(struct uftrace_unit_test **test_cases, size_t *test_num, char *filter)
{
char *exename;
Expand Down Expand Up @@ -202,6 +245,14 @@ static int setup_unit_test(struct uftrace_unit_test **test_cases, size_t *test_n
elf_get_secdata(&elf, &iter);
elf_read_secdata(&elf, &iter, 0, tcases, sec_size);

/* check if test cases are set properly */
for (i = 0, actual = 0; i < num; i++) {
if (tcases[i].func && tcases[i].name)
actual++;
}
if (actual != num)
update_test_cases(tcases, num, &elf);

/* relocate section symbols in case of PIE */
for (i = 0, actual = 0; i < num && (load_base || filter); i++) {
struct uftrace_unit_test *tc = &tcases[i];
Expand Down Expand Up @@ -237,7 +288,7 @@ static int setup_unit_test(struct uftrace_unit_test **test_cases, size_t *test_n
return ret;
}

static int finish_unit_test(struct uftrace_unit_test *test_cases, int *test_stats)
static int finish_unit_test(struct uftrace_unit_test *test_cases, int test_num, int *test_stats)
{
int i;

Expand All @@ -247,6 +298,11 @@ static int finish_unit_test(struct uftrace_unit_test *test_cases, int *test_stat
printf("%3d %s\n", test_stats[i], messages[i]);

printf("\n");

if (free_test_names) {
for (i = 0; i < test_num; i++)
free((void *)test_cases[i].name);
}
free(test_cases);
return test_stats[TEST_NG] + test_stats[TEST_BAD] + test_stats[TEST_SIG] > 0 ?
EXIT_FAILURE :
Expand Down Expand Up @@ -342,5 +398,5 @@ int main(int argc, char *argv[])
for (i = 0; i < test_num; i++)
run_unit_test(&test_cases[i], test_stats, filter);

return finish_unit_test(test_cases, test_stats);
return finish_unit_test(test_cases, test_num, test_stats);
}
19 changes: 13 additions & 6 deletions tests/unittest.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,27 +110,34 @@ struct uftrace_unit_test {
int (*func)(void);
};

#define TEST_FUNC_PREFIX func_
#define TEST_FUNC_PREFIX_STR stringify(TEST_FUNC_PREFIX)

#define __concat(a, b) a##b
#define concat(a, b) __concat(a, b)
#define TEST_FUNC_NAME(t) concat(TEST_FUNC_PREFIX, t)

#ifdef __clang__
#define TEST_CASE(t) \
extern int func_##t(void); \
extern int TEST_FUNC_NAME(t)(void); \
\
__attribute__((section(TEST_SECTION), used, no_sanitize("address"))) \
const struct uftrace_unit_test test_##t = { \
.name = stringify(t), \
.func = func_##t, \
.func = TEST_FUNC_NAME(t), \
}; \
\
int func_##t(void)
int TEST_FUNC_NAME(t)(void)
#else /* #ifdef __clang__ */
#define TEST_CASE(t) \
extern int func_##t(void); \
extern int TEST_FUNC_NAME(t)(void); \
\
__attribute__((section(TEST_SECTION), used)) const struct uftrace_unit_test test_##t = { \
.name = stringify(t), \
.func = func_##t, \
.func = TEST_FUNC_NAME(t), \
}; \
\
int func_##t(void)
int TEST_FUNC_NAME(t)(void)
#endif /* #ifdef __clang__ */

#define TERM_COLOR_NORMAL ""
Expand Down

0 comments on commit 96f2ea1

Please sign in to comment.