Changeset 1a3b953 in mainline for uspace/lib/bithenge/failure.c


Ignore:
Timestamp:
2012-08-20T00:22:19Z (13 years ago)
Author:
Sean Bartell <wingedtachikoma@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
0784869
Parents:
5e514c0
Message:

Bithenge: better error injection

File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/bithenge/failure.c

    r5e514c0 r1a3b953  
    3636
    3737#include <errno.h>
     38#include <execinfo.h>
    3839#include <fcntl.h>
    3940#include <stdio.h>
     
    4546#define BITHENGE_FAILURE_DECLS_ONLY 1
    4647#include "failure.h"
     48#include "os.h"
    4749
    4850/* This file raises fake errors from system calls, to test that Bithenge
     
    5456 * correctly (exit code is 1), the main process continues without errors. If
    5557 * the child process has a problem, the main process raises the fake error
    56  * again and shows all stdout and stderr output. For speed, failures only occur
    57  * for some system calls after the first 128.
     58 * again and shows all stdout and stderr output. For speed, errors are only
     59 * raised when part of the backtrace has not been seen before.
    5860 *
    5961 * BITHENGE_FAILURE_INDEX set: the program runs normally until system call
     
    6163 * call. */
    6264
     65static int g_initialized = 0;
    6366static int g_failure_index = -1;
    64 static int g_failure_index_selected = -2;
    65 
    66 static int should_fail(void)
     67static int g_failure_index_selected = -1;
     68
     69typedef struct backtrace_item {
     70        struct backtrace_item *next;
     71        void *backtrace_item;
     72} backtrace_item_t;
     73
     74static backtrace_item_t *g_backtrace_items = NULL;
     75
     76static void atexit_handler(void)
     77{
     78        while (g_backtrace_items) {
     79                backtrace_item_t *first = g_backtrace_items;
     80                g_backtrace_items = first->next;
     81                free(first);
     82        }
     83}
     84
     85static inline void initialize(void)
     86{
     87        g_initialized = 1;
     88        int rc = atexit(atexit_handler);
     89        if (rc)
     90                exit(127);
     91
     92        char *sel_str = getenv("BITHENGE_FAILURE_INDEX");
     93        if (sel_str)
     94                g_failure_index_selected = strtol(sel_str, NULL, 10);
     95}
     96
     97/* Record a hit for a backtrace address and return whether this is the first
     98 * hit. */
     99static inline int backtrace_item_hit(void *addr)
     100{
     101        backtrace_item_t **bip;
     102        for (bip = &g_backtrace_items; *bip; bip = &(*bip)->next) {
     103                backtrace_item_t *bi = *bip;
     104                if (bi->backtrace_item == addr) {
     105                        /* Keep frequently accessed items near the front. */
     106                        *bip = bi->next;
     107                        bi->next = g_backtrace_items;
     108                        g_backtrace_items = bi;
     109                        return 0;
     110                }
     111        }
     112
     113        /* No item found; create one. */
     114        backtrace_item_t *i = malloc(sizeof(*i));
     115        if (!i)
     116                exit(127);
     117        i->next = g_backtrace_items;
     118        i->backtrace_item = addr;
     119        g_backtrace_items = i;
     120        return 1;
     121}
     122
     123int bithenge_should_fail(void)
    67124{
    68125        g_failure_index++;
    69126
    70         if (g_failure_index_selected == -2) {
    71                 char *sel_str = getenv("BITHENGE_FAILURE_INDEX");
    72                 if (sel_str) {
    73                         g_failure_index_selected = strtol(sel_str, NULL, 10);
    74                 } else {
    75                         g_failure_index_selected = -1;
    76                 }
    77         } else if (g_failure_index_selected != -1) {
     127        if (!g_initialized)
     128                initialize();
     129
     130        if (g_failure_index_selected != -1) {
    78131                if (g_failure_index == g_failure_index_selected)
    79132                        return 1; /* breakpoint here */
     
    81134        }
    82135
    83         /* Only fail half the time after 128 failures, 1/4 the time after 256
    84          * failures, 1/8 the time after 512 failures... */
    85         int index = g_failure_index;
    86         while (index >= 128) {
    87                 int test = (index & (64 | 1));
    88                 if (test == (64 | 1) || test == 0)
    89                         return 0;
    90                 index >>= 1;
    91         }
     136        /* If all backtrace items have been seen already, there's no need to
     137         * try raising an error. */
     138        void *trace[256];
     139        int size = backtrace(trace, 256);
     140        int raise_error = 0;
     141        for (int i = 0; i < size; i++) {
     142                if (backtrace_item_hit(trace[i]))
     143                        raise_error = 1;
     144        }
     145        if (!raise_error)
     146                return 0;
    92147
    93148        if (!fork()) {
    94                 /* Child */
     149                /* Child silently fails. */
    95150                int null = open("/dev/null", O_WRONLY);
    96151                if (null == -1)
     
    102157        }
    103158
    104         /* Parent */
     159        /* Parent checks whether child failed correctly. */
    105160        int status;
    106161        wait(&status);
     
    108163                return 0;
    109164
    110         /* The child had an error! We couldn't see it because stdout and stderr
    111          * were redirected, and we couldn't debug it easily because it was a
    112          * separate process. Do it again without redirecting or forking. */
     165        /* The child had an error! We couldn't easily debug it because it was
     166         * in a separate process with redirected stdout and stderr. Do it again
     167         * without redirecting or forking. */
    113168        fprintf(stderr, "** Fake error raised here (BITHENGE_FAILURE_INDEX=%d)\n",
    114169            g_failure_index);
     
    118173void *bithenge_failure_malloc(size_t size)
    119174{
    120         if (should_fail())
     175        if (bithenge_should_fail())
    121176                return NULL;
    122177        return malloc(size);
     
    125180void *bithenge_failure_realloc(void *ptr, size_t size)
    126181{
    127         if (should_fail())
     182        if (bithenge_should_fail())
    128183                return NULL;
    129184        return realloc(ptr, size);
     
    132187ssize_t bithenge_failure_read(int fd, void *buf, size_t count)
    133188{
    134         if (should_fail()) {
     189        if (bithenge_should_fail()) {
    135190                errno = EIO;
    136191                return -1;
     
    141196off_t bithenge_failure_lseek(int fd, off_t offset, int whither)
    142197{
    143         if (should_fail()) {
     198        if (bithenge_should_fail()) {
    144199                errno = EINVAL;
    145200                return (off_t) -1;
     
    148203}
    149204
     205int bithenge_failure_ferror(FILE *stream)
     206{
     207        if (bithenge_should_fail())
     208                return 1;
     209        return ferror(stream);
     210}
     211
     212char *bithenge_failure_str_ndup(const char *s, size_t max_len)
     213{
     214        if (bithenge_should_fail())
     215                return NULL;
     216        return str_ndup(s, max_len);
     217}
     218
     219int bithenge_failure_open(const char *pathname, int flags)
     220{
     221        if (bithenge_should_fail()) {
     222                errno = EACCES;
     223                return -1;
     224        }
     225        return open(pathname, flags);
     226}
     227
     228int bithenge_failure_fstat(int fd, struct stat *buf)
     229{
     230        if (bithenge_should_fail()) {
     231                errno = EIO;
     232                return -1;
     233        }
     234        return fstat(fd, buf);
     235}
     236
    150237/** @}
    151238 */
Note: See TracChangeset for help on using the changeset viewer.