unsigned long* sieve(size_t max, size_t *size) {
bool maybe_prime[max];
memset(maybe_prime, true, max-3);
maybe_prime[0] = false;
maybe_prime[1] = false;
size[0] = 0;
for (int i = 2; i < max; ++i) {
// Mark multiples as not prime
// Go through every multiple of i, starting with 2*i
// Then 2*i+i, 3*i+i, etc
if (maybe_prime[i]) {
size[0] += 1;
for (int j = 2*i; j < max; j += i) {
maybe_prime[j] = false;
}
}
}
// Allocate memory for all of the primes
unsigned long* primes = calloc(*size, sizeof(unsigned long));
unsigned long* cur_prime = primes;
int index = 0;
for (int i = 0; i < max; ++i) {
if (maybe_prime[i]) {
// V 1
//primes[index] = i;
//++index;
// V 2
//*(primes + index) = i;
//++index;
// V 3
//*cur_prime = i;
//++index;
//cur_prime = primes + index;
// V 4
*cur_prime = i;
++cur_prime;
}
}
return primes;
}
```
---
## Setting a breakpoint
(gdb) break sieve.c:25
Breakpoint 1 at 0x555555555458: file sieve.c, line 25.
(gdb) run 10
Starting program: /common/home/bfirner/examples/debug_sieve_example 10
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, sieve (max=10, size=0x7fffffffe0e0) at sieve.c:25
25 unsigned long* primes = calloc(*size, sizeof(unsigned long));
(gdb)
---
## Inspection
* You can now inspect values
(gdb) print primes
$1 = (unsigned long *) 0x555555557d90
(gdb) print primes %p
No symbol "p" in current context.
(gdb) print size
$2 = (size_t *) 0x7fffffffe0e0
(gdb) print primes
$3 = (unsigned long *) 0x555555557d90
(gdb) print *primes
$4 = 93824992235968
(gdb) print primes[0]
$5 = 93824992235968
(gdb) print max
$6 = 10
(gdb) print *size
$7 = 3
---
## Stepping
* `primes` isn't actually set yet
* The breakpoint stop at the line of code, before it's happened
* We can advance by one line with the `step` command
---
## Stepping
(gdb) step
__libc_calloc (n=3, elem_size=8) at ./malloc/malloc.c:3699
warning: 3699 ./malloc/malloc.c: No such file or directory
(gdb) step
3709 in ./malloc/malloc.c
* These steps are too small!
* And we don't have debugging symbols in malloc.c!
* Use `finish` to get out
* Or continue to let the program run
(gdb) finish
Run till exit from #0 __libc_calloc (n=3, elem_size=8) at ./malloc/malloc.c:3709
0x000055555555546c in sieve (max=10, size=0x7fffffffe0e0) at sieve.c:25
25 unsigned long* primes = calloc(*size, sizeof(unsigned long));
Value returned is $8 = (void *) 0x5555555592a0
---
## Examination
* You can see what functions have been called with `bt` (for backtrace)
(gdb) bt
#0 sieve (max=10, size=0x7fffffffe0e0) at sieve.c:28
#1 0x000055555555529b in main (argc=2, argv=0x7fffffffe228) at using_sieve.c:21
* Not very deep
* `frame` will print your current line
---
## Going up and down
* `up` and `down` go up and down the call list
(gdb) run 10
Starting program: /common/home/bfirner/examples/debug_sieve_example 10
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, sieve (max=10, size=0x7fffffffe0e0) at sieve.c:25
25 unsigned long* primes = calloc(*size, sizeof(unsigned long));
(gdb) up
#1 0x000055555555529b in main (argc=2, argv=0x7fffffffe228) at using_sieve.c:21
21 unsigned long* primes = sieve(max, &num_primes);
(gdb) up
Initial frame selected; you cannot go up.
(gdb) down
#0 sieve (max=10, size=0x7fffffffe0e0) at sieve.c:25
25 unsigned long* primes = calloc(*size, sizeof(unsigned long));
(gdb) down
Bottom (innermost) frame selected; you cannot go down.
(gdb) up
#1 0x000055555555529b in main (argc=2, argv=0x7fffffffe228) at using_sieve.c:21
21 unsigned long* primes = sieve(max, &num_primes);
(gdb) print max
$21 = 10
(gdb) print num_primes
$22 = 3
(gdb) print *num_primes
Cannot access memory at address 0x3
(gdb) print &num_primes
$23 = (size_t *) 0x7fffffffe0e0
---
## Debugging
* Using gdb is far better than printing out variables and hoping to catch your error
* Using a makefile makes it simpler to set different flags
* Also reduces the chance of a typo deleting your files
---
## Valgrind
* Another tool for error checking is `valgrind`
* Searches for memory errors
* Not all-knowing
---
bfirner@ilab3:~/examples$ valgrind ./debug_sieve_example 10
==3064832== Memcheck, a memory error detector
==3064832== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==3064832== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info
==3064832== Command: ./debug_sieve_example 10
==3064832==
==3064832== Conditional jump or move depends on uninitialised value(s)
==3064832== at 0x10940D: sieve (sieve.c:16)
==3064832== by 0x10929A: main (using_sieve.c:21)
==3064832==
==3064832== Conditional jump or move depends on uninitialised value(s)
==3064832== at 0x109497: sieve (sieve.c:29)
==3064832== by 0x10929A: main (using_sieve.c:21)
==3064832==
2 is prime.
3 is prime.
5 is prime.
7 is prime.
==3064832==
==3064832== HEAP SUMMARY:
==3064832== in use at exit: 0 bytes in 0 blocks
==3064832== total heap usage: 2 allocs, 2 frees, 1,056 bytes allocated
==3064832==
==3064832== All heap blocks were freed -- no leaks are possible
==3064832==
==3064832== Use --track-origins=yes to see where uninitialised values come from
==3064832== For lists of detected and suppressed errors, rerun with: -s
==3064832== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
---
## Desired change
```C[|7]
#include
#include
#include
#include
unsigned long* sieve(size_t max, size_t *size) {
bool maybe_prime[max] = {};
memset(maybe_prime, true, max-3);
maybe_prime[0] = false;
maybe_prime[1] = false;
size[0] = 0;
for (int i = 2; i < max; ++i) {
// Mark multiples as not prime
// Go through every multiple of i, starting with 2*i
// Then 2*i+i, 3*i+i, etc
if (maybe_prime[i]) {
size[0] += 1;
for (int j = 2*i; j < max; j += i) {
maybe_prime[j] = false;
}
}
}
// Allocate memory for all of the primes
unsigned long* primes = calloc(*size, sizeof(unsigned long));
unsigned long* cur_prime = primes;
int index = 0;
for (int i = 0; i < max; ++i) {
if (maybe_prime[i]) {
// V 1
//primes[index] = i;
//++index;
// V 2
//*(primes + index) = i;
//++index;
// V 3
//*cur_prime = i;
//++index;
//cur_prime = primes + index;
// V 4
*cur_prime = i;
++cur_prime;
}
}
return primes;
}
int main(int argc, char** argv) {
if (argc < 2) {
printf("Give me an argument!\n");
return 1;
}
size_t max = atoi(argv[1]);
if (max < 3) {
printf("Give me a larger number!\n");
return 1;
}
size_t num_primes = 0;
unsigned long* primes = sieve(max, &num_primes);
for (int i = 0; i < num_primes; ++i) {
printf("%lu is prime.\n", primes[i]);
}
free(primes);
return 0;
}
```
---
## Happy valgrind
bfirner@ilab3:~/examples$ valgrind ./debug_sieve_example 10
==3068134== Memcheck, a memory error detector
==3068134== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==3068134== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info
==3068134== Command: ./debug_sieve_example 10
==3068134==
2 is prime.
3 is prime.
5 is prime.
==3068134==
==3068134== HEAP SUMMARY:
==3068134== in use at exit: 0 bytes in 0 blocks
==3068134== total heap usage: 2 allocs, 2 frees, 1,048 bytes allocated
==3068134==
==3068134== All heap blocks were freed -- no leaks are possible
==3068134==
==3068134== For lists of detected and suppressed errors, rerun with: -s
==3068134== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
---
## Next topics
* In C
* File I/O
* Structs