CS32, Fall 2012

Lab04:
Using g++, make and gdb


Goals for this lab

By the time you have completed this lab, you should be able to

Step by Step Instructions

Step 1: Choose initial roles, create a directory and get files

Partner up (and remember to switch roles after awhile). If your regular partner is more than 5 minutes late, ask the TA to pair you with someone else for this week.

This lab's pilot should log in. You will (both) work in this account for the rest of the lab.

Create a ~/cs32/lab04 directory and make it your current directory:

mkdir ~/cs32/lab04
cd ~/cs32/lab04

Now copy all of the files for this lab from ~cs32/labs/lab04/ in the class account to your cs32/lab04 directory:

cp ~cs32/labs/lab04/* ~/cs32/lab04

Step 2: Review compiling and linking with g++

With a simple program like hello.cpp, we normally compile and link in one step, and then it is ready to run like so:

g++ hello.cpp -o hello
./hello

But we can also compile the program without linking it. Type the following, and then use ls to find out what happened:

-bash-4.2$ g++ -c hello.cpp
-bash-4.2$ ls hello.*
hello.cpp  hello.o

Notice the new object file hello.o, which contains the machine language instructions in binary form. In a second step we would link this object file (just to standard libraries in this case) as follows:

g++ hello.o -o hello

That step will finally produce the executable file "hello" - but only because we specified by the g++ option -o that the output file should be named hello. The program is run exactly like before by typing ./hello at the prompt.

Step 3: Understanding separate compilation

When it comes to large projects both in C and C++, it's always useful to organize your project into separate parts. When some parts of your program change, then only these parts need to be recompiled.

See the three files main.cpp, functions.cpp and functions.h. Notice that both of the .cpp files include the .h file - this is a typical case. It won't be necessary to compile functions.h by itself, as it will become part of both object files produced when the .cpp files are compiled.

Your job for Step 3:

  1. Compile both of main.cpp and functions.cpp separately.
  2. Then link the object files (will be main.o and functions.o) together to produce an executable file named "hello2" - this executable's name is important, and part of the lab requirement.
  3. Run ./hello2 to verify success.

Thought question: Why is it unnecessary to separately compile functions.h?

Other useful g++ options

When first compiling your programs it's always good to use the "-Wall" and the "-g" options in order to force g++ to give you warnings about possible errors in the source code, and include extra debugging information in its output, respectively.

The -g option is also necessary in order to use the gdb debugger later in the lab.

Before proceeding to Step 4, check "man g++" command for an extensive list of g++ options.

Step 4: Using a Makefile

Now imagine that you have a project with 20 different files, or even hundreds of files ...

Wouldn't it be better if we could compile all our files just by typing one command? Wouldn't it be better and save us time if we have to compile only the files we have changed, and not all of them? This is why we use the Makefile and the make command.

Now let's write a simple makefile to easily compile the files main.cpp, functions.cpp, and functions.h that we used on the previous section. Open a text editor and name the file "Makefile" (or "makefile"). You should always name your make files in this way.

Don't trust copy & paste with this text! Be sure to put a tab character at the beginning of every command line (in this case, the three g++ commands and the rm command) - spaces will not work.

hello: main.o functions.o
	g++ main.o functions.o -o hello

main.o: main.cpp functions.h
	g++ main.cpp -c

functions.o: functions.cpp functions.h
	g++ functions.cpp -c

clean:
	rm hello main.o functions.o

Save Makefile. Then make a minor change to one of the source code files (main.cpp, functions.cpp or functions.h). And finally type "make" in your console. Fix Makefile if it doesn't compile the changed parts and reproduce the executable file. If it does, then find out what happens if you type "make" again. ;-)

Hey notice that everything depends ultimately on functions.h. Also notice that this header is missing the usual conditional compilation specifiers, so:

  1. Add a C++ comment with your name(s) and the date at the top of functions.h.
  2. Then add the following two lines right after your header comment in functions.h (before sayHello is defined):
    #ifndef FUNCTIONS_H
    #define FUNCTIONS_H
  3. And add the following line at the end of functions.h (after printDate is defined):
    #endif
  4. Now type "make" again at the shell prompt, and test the program to be sure it still works properly.

By default, make will execute whatever is necessary to produce the first target which is hello (the executable) in this case. Alternatively, you can specify the target you want make to make by typing it as a command line argument, as in "make hello" to execute the default another way.

Suppose that now you want to remove the objects and executable file. What should you type in your console? Be prepared to answer that question if the TA asks you.

Step 5: Using gdb debugger

Maybe time to switch partner roles?

Inevitably sometime you will need to debug your code. gdb is a very powerful tool. Here we learn just the basics.

Look at buggy1.cpp to know its major parts.

(Emacs Users Hint: If you are using emacs you can see line numbers on the editor by typing M-x linum-mode. This will make your life easier. If you prefer running gdb on e-macs maybe you should try M-x gdb-many-windows in order to split your screen and be able to see variable values, gdb and e-macs all together. You gain control back to emacs by typing: C-x 0)

This program is supposed to ask a student about the courses he/she plans to register. So a non-buggy version of the program would execute like this:

Enter a course name:
Computer Networks
You are registered for the following 1 courses:
1. Computer Networks

 Do you Want to register for  another course? (y/n)
y
Your Answer is:y
Enter a course name:
Computer Security
You are registered for the following 2 courses:
1. Computer Networks
2. Computer Security

 Do you Want to register for  another course? (y/n)

But this code contains errors. Your job will be to find one of them using gdb. No need to fix it now, just find it. That's the purpose of gdb - it helps you find errors in your code.

Compile the code (don't forget to compile with the -g option), and run it. Here is an example run:

Enter a course name:
history
You are registered for the following 1 courses:
1. history

 Do you Want to register for  another course? (y/n)
y
Your Answer is:y
Enter a course name:
You are registered for the following 2 courses:
1. history
2. 

 Do you Want to register for  another course? (y/n)
y
Your Answer is:y
Enter a course name:
You are registered for the following 3 courses:
1. history
2. 
3. 

 Do you Want to register for  another course? (y/n)
y
Your Answer is:y
Enter a course name:
You are registered for the following 4 courses:
1. history
2. 
3. 
4. 

 Do you Want to register for  another course? (y/n)

Hmmm... Well it seems that our program doesn't wait for our input... Let's try to debug it with gdb to see how the basic gdb commands work. Begin by starting gdb with buggy1 as its command line argument:

bash-4.2$ gdb buggy1

Then type "run" (or just "r") at the gdb prompt:

(gdb) run

The program starts and asks for user input. Push CTRL-C and write the gdb command to set a breakpoint at line 19 (You could also do the following by typing just "b 19")

(gdb) break 19

Enter "run" again, and then start the program from the beginning by typing "y" when asked:

(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y

This way the program will run until line 19. Write the command "step" or just "s" in order to move one step forward. Now the program asks for your input:

Enter a course name: 
20	    getline(cin,courseName,'\n');

Don't give your input at this time. Instead move to the next step by writing again "step" or just by hitting enter (enter repeats the previous command you had typed). After you hit enter the getline function on line 20 is called and waits for your input. So you just have to enter a course name again - "Math" for example.

Let's say we would like to know if the courseName variable has the value we assigned to it. Just run:

(gdb) print courseName
$1 = {
  _M_dataplus = {
    > = {
      <__gnu_cxx::new_allocator> = {}, }, 
    members of std::string::_Alloc_hider: 
    _M_p = 0x1001008c8 "Math"
  }
}

So indeed variable courseName has the value we entered as an input ("Math"). Use the step command to move forward up to line 32. Use the "next" or "n" in order to step into and execute the subroutine trackUserAnswer(). If you wish to exit from the trackUserAnswer function you can write (f)inish. (But be aware that this function needs user input. So you can do this just after you have entered your input.)

More about breakpoints: Write command break to put a breakpoint on line 29. Now you have two breakpoints set - this new one is number 2. You can disable it by entering "disable 2" (you could also use dis 2). And you can enter "enable 2" (or "ena 2") to enable the breakpoint again, and "delete 2" (or "d 2") to delete the breakpoint.

Other stuff: Enter "help" at the gdb prompt to find out about more commands. One useful one, for example, is "list" - try it.

Ok... Now you have all the tools you need in order to find where is one of the bugs in this program. Find what exactly happens and tell your TA where the program needs fixing. You'll correct it for the homework.

Step 6: Show off your work and get credit for the in-lab requirements

Get your TA's attention to inspect your work, and to record completion of your in-lab work.

Don't leave early though ... begin the after-lab work below.

Step 6a. ONLY IF YOU RAN OUT OF TIME TO HAVE THE TA INSPECT YOUR WORK

If you must complete this assignment at CSIL, then you must do a few more things and submit it with the turnin program - but do NOT turn it in if the TA already checked you off. You MUST have both your name and your partner's name in appropriate comments in each file to be turned in to receive credit.

  1. You should already have added your name(s) in a comment to the top of functions.h in Step 4 above. If not, then do it now, save the file and test it to be sure it still works.
  2. Add your name(s) and the date in a comment at the top of buggy1.cpp. Then add a comment that identifies where the program needs to be fixed. Test to be sure it still compiles.
  3. Use turnin to turn in both functions.h and buggy1.cpp at the same time:
    turnin lab04@cs32 functions.h buggy1.cpp

Evaluation and Grading

Each pair of students must accomplish the following to earn full credit for this lab:


After lab-work is done

Work on the following after completing the required work

Prepare for Hw4 (all students, whether or not you finished lab work early)

  1. Learn how to use variables in make files. Start with the Basics, then learn about some automatic variables and how patterns match.
  2. See how to create a better Makefile for Step 4 above.

Still got some time?

buggy1.cpp also has another bug. After inserting more that 5 courses, a segmentation fault (eventually) takes place. It's obvious why, but every bug in your program is a chance to learn more useful commands to improve your programming life! Try the gdb command backtrace (or bt) to see the sequence of the commands that produce the crash. Then use the x command, and list (or l). By the way, what are your options for fixing this bug?

Homework - to be done before lab next week: November 1

This week's homework includes splitting up the program from Lab03 - so be sure to retrieve a copy of your grading2.cpp from last week's lab (or do that part of Lab03 now if you did not save it from before). Create a "~/cs32/hw4/" directory in your account for this purpose.

The homework requires fixing buggy1.cpp too, so you might as well copy that file now from your ~/cs32/lab04/ to ~/cs32/hw4/ too.

At the beginning of this lab you also should have copied another program to debug. Verify that buggy2.cpp is in your lab04 directory, and then move it to your ~/cs32/hw4/ directory (using mv; or you may use cp to get a copy of this file from ~cs32/labs/lab04/ if you prefer).

You will use the turnin program to submit some of the work, and that must be done before your next lab - the TA will check for it then. A written part is also required, to be turned in at your lab next week. Please do NOT print the homework in Cooper Lab.

Each student must complete the paper part of the homework, even if you do the programming parts in pairs.

Here are the hw4 instructions.


Prepared by Stratos Dimopoulos (F11 TA), and Michael Costanzo.