I'm trying to use realloc() every loop, so I only use necessary memory for my int array in C, but the output values are changed. Nevertheless, when using Valgrind on my code, I have the right values.
I'm doing the first day of Advent of Code 2022.
The input file is a .txt file that looks like this:
7569
1357
10134
4696
4423
8869
3562
6597
4038
9038
1352
8005
4811
6281
3961
4023
7234
3510
7728
1569
4583
7495
3941
6015
6531
2637
I was trying to sum numbers and store it in my array in a specific index, and if there is a blank line increase my index.
Given that example input, it should print like this:
elf [0] = 47207
elf [1] = 41509
elf [2] = 51243
What I got:
elf [245] = 63138
elf [246] = 181168
elf [247] = 41570
elf [248] = 36264
elf [249] = 59089
elf [250] = 185061
What I want (result using valgrind):
elf [245] = 63138
elf [246] = 52399
elf [247] = 41570
elf [248] = 36264
elf [249] = 59089
elf [250] = 56308
My code:
int *read_calories(char *filename)
{
FILE *fp = fopen(filename, "r");
char *line = NULL;
int i = 0;
size_t len = 0;
ssize_t nread;
struct stat size;
stat(filename, &size);
int tab_size = 1;
int *calories = malloc(sizeof(int) * 2);
if (fp == NULL)
{
perror("Can't open file\n");
exit(EXIT_FAILURE);
}
while ((nread = getline(&line, &len, fp)) != -1)
{
if (nread == 1) {
i++;
++tab_size;
calories = realloc(calories, tab_size * sizeof(int));
} else {
calories[i] += atoi(line);
}
}
calories[i + 1] = '\0';
free(line);
fclose(fp);
return calories;
}
int main()
{
int *calories = read_calories("input.txt");
for (int i = 0; calories[i] != '\0'; i++) {
printf("elf [%d] = %d \n", i, calories[i]);
}
free(calories);
return 0;
}
Your calorie reading code has good stuff in it, but is rather disorganized. The data allocated by malloc() is not zeroed, so using += in calories[i] += atoi(line); is not good. You've not shown the input data format.
It isn't clear whether you have to read a bunch of numbers up to a blank line and store the sum in the array (and rinse and repeat to EOF), or whether you just need to read numbers from the file and store those into the array.
Each line has a number to be stored separately
The code below assumes that each line contains a number that should be stored in the array. Adapting to the other style of processing is not difficult.
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern int *read_calories(const char *filename);
int *read_calories(const char *filename)
{
FILE *fp = fopen(filename, "r");
if (fp == NULL)
{
fprintf(stderr, "Failed to open file '%s' for reading (%d: %s)\n", filename, errno, strerror(errno));
exit(EXIT_FAILURE);
}
int tab_used = 0;
int tab_size = 2;
int *calories = malloc(sizeof(int) * tab_size);
if (calories == NULL)
{
fprintf(stderr, "Failed to allocate memory (%d: %s)\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
char *line = NULL;
size_t len = 0;
while (getline(&line, &len, fp) != -1)
{
if (tab_used == tab_size - 1)
{
size_t new_size = 2 * tab_size;
void *new_data = realloc(calories, new_size * sizeof(int));
if (new_data == NULL)
{
fprintf(stderr, "Failed to allocate memory (%d: %s)\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
calories = new_data;
tab_size = new_size;
}
calories[tab_used++] = atoi(line);
}
calories[tab_used] = 0;
free(line);
fclose(fp);
return calories;
}
int main(void)
{
int *calories = read_calories("input.txt");
assert(calories != NULL);
for (int i = 0; calories[i] != 0; i++)
printf("elf [%d] = %d \n", i, calories[i]);
free(calories);
return 0;
}
I'm not keen on perror() ā it does a job and is simple, but it's relatively hard to get good messages out of it. The code ensures there's an extra entry in the array for a zero entry at the end. It does not, however, spot a zero entry in the middle of the array. That would usually be caused by atoi() failing to convert the value.
I generated an input.txt file containing 10 random values between 100 and 1000:
478
459
499
997
237
423
185
630
964
594
The output from the program was:
elf [0] = 478
elf [1] = 459
elf [2] = 499
elf [3] = 997
elf [4] = 237
elf [5] = 423
elf [6] = 185
elf [7] = 630
elf [8] = 964
elf [9] = 594
Blocks of numbers to be summed, separated by blank lines
This code is closely based on the previous answer, but the 'add to the array' code is extracted into a function so it can be used twice. It might be better to use a structure to encapsulate the array details. I should probably also use size_t rather than int for the sizes.
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void add_to_array(int **table, int *tab_size, int *tab_used, int value)
{
if (*tab_used == *tab_size - 1)
{
size_t new_size = 2 * *tab_size;
void *new_data = realloc(*table, new_size * sizeof(int));
if (new_data == NULL)
{
fprintf(stderr, "Failed to allocate memory (%d: %s)\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
*table = new_data;
*tab_size = new_size;
}
(*table)[(*tab_used)++] = value;
}
static int *read_calories(const char *filename)
{
FILE *fp = fopen(filename, "r");
if (fp == NULL)
{
fprintf(stderr, "Failed to open file '%s' for reading (%d: %s)\n", filename, errno, strerror(errno));
exit(EXIT_FAILURE);
}
int tab_used = 0;
int tab_size = 2;
int *calories = malloc(sizeof(int) * tab_size);
if (calories == NULL)
{
fprintf(stderr, "Failed to allocate memory (%d: %s)\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
char *line = NULL;
size_t len = 0;
int current_sum = 0;
ssize_t nread;
while ((nread = getline(&line, &len, fp)) != -1)
{
if (nread == 1)
{
add_to_array(&calories, &tab_size, &tab_used, current_sum);
current_sum = 0;
}
else
current_sum += atoi(line);
}
if (current_sum > 0)
add_to_array(&calories, &tab_size, &tab_used, current_sum);
calories[tab_used] = 0;
free(line);
fclose(fp);
return calories;
}
int main(void)
{
int *calories = read_calories("input.txt");
assert(calories != NULL);
for (int i = 0; calories[i] != 0; i++)
printf("elf [%d] = %d \n", i, calories[i]);
free(calories);
return 0;
}
Revised data file:
184
861
513
507
790
897
715
287
729
534
777
945
950
696
605
287
763
839
860
779
522
140
281
190
744
976
420
462
591
710
435
707
580
855
208
806
205
799
537
395
922
356
397
464
435
470
973
203
713
264
(Note that there isn't a blank line at the end!)
Output:
elf [0] = 2855
elf [1] = 4884
elf [2] = 2251
elf [3] = 3528
elf [4] = 2853
elf [5] = 4968
elf [6] = 1810
elf [7] = 932
elf [8] = 4017
elf [9] = 1180
Awk script to cross-check the result:
awk 'NF == 0 { print sum; sum = 0 } NF == 1 { sum += $1 } END { print sum }' input.txt
Results:
2855
4884
2251
3528
2853
4968
1810
932
4017
1180
Related
I'm facing a very odd issue when trying to read a file in C.
I've parsed a file path via a command line argument, and gotten the size using the stat() function, and this works fine. However, after I read the file, all of my integers become arbitrarily large, and I cannot for the life of me figure out why!
Here is the relevant code from my main function:
int main( int argc, char *argv[] ) {
char *filepath = argv[1];
startaddress = strtol(argv[2], &endptra, 16);
endaddress = strtol(argv[3], &endptrb, 16);
int filesize = getfilesize(filepath);
printf("Filesize is %d\n", filesize);
unsigned char *mem = malloc( filesize );
printf("Filesize here is %d\n", filesize);
int size2 = filesize;
int test3 = 18;
printf("Size 2 set to %d\n", size2);
printf("Test 3 set to %d\n", test3);
// READ FILE
loadimage(filepath, &mem, filesize);
printf("Right after load image, file size is %d\n", filesize);
printf("%s\n", strerror(errno));
printf("Filesize is %d\n", filesize);
printf("size2: %d\n", size2);
printf("test3: %d\n", test3);
exit(0);
}
"getfilesize" is a relatively simple function that appears to work well:
int getfilesize(char *path) {
struct stat sbuffer;
int filesize = 0;
filesize = stat(path, &sbuffer);
if (filesize == -1) {
return 0;
} else {
return sbuffer.st_size;
}
}
Here is the loadimage function:
int loadimage(char *path, unsigned char *mem[], int size) {
int fdin, retval;
unsigned char buf[2048];
int nread;
printf("Path is: %s\n", path);
printf("Size is: %d\n", size);
fdin = open(path, O_RDONLY);
printf("fdin: %d\n", fdin);
if(fdin == -1) {
die(strerror( errno ));
}
int count = 0;
nread = read(fdin, buf, 2048);
for(; count < nread; count++) {
mem[count] = &buf[count];
}
if(nread == -1) {
die(strerror( errno ));
}
retval = close(fdin);
printf("Size is now %d\n", size);
return 1;
}
And this is the output of the result:
Filesize is 39
Filesize here is 39
Size 2 set to 39
Test 3 set to 18
Path is: test_file.txt
Size is: 39
fdin: 3
Size is now 39
Right after load image, file size is 32765
Success
Filesize is 32765
size2: 1418855892
test3: 32765
This is baffling to me and I cannot figure it out! It's confusing that even integers that I don't pass to the function are being modified as well. I'm assuming there's some sort of memory overflow happening somewhere, but I'm not used to working in the file system in C.
Thanks!
It seems to me that the problem is here:
int loadimage(char *path, unsigned char *mem[], int size) {
The mem argument should be just a pointer, or just an array, but not both. Aside from that, you're doing the same with the buf local variable, you're dereferencing it more than once when you use it (however that case might be harmless, I'm not sure). loadimage() should be:
int loadimage(char *path, unsigned char *mem, int size) {
int fdin, retval;
unsigned char buf[2048];
int nread;
printf("Path is: %s\n", path);
printf("Size is: %d\n", size);
fdin = open(path, O_RDONLY);
printf("fdin: %d\n", fdin);
if(fdin == -1) {
die(strerror( errno ));
}
int count = 0;
nread = read(fdin, buf, 2048);
for(; count < nread; count++) {
mem[count] = buf[count]; //no need for derefencing
}
if(nread == -1) {
die(strerror( errno ));
}
retval = close(fdin);
printf("Size is now %d\n", size);
return 1;
}
then, when calling it, do not dereference mem:
loadimage(filepath, mem, filesize);
Sorry, I don't have time so I haven't compiled it, but you get the idea, most probably that's the problem.
I'm trying to test possible problems for reading from and writing to same files concurrently on an embedded device which has embedded Linux on it.
I have two processes: Writer and Reader. As the names suggest, the Writer constantly writes 1 of 3 const strings to 10 files one-by-one and at the same time the Reader reads the same files and compares the outputs to be sure they are correct. In order to overcome synchronization issues, I thought I could use mandatory file locking mechanism.
Note: Reader process is actually a placeholder for an user application which will read the files in a similar fashion. Since it will not be in my control, advisory file locking will not be applicable.
I have mounted a tmpfs with mandatory file locking enabled.
mount -t tmpfs -o mand,size=1m tmpfs /tmp2
The Writer creates 10 files with mandatory file locking enabled. And in each iteration one of the three strings below is written to files.
const char* s1 = "31415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679";
const char* s2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
const char* s3 = "************************************************************************";
The Reader reads these files one-by-one and reports if there is a mismatch. And it occasionally (roughly once in every 1000 iterations, see the codes below) reads strings like:
"************************************************************************62862089986280348253421170679"
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz********************"
This is exactly the problem I was expecting to encounter. The smaller strings are written on top of the bigger ones and the remaining characters are not deleted.
I tried to use fsync(fn) after writing to file, but it did not work. It should not be needed anyway, since this is a tmpfs.
What is the reason for this and what can I do to avoid this?
Here are the codes for the Writer and the Reader:
The Writer
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <errno.h>
const char* s1 = "31415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679";
const char* s2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
const char* s3 = "************************************************************************";
int main()
{
// create /tmp2/test directory
if (mkdir("/tmp2/test", 0777) == -1) {
if (errno != EEXIST) {
perror("mkdir");
return -1;
}
}
// create 10 input files input0, input1, ..., input9
for (int i = 0; i < 10; i++) {
char path[50] = "/tmp2/test/input";
sprintf(path + 16, "%d", i);
FILE* fp = fopen(path, "w");
if (fp == NULL) {
fprintf(stderr, "Unable to create file %s\n", path);
perror("create: ");
return -1;
}
chmod(path, 02644); //mandatory file locking enabled
fclose(fp);
}
// flock structure
struct flock fl;
fl.l_type = F_WRLCK;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0;
fl.l_pid = 0;
// loop
for(int step = 0; ; step++) {
usleep(50000);
for (int i = 0; i < 10; i++) {
char path[50] = "/tmp2/test/input";
sprintf(path + 16, "%d", i);
FILE* fp = fopen(path, "w+");
if (fp == NULL) {
fprintf(stderr, "Unable to open file %s\n", path);
perror("fopen: ");
return -1;
}
int fd = fileno(fp);
// place a write lock
fl.l_type = F_WRLCK;
if (fcntl(fd, F_SETLK, &fl) == -1) {
perror("lock: ");
return -1;
}
// write 1 of 3 strings
if (step % 3 == 0) {
write(fd, s1, strlen(s1));
} else if (step % 3 == 1) {
write(fd, s2, strlen(s2));
} else {
write(fd, s3, strlen(s3));
}
//fsync(fd); // fsync should not be needed since this is a tmpfs
// unlock
fl.l_type = F_UNLCK;
if (fcntl(fd, F_SETLK, &fl) == -1) {
perror("lock: ");
return -1;
}
fclose(fp);
}
}
return 0;
}
The Reader:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
const char* s1 = "31415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679";
const char* s2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
const char* s3 = "************************************************************************";
int main()
{
// counters for errors
int err_ctr = 0;
int read_err_ctr = 0;
// loop
for(int step = 0; ; step++) {
usleep(50000);
for (int i = 0; i < 10; i++) {
char path[50] = "/tmp2/test/input";
sprintf(path + 16, "%d", i);
FILE* fp = fopen(path, "r");
if (fp == NULL) {
fprintf(stderr, "Unable to open file %s\n", path);
perror("read: ");
return -1;
}
// read the file
char reading[150];
if (fgets(reading, 150, fp) == NULL) {
fclose(fp);
read_err_ctr++;
printf("Step = %d; ReadError = %d; from %s\n", step, read_err_ctr, path);
continue;
}
fclose(fp);
// continue if the string matches
if (strcmp(reading, s1) == 0) {
continue;
} else if (strcmp(reading, s2) == 0) {
continue;
} else if (strcmp(reading, s3) == 0) {
continue;
}
// print error
err_ctr++;
printf("Step = %d; Error = %d; I've read %s from %s\n", step, err_ctr, reading, path);
}
}
return 0;
}
I found this code on git and wanted to use it, but someone made a comment about a security bug in it. I don't seem to identify it:
int32_t read_arrbuff(FILE *f, uint32_t *arrmap) {
int32_t i = 0, n_map;
fread(&n_map, sizeof(n_map), 1, f);
if (n_map > 256)
return -1;
while (n_map--) {
fread(&arrmap[i++], sizeof(uint32_t), 1, f);
}
return n_map;
}
Taken in isolation, the problems include:
You don't check that f is not NULL.
You don't check that arrmap is not NULL.
You don't check that the first fread() was successful.
You don't fully validate the value that is read; negative or zero values will throw things off badly.
You don't check that the second fread() was successful.
You always return -1, regardless of whether anything worked or failed.
Which of those are security problems? At some levels, all of them. In some contexts, you could assume that f and arrmap are legitimate without checking. Not checking that the reads are successful, especially the first, is a serious problem. The negative values for n_map would be a serious issue. Claiming success when every read failed would be a problem.
When the loop completes, n_map is set to -1 (it was zero before the post-decrement). So, you return -1 on failure or success. That's not helpful. It should almost certainly return the value of n_map that was read from file, so the caller can tell how many values are in the array.
It is usually best not to hard code a size limit like 256 into the program. The interface should include the array size and you should check against the passed array size.
Working to the original interface, you could use:
#include "stderr.h"
#include <stdio.h>
#include <inttypes.h>
extern int32_t read_arrbuff(FILE *f, uint32_t *arrmap);
int32_t read_arrbuff(FILE *f, uint32_t *arrmap)
{
int32_t n_map;
if (fread(&n_map, sizeof(n_map), 1, f) != 1)
err_syserr("failed to read data (count)\n");
if (n_map > 256 || n_map <= 0)
return -1;
for (int32_t i = 0; i < n_map; i++)
{
if (fread(&arrmap[i], sizeof(uint32_t), 1, f) != 1)
err_syserr("failed to read data (value %" PRId32 ")\n", i);
}
return n_map;
}
int main(int argc, char **argv)
{
err_setarg0(argv[0]);
if (argc != 1)
err_usage("");
char file[] = "data";
/* Create data file */
FILE *fp = fopen(file, "wb");
if (fp == NULL)
err_syserr("failed to open file '%s' for writing\n", file);
int32_t nmap = 32;
if (fwrite(&nmap, sizeof(nmap), 1, fp) != 1)
err_syserr("failed to write to file '%s'\n", file);
for (int32_t i = 0; i < nmap; i++)
{
if (fwrite(&i, sizeof(i), 1, fp) != 1)
err_syserr("failed to write to file '%s'\n", file);
}
fclose(fp);
/* Read data file */
fp = fopen(file, "rb");
if (fp == NULL)
err_syserr("failed to open file '%s' for reading\n", file);
uint32_t amap[256];
int32_t rc = read_arrbuff(fp, amap);
printf("rc = %" PRId32 "\n", rc);
for (int32_t i = 0; i < rc; i++)
printf("%3" PRId32 " = %3" PRId32 "\n", i, amap[i]);
fclose(fp);
return 0;
}
You can debate whether the unilateral exits imposed by err_syserr() are appropriate. (The declarations and source for the err_*() functions is in stderr.h and stderr.c, and is available from GitHub.)
An alternative version taking the maximum array size from a function parameter is:
#include "stderr.h"
#include <assert.h>
#include <stdio.h>
#include <inttypes.h>
extern int32_t read_arrbuff(FILE *f, int32_t a_size, uint32_t arrmap[a_size]);
int32_t read_arrbuff(FILE *f, int32_t a_size, uint32_t arrmap[a_size])
{
int32_t n_map;
assert(f != NULL && arrmap != NULL && a_size > 0 && a_size <= 256);
if (fread(&n_map, sizeof(n_map), 1, f) != 1)
{
err_sysrem("failed to read data (count)\n");
return -1;
}
if (n_map > a_size || n_map <= 0)
{
err_sysrem("count %" PRId32 " is out of range 1..%" PRId32 "\n",
n_map, a_size);
return -1;
}
for (int32_t i = 0; i < n_map; i++)
{
if (fread(&arrmap[i], sizeof(uint32_t), 1, f) != 1)
{
err_syserr("failed to read data (value %" PRId32 " of %" PRId32 ")\n",
i, n_map);
}
}
return n_map;
}
int main(int argc, char **argv)
{
err_setarg0(argv[0]);
if (argc != 1)
err_usage("");
char file[] = "data";
/* Create data file */
FILE *fp = fopen(file, "wb");
if (fp == NULL)
err_syserr("failed to open file '%s' for writing\n", file);
int32_t nmap = 32;
if (fwrite(&nmap, sizeof(nmap), 1, fp) != 1)
err_syserr("failed to write to file '%s'\n", file);
for (int32_t i = 0; i < nmap; i++)
{
if (fwrite(&i, sizeof(i), 1, fp) != 1)
err_syserr("failed to write to file '%s'\n", file);
}
fclose(fp);
/* Read data file */
fp = fopen(file, "rb");
if (fp == NULL)
err_syserr("failed to open file '%s' for reading\n", file);
enum { AMAP_SIZE = 256 };
uint32_t amap[AMAP_SIZE];
int32_t rc = read_arrbuff(fp, AMAP_SIZE, amap);
printf("rc = %" PRId32 "\n", rc);
for (int32_t i = 0; i < rc; i++)
printf("%3" PRId32 " = %3" PRId32 "\n", i, amap[i]);
fclose(fp);
return 0;
}
This version reports a specific error and returns on error. It makes semi-appropriate assertions on entry (the 256 limit is not necessarily appropriate, but is consistent with the original code).
The output is unexciting (and the same from both):
rc = 32
0 = 0
1 = 1
2 = 2
3 = 3
4 = 4
5 = 5
6 = 6
7 = 7
8 = 8
9 = 9
10 = 10
11 = 11
12 = 12
13 = 13
14 = 14
15 = 15
16 = 16
17 = 17
18 = 18
19 = 19
20 = 20
21 = 21
22 = 22
23 = 23
24 = 24
25 = 25
26 = 26
27 = 27
28 = 28
29 = 29
30 = 30
31 = 31
I created a program to get all files in a directory, find the individual checksums and then find the total checksums using multithreading.
I am receiving a segmentation fault so I ran gdb and saw that the error is on line 60 where open() is. After researching the seg fault on SO, and on other forums, I attempted to implement a few different approaches such as changing open() to fopen() with a FILE *handle rather than an int. That change proved incorrect.
After hours of debugging and searching, I am clueless and would greatly appreciate any insight.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <dirent.h>
#include <pthread.h> ///Compile with -pthread or -lpthread
#include <sys/stat.h>
#define BUFFER_SIZE (1<<16)
void cleanup();
void get_filenames();
void* get_checksum();
char **filenames;
int file_cnt;
DIR *dir;
//int handle;
FILE *handle;
unsigned int checksum;
unsigned char* ptr;
int length;
int count;
unsigned char* buffer;
int* sum;
unsigned int total = 0;
int main(int argc, char *argv[]){
int i;
pthread_t* file;
atexit(cleanup);
get_filenames();
printf("There are %d files:\n", file_cnt);
file = calloc(sizeof(pthread_t), file_cnt);
sum = calloc(sizeof(int), file_cnt);
for(i=0; i<file_cnt; i++){
printf("%s\n", filenames[i]);
pthread_create(&(file[i]), NULL, get_checksum, (void*)&filenames[i]);
}
for(i=0; i<file_cnt; i++){
total += sum[i];
}
printf("total is: %u\n", total);
return EXIT_SUCCESS;
}
void* get_checksum(void* a){
int b = *((int *)a);
//handle = open(filenames[b], O_RDONLY); //SEG FAULT HERE
handle = fopen(filenames[b], "r"); //SEG FAULT HERE
if( handle == NULL ){
printf( "Can't open file: %s\n", filenames[b]);
exit(1);
}
buffer = malloc(BUFFER_SIZE);
if( buffer == NULL ){
printf( "Can't get enough memory\n" );
exit(1);
}
checksum = 0;
do{
//length = read( handle, buffer, BUFFER_SIZE );
length = read( handle, buffer, (sizeof(char)));
if( length == -1 ){
printf( "Error reading file: %s\n", filenames[b]);
//return NULL;
exit(1);
}
ptr = buffer;
count = length;
while( count-- ){
checksum = checksum + (unsigned int)( *ptr++ );
sum[b] = checksum;
}
} while( length );
printf("Checksum= %d\nTimes at: %d\n", checksum, (int)clock());
}
void cleanup() {
if(filenames && file_cnt > 0) {
while(file_cnt-- > 0) {
if(filenames[file_cnt]) {
free(filenames[file_cnt]);
}
}
free(filenames);
}
if(dir) {
closedir(dir);
}
return;
}
void get_filenames() {
struct dirent *dir_entry;
if((dir = opendir(".")) == NULL) {
fprintf(stderr, "Couldn't open the directory entry for reading\n");
exit(1);
}
errno = 0;
file_cnt = 0;
while((dir_entry = readdir(dir)) != NULL) {
char **new_filenames = filenames;
static int realative_dirs = 0;
if(realative_dirs < 2 &&
(strcmp(".", dir_entry->d_name) == 0 || strcmp("..", dir_entry->d_name) == 0)
) {
realative_dirs++;
continue;
}
new_filenames = (char **)realloc(filenames, sizeof(char **) * (file_cnt + 1));
if(new_filenames == NULL) {
free(filenames[file_cnt]);
fprintf(stderr, "Could not allocate reference for filename[%d]\n", file_cnt);
exit(1);
}
filenames = new_filenames;
filenames[file_cnt] = (char *)calloc(strlen(dir_entry->d_name) + 1, sizeof(char));
if(filenames[file_cnt] == NULL) {
fprintf(stderr, "Could not allocate memory for filename[%d]'s string: \"%s\"\n",
file_cnt, dir_entry->d_name);
exit(1);
}
strcpy(filenames[file_cnt], dir_entry->d_name);
file_cnt++;
}
if(errno != 0) {
fprintf(stderr, "An error occured getting the filenam list\n");
exit(1);
}
return;
}
Below is the output and gdb debugging:
There are 24 files:
.windows
.xscreensaver
.alias
.cshrc
Segmentation fault
(gdb) run
Starting program: /home/nolooking/a.out
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
There are 24 files:
.windows
[New Thread 0x7ffff781e700 (LWP 15957)]
.xscreensaver
[New Thread 0x7ffff701d700 (LWP 15958)]
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff781e700 (LWP 15957)]
0x0000000000400d53 in get_checksum (a=0x60b610) at checksum.c:60
60 handle = open(filenames[b], O_RDONLY);
(gdb) backtrace
#0 0x0000000000400d53 in get_checksum (a=0x60b610) at checksum.c:60
#1 0x00007ffff7bc6374 in start_thread () from /lib64/libpthread.so.0
#2 0x00007ffff7907c3d in clone () from /lib64/libc.so.6
(gdb) quit
A debugging session is active.
UPDATE:
I took the advice of one user in the comments who suggested that I use:
handle=fopen((char*)a, "r");. I can successfully print out the file names when the if statement if(handle==NULL) is commented out. When I include that if statement I receive the following output:
There are 24 files:
.windows
.xscreensaver
.alias
.cshrc
Can't open file: pā`
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <dirent.h>
#include <pthread.h>
#include <sys/stat.h>
#define BUFFER_SIZE (1<<16)
void cleanup();
void get_filenames();
void* get_checksum();
char **filenames;
int file_cnt;
DIR *dir;
//int handle;
FILE *handle;
unsigned int checksum;
unsigned char* ptr;
int length;
int count;
unsigned char* buffer;
int* sum;
unsigned int total = 0;
int main(int argc, char *argv[]){
int i;
pthread_t* file;
atexit(cleanup);
get_filenames();
printf("There are %d files:\n", file_cnt);
file = calloc(sizeof(pthread_t), file_cnt);
sum = calloc(sizeof(int), file_cnt);
for(i=0; i<file_cnt; i++){
printf("%s\n", filenames[i]);
pthread_create(&(file[i]), NULL, get_checksum, (void*)&filenames[i]);
}
for(i=0; i<file_cnt; i++){
total += sum[i];
}
printf("total is: %u\n", total);
return EXIT_SUCCESS;
}
void* get_checksum(void* a){
int b = *((int *)a);
handle = fopen(((char*)a), "r");
if( handle == NULL ){
printf( "Can't open file: %s\n", ((char*)a));
exit(1);
}
buffer = malloc(BUFFER_SIZE);
if( buffer == NULL ){
printf( "Can't get enough memory\n" );
exit(1);
}
checksum = 0;
do{
length = read( handle, buffer, BUFFER_SIZE );
if( length == -1 ){
printf( "Error reading file: %s\n", ((char*)a));
//return NULL;
exit(1);
}
ptr = buffer;
count = length;
while( count-- ){
checksum = checksum + (unsigned int)( *ptr++ );
//sum[a] = checksum;
}
} while( length );
printf("Checksum= %d\nTimes at: %d\n", checksum, (int)clock());
}
void cleanup() {
if(filenames && file_cnt > 0) {
while(file_cnt-- > 0) {
if(filenames[file_cnt]) {
free(filenames[file_cnt]);
}
}
free(filenames);
}
if(dir) {
closedir(dir);
}
return;
}
void get_filenames() {
struct dirent *dir_entry;
if((dir = opendir(".")) == NULL) {
fprintf(stderr, "Couldn't open the directory entry for reading\n");
exit(1);
}
errno = 0;
file_cnt = 0;
while((dir_entry = readdir(dir)) != NULL) {
char **new_filenames = filenames;
static int realative_dirs = 0;
if(realative_dirs < 2 &&
(strcmp(".", dir_entry->d_name) == 0 || strcmp("..", dir_entry->d_name) == 0)
) {
realative_dirs++;
continue;
}
new_filenames = (char **)realloc(filenames, sizeof(char **) * (file_cnt + 1));
if(new_filenames == NULL) {
free(filenames[file_cnt]);
fprintf(stderr, "Could not allocate reference for filename[%d]\n", file_cnt);
exit(1);
}
filenames = new_filenames;
filenames[file_cnt] = (char *)calloc(strlen(dir_entry->d_name) + 1, sizeof(char));
if(filenames[file_cnt] == NULL) {
fprintf(stderr, "Could not allocate memory for filename[%d]'s string: \"%s\"\n",
file_cnt, dir_entry->d_name);
exit(1);
}
strcpy(filenames[file_cnt], dir_entry->d_name);
file_cnt++;
}
if(errno != 0) {
fprintf(stderr, "An error occured getting the filenam list\n");
exit(1);
}
return;
}
Why I am receiving that output once I uncomment the if statement?
Change this
pthread_create(&(file[i]), NULL, get_checksum, (void*)&filenames[i]);
to be
pthread_create(&(file[i]), NULL, get_checksum, (void*)i);
and this
int b = *((int *)a);
to be
int b = (int)a;
Also you cannot call read() on a FILE* as it is returned by fopen(). Use fread() instead.
Don't use &i. I'll explain in a bit. The argument you're passing to the thread is wrong a is not an integer. It's meant to be a pointer to a string...
Change the thread create to this...
pthread_create(&(file[i]), NULL, get_checksum, filenames[i]);
then print the string as follows...
void* get_checksum(void *a){
char *file_name = (char *)a;
printf("filename=%s\n", file_name);
You're passing the string as a pointer to the called function. In your code you're trying to use this as an index into the array.
If you want to pass the index as an integer beware... this won't work..
pthread_create(&(file[i]), NULL, get_checksum, &i);
This is multithreaded and the value pointed to by &i is changing as the loop runs. Pass the pointer to the string and do not under any circumstances change filenames as the threads run.
I think your problem is simply because you are passing &filenames[i] instead of simply &i.
Then in void* get_checksum(void* a) you are trying to use a char* as an int.
The code would be more like :
for(i=0; i<file_cnt; i++){
printf("%s\n", filenames[i]);
pthread_create(&(file[i]), NULL, get_checksum, (void*)&i);
}
and in void* get_checksum(void* a) :
int b = *((int *)a);
handle = fopen(filenames[b], "r");
if( handle == NULL ){
printf( "Can't open file: %s\n", filenames[b]);
exit(1);
}
This code receives a input file with 10 filenames, stores them into an 2d array and creates 10+1 threads: a requester and 10 converters. This is only a skeleton, so my threads only print their id, thei're not accessing any global or shared variable or something that requires a mutex. So, what should I do to avoid a segmentation fault here?
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>
#include <time.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/time.h>
#include <math.h>
#include <unistd.h>
#include <errno.h>
#define READ_BUFFER_LEN 16
//#define MAX_LENGTH 128
enum {
MAX_LENGTH = 512
};
typedef struct FileNameArray {
size_t nfiles; /* Number of file names allocated and in use */
size_t maxfiles; /* Number of entries allocated in array */
char **files; /* Array of file names */
} FileNameArray;
//GLOBAL vars
int num_images, *threads_ids /*threads ids*/;
pthread_t *threads;
void deallocate2D(FileNameArray *names) {
size_t i;
for (i = 0; i < names->nfiles; i++)
free(names->files[i]);
free(names->files);
names->nfiles = 0;
names->files = 0;
names->maxfiles = 0;
}
int readInputFile(FILE *fp, FileNameArray *names) {
num_images = names->nfiles;
int max_lines = names->maxfiles;
char **file_names = names->files;
char line[MAX_LENGTH];
char **final_filenames, **array;
while (fgets(line, sizeof line, fp) != NULL) {
if (line[0] != '\n') {
/* Remove newline from end of file name */
char *nl = strchr(line, '\n');
if (nl != 0)
*nl = '\0';
if (num_images >= max_lines) {
max_lines += 100;
array = realloc(file_names, max_lines * sizeof (char*));
if (array == NULL) {
fprintf(stderr, "Error reallocating space for 2d array: %s\n",
strerror(errno));
return -1;
}
names->maxfiles = max_lines;
names->files = array;
file_names = array;
}
if ((file_names[num_images] = malloc(strlen(line) + 1)) == NULL) {
fprintf(stderr, "Error allocating space for 2d array: %s\n",
strerror(errno));
return -1;
}
names->nfiles++;
strcpy(file_names[num_images], line);
printf("name of file %d is: %s \n", num_images, file_names[num_images]);
num_images++;
}
}
printf("Num_lines: %d\n", num_images);
//realloc to number of lines in the file, to avoid wasting memory
if ((final_filenames = realloc(file_names, num_images * sizeof (char*))) == NULL) {
fprintf(stderr, "Error reallocating space for 2d array: %s\n",
strerror(errno));
return -1;
}
names->maxfiles = num_images;
names->files = final_filenames;
return 0;
}
void* requester(void* arg) {
printf("This is the requester thread\n");
//sleep(1);
pthread_exit(NULL);
return NULL;
}
void* converter(void *id) {
int my_id = *((int*) id);
printf("Thread's id is: %d\n", my_id);
pthread_exit(NULL);
return NULL;
}
int main(int argc, char *argv[]) {
FileNameArray names = {0, 0, 0};
int i, rc;
pthread_attr_t attr;
//check parameters
if (argc < 4) {
fprintf(stderr, "Usage: %s input_filename.ppm charWidth charHeight\n",
argv[0]);
return -1;
}
printf("Opening input file [%s]\n", argv[1]);
FILE *fpin = fopen(argv[1], "r");
if (fpin == NULL) {
fprintf(stderr, "Could not open input file %s (%s)\n",
argv[1], strerror(errno));
return -1;
}
if ((names.files = malloc(10 * sizeof (char*))) == NULL) {
fprintf(stderr, "Error allocating initial space for 2d array: %s\n",
strerror(errno));
return -1;
}
names.maxfiles = 10;
if (readInputFile(fpin, &names) == -1) {
fprintf(stderr, "Error reading image filenames from input\n");
return -1;
}
threads = (pthread_t*) malloc(num_images + 1 * sizeof (pthread_t));
threads_ids = (int*) malloc(num_images + 1 * sizeof (int));
/* Initialize and set thread detached attribute */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
//creates requester thread, the 1st one on the list
pthread_create(&threads[0], &attr, requester, &threads_ids[0]);
threads_ids[0] = 0;
//creates so many converter threads as the number of images to convert
for (i = 1; i < num_images + 1; i++) {
threads_ids[i] = i;
rc = pthread_create(&threads[i], &attr, converter, &threads_ids[i]);
if (rc) {
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
/* Free attribute and wait for the other threads */
pthread_attr_destroy(&attr);
for (i = 0; i < num_images + 1; i++) {
printf("Waiting for thread nr %d\n", i);
rc = pthread_join(threads[i], NULL);
if (rc) {
printf("ERROR; return code from pthread_join() is %d\n", rc);
exit(-1);
}
}
fclose(fpin);
free(threads);
free(threads_ids);
printf("###########\n");
deallocate2D(&names);
printf("Done!\n");
return 0;
}
This is one of the possible ouputs:
Num_lines: 10
This is the requester thread
Thread's id is: 1
Thread's id is: 2
Thread's id is: 3
Thread's id is: 5
Thread's id is: 4
Thread's id is: 6
Thread's id is: 7
Thread's id is: 8
Thread's id is: 9
Waiting for thread nr 0
Thread's id is: 10
Segmentation fault
and this is the result of gdb:
Program received signal SIGSEGV, Segmentation fault.
0x001bfea9 in *__GI___libc_free (mem=0x804c278) at malloc.c:3724
3724 malloc.c: No such file or directory.
in malloc.c
(gdb) where
#0 0x001bfea9 in *__GI___libc_free (mem=0x804c278) at malloc.c:3724
#1 0x00120802 in *__GI__dl_deallocate_tls (tcb=<value optimized out>,
dealloc_tcb=112) at dl-tls.c:487
#2 0x00133748 in __free_stacks (limit=41943040) at allocatestack.c:274
#3 0x00133849 in queue_stack (pd=0xb5fd9b70) at allocatestack.c:302
#4 __deallocate_stack (pd=0xb5fd9b70) at allocatestack.c:740
#5 0x00134b37 in pthread_join (threadid=3053296496, thread_return=0x0)
at pthread_join.c:110
#6 0x0804955c in main (argc=4, argv=0xbffff4a4) at main.c:261
threads = (pthread_t*) malloc(num_images + 1 * sizeof (pthread_t));
threads_ids = (int*) malloc(num_images + 1 * sizeof (int));
Certainly you must have intended to write
threads = malloc((num_images + 1) * sizeof (pthread_t));
threads_ids = malloc((num_images + 1) * sizeof (int));
or
threads = calloc(num_images + 1, sizeof (pthread_t));
threads_ids = calloc(num_images + 1, sizeof (int));
instead?