CS3841
Debugging
# Introduction You are working on your lab and you are stuck. It's 9pm, and the instructor won't answer your email until tomorrow morning. What are your tools to get unstuck and move on with the lab? When do you decide to stop trying? This document is intended to give you tools to help you in this situtation. # Basic strategy Our basic debugging strategy is this: 1. Find the bug: Figure out WHY the program is breaking 2. Fix the bug: Change the code to prevent the break The first step is not complete until you UNDERSTAND why the bug exists. Understanding the root cause of a faulty line is essential to forming a good idea about how to fix it. All of our debugging strategies will be doing one of three things for us: * Narrowing in on a faulty line of code * Confirming the program is working the way we expect it to * Revealing how the program is behaving differently than we expect You should know which of these you are trying to do at any given point when you are debugging. Am I trying to narrow in on where the problem occurs, or trying to understand what it is doing? # Debugging Tools -- Checklist Scan through this list to see if you think any of these strategies could help you to narrow in on the bug or understand the bug better: * If you have no idea what is going on: * Think about the types of your variables. Are they pointers? * Draw a memory diagram, disinguishing between pointers and the memory they point to. Note that only variables have names, not memory that is malloc'd. * Step through the debugger and compare the values with your memory diagram * If you are wanting to manipulate the memory, not the pointers, use the standard memory functions: * malloc * free * memset * memcpy * memcmp * strncpy * strncmp * If the broken part worked previously: * Comment out code you suspect may be broken and check if it works * Roll back to a previous git version * Slowly add back in code until the broken behavior occurs again * If memory is corrupt: * Look at variables near the corrupted memory to see if the code writing to them writes outside of its bounds * Watch memory, stepping through until the memory becomes corrupt * Consider what is pointed to on the line that corrupts memory. How is the size of that object known? Check that the size is correct. * Consider if you are using any unsafe memory functions (these do not check if they are writing past the end of their input buffer). If you are using these, consider whether a very long input is writing past the end of your buffer. Using a safer function is a good way to fix this problem. * scanf("%s") instead of scanf("%31s"), * strcpy instead of strncpy * puts * Consider where the memory was allocated * If was malloc'd, has it been free'd? * If it was declared on the stack, has that function retured? * Run valgrind and look for warnings about invalid free's. These will be interspersed with the output of your running code * If you have multiple processes * See Lab 4 hints for using pipes * See the section "Debugging a child process in GDB" for how to step a debugger through the child process. * Have you tried all of the above that apply and still can't see what's wrong? * It's probably time to turn in for the night. # Debugging a child process in GDB Eclipse and gdb do not automatically debug child processes. To debug a child process, I recommend using two gdb sessions. Step by step: 1. Your parent program should print the PID of the child process right after the fork. 2. To debug your parent in Eclipse, simply start the Eclipse debugger. To debug your parent in gdb, open a console and from the folder with your executable, run <br>```gdb ./myLab3Executable```<br> Then, in GDB, run <br>```run C 1 filename.txt```<br> where ```C 1 filename.txt``` are the arguments for your program. 3. Your program should print the PID of the child process. 4. Open a second console, and run <br>```gdb```<br> Then, in GDB, run <br>```attach 4817```<br> where 4817 is the PID of your child process. It will immediately attach to the already-running child process and stop where the child is waiting. <br> General tips on using GDB. All of these must be run from the (gdb) prompt: * To see where you are in the process <br>```backtrace```<br> to see the stack and <br>```up```<br> or <br>```down```<br> to go up and down the stack. I recommend going up until you recognize something as your code. To continue running a process, type <br>```continue```<br> And to pause debugging, type the keys <br>&lt;control&gt;+&lt;c&gt;<br> * Note that the output of the child process goes to the same command prompt as the parent process. * To evaluate a variable, type <br>```print myVariable```<br> You can see which variables are in the local scope by running the commands <br>```info args```<br> and <br>```info locals```<br> * This seems to be [a nice gdb cheatsheet](https://darkdust.net/files/GDB%20Cheat%20Sheet.pdf) # Hints by Lab ## Lab 1 * See [Lab 1 Bug Fix Page](Lab1BugFix) ## Lab 4 Hints * A socket will not return 0 bytes on a read unless ALL processes (the parent and all child processes) close the writing end of the pipe. * Look at the values of your file descriptors and see if they are what you expect. * If you cannot figure out which child is keeping the process open, you can send a one-byte message through the pipe to indicate the read is complete. This is hacky, but it will work in this lab, because we always writes 32 bytes of data into the socket for the ordinary words. * To determine WHICH child you are, note that the child process inherits everything from the parent except the PID, so it has any variables distinguishing the children which the parent had either on the stack or on the heap just before the fork. So it should be EASY for a child to know which child it is. (This will become clearer as you start to write your code.)