C:Workshop:2016:D0

De wiki-prog
Aller à : navigation, rechercher


Introduction

This session is dedicated to debugging. Your goal is to explore gdb, valgrind and similar tools as much as you can.

Since, debugging is an unbound process the subject is open.

First Step: find the bug !

Here are some code samples with bugs, the idea is to use debugging tools to track the bug.

First a simple Makefile:

# Makefile
 
# CC=clang -fsanitize=address
# CC=gcc -fsanitize=address
CC=gcc
CPPFLAGS=
CFLAGS= -Wall -Wextra -std=c99 -O0 -g3
LDFLAGS=
LDLIBS=
 
all:
 
clean:
	rm -f *.o
 
# END

Next: the most basic segfault, basic.c:

# include <stdio.h>
# include <stdlib.h>
 
int main() {
  int *p = NULL;
  *p = 42;
  printf("*p = %d\n", *p);
  return 0;
}

Then, more hideous bug, overflow.c:

# include <stdio.h>
# include <stdlib.h>
# include <string.h>
 
void foo(char *str) {
  char buf[8];
  strcpy(buf, str);
  printf("buf: %s\n", buf);
}
 
int main() {
  char str[32];
  for (size_t i = 0; i < 31; i++)
    str[i] = 'a';
  str[31] = 0;
  foo(str);
  return 0;
}

For these files, you job is too:

  • Compile, run and observe
  • Use gdb to find the bug(s)
  • Use valgrind to find the bug(s)
  • Use the address sanitizer of the compiler to find the bug(s)

Memory Leaks

Similar to the previous section, we now want to find memory leaks, and of course correct them in the following files.

First, leak01.c:

# include <stdio.h>
# include <stdlib.h>
# include <string.h>
 
void foo(char *str) {
  char *buf = NULL;
  size_t len = 1 + strlen(str);
  buf = calloc(len, 1);
  strncpy(buf, str, len);
  printf("buf = %s\n", buf);
}
 
int main() {
  char *str = "A simple string ...";
  foo(str);
  return 0;
}

Then, leak02.c:

# include <stdlib.h>
# include <stdio.h>
# include <string.h>
 
struct list {
  int          data;
  struct list *next;
};
 
static
struct list* add_front(struct list *list, int val) {
  struct list *tmp = malloc(sizeof (struct list));
  tmp->data = val;
  tmp->next = list;
  return tmp;
}
 
static
void print_list(struct list *list) {
  int line = 2;
  printf("list = [\n  ");
  for (; list; list = list->next) {
    if (line > 72) {
      printf("\n  ");
      line = 2;
    }
    line += printf("%4d;", list->data);
  }
  printf("\n]\n");
}
 
static
struct list* fill_list(size_t len) {
  struct list *list = NULL;
  for (size_t i = 0; i < len; i++)
    list = add_front(list, i);
  return list;
}
 
static
void delete_list(struct list *list) {
  struct list *tmp = list;
  while (tmp) {
    memset(tmp, 0, sizeof (struct list));
    list = tmp->next;
    free(tmp);
    tmp = list;
  }
}
 
int main(int argc, char *argv[]) {
  size_t len = 8;
  if (argc > 1)
    len = strtoul(argv[1], NULL, 10);
  struct list *list = fill_list(len);
  print_list(list);
  delete_list(list);
  return 0;
}

Finally, leak03.c:

# define _XOPEN_SOURCE 700
 
# include <err.h>
# include <stdio.h>
# include <stdlib.h>
 
void mycat(FILE *in, FILE *out) {
  for (;;) {
    char *line = NULL;
    size_t n = 0;
    if (getline(&line, &n, in) == -1) break;
    fprintf(out, "%s", line);
  }
}
 
int main(int argc, char *argv[]) {
  FILE *in = stdin;
  if (argc > 1) {
    in = fopen(argv[1], "r");
    if (!in) err(1, "can't open %s", argv[1]);
  }
  mycat(in, stdout);
  if (argc > 1)
    fclose(in);
  return 0;
}

For each files, you must:

  • Run, compile and observe
  • Find memory leaks using valgrind
  • Find memory leaks using the address sanitizer of your compiler
  • Correct the code

Code optimization

Now, our purpose is to optimize a piece of code. The provided code is a small program that reads lines from its standard input, stores them uniquely and finally prints them in order (with respect to the order defined by strcmp(3).)

Your goal is to:

  • Check for bugs and memory leaks
  • Use gprof to get some profiling information on the program
  • Try to optimize the code as much as you can …

Here is the file lines.c:

# define _XOPEN_SOURCE 700
 
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
 
# define LINE_LENGTH 256
 
static inline
size_t max_len(size_t a, size_t b) { return a > b ? a : b; }
 
static
void str_swap(char *str1, char *str2) {
  size_t len = max_len(strlen(str1), strlen(str2));
  char *tmp = calloc(len, 1);
  strncpy(tmp, str1, len);
  strncpy(str1, str2, len);
  strncpy(str2, tmp, len);
  free(tmp);
}
 
static
int member(char *lines[], size_t len, char *l) {
  char **cur = lines;
  for (; cur < lines + len && strcmp(*cur,  l); cur++) {}
  return cur < lines + len;
}
 
static
void sort_lines(char *lines[], size_t len) {
  for (size_t i = 0; i < len - 1; i++) {
    size_t min = i;
    for (size_t j = i + 1; j < len; j++) {
      if (strcmp(lines[j], lines[min]) < 0)
	min = j;
    }
    if (min != i) str_swap(lines[i], lines[min]);
  }
}
 
static
size_t read_lines(char ***plines) {
  size_t capa = 1, len = 0;
  char **lines = malloc(capa * sizeof (char*));
  char *tmp= NULL;
  size_t n = 0;
  while (getline(&tmp, &n, stdin) != -1) {
    if (!member(lines, len, tmp)) {
      len += 1;
      if (capa <= len) {
	capa *= 2;
	lines = realloc(lines, capa * sizeof (char*));
      }
      lines[len - 1] = calloc(n + 1, 1);
      strncpy(lines[len - 1], tmp, n);
      sort_lines(lines, len);
    }
    free(tmp);
    tmp = NULL;
    n = 0;
  }
  lines = realloc(lines, len * sizeof (char*));
  *plines = lines;
  return capa;
}
 
int main() {
  size_t len;
  char **lines;
  len = read_lines(&lines);
  for (size_t i = 0; i < len; i++) {
    printf("lines[%zu]: %s", i, lines[i]);
  }
  free(lines);
  return 0;
}