root/src/tests/servers/debug/crashing_app.cpp
/*
 * Copyright 2005-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */


#undef NDEBUG

#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <OS.h>

extern const char *__progname;

// usage
static const char *kUsage =
        "%s <options> [ <mode> ]\n"
        "Crashes in more or less inovative ways.\n"
        "\n"
        "Options:\n"
        "  -d                   - call disable_debugger() first\n"
        "  -f, --fork           - fork() and continue in the child\n"
        "  -h, --help           - print this info text\n"
        "  --multi              - crash in multiple threads\n"
        "  --signal             - crash in a signal handler\n"
        "  --thread             - crash in a separate thread\n"
        "\n"
        "Modes:\n"
        "[general]\n"
        "  segv                 - dereferences a null pointer (default)\n"
        "  segv2                - strcmp() using a 0x1 pointer\n"
        "  div                  - executes a division by zero\n"
        "  debugger             - invokes debugger()\n"
        "  assert               - failed assert(), which should invoke the\n"
        "                         debugger\n"
        "\n"
        "[x86 specific]\n"
        "  int3                 - executes the int3 (breakpoint) instruction\n"
        "  protection           - executes an instruction that causes a general\n"
        "                         protection exception\n";

// application name
const char *kAppName = __progname;

static void
print_usage(bool error)
{
        fprintf(error ? stderr : stdout, kUsage, kAppName);
}


static void
print_usage_and_exit(bool error)
{
        print_usage(error);
        exit(error ? 0 : 1);
}


static int
crash_segv()
{
        int *a = 0;
        *a = 0;
        return 0;
}

static int
crash_segv2()
{
        const char *str = (const char*)0x1;
        return strcmp(str, "Test");
}

static int
crash_div()
{
        int i = 0;
        i = 1 / i;
        return i;
}

static int
crash_debugger()
{
        debugger("crashing_app() invoked debugger()");
        return 0;
}


static int
crash_assert()
{
        assert(0 > 1);
        return 0;
}


#if __i386__

static int
crash_int3()
{
        asm("int3");
        return 0;
}

static int
crash_protection()
{
        asm("movl %0, %%dr7" : : "r"(0));
        return 0;
}

#endif  // __i386__


typedef int crash_function_t();


struct Options {
        Options()
                :
                inThread(false),
                multipleThreads(false),
                inSignalHandler(false),
                disableDebugger(false),
                fork(false)
        {
        }

        crash_function_t*       function;
        bool                            inThread;
        bool                            multipleThreads;
        bool                            inSignalHandler;
        bool                            disableDebugger;
        bool                            fork;
};

static Options sOptions;


static crash_function_t*
get_crash_function(const char* mode)
{
        if (strcmp(mode, "segv") == 0) {
                return crash_segv;
        } else if (strcmp(mode, "segv2") == 0) {
                return crash_segv2;
        } else if (strcmp(mode, "div") == 0) {
                return (crash_function_t*)crash_div;
        } else if (strcmp(mode, "debugger") == 0) {
                return crash_debugger;
        } else if (strcmp(mode, "assert") == 0) {
                return crash_assert;
#if __i386__
        } else if (strcmp(mode, "int3") == 0) {
                return crash_int3;
        } else if (strcmp(mode, "protection") == 0) {
                return crash_protection;
#endif  // __i386__
        }

        return NULL;
}


static void
signal_handler(int signal)
{
        sOptions.function();
}


static void
do_crash()
{
        if (sOptions.inSignalHandler) {
                signal(SIGUSR1, &signal_handler);
                send_signal(find_thread(NULL), SIGUSR1);
        } else
                sOptions.function();
}


static status_t
crashing_thread(void* data)
{
        snooze(100000);
        do_crash();
        return 0;
}


int
main(int argc, const char* const* argv)
{
        const char* mode = "segv";

        // parse args
        int argi = 1;
        while (argi < argc) {
                const char *arg = argv[argi++];

                if (arg[0] == '-') {
                        if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
                                print_usage_and_exit(false);
                        } else if (strcmp(arg, "-d") == 0) {
                                sOptions.disableDebugger = true;
                        } else if (strcmp(arg, "-f") == 0 || strcmp(arg, "--fork") == 0) {
                                sOptions.fork = true;
                        } else if (strcmp(arg, "--multi") == 0) {
                                sOptions.inThread = true;
                                sOptions.multipleThreads = true;
                        } else if (strcmp(arg, "--signal") == 0) {
                                sOptions.inSignalHandler = true;
                        } else if (strcmp(arg, "--thread") == 0) {
                                sOptions.inThread = true;
                        } else {
                                fprintf(stderr, "Invalid option \"%s\"\n", arg);
                                print_usage_and_exit(true);
                        }
                } else {
                        mode = arg;
                }
        }

        sOptions.function = get_crash_function(mode);
        if (sOptions.function == NULL) {
                fprintf(stderr, "Invalid mode \"%s\"\n", mode);
                print_usage_and_exit(true);
        }

        if (sOptions.disableDebugger)
                disable_debugger(true);

        if (sOptions.fork) {
                pid_t child = fork();
                if (child < 0) {
                        fprintf(stderr, "fork() failed: %s\n", strerror(errno));
                        exit(1);
                }

                if (child > 0) {
                        // the parent exits
                        exit(1);
                }

                // the child continues...
        }

        if (sOptions.inThread) {
                thread_id thread = spawn_thread(crashing_thread, "crashing thread",
                        B_NORMAL_PRIORITY, NULL);
                if (thread < 0) {
                        fprintf(stderr, "Error: Failed to spawn thread: %s\n",
                                strerror(thread));
                        exit(1);
                }

                resume_thread(thread);

                if (sOptions.multipleThreads) {
                        snooze(200000);
                        do_crash();
                } else {
                        status_t result;
                        while (wait_for_thread(thread, &result) == B_INTERRUPTED) {
                        }
                }
        } else {
                do_crash();
        }

        return 0;
}