Sometimes you are going to get stuck while working on code. This page gives a short method for working through the road blocks. Going through each step will help you either find the answer or ask the right questions to get moving again quickly.
Try to reduce your problem to a set of specific unknowns. Put tests in your code to help locate the precise source of trouble. For example, if you made some changes to PPL and it won't compile, then the issue is obviously erroneous new code. A specific unknown would be something like: "when I try to pass this Cfg by reference, the compiler throws an error for 'cannot convert from type const Cfg', but I don't know why the object is const." Specific unknowns such as this let you and your potential helper spend time fixing the error instead of finding it.
If something that used to work is now broken, chances are that recent events played a significant role. If you have this kind of problem, try to identify any possibly significant events that occurred between the last known functional point and now. What did you change in the code? Did any software get updated? Did you update from git? If you did, did you run make reallyreallyclean? Try reverting specific changes in your working copy to see if they are a source of trouble.
See if you can resolve any unknowns by scouring common information sources. Even if you can't, discussing what you find will help others understand your issue when you ask for help. Check the best known relevant reference, plus Google. Here are some good references to look at for various problems:
Note: some of these sources are very long! Don't waste your time scrolling around: get used to using the find command (Ctrl+F) to jump straight to the content you need. Use the forward slash command / to do the same in vim.
If the documentation is not sufficient, the next step is to search for examples in the trunk. The tools for doing this are grep and find.
grep -r -n "IsConnected" .This will generate a bunch of lines that look like this...
./LocalPlanners/AStar.h:38: virtual bool IsConnected(...which indicate that grep found the line of text virtual bool IsConnected( which contained the search term IsConnected in line 38 of the file AStar.h in the LocalPlanners/ directory.
./LocalPlanners/AStar.h:55: virtual bool IsConnectedOneWay(
./LocalPlanners/AStar.h:109: AStar<MPTraits>IsConnected(
...
find /research/me/ -name "AStar.h"If you checked out PPL to the directory /research/me/PPL/, then find would print:
/research/me/PPL/simulator/src/MPLibrary/LocalPlanners/AStar.hIf you have any other files named AStar.h, then find will print those too. This is a great tool because you don't have to remember where anything is so long as you remember how to find it.
If you are tracking down a seg fault, gdb (the GNU debugger) is your tool. The debugger allows you to run your program in a sort of sandbox. Using the debugger, you can stop, start, and step program execution one line at a time, view a backtrace (i.e. the list of function calls that arrived at the current state), as well as examine data.
To use GDB, the basic paradigm is to load your program into the debugger, set any breakpoints you want, run the program, then search for the cause of your problem.
Starting Your Program in GDB: To run your program in gdb, the first step is to ensure that you compiled with debug symbols. In PPL, this is done by setting a flag in the Makefile. Ensure that the debug option in src/Makefile is set to 1. If you had to change this setting, be sure to rebuild the entire library (i.e. make reallyreally clean, make).
Once your program is compiled with debug symbols, to run your program in gdb use:
gdb <program_name>
To run your program with arguments instead use:
gdb --args <program_name> <program_arguments>
gdb --args ./ppl -f Examples/CfgExamples.xml
Once your program is loaded, execution can be started with:
run
This will run your program to completion because we haven't yet told gdb to stop anywhere. This can be useful to find where exactly a segfault is (more on that later).
Controlling Execution: Gdb can run your program in two ways, either continuously, where each subsequent instruction is run after the previous finishes (i.e. like your program normally runs), or one instruction at a time, stopping after each line. Normally, what you want to do is run your program continuously until you get to the problem area, at which point you want to step through and see exactly what is happening. To run the program until a certain point (or condition) is met, breakpoints are used. To set a breakpoint at a certain line number use:
break <filename>:<line_number>
break SamplerMethod.h:42
With a breakpoint set, the code will run until a breakpoint is hit, at which point execution will stop and you can enter more commands. You should also note that many breakpoints can be set, and set breakpoints can be removed. If you try to set a breakpoint in a file and gdb asks about a a future library load, it means you either mispelled something, or did not compile with debug mode on (see compiling for the debugger above).
Once the execution has hit a breakpoint, gdb will halt execution and wait for input. At this point, you can examine the program state, step through lines of code, or continue execution. To continuce execution until the next breakpoint, simply use:
continue
To step through execution a little at a time, there are two options. Either you can run the entire next line (function calls and all) with next, or you can run the next line stepping into any functions with step. Typically, you want to use next to get where you need to or to skip cumbersome (and usually correct) parts of the code, such as printing or manipulations of standard library data structures. Use step when calling functions for which you want to examine the execution.
Examining Data: Gdb provides powerful tools for examining data. You can access all data available in the current context (i.e. whatever function you are currently in) using the corresponding variable names. Further, the syntax looks very similar to C++ code. This is done using the print command. Below are a few examples.
Print a variable in the current context.
print myVariable
Print the current object.
print this
Print a member variable from the current object.
print this->m_variable
Print the return value from a function from the current object.
print this->MyClassFunction()
Arguments can also be passed.
print this->MyClassFunctionWithArg(42)
The print command can be very useful for tracking down a Null pointer.
Examining The Stack: Gdb can also show a backtrace of the stack. This will show you a history of the functions that were called to get to the current context. The most recent functions are at the top, and the oldest are at the bottom. The backtrace contains useful information such as what arguments were used to call the function. Note that the bracketed number starting each line is the frame number.
backtrace
You can also switch the current context in the backtrace (for example, up a level to a different function).
frame <frame_number>
Further GDB Readings: Gdb is a powerful tool with many uses and far too many commands to cover here. For more information in an easily digestable form, take a look at this tutorial. For a complete reference, check out this more complete GDB tutorial.
If you can't find the source of your problem or you think it will take you prohibitively long to fix it, gather up what you have thus far and ask for help. If you went through the short process above, you will be prepared with some essential information:
A question primed with these ingredients is likely to receive a good answer. Doing your best to investigate problems to a reasonable extent also improve your troubleshooting skills and prepare you to apply what you learn from others.
Another important thing to consider is what kind of help to ask for. If you think that you could solve the problem given some resource, ask where to find the resource. Even when asking for help, try to do as much as possible yourself.
Who should you ask? Start with your direct peers and continue down the list as necessary.