unsigned long* sieve(size_t max, size_t *size) {
bool not_prime[max];
not_prime[0] = true;
not_prime[1] = true;
// We need to initialize the rest ourselves
// Remember how many primes we have found.
*size = 0;
// Loop over all numbers to 100
for (int i = 2; i < max; ++i) {
// Loop over all multiples of any prime numbers
if (!not_prime[i]) {
*size += 1;
// Mark all multiples as not prime
for (int j = 2*i; j < max; j+=i) {
not_prime[j] = true;
}
}
}
// 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 (!not_prime[i]) {
*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;
}
```
---
## Debug Symbols
* We're going to add a new flag when compiling
* gcc -g sieve.c -o sieve
* `-g` tells the compiling to leave in extra debugging information
* It will allow valgrind to give us code locations for an error
---
$ valgrind ./sieve 10
==635381== Memcheck, a memory error detector
==635381== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==635381== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info
==635381== Command: ./sieve 10
==635381==
==635381== Conditional jump or move depends on uninitialised value(s)
==635381== at 0x1092D0: sieve (sieve.c:18)
==635381== by 0x109432: main (sieve.c:52)
==635381==
==635381== Conditional jump or move depends on uninitialised value(s)
==635381== at 0x10935D: sieve (sieve.c:32)
==635381== by 0x109432: main (sieve.c:52)
==635381==
2 is prime.
3 is prime.
5 is prime.
7 is prime.
==635381==
==635381== HEAP SUMMARY:
==635381== in use at exit: 0 bytes in 0 blocks
==635381== total heap usage: 2 allocs, 2 frees, 1,056 bytes allocated
==635381==
==635381== All heap blocks were freed -- no leaks are possible
==635381==
==635381== Use --track-origins=yes to see where uninitialised values come from
==635381== For lists of detected and suppressed errors, rerun with: -s
==635381== ERROR SUMMARY: 8 errors from 2 contexts (suppressed: 0 from 0)
---
## --track-origins=yes
$ valgrind --track-origins=yes ./sieve 10
==635391== Memcheck, a memory error detector
==635391== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==635391== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info
==635391== Command: ./sieve 10
==635391==
==635391== Conditional jump or move depends on uninitialised value(s)
==635391== at 0x1092D0: sieve (sieve.c:18)
==635391== by 0x109432: main (sieve.c:52)
==635391== Uninitialised value was created by a stack allocation
==635391== at 0x1091E9: sieve (sieve.c:6)
==635391==
==635391== Conditional jump or move depends on uninitialised value(s)
==635391== at 0x10935D: sieve (sieve.c:32)
==635391== by 0x109432: main (sieve.c:52)
==635391== Uninitialised value was created by a stack allocation
==635391== at 0x1091E9: sieve (sieve.c:6)
---
## Heap and Stack
* The VLA is using stack memory
* And valgrind checks both the stack and the heap
* Let's fix the initialization and see what valgrind has to say
* `bool not_prime[max] = {};`
---
## Happy valgrind
$ valgrind --track-origins=yes ./sieve 10
==635455== Memcheck, a memory error detector
==635455== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==635455== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info
==635455== Command: ./sieve 10
==635455==
2 is prime.
3 is prime.
5 is prime.
7 is prime.
==635455==
==635455== HEAP SUMMARY:
==635455== in use at exit: 0 bytes in 0 blocks
==635455== total heap usage: 2 allocs, 2 frees, 1,056 bytes allocated
==635455==
==635455== All heap blocks were freed -- no leaks are possible
==635455==
==635455== For lists of detected and suppressed errors, rerun with: -s
==635455== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
---
## Using Valgrind
* You can run valgrind on any program
* At worst, it costs you a few seconds
---
## Debugging crashes
* What if our program crashes?
* Valgrind won't help us out
* The `-g` flag tells the compiler to leave information in the binary file
* Associates compiled code to its source
* It helped out Valgrind, but another tool can also use it
---
## gdb
* `gdb` (the gnu debugger) is a tool (like `gcc`)
* Launch a program through gdb to debug it:
$ gcc -g sieve.c -o sieve
$ gdb sieve
GNU gdb (Ubuntu 15.0.50.20240403-0ubuntu1) 15.0.50.20240403-git
Copyright (C) 2024 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
.
Find the GDB manual and other documentation resources online at:
.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from sieve...
(gdb)
---
## Now What?
* `gdb` has a `help` command
* To run you program, use `run `
---
## Example
(gdb) run 10
Starting program: /home/bfirner/Documents/Teaching/teaching/CS211/lectures/sieve 10
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
2 is prime.
3 is prime.
5 is prime.
7 is prime.
[Inferior 1 (process 3037633) exited normally]
---
## Breakpoints
* Let's examine our code as it executes
---
## Setting a breakpoint
(gdb) break sieve.c:28
Breakpoint 1 at 0x55555555534f: file sieve.c, line 28.
(gdb) run 10
Starting program: /home/bfirner/Documents/Teaching/teaching/CS211/lectures/sieve 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=0x7fffffffe530) at sieve.c:28
28 unsigned long* primes = calloc(*size, sizeof(unsigned long));
(gdb)
---
## Inspection
* You can now inspect values
(gdb) print primes
$4 = (unsigned long *) 0x555555557d90
(gdb) print *primes
$5 = 93824992235968
(gdb) print primes[0]
$6 = 93824992235968
(gdb) print max
$7 = 10
(gdb) print *size
$8 = 4
---
## Stepping
* `primes` isn't actually set yet
* The breakpoint stopped 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=4, elem_size=8) at ./malloc/malloc.c:3699
0x0000555555555363 in sieve (max=10, size=0x7fffffffe530) at sieve.c:28
28 unsigned long* primes = calloc(*size, sizeof(unsigned long));
Value returned is $9 = (void *) 0x5555555592a0
(gdb) print primes
$10 = (unsigned long *) 0x555555557d90
---
## 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 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
---
## Next topics
* Structs
* Cleaning up our code
* And then we'll go on to non-integer numbers!
---
## Class Code
```C
#include
#include
#include
unsigned int* sieve(int max, size_t* num_primes) {
// Set to 0 because I don't trust the caller
*num_primes = 0;
bool not_prime[max] = {};
not_prime[0] = true;
not_prime[1] = true;
for (int i = 2; i < max; ++i) {
if (!not_prime[i]) {
++(*num_primes);
// Set all multiples to be not prime
for (int j = 2*i; j < max; j += i) {
not_prime[j] = true;
}
}
}
// Calloc sets the memory to 0
unsigned int* primes = calloc(*num_primes, sizeof(unsigned int));
// Malloc doesn't change the values
/*
unsigned int* primes = malloc(*num_primes * sizeof(unsigned int));
for (int i = 0; i < *num_primes; ++i) {
primes[0] = 0;
}
*/
int cur_idx = 0;
unsigned int* cur_ptr = primes;
// Put the primes inside
for (int i = 2; i < max; ++i) {
if (!not_prime[i]) {
//primes[cur_idx] = i;
//++cur_idx;
//*(primes + cur_idx) = i;
//++cur_idx;
*cur_ptr = i;
++cur_ptr;
}
}
return primes;
}
int main(int argc, char** argv) {
if (argc < 2) {
printf("We need a number!\n");
return 1;
}
int size = atoi(argv[1]);
if (size < 2) {
printf("The number %i is too small!\n", size);
return 1;
}
size_t num_primes;
unsigned int* primes = sieve(size, &num_primes);
// Print out the primes
unsigned int* end = primes+num_primes;
for (unsigned int* p = primes; p != end; ++p) {
printf("%u is prime\n", *p);
}
free(primes);
return 0;
}
```