Add error injection library
This commit is contained in:
1 parent
e3cb1f76cd
commit
821d6404b6
4 files changed
+141
-1
No files matched your search
+8
-1
@@ -8,7 +8,6 @@ LIBJQ_SRC = locfile.c bytecode.c compile.c execute.c builtin.c jv.c \
|
|||||||
jv_parse.c jv_print.c jv_dtoa.c jv_unicode.c jv_aux.c jv_file.c \
|
jv_parse.c jv_print.c jv_dtoa.c jv_unicode.c jv_aux.c jv_file.c \
|
||||||
jv_alloc.c jq_test.c util.c linker.c ${LIBJQ_INCS}
|
jv_alloc.c jq_test.c util.c linker.c ${LIBJQ_INCS}
|
||||||
|
|
||||||
|
|
||||||
### C build options
|
### C build options
|
||||||
|
|
||||||
AM_CFLAGS = -Wextra -Wall -Wno-missing-field-initializers \
|
AM_CFLAGS = -Wextra -Wall -Wno-missing-field-initializers \
|
||||||
@@ -47,6 +46,14 @@ libjq_la_LDFLAGS = -export-symbols-regex '^j[qv]_' -version-info 1:4:0
|
|||||||
|
|
||||||
include_HEADERS = jv.h jq.h
|
include_HEADERS = jv.h jq.h
|
||||||
|
|
||||||
|
### Error injection for testing
|
||||||
|
|
||||||
|
if ENABLE_ERROR_INJECTION
|
||||||
|
lib_LTLIBRARIES += libinject_errors.la
|
||||||
|
libinject_errors_la_SOURCES = inject_errors.c
|
||||||
|
libinject_errors_la_LIBADD = -ldl
|
||||||
|
libinject_errors_la_LDFLAGS = -module
|
||||||
|
endif
|
||||||
|
|
||||||
### Building the jq binary
|
### Building the jq binary
|
||||||
|
|
||||||
|
|||||||
@@ -87,6 +87,10 @@ dnl Don't attempt to build docs if there's no Ruby lying around
|
|||||||
AC_ARG_ENABLE([docs],
|
AC_ARG_ENABLE([docs],
|
||||||
AC_HELP_STRING([--disable-docs], [don't build docs]))
|
AC_HELP_STRING([--disable-docs], [don't build docs]))
|
||||||
|
|
||||||
|
dnl Don't attempt to build the error injection object (if there is no LD_PRELOAD support)
|
||||||
|
AC_ARG_ENABLE([error-injection],
|
||||||
|
AC_HELP_STRING([--enable-error-injection], [build and test with error injection]))
|
||||||
|
|
||||||
AS_IF([test "x$enable_docs" != "xno"],[
|
AS_IF([test "x$enable_docs" != "xno"],[
|
||||||
AC_CHECK_PROGS(bundle_cmd, bundle)
|
AC_CHECK_PROGS(bundle_cmd, bundle)
|
||||||
|
|
||||||
@@ -112,6 +116,7 @@ EOF
|
|||||||
])
|
])
|
||||||
|
|
||||||
AM_CONDITIONAL([ENABLE_DOCS], [test "x$enable_docs" != xno])
|
AM_CONDITIONAL([ENABLE_DOCS], [test "x$enable_docs" != xno])
|
||||||
|
AM_CONDITIONAL([ENABLE_ERROR_INJECTION], [test "x$enable_error_injection" = xyes])
|
||||||
|
|
||||||
AC_FIND_FUNC([isatty], [c], [#include <unistd.h>], [0])
|
AC_FIND_FUNC([isatty], [c], [#include <unistd.h>], [0])
|
||||||
AC_FIND_FUNC([_isatty], [c], [#include <io.h>], [0])
|
AC_FIND_FUNC([_isatty], [c], [#include <io.h>], [0])
|
||||||
|
|||||||
+112
@@ -0,0 +1,112 @@
|
|||||||
|
|
||||||
|
#define _GNU_SOURCE /* for RTLD_NEXT */
|
||||||
|
#include <assert.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static FILE *fail;
|
||||||
|
static FILE *fail_read;
|
||||||
|
static FILE *fail_write;
|
||||||
|
static FILE *fail_close;
|
||||||
|
static int error;
|
||||||
|
|
||||||
|
static FILE * (*real_fopen)(const char *, const char *);
|
||||||
|
static int (*real_fclose)(FILE *);
|
||||||
|
static int (*real_ferror)(FILE *);
|
||||||
|
static void (*real_clearerr)(FILE *);
|
||||||
|
static char * (*real_fgets)(char *, int, FILE *);
|
||||||
|
static size_t (*real_fread)(void *, size_t, size_t, FILE *);
|
||||||
|
static size_t (*real_fwrite)(const void *, size_t, size_t, FILE *);
|
||||||
|
|
||||||
|
#define GET_REAL(sym) \
|
||||||
|
do { \
|
||||||
|
if (real_ ## sym == 0) { \
|
||||||
|
real_ ## sym = dlsym(RTLD_NEXT, #sym); \
|
||||||
|
assert(real_ ## sym != 0); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define dbg_write(msg) (void)write(2, msg, sizeof(msg) - 1)
|
||||||
|
|
||||||
|
#define dbg() \
|
||||||
|
do { \
|
||||||
|
dbg_write("here: "); \
|
||||||
|
dbg_write(__func__); \
|
||||||
|
dbg_write("!\n"); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
FILE *fopen(const char *path, const char *mode) {
|
||||||
|
GET_REAL(fopen);
|
||||||
|
fail = fail_read = fail_write = fail_close = 0;
|
||||||
|
FILE *f = real_fopen(path, mode);
|
||||||
|
error = EIO;
|
||||||
|
if (strcmp(path, "fail_read") == 0) {
|
||||||
|
fail = fail_read = f;
|
||||||
|
} else if (strncmp(path, "fail_write", sizeof("fail_write") - 1) == 0) {
|
||||||
|
// Not that jq opens files for write anyways...
|
||||||
|
fail = fail_write = f;
|
||||||
|
if (strcmp(path, "fail_write_enospc") == 0)
|
||||||
|
error = ENOSPC;
|
||||||
|
} else if (strncmp(path, "fail_close", sizeof("fail_close") - 1) == 0) {
|
||||||
|
fail = fail_close = f;
|
||||||
|
if (strcmp(path, "fail_close_enospc") == 0)
|
||||||
|
error = ENOSPC;
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fclose(FILE *f) {
|
||||||
|
GET_REAL(fclose);
|
||||||
|
int res = real_fclose(f);
|
||||||
|
if (fail_close == f) {
|
||||||
|
fail = fail_read = fail_write = fail_close = 0;
|
||||||
|
return EOF;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
char * fgets(char *buf, int len, FILE *f) {
|
||||||
|
GET_REAL(fgets);
|
||||||
|
char *res = real_fgets(buf, len, f);
|
||||||
|
if (fail_read == f)
|
||||||
|
return 0;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t fread(void *buf, size_t sz, size_t nemb, FILE *f) {
|
||||||
|
GET_REAL(fread);
|
||||||
|
size_t res = real_fread(buf, sz, nemb, f);
|
||||||
|
if (fail_read == f)
|
||||||
|
return 0;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t fwrite(const void *buf, size_t sz, size_t nemb, FILE *f) {
|
||||||
|
GET_REAL(fwrite);
|
||||||
|
size_t res = real_fwrite(buf, sz, nemb, f);
|
||||||
|
if (fail_write == f)
|
||||||
|
return 0;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ferror(FILE *f) {
|
||||||
|
GET_REAL(ferror);
|
||||||
|
int res = real_ferror(f);
|
||||||
|
if (fail == f) {
|
||||||
|
errno = error;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearerr(FILE *f) {
|
||||||
|
GET_REAL(clearerr);
|
||||||
|
real_clearerr(f);
|
||||||
|
if (fail == f) {
|
||||||
|
fail = fail_read = fail_write = fail_close = 0;
|
||||||
|
error = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,6 +31,22 @@ if [ -z "$d" ]; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -f $PWD/.libs/libinject_errors.so ]; then
|
||||||
|
# Do some simple error injection tests to check that we're handling
|
||||||
|
# I/O errors correctly.
|
||||||
|
(
|
||||||
|
jq=$PWD/jq
|
||||||
|
libinject=$PWD/.libs/libinject_errors.so
|
||||||
|
cd $d
|
||||||
|
LD_PRELOAD=$libinject $jq . /dev/null
|
||||||
|
touch fail_read
|
||||||
|
LD_PRELOAD=$libinject $jq . fail_read && exit 2
|
||||||
|
touch fail_close
|
||||||
|
LD_PRELOAD=$libinject $jq . fail_close && exit 2
|
||||||
|
true
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
printf 'a\0b\nc\0d\ne' > $d/input
|
printf 'a\0b\nc\0d\ne' > $d/input
|
||||||
$VALGRIND $Q ./jq -Rse '. == "a\u0000b\nc\u0000d\ne"' $d/input
|
$VALGRIND $Q ./jq -Rse '. == "a\u0000b\nc\u0000d\ne"' $d/input
|
||||||
$VALGRIND $Q ./jq -Rne '[inputs] == ["a\u0000b", "c\u0000d", "e"]' $d/input
|
$VALGRIND $Q ./jq -Rne '[inputs] == ["a\u0000b", "c\u0000d", "e"]' $d/input
|
||||||
|
|||||||
Reference in new issue
Block a user