diff --git a/info0939/README.md b/info0939/README.md new file mode 100644 index 0000000000000000000000000000000000000000..623b3675d9a0e5a2cc66da8246f68b263ec15bb1 --- /dev/null +++ b/info0939/README.md @@ -0,0 +1,55 @@ +Prerequisites for the class: +--- + * Having access to a Unix machine + * Having access to the compiler (`gcc` or `clang`), the debugger (`gdb`) and `wget` + * NIC5 satisfies all the requirements + +Instructions for downloading the example code +--- +The git repository is located at [here](https://gitlab.onelab.info/mcicuttin/snippets/INFO0939) (`https://gitlab.onelab.info/mcicuttin/snippets/INFO0939`). We will use the following files: +- `intdiv.c` for the `assert()` example on the integer division program +- `stack_corrupt.c` for a debugging exercise +- `cache.c` for a profiling exercise + +You can download the files on your machine or on NIC5 via `wget`: + +``` +# This command is needed only when you open a new shell +export REPO_BASE=https://gitlab.onelab.info/mcicuttin/snippets/INFO0939/-/raw/master/ + +# Files are then donwloaded as following: +wget $REPO_BASE/intdiv.c +wget $REPO_BASE/stack_corrupt.c +wget $REPO_BASE/cache.c +``` + +GDB Cheatsheet +--- +To debug with GDB: `gdb <progname>`, then + +* `r`: Run the program +* `bt`: Print the backtrace of a crashed program +* `frame`: Select the stack frame you want to analyze +* `s` or `step`: step one statement, enter the function if statement is a function call +* `n` or `next`: execute next statement +* `c`: continue until the end of the program or the next breakpoint +* `break <file:line>`: set a breakpoint +* `p`: print the value of a variable + +Integer division program & `assert()` +--- +You are not really required to run this code, but... + +* Compile with `gcc -g -o intdiv intdiv.c` (debug mode) +* Compile with `gcc -O3 -DNDEBUG -o intdiv intdiv.c` (release mode) +* Try to pass a negative integer to the function, compile the program in both modes and see what happens + +Debugging exercise with `stack_corrupt.c` +--- + +* Compile with `gcc -g -o stack_corrupt stack_corrupt.c` +* Run with `./stack_corrupt`. What do you see? +* Launch the code in GDB and try to explain what you see + +Profiling exercise with `cache.c` +--- diff --git a/info0939/cache.c b/info0939/cache.c new file mode 100644 index 0000000000000000000000000000000000000000..2a5f7ffa9d7be301924531d7c843192c9cd1da3f --- /dev/null +++ b/info0939/cache.c @@ -0,0 +1,110 @@ +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/resource.h> + +static void * +Malloc(size_t size) +{ + void *ret = malloc(size); + if (!ret) + { + printf("malloc() failed\n"); + abort(); + } + return ret; +} + +static void +preheat(double *out, const double *mat, const double *vec, + size_t m, size_t n) +{ + for (size_t i = 0; i < m; i++) + for (size_t j = 0; j < n; j++) + out[i] += mat[i*n + j] * vec[j]; +} + +static void +mvp_good(double *out, const double *mat, const double *vec, + size_t m, size_t n) +{ + for (size_t i = 0; i < m; i++) + for (size_t j = 0; j < n; j++) + out[i] += mat[i*n + j] * vec[j]; +} + +static void +mvp_bad(double *out, const double *mat, const double *vec, + size_t m, size_t n) +{ + for (size_t j = 0; j < n; j++) + for (size_t i = 0; i < m; i++) + out[i] += mat[i*n + j] * vec[j]; +} + +static double +compute_time(const struct rusage *rstart, const struct rusage *rend) +{ + double ret = rend->ru_utime.tv_sec + rend->ru_utime.tv_usec/1e6; + ret -= rstart->ru_utime.tv_sec + rstart->ru_utime.tv_usec/1e6; + return ret; +} + +int +main(int argc, char * const argv[]) +{ + int rows = 0, cols = 0; + int ch; + + while ( (ch = getopt(argc, argv, "r:c:")) != -1 ) + { + switch (ch) + { + case 'r': + rows = atoi(optarg); + break; + + case 'c': + cols = atoi(optarg); + break; + + case '?': + default: + printf("Usage: %s -r <rows> -c <cols>\n", argv[0]); + exit(EXIT_FAILURE); + } + } + + if (rows < 1 || cols < 1) + { + printf("Number of rows and cols must be >1\n"); + exit(EXIT_FAILURE); + } + + double *mat = (double *) Malloc(rows*cols*sizeof(double)); + double *vec = (double *) Malloc(cols*sizeof(double)); + double *out = (double *) Malloc(rows*sizeof(double)); + + preheat(out, mat, vec, rows, cols); + + struct rusage rstart, rend; + getrusage(RUSAGE_SELF, &rstart); + mvp_good(out, mat, vec, rows, cols); + getrusage(RUSAGE_SELF, &rend); + + double time = compute_time(&rstart, &rend); + printf("Good MVP: %g secs\n", time); + + getrusage(RUSAGE_SELF, &rstart); + mvp_bad(out, mat, vec, rows, cols); + getrusage(RUSAGE_SELF, &rend); + + time = compute_time(&rstart, &rend); + printf("Bad MVP: %g secs\n", time); + + free(out); + free(vec); + free(mat); + return 0; +} + diff --git a/info0939/intdiv.c b/info0939/intdiv.c new file mode 100644 index 0000000000000000000000000000000000000000..19c965ec47d96113aa20822df61bb4a1a1b1a364 --- /dev/null +++ b/info0939/intdiv.c @@ -0,0 +1,46 @@ +#include <stdio.h> +#include <assert.h> + +typedef struct div_result { + int quo; + int rem; +} div_result_t; + +static void +integer_division(int x, int y, struct div_result *res) +{ + /* Precondition: for the program to work, the input must satisfy + * x >= 0 (just to keep things simple) + * y > 0 (because division by zero is not defined) + */ + assert(x >= 0 && y > 0); + + res->quo = 0; + res->rem = x; + + /* The loop invariant is that + * - quotient * y + res has to be equal to x + * - y > 0 + * The invariant must be true before, during and after the loop. + */ + assert( (res.quo*y + res.rem == x) && res.rem >= 0 && y > 0 ); + + while (res.rem >= y) + { + /* At this point invariant AND while condition should be valid */ + assert( (res.quo*y + res.rem == x) && res.rem >= 0 && y > 0 && res.rem >= y ); + + res.rem = res.rem - y; + res.quo = res.quo + 1; + + /* At this point the invariant should be re-established */ + assert( (res.quo*y + res.rem == x) && res.rem >= 0 && y > 0 ); + } + + /* Postcondition: invariant AND NOT while condition */ + assert( (res.quo*y + res.rem == x) && res.rem >= 0 && y > 0 && rem < y ); + + /* In addition, note that IF we enter the cycle THEN res.rem ALWAYS + * decreases. res.rem is a ``bound function'' and allows you to say + * that your program ALWAYS terminates. */ +} diff --git a/info0939/stack_corrupt.c b/info0939/stack_corrupt.c new file mode 100644 index 0000000000000000000000000000000000000000..76b07ea222fde87bcfd8a6af16ae942819dec65d --- /dev/null +++ b/info0939/stack_corrupt.c @@ -0,0 +1,27 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define BUFSZ 4 + +struct mystruct +{ + int x; + char y[BUFSZ]; +}; + +static void +fun(struct mystruct *s) +{ + s->x = 42; + strcpy(s->y, "Hello world, this is INFO0939"); +} + +int main(void) +{ + int *x = (int *) malloc(400); + struct mystruct s; + fun(&s); + free(x); + return 0; +}