/*
 * Sun 11 May 2008 09:34:14 PM PDT (dvolper)
 * Updated: Tue Sep  9 10:18:15 PDT 2014 (mmead, just comments at bottom)
 * Updated: Wed Jan 13 09:01:33 PDT 2016 (mmead, just comments at bottom)                                                                                                                
 * ALL tests in this file are BAD: most of them are illegal (from                                                                                                                        
 * C++ standard point of view at least).                                                                                                                                                 
 *---------------------------------------------------------------------                                                                                                                  
 * The really bad news is that most of these tests run without crashing.                                                                                                                 
 * Therefore these errors may stay un-noticed till later time, usually                                                                                                                   
 * when you are presenting your program to someone.                                                                                                                                      
 *                                                                                                                                                                                       
 *   "...the results are undefined, and we all know what "undefined"                                                                                                                     
 *    means: it means it works during development, it works during                                                                                                                       
 *    testing, and it blows up in your most important customers'                                                                                                                         
 *    faces." --Scott Meyers                                                                                                                                                             
 *                                                                                                                                                                                       
 * So, what is the answer -- memory debuggers.                                                                                                                                           
 *---------------------------------------------------------------------                                                                                                                  
 *                                                                                                                                                                                       
 * File format:                                                                                                                                                                          
 * each test has a short description, for each test there is                                                                                                                             
 * a result of running the executable obtained with the following compilers:                                                                                                             
 * GNU 4.8.2 on Linux Mint 17 <-- updated on Tue 09 Sep 2014                                                                                                                             
 * Clang 3.6 on Mac OS X <-- updated Tue 12 Jan 2016                                                                                                                                     
 * Borland 5.82                                                                                                                                                                          
 * MicroSoft 8.0 (or whatever is VS2005)                                                                                                                                                 
 * (see Makefile)                                                                                                                                                                        
 *                                                                                                                                                                                       
 * +++You can use newer versions of the compilers.                                                                                                                                       
 *
 * The result of using memory debuggers is presented at the end of this file.
 * */
#include <iostream>
#include <cstdlib>
#include <stdlib.h> /*free*/
#include <string>
#include <stdio.h>

void test0() {
    std::cout << "test0" << std::endl;
    int* pI = new int;
    *pI=2;
    delete(pI);
    std::cout << "Now deleting a pointer twice..." << std::endl;
    delete(pI);
    std::cout << "Did you notice?" << std::endl;
}
/* Runs - deleting PODs twice usually doesn't cause any error,
 * since there is no destructor, which may fail. See test1 - an
 * example with a non-trivial dtor.
 * gcc: crashes "*** Error in `./a.out': double free or corruption (fasttop): 0x0000000000679010 ***"
 * msc: OK
 * bcc: OK
 */

class TestDoubleDelete {
    int * data;
    public:
    TestDoubleDelete(int _i) : data(new int(_i)) {}
    ~TestDoubleDelete() { delete data; }
};

void test1() {
    std::cout << "test1" << std::endl;
    TestDoubleDelete* p = new TestDoubleDelete(11);
    std::cout << "Now deleting a pointer ..." << std::endl;
    delete p;
    std::cout << "Now deleting a pointer one more time ..." << std::endl;
    delete p;
    std::cout << "Did you notice?" << std::endl;
}
/* Runs - 
 * gcc: crashes "*** Error in `./a.out': free(): invalid pointer: 0x000000000139f020 ***"
 * msc: OK
 * bcc: OK
 */

void test2() {
    std::cout << "test2" << std::endl;
    int* pI = new int(2);
    std::cout << "Now freeing a pointer instead of deleting it..." << std::endl;
    free(pI);
    std::cout << "Did you notice?" << std::endl;
}
/* Runs - as we'll discuss in class, delete is actually a "free" + "dtor",
 * thus calling free instead of delete skips a dtor, which doesn't 
 * cause an error, but results in a memory leak.
 * gcc: OK
 * msc: OK
 * bcc: OK
 */

void test3() {
    std::cout << "test3" << std::endl;
    int* pI = new int;
    std::cout << "Now reading uninitialized memory" << std::endl;
    int j = *pI+2;
    std::cout << "Did you notice? (value was " << j << ") probably garbage" 
        << std::endl;
    delete pI; /*no leaks*/
}
/* Runs - this is not a legal C++ code, C++ standard says that program
 * is not supposed to use unintialized memory. So memory has to be 
 * 1) allocated (done in test3)
 * 2) initialized (not done in test3)
 *
 * gcc: OK (value was 0)
 * msc: OK (value was garbage)
 * bcc: OK (value was garbage)
 */

void test4() {
    std::cout << "test4" << std::endl;
    int* pJ;
    std::cout << "Now writing to uninitialized pointer (un-allocated memory) pJ = " << pJ << std::endl;
    *pJ = 100;
    std::cout << "Did you notice?" << std::endl;
}
/* Runs - notice that this is much worse than what we did in test3, 
 * in test4 memory for the int wasn't even allocated. So what happens is
 * pJ contains garbage, then this garbage is treated as an address, and we 
 * attempt to write to that address.
 * gcc: crashes "Segmentation fault"
 * msc: crashes (memory could not be written), it also warns at compile time 
 "mem.leaks.cpp(82) : warning C4700: 
 uninitialized local variable 'pJ' used"
 * bcc: OK, but!!! there was a warning 
 *     "Warning W8013 mem.leaks.cpp 82: 
 *      Possible use of 'pJ' before definition in function test4()" 
 */

void test5() {
    std::cout << "test5" << std::endl;
    int* pI = new int[10];
    std::cout << "Let's delete instead of delete [] " << std::endl;
    delete pI;
    std::cout << "Did you notice?" << std::endl;
}
/* Runs - delete [] is actually quite complicated. It calls a destructor on
 * each element of the array, and than frees the array itself.  Thus, calling
 * "delete" instead of "delete []" doesn't produce an error, but it skips all
 * destructors, which may be a memory leak (if the objects in the array had
 * dynamically allocated memory) 
 * gcc: OK 
 * msc: OK 
 * bcc: OK 
 * This particular example doesn't have a leak, since there is no dynamically 
 * allocated memory inside int objects. But it's still an illegal code.
 */

class CTest1 {
    int * pi;
    public:
    CTest1() : pi(new int (2)) {}
    ~CTest1() { std::cout << "In ~CTest1()" << std::endl; delete pi; }
};

void test6() {
    std::cout << "test6" << std::endl;
    CTest1* p = new CTest1[10];
    std::cout << "Let's free instead of delete [] " << std::endl;
    free(p);
    std::cout << "Did you notice?" << std::endl;
}
/* Runs - this is a continuation of test5. Again, there MAY be run-time 
 * crashes, and definitely there is a leak. 
 * What happens: NO destructor is called, so all 10 dynamically allocated 
 * integers (pi pointer) are still on the heap 
 *
 * Those compilers that pay attention to the type information (array vs.
 * single) may crash when calling free, others won't (but leak dynamically
 * allocated memory, if any, from the other 9 objects).
 * gcc: crashes "*** Error in `./a.out': munmap_chunk(): invalid pointer: 0x0000000001d17018 ***"
 * msc: OK
 * bcc: OK
 * Here is the output that proves that NO destructor is called.
 * Change "delete" to "delete []" and you'll see 10 destructors.
 ===================================
 Let's delete instead of delete []
 Did you notice?
 ===================================
 * Note: gcc on Linux (4.1.3) crashes!!! (CygWin gcc is 3.4.4)
 */

class CTest2 {
    int i;
    public:
    CTest2() : i(2) {}
    ~CTest2() { std::cout << "In ~CTest2()" << std::endl; }
};

void test7() {
    std::cout << "test7" << std::endl;
    CTest2* p = new CTest2[10];
    std::cout << "Let's delete instead of delete [] " << std::endl;
    delete p;
    std::cout << "Did you notice?" << std::endl;
}
/* Runs - this is a continuation of test5. Again, there MAY be run-time 
 * crashes, and definitely there is a leak. 
 * What happens: 
 * when delete is called, it calls a SINGLE destructor on the 
 * pointer "p", and then free.
 *
 * Those compilers that pay attention to the type information (array vs.
 * single) may crash when calling free, others won't (but leak dynamically
 * allocated memory, if any, from the other 9 objects).
 * gcc: crashes "*** Error in `./a.out': munmap_chunk(): invalid pointer: 0x000000000126a018 ***"
 * msc: OK
 * bcc: OK
 * Here is the output that proves that only one destructor is called.
 * Change "delete" to "delete []" and you'll see 10 destructors.
 ===================================
 Let's delete instead of delete []
 In ~CTest2() 
 Did you notice?
 ===================================
 * Note: gcc on Linux (4.1.3) crashes!!! (CygWin gcc is 3.4.4)
 */


void test8() {
    std::cout << "test8" << std::endl;
    int ar[10]={0}; //array
    std::cout << "Let's read out of bounds " << std::endl;
    std::cout << "ar[10] = " << ar[10] << std::endl;
    std::cout << "Did you notice?" << std::endl;
}
/* Runs - 
 * gcc: OK
 * msc: OK
 * bcc: OK
 */

void test9() {
    std::cout << "test9" << std::endl;
    int ar[10]={0}; //array
    std::cout << "Let's write out of bounds " << std::endl;
    ar[10] = 7;
    std::cout << "Did you notice?" << std::endl;
}
/* Runs - 
 * gcc: OK
 * msc: OK
 * bcc: OK
 */

void test10() {
    std::cout << "test10" << std::endl;
    int * ar = new int[3];
    std::cout << "Let's read out of bounds " << std::endl;
    std::cout << "ar[3] = " << ar[3] << std::endl;
    std::cout << "Did you notice?" << std::endl;
    delete [] ar;
}
/* Runs - 
 * gcc: OK
 * msc: OK
 * bcc: OK
 */

void test11() {
    std::cout << "test11" << std::endl;
    int * ar = new int[3];
    std::cout << "Let's write out of bounds " << std::endl;
    ar[3] = 7;
    std::cout << "Did you notice?" << std::endl;
    delete [] ar;
}
/* Runs - 
 * gcc: OK
 * msc: OK
 * bcc: OK
 */

void test12() {
    std::cout << "test12" << std::endl;
    int* pI = new int;
    std::cout << "Let's leak a pointer to int" << std::endl;
    *pI = 303;
}
/* Runs - just a leak, so all 3 OK */

void (*pTests[])() = {
    test0,test1,test2,test3,test4,test5,test6,
    test7,test8,test9,test10,test11,test12
};

#include <cstdio> /* sscanf */

int main(int argc, char *argv[] ) {
    if (argc >1) {
        int test = 0;
        std::sscanf(argv[1],"%i",&test);
        try {
            pTests[test]();
        } catch( const char* msg) {
            std::cerr << msg << std::endl;
        }
    }
    return 0;
}

/*
 * Here are results from using:
 * Borland CodeGuard (Windows only)
 * Valgrind (Linux/Mac only)
 * Dr. Memory (Linux/Mac/Windows, 32-bit only)
 * Rational Purify (Windows only)
 * =================================================
 * To use CodeGuard :
 * 1) This is a one-time step
 *    a) Run from the command line: cgconfig
 *    b) in Preferences check "Resource Leaks" and "Repeated Errors",
 *       uncheck the rest
 *    c) in "Error Message Box" - check it if you want a pop-up message box,
 *       otherwise you'll have to look into "exec_name.cgl" file each time.
 *    d) in "Debugging Information" check the "Read debug info"
 *    e) click OK
 *    f) there are more tabs, you don't modify defaults there to run this code,
 *       but "Ignored Modules" may be useful when working with STL.
 * 2) Compile you code with Borland using additional "-v -vG" flags, like
 *    bcc32.exe -emy.exe -v -vG -w <source files>
 *    executable "my.exe" is created
 *    run it normally - if there is an error, a log file will be created named
 *    "my.cgl" (CodeGuard Log). Read it and weep.
 * =================================================
 * To use valgrind:
 * 1) install valgrind and, optionally, electric fence:
 *    sudo-apt-get install valgrind electric-fence
 *    (pretty dumb! - that's why Linux rocks)
 * 2) compile code using debug information and electric fence's malloc
 *    g++ -ggdb -pedantic -ansi -Wall -Wextra -lefence <source> -omy.exe
 * 3) run it
 *    valgrind -q ./my.exe <test#>
 *    output will be displayed in the terminal. Read it and weep.
 * 4) there is a nice add-on to valgrind - kcachegrind
 * Note: You don't need electric fence, it's just optional.
 * =================================================
 * To use Dr. Memory
 * 1) Compile your program as a 32-bit program (gcc/clang use -m32)
 * 2) Run it like this: drmemory -- my.exe <test#>
 * Read the manual that explains all of the options it has.
 * =================================================
 * To use Purify: see http://digipen.edu/~dvolper,
 * search for "Rational Purify" -
 * close to bottom of the page
 * =================================================
 *
 *          CodeGuard        Valgrind             Dr. Memory             Purify
 *          Windows          Linux/Mac         Linux/Mac/Windows         Windows       Type of test run  
 * ================================================================================================================
 * test0  | caught         caught/caught      caught/caught/caught       caught     | double delete POD
 * test1  | caught         caught/caught      caught/caught/caught       caught     | double delete user-defined type
 * test2  | caught         caught/caught      caught/caught/not          caught     | free instead of delete
 * test3  | not            caught/caught      caught/caught/caught       caught     | read uninitialized data
 * test4  | not            caught/caught      caught/caught/not (!!)     caught     | write uninitialized data
 * test5  | caught         caught/not         caught/caught/not          not        | delete instead of delete [] POD
 * test6  | caught         caught/caught      caught/caught/caught       caught     | free instead of delete []
 * test7  | caught         caught/caught      caught/caught/caught       caught     | delete instead of delete [] user-defined type
 * test8  | caught         caught/not         not/not/not                not        | static array, out-of-bound read
 * test9  | caught         not/caught         not/caught/not (!!)        not        | static array, out-of-bound write
 * test10 | caught         caught/caught      caught/caught/caught       caught     | dynamic array, out-of-bound read
 * test11 | caught         caught/caught      caught/caught/caught       caught     | dynamic array, out-of-bound write
 * test12 | caught         caught/caught      caught/caught/caught       caught     | just a stupid leak
 *
 * (!!) The program crashed and Dr. Memory couldn't prevent/detect anything.
 *
 */