1 / 51

CS 241 Section Week #2

CS 241 Section Week #2. 9/9/10. Topics This Section. MP1 issues MP2 overview Process creation using fork() ‏ Debugging tools: valgrind, gdb. MP1 Issues. Q: My printf prints my string correctly but appends a lot of garbage after it. Why?. MP1 Issues.

gconklin
Download Presentation

CS 241 Section Week #2

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. CS 241Section Week #2 9/9/10

  2. Topics This Section • MP1 issues • MP2 overview • Process creation using fork()‏ • Debugging tools: valgrind, gdb

  3. MP1 Issues • Q: My printf prints my string correctly but appends a lot of garbage after it. Why?

  4. MP1 Issues • Q: My printf prints my string correctly but appends a lot of garbage after it. Why? • A: The line probably does not have a termination char ('\0')

  5. MP1 Issues • Q: Why there's no '\0' at the end of my string?

  6. MP1 Issues • Q: Why there's no '\0' at the end of my string? • A: Some functions (e.g., strncpy(), strncat()), do not add the \0 at the end of the string • S: add the termination char manually: • strncpy(str,src,i); • str[i]='\0';

  7. MP1 Issues What’s wrong with this code? #include <string.h> #include <stdio.h> #include <stdlib.h> int main(){ char *str = (char *) malloc(5); strcpy(str,"Hello"); printf("%s",str); }

  8. MP1 Issues What’s wrong with this code? #include <string.h> #include <stdio.h> #include <stdlib.h> int main(){ char *str = (char *) malloc(5); strcpy(str,"Hello"); printf("%s",str); } No space for \0 Valgrind helps finding these errors: ==15648== Invalid write of size 1 ==15648== at 0x4027167: memcpy (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) ==15648== by 0x804847A: main (a.c:8) ==15648== Address 0x419802d is 0 bytes after a block of size 5 alloc'd

  9. MP1 Issues What’s wrong with this code? #include <string.h> #include <stdio.h> #include <stdlib.h> int main(){ int i; char str[6]; scanf("%s",str); if (!strcmp(str,"A")) i=1; if (!strcmp(str,"B")) i=2; printf("%d",i); }

  10. MP1 Issues What’s wrong with this code? #include <string.h> #include <stdio.h> #include <stdlib.h> int main(){ int i; char str[6]; scanf("%s",str); if (!strcmp(str,"A")) i=1; if (!strcmp(str,"B")) i=2; printf("%d",i); } i might not be initialized Valgrind helps finding these errors: ==15721== Conditional jump or move depends on uninitialised value(s)

  11. MP1 Issues What’s wrong with this code? #include <string.h> #include <stdio.h> #include <stdlib.h> void init(dictionary_t* d){ d = malloc(sizeof(dictionary_t)); d->next = NULL; } int main(){ dictionary_t dic; init(&dic) }

  12. MP1 Issues STACK What’s wrong with this code? dic #include <string.h> #include <stdio.h> #include <stdlib.h> void init(dictionary_t* d){ d = malloc(sizeof(dictionary_t)); d->next = NULL; } int main(){ dictionary_t dic; init(&dic) } HEAP

  13. MP1 Issues STACK What’s wrong with this code? dic #include <string.h> #include <stdio.h> #include <stdlib.h> void init(dictionary_t* d){ d = malloc(sizeof(dictionary_t)); d->next = NULL; } int main(){ dictionary_t dic; init(&dic) } HEAP

  14. MP1 Issues STACK What’s wrong with this code? dic #include <string.h> #include <stdio.h> #include <stdlib.h> void init(dict_t* d){ d = malloc(sizeof(dictionary_t)); d->next = NULL; } int main(){ dict_t dic; init(&dic) } (dict_t*) d HEAP

  15. MP1 Issues STACK What’s wrong with this code? dic #include <string.h> #include <stdio.h> #include <stdlib.h> void init(dictionary_t* d){ d = malloc(sizeof(dictionary_t)); d->next = NULL; } int main(){ dictionary_t dic; init(&dic) } (dict_t*) d dict_t: next=???? HEAP

  16. MP1 Issues STACK What’s wrong with this code? dic #include <string.h> #include <stdio.h> #include <stdlib.h> void init(dictionary_t* d){ d = malloc(sizeof(dictionary_t)); d->next = NULL; } int main(){ dictionary_t dic; init(&dic) } (dict_t*) d dict_t: next = NULL HEAP

  17. MP1 Issues STACK What’s wrong with this code? dic #include <string.h> #include <stdio.h> #include <stdlib.h> void init(dictionary_t* d){ d = malloc(sizeof(dictionary_t)); d->next = NULL; } int main(){ dictionary_t dic; init(&dic) } Still uninitialized Lost memory dict_t: next = NULL HEAP

  18. MP1 Issues STACK Solution: dic #include <string.h> #include <stdio.h> #include <stdlib.h> void init(dictionary_t* d){ d = malloc(sizeof(dictionary_t)); d->next = NULL; } int main(){ dictionary_t dic; init(&dic) } (dict_t*) d HEAP

  19. MP1 Issues What’s wrong with this code? int x; int*xptr = (int*)malloc(sizeof(int)); xptr = &x; Memory Lost !!! 4 bytes xptr x

  20. MP2

  21. MP2 overview • Simple Unix shell • Non built-in commands • Built-in commands • Change Directory • Termination • History • Error handling • Concepts needed: process, fork, exec, wait

  22. Processes A process is an instance of a running program A process contains: Instructions (i.e. the program)‏ Resources (variables, buffers, links, etc.)‏ State (identity, ready/running/locked, etc.)‏ Processes can create other processes

  23. Process Creation with fork()‏ fork() creates a new process: env env fork()‏ creates stack stack free free heap heap static static code code Parent Child • The new (child) process is identical to the old (parent) process, except…

  24. Differences between parent and child Process ID (getpid())‏ Parent ID (getppid())‏ Return value of fork()‏ In parent, fork() returns child pid In child, fork() returns 0

  25. fork() Example 1 • What does this do? #include <stdio.h> #include <sys/types.h> #include <unistd.h> int main() { printf(“%d\n”, fork()); return 0; } • Try it!

  26. fork() example 2 #include <stdio.h> #include <sys/types.h> #include <unistd.h> int main() { pid_t child_pid = fork(); if (child_pid < 0) { // error code perror(“Fork Failed”); return –1; } printf(“I'm process %d\n”,getpid()); if (child_pid == 0) { // child code printf(”I’m the child of parent process %d.\n”, getppid()); } else { /* child_pid > 0 */ // parent code printf(“I’m the parent of child process %d.\n”, child_pid); } return 0; }

  27. Example 2 cont’d • This exits too quickly; let’s slow it down: if (child_pid == 0) { // child code sleep(15); printf(”I’m the child of parent process %d.\n”, getppid()); } else { // parent code sleep(20); printf(”I’m the parent of child process %d.\n”, child_pid); }

  28. Example 2 cont’d • In a second window, run ps –a, and look for the pids from the program output. • Periodically run ps –a again, as the program in the first window executes. • What happens when the program runs? • What happens when the child finishes? • What happens when the parent finishes? • What happens when you switch the parent and child sleep statements?

  29. Orphans and Zombies • When a process finishes, it becomes a zombie until its parent cleans up after it. • If its parent finishes first, the process becomes an orphan, and the init process (id 1) adopts it.

  30. How can a parent know when its children are done?

  31. Solution: wait(…) wait() allows a parent to wait for its child process, and save its return value pid= wait(&status); pid= waitpid(pid, &status, options); wait() waits for any child; waitpid() waits for a specific child.

  32. wait cont’d wait() blocks until child finishes wait() does not block if the option WNOHANG is included. When would we want to use this? • The child’s return value is stored in *status

  33. wait(...) macros WIFEXITED(status) is true iff the process terminated normally. WEXITSTATUS(status) gives the last 8 bits of the process’s return value (assuming normal exit)‏

  34. Example 3: wait #include <sys/wait.h> … … // add this to parent code if (waitpid(child_pid, &result, 0) == -1) { perror(”Wait failed”); return -1; } if(WIFEXITED(result)) { fprintf(stdout, ”child %d returned %d\n”, child_pid, WEXITSTATUS(result)); }

  35. exec exec replaces the current process image(code, variables, etc.) with that of a new program: Before After env env* exec stack New Program free heap static code * The program may choose to change the environment

  36. exec variations • There are 6 different ways of calling exec. Which one to use depends on three conditions: • How arguments are passed • execl, execv • How the path is specified • execlp, execvp • Whether a new environment is used • execle, execve

  37. exec variations: passing parameters exec can have parameters passed to it two different ways: List of parameters: execl(“/bin/ls”, ”ls”, ”-l”, NULL); Argument Vector (like argv): execv(“/bin/ls”, argv); // char *argv[] Q: When would you use execl? When would you use execv?

  38. exec variations: command path Adding a “p” to an exec call tells the system to look for the command in the environment’s path. Compare: • execl(“/bin/ls”, ”ls”, ”-l”, NULL); • execlp(“ls”, ”ls”, ”-l”, NULL); The difference is similar for execv and execvp.

  39. exec variations (cont’d)‏ By default, the new program inherits the old program’s environment. Adding an “e” to the exec call allows the new program to run with a new environment **environ execve and execle allow the user to specify the environment. The others inherit the old environment.

  40. fork + exec • If exec succeeds, it does not return, as it overwrites the program. • No checking return values, executing multiple commands, monitoring results, etc. • Solution: fork a new process, and have the child run exec

  41. Example 4: exec int main() { char command[10]; while(fscanf(stdin, “%s”, command)) { pid_t child_pid = fork(); if (child_pid < 0) { perror("Fork failed"); return -1; } else if (child_pid == 0) { // child code // execute command // check errors } else { // parent code // What belongs here? } } return 0; }

  42. Debugging Tools

  43. Using valgrind to check memory leak • To run a program ( ./part1 )‏ • To use valgrind ( valgrind –-leak-check=full ./part1 )‏ • valgrind output with no memory leak ==19376== ERROR SUMMARY: 0 errors from 0 contexts ... ==19376== malloc/free: 1 allocs, 1 frees, 10 bytes allocated. ... ==19376== All heap blocks were freed -- no leaks are possible.

  44. valgrind output withmemory leak • Memory is allocated using malloc(...)inside the function testmalloc(...)‏ ==20086== ERROR SUMMARY: 1 errors from 1 contexts ... ==20086== malloc/free: 1 allocs, 0 frees, 10 bytes allocated. ... ==20086== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==20086== at 0x4022AB8: malloc (vg_replace_malloc.c:207)‏ ==20086== by 0x8048447: testmalloc (in /home/farhana/test)‏ ==20086== by 0x804848A: main (in /home/farhana/test)‏ ... ==20086== LEAK SUMMARY: ==20086== definitely lost: 10 bytes in 1 blocks. ...

  45. Preparing to use GDB • Compile and link with -g option • compiler includes symbolic information with object and executable files • examples: type information, function and variable names • Debugging optimized code (-g and -O) • will work with gcc (not with most others) • but gdb cannot undo optimizations • if you can’t either, safer to turn off optimizer

  46. Starting GDB • before starting a program [user]$ gdb <executable> (gdb) run <arguments> • after starting a program [user]$ gdb <exec.> <proc. id> or (gdb) attach <pid> • use “file” command or restart gdb after recompilation

  47. Some Basic Commands • backtrace or where • show the stack trace (sequence of procedure calls) • moving from one stack frame to another • up: move one frame higher • down: move one frame lower • frame <n>: move to frame n • list: show the source code corresponding to the current stack frame (by default)

  48. Basics of Showing Program State • print • evaluate an expression (usually in C/C++) • show the result • display (like print) • evaluate and show result of expression • each time the program stops • returns an integer identifier • undisplay <#>: stop displaying something • info locals: show the values of all variables defined within the current frame

  49. Controlling the Program • continue: let the program run at full speed • one step (source line!) at a time • next • step over any function calls • run until the function returns and any remaining code on the line finishes • step: step into function calls, to the first line • finish: run until the current frame returns • return [<value>]: cut the current frame short and return immediately

  50. Using Breakpoints • break [<file>:]<line> • set a breakpoint • returns an integer identifier • break <function/label name> • cond <breakpoint #> <logical expression> • disable [<breakpoint #>]: disable temporarily • enable [<breakpoint #>]: re-enable • delete [<breakpoint #>]: remove permanently • info breakpoints: show all breakpoints, conditions, etc

More Related