Commit b9cc8f2b authored by Philippe Gerum's avatar Philippe Gerum

testsuite/smokey: add leakage detection test

parent a40320e8
......@@ -937,10 +937,9 @@ AC_CONFIG_FILES([ \
testsuite/smokey/bufp/Makefile \
testsuite/smokey/sigdebug/Makefile \
testsuite/smokey/timerfd/Makefile \
testsuite/smokey/leaks/Makefile \
testsuite/clocktest/Makefile \
testsuite/xeno-test/Makefile \
testsuite/regression/Makefile \
testsuite/regression/posix/Makefile \
utils/Makefile \
utils/hdb/Makefile \
utils/can/Makefile \
......
......@@ -102,8 +102,8 @@ struct smokey_test {
int __ret = (__expr); \
if (__ret < 0) { \
__ret = -errno; \
smokey_warning(__FILE__, __LINE__, "%s: %s", \
#__expr, strerror(errno)); \
__smokey_warning(__FILE__, __LINE__, "%s: %s", \
#__expr, strerror(errno)); \
} \
__ret; \
})
......@@ -112,8 +112,8 @@ struct smokey_test {
({ \
int __ret = (__expr); \
if (__ret) { \
smokey_warning(__FILE__, __LINE__, "%s: %s", \
#__expr, strerror(__ret)); \
__smokey_warning(__FILE__, __LINE__, "%s: %s", \
#__expr, strerror(__ret)); \
__ret = -__ret; \
} \
__ret; \
......@@ -123,11 +123,14 @@ struct smokey_test {
({ \
int __ret = (__expr); \
if (!__ret) \
smokey_warning(__FILE__, __LINE__, \
"assertion failed: %s", #__expr); \
__smokey_warning(__FILE__, __LINE__, \
"assertion failed: %s", #__expr); \
__ret; \
})
#define smokey_warning(__fmt, __args...) \
__smokey_warning(__FILE__, __LINE__, __fmt, #__args)
#ifdef __cplusplus
extern "C" {
#endif
......@@ -150,8 +153,8 @@ void smokey_trace(const char *fmt, ...);
void smokey_note(const char *fmt, ...);
void smokey_warning(const char *file, int lineno,
const char *fmt, ...);
void __smokey_warning(const char *file, int lineno,
const char *fmt, ...);
#ifdef __cplusplus
}
......
......@@ -146,8 +146,8 @@ void smokey_trace(const char *fmt, ...)
va_end(ap);
}
void smokey_warning(const char *file, int lineno,
const char *fmt, ...)
void __smokey_warning(const char *file, int lineno,
const char *fmt, ...)
{
va_list ap;
......
......@@ -4,7 +4,6 @@ SUBDIRS = latency smokey
if XENO_COBALT
SUBDIRS += \
clocktest \
regression \
switchtest \
xeno-test
endif
......@@ -12,7 +11,6 @@ endif
DIST_SUBDIRS = \
clocktest \
latency \
regression \
smokey \
switchtest \
xeno-test
testdir = @XENO_TEST_DIR@
CCLD = $(top_srcdir)/scripts/wrap-link.sh $(CC)
noinst_HEADERS = check.h
test_PROGRAMS = \
leaks
CPPFLAGS = $(XENO_USER_CFLAGS) \
-I$(top_srcdir)/include
LDFLAGS = @XENO_AUTOINIT_LDFLAGS@ $(XENO_POSIX_WRAPPERS)
LDADD = \
../../../lib/cobalt/libcobalt.la \
@XENO_USER_LDADD@ \
-lpthread -lrt -lm
/*
* Copyright (C) 2012 Gilles Chanteperdrix <gch@xenomai.org>
*
* 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.
*/
#ifndef POSIX_CHECK_H
#define POSIX_CHECK_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define check_pthread(expr) \
({ \
int rc = (expr); \
if (rc > 0) { \
fprintf(stderr, "%s:%d: "#expr ": %s\n", __FILE__, __LINE__, strerror(rc)); \
exit(EXIT_FAILURE); \
} \
rc; \
})
#define check_unix(expr) \
({ \
int rc = (expr); \
if (rc < 0) { \
fprintf(stderr, "%s:%d: "#expr ": %s\n", __FILE__, __LINE__, strerror(errno)); \
exit(EXIT_FAILURE); \
} \
rc; \
})
#endif /* POSIX_CHECK_H */
......@@ -11,6 +11,7 @@ SUBDIRS = \
arith \
bufp \
iddp \
leaks \
posix-clock \
posix-cond \
posix-fork \
......@@ -53,6 +54,7 @@ DIST_SUBDIRS = \
arith \
bufp \
iddp \
leaks \
posix-clock \
posix-cond \
posix-fork \
......
noinst_LIBRARIES = libleaks.a
libleaks_a_SOURCES = leaks.c
CCLD = $(top_srcdir)/scripts/wrap-link.sh $(CC)
libleaks_a_CPPFLAGS = \
@XENO_USER_CFLAGS@ \
-I$(top_srcdir)/include
......@@ -38,9 +38,13 @@
#include <boilerplate/compiler.h>
#include <rtdm/rtdm.h>
#include <cobalt/uapi/kernel/heap.h>
#include <asm/xenomai/syscall.h>
#include <xeno_config.h>
#include <smokey/smokey.h>
#include "check.h"
smokey_test_plugin(leaks,
SMOKEY_NOARGS,
"Check for resource leakage in the Cobalt core."
);
#define SEM_NAME "/sem"
#define MQ_NAME "/mq"
......@@ -49,35 +53,34 @@
({ \
unsigned long long after = get_used(); \
if (before != after) { \
fprintf(stderr, object \
" leaked %Lu bytes\n", after-before); \
smokey_warning(object \
" leaked %Lu bytes", after-before); \
failed = 1; \
} else \
fprintf(stderr, object ": OK\n"); \
smokey_trace("no leak with" object); \
})
#define devnode_root "/dev/rtdm/"
const char *memdev[] = {
devnode_root COBALT_MEMDEV_PRIVATE,
devnode_root COBALT_MEMDEV_SHARED,
devnode_root COBALT_MEMDEV_SYS,
NULL,
};
static int memdevfd[3];
static unsigned long long get_used(void)
{
const char *memdev[] = {
devnode_root COBALT_MEMDEV_PRIVATE,
devnode_root COBALT_MEMDEV_SHARED,
devnode_root COBALT_MEMDEV_SYS,
NULL,
};
struct cobalt_memdev_stat statbuf;
unsigned long long used = 0;
int i, fd;
int i, ret;
for (i = 0; memdev[i]; i++) {
fd = open(memdev[i], O_RDONLY);
if (fd < 0 || ioctl(fd, MEMDEV_RTIOC_STAT, &statbuf)) {
fprintf(stderr, "Error: could not open/ioctl device %s\n",
memdev[i]);
exit(EXIT_FAILURE);
}
used += statbuf.size - statbuf.free;
ret = smokey_check_errno(ioctl(memdevfd[i], MEMDEV_RTIOC_STAT, &statbuf));
if (ret == 0)
used += statbuf.size - statbuf.free;
}
return used;
......@@ -88,99 +91,171 @@ static void *empty(void *cookie)
return cookie;
}
static int subprocess_status;
static inline void subprocess_leak(void)
{
struct sigevent sevt;
pthread_mutex_t mutex;
pthread_cond_t cond;
pthread_t thread;
sem_t sem, *psem;
timer_t tm;
int fd;
sem_t sem;
int ret;
ret = smokey_check_status(pthread_create(&thread, NULL, empty, NULL));
if (ret)
goto fail;
ret = smokey_check_status(pthread_mutex_init(&mutex, NULL));
if (ret)
goto fail;
ret = smokey_check_status(pthread_cond_init(&cond, NULL));
if (ret)
goto fail;
ret = smokey_check_errno(sem_init(&sem, 0, 0));
if (ret)
goto fail;
ret = smokey_check_errno(-!(sem_open(SEM_NAME, O_CREAT, 0644, 1)));
if (ret)
goto fail;
check_pthread(pthread_create(&thread, NULL, empty, NULL));
check_pthread(pthread_mutex_init(&mutex, NULL));
check_pthread(pthread_cond_init(&cond, NULL));
check_unix(sem_init(&sem, 0, 0));
check_unix(-!(psem = sem_open(SEM_NAME, O_CREAT, 0644, 1)));
sevt.sigev_notify = SIGEV_THREAD_ID;
sevt.sigev_signo = SIGALRM;
sevt.sigev_notify_thread_id = syscall(__NR_gettid);
check_unix(timer_create(CLOCK_MONOTONIC, &sevt, &tm));
check_unix(fd = mq_open(MQ_NAME, O_RDWR | O_CREAT, 0644, NULL));
ret = smokey_check_errno(timer_create(CLOCK_MONOTONIC, &sevt, &tm));
if (ret)
goto fail;
ret = smokey_check_errno(mq_open(MQ_NAME, O_RDWR | O_CREAT, 0644, NULL));
if (ret >= 0)
return;
fail:
subprocess_status = ret;
}
int main(void)
static int run_leaks(struct smokey_test *t, int argc, char *const argv[])
{
unsigned long long before;
struct sigevent sevt;
pthread_mutex_t mutex;
pthread_cond_t cond;
int fd, failed = 0;
int fd, failed = 0, i, ret;
pthread_t thread;
sem_t sem, *psem;
timer_t tm;
__maybe_unused pid_t child;
mlockall(MCL_CURRENT|MCL_FUTURE);
fprintf(stderr, "Checking for leaks in posix skin objects\n");
for (i = 0; memdev[i]; i++) {
memdevfd[i] = smokey_check_errno(open(memdev[i], O_RDONLY));
if (memdevfd[i] < 0)
return memdevfd[i];
}
before = get_used();
check_pthread(pthread_create(&thread, NULL, empty, NULL));
check_pthread(pthread_join(thread, NULL));
ret = smokey_check_status(pthread_create(&thread, NULL, empty, NULL));
if (ret)
return ret;
ret = smokey_check_status(pthread_join(thread, NULL));
if (ret)
return ret;
sleep(1); /* Leave some time for xnheap
* deferred free */
check_used("thread", before, failed);
before = get_used();
check_pthread(pthread_mutex_init(&mutex, NULL));
check_pthread(pthread_mutex_destroy(&mutex));
check_used("mutex", before, failed);
ret = smokey_check_status(pthread_mutex_init(&mutex, NULL));
if (ret)
return ret;
ret = smokey_check_status(pthread_mutex_destroy(&mutex));
if (ret)
return ret;
check_used("mutex", before, failed);
before = get_used();
check_pthread(pthread_cond_init(&cond, NULL));
check_pthread(pthread_cond_destroy(&cond));
check_used("cond", before, failed);
ret = smokey_check_status(pthread_cond_init(&cond, NULL));
if (ret)
return ret;
ret = smokey_check_status(pthread_cond_destroy(&cond));
if (ret)
return ret;
check_used("cond", before, failed);
before = get_used();
check_unix(sem_init(&sem, 0, 0));
check_unix(sem_destroy(&sem));
check_used("sem", before, failed);
ret = smokey_check_errno(sem_init(&sem, 0, 0));
if (ret)
return ret;
ret = smokey_check_errno(sem_destroy(&sem));
if (ret)
return ret;
check_used("sem", before, failed);
before = get_used();
check_unix(-!(psem = sem_open(SEM_NAME, O_CREAT, 0644, 1)));
check_unix(sem_close(psem));
check_unix(sem_unlink(SEM_NAME));
check_used("named sem", before, failed);
ret = smokey_check_errno(-!(psem = sem_open(SEM_NAME, O_CREAT, 0644, 1)));
if (ret)
return ret;
ret = smokey_check_errno(sem_close(psem));
if (ret)
return ret;
ret = smokey_check_errno(sem_unlink(SEM_NAME));
if (ret)
return ret;
check_used("named sem", before, failed);
before = get_used();
sevt.sigev_notify = SIGEV_THREAD_ID;
sevt.sigev_signo = SIGALRM;
sevt.sigev_notify_thread_id = syscall(__NR_gettid);
check_unix(timer_create(CLOCK_MONOTONIC, &sevt, &tm));
check_unix(timer_delete(tm));
check_used("timer", before, failed);
ret = smokey_check_errno(timer_create(CLOCK_MONOTONIC, &sevt, &tm));
if (ret)
return ret;
ret = smokey_check_errno(timer_delete(tm));
if (ret)
return ret;
check_used("timer", before, failed);
before = get_used();
check_unix(fd = mq_open(MQ_NAME, O_RDWR | O_CREAT, 0644, NULL));
check_unix(mq_close(fd));
check_unix(mq_unlink(MQ_NAME));
check_used("mq", before, failed);
fd = smokey_check_errno(mq_open(MQ_NAME, O_RDWR | O_CREAT, 0644, NULL));
if (fd < 0)
return fd;
ret = smokey_check_errno(mq_close(fd));
if (ret)
return ret;
ret = smokey_check_errno(mq_unlink(MQ_NAME));
if (ret)
return ret;
check_used("mq", before, failed);
#ifdef HAVE_FORK
before = get_used();
check_unix(child = fork());
child = smokey_check_errno(fork());
if (child < 0)
return child;
if (!child) {
subprocess_leak();
return EXIT_SUCCESS;
}
while (waitpid(child, NULL, 0) == -1 && errno == EINTR);
sleep(1); /* Leave some time for xnheap
* deferred free */
check_unix(sem_unlink(SEM_NAME));
check_unix(mq_unlink(MQ_NAME));
sleep(1);
ret = smokey_check_errno(sem_unlink(SEM_NAME));
if (ret)
return ret;
ret = smokey_check_errno(mq_unlink(MQ_NAME));
if (ret)
return ret;
if (subprocess_status)
return subprocess_status;
check_used("fork", before, failed);
#endif
return failed ? EXIT_FAILURE : EXIT_SUCCESS;
for (i = 0; memdev[i]; i++)
close(memdevfd[i]);
return failed ? -EINVAL : 0;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment