COMP 280 - Lab Session 02

1 Introduction

In this week’s lab we will be learning about Git, a widely-used program for managing software development. Git will help you track changes in your code and work more efficiently with a partner. Those of you who took COMP 110 and 120 last year are used to using Git from VS Code; in this class you will learn the commands that VS Code uses for Git behind the scenes. For everyone else, don’t worry: I am not expecting any previous experience with Git.

You will also learn the basics of using the GNU Debugger (GDB) to debug C programs. Specifically, you will work towards developing the ability to identify and fix segmentation faults in your programs.

2 Lab Learning Objectives

  1. Create and manage a Git repository using Git’s clone, add, commit, status, and log commands.
  2. Use GDB to run a program, set breakpoints, and print the contents of variables during execution.
  3. Debug segmentation faults using GDB.

3 Version Control with Git

IMPORTANT: For this part of the lab, you should work alone. Later, you will pair up with a neighbor to do some C coding but for now it’s important that you run the commands on your own.

All modern software development relies on some kind of software version control software (VCS). A good VCS has the following benefits:

  1. It allows programmers to share code in a more systematic way than simply emailing files or using shared folders (like Google Drive).

  2. It tracks changes made to files as well as the who made specific changes without having to resort to tricks such as saving multiple versions of the same file with slightly different names (e.g. foo.v1.c vs foo.v2.c). This can allow you to “go back in time” to an earlier version if for example you introduced a bug.

  3. It allows programmers to work on separate features concurrently and combine their results with relatively little effort.

While there are many different types of VCS available, we’ll be using a powerful, open-source program called Git (pronounced like “hit”, not “get”). Git is available for all major operating systems. All of the BEC315DL machines have Git installed.

To get started with Git, you first need to either create a new project (a.k.a. repository) or clone an existing repository. In this lab we’ll explore how to clone existing repositories, which is what you will be doing most often in this class (and throughout your career).

You likely have heard of the website GitHub, which many people confuse for Git itself. GitHub is a site for hosting Git repositories. GitHub isn’t the only hosting service available, it just happens to be the most popular one at this time. For this class, we won’t be using GitHub but I encourage you to explore it on your own.

Before doing anything else, log into one of the BEC315DL machines using SSH as shown below.

# replace "YOU" with your username
# replace XX with your assigned machine number
ssh YOU@BEC315DLXX.sandiego.edu

Change to your comp280/labs directory, which you should have created in last week’s lab. Run the pwd command to make sure you are in the correct directory.

For this course, we will start by cloning an existing repository stored on a remote server (code.sandiego.edu). Enter the following command to clone the lab02 repository and switch into it.

git clone git@code.sandiego.edu:comp280-fa22-lab02 lab02
cd lab02

If you get an error message about permission denied, that likely means you never shared your public SSH key with me for project 1. You’ll need to do that now, looking at the project 1 write-up for instructions on generating and sharing the key.

Git stores all of its repository information in a folder called .git. If you do the ls -a command, you should see this directory, along with a few other files.

Q1: Check the ls manual page (man ls). What does the -a option do?

Our Git repository is pretty small so let’s add some files to it. First, open a file named “hello.txt” using your favorite command line text editor (it’s Vim, isn’t it?). Add a line to the hello.txt file that reads “hello world”, save the file, and exit your editor. Now let’s tell Git that we have a file that needs added to the repository, using the git add command as follows.

git add hello.txt

You’ll use the git add command whenever you want to add a new file to your repository OR if you have made changes to a file that is already in the repository. This is the command that is run when you click the “+” button in the VS Code “Git” menu.

The git add command doesn’t modify the repository: it simply marks that there are changes ready to be committed. The terminology that Git uses for this is that files have been “staged” for committing. We can get a look at which files are ready to be committed using the following command:

git status

If you erroneously add a file using git add you can unstage the file.

Q2: Based on the output of the previous git status command, what command would you run to unstage the hello.txt file? NOTE: You should NOT actually run this command. Hint: There will be a “--staged” somewhere in the command.

Share your answer in our CampusWire #labs chatroom, including the question number.

The git status command will also tell you when there are files that haven’t been added to the repository. Create a new file named foo.txt, then rerun the git status command.

Q3: What is the terminology used for new files that haven’t been added to the repository?

Use the git add command to stage the foo.txt file for committing.

Q4: What command did you just run?

Run the git status command once again to confirm that both hello.txt and foo.txt are staged.

When we commit a set of changes, Git creates a new version of our project for us. Git tracks the history of all commits we make, including the changes that happened from one commit to another, and the person who made the changes. In the future we’ll learn how we can easily switch back to an earlier commit.

Q5: Give at least one reason why you might want to return to an earlier version (i.e. commit) of your repository?

We can commit changes using the git commit command. However, before we do that, we first want to give Git some basic information about who we are so it can accurately track who made changes to the repository. Run the following git config commands, replacing “Your Name” with your full name (leaving the quotation marks, e.g. "Sat Garcia") and youremail@sandiego.edu with your actual sandiego.edu email address.

# You only need to run these commands once.
# Make sure you modify them to match your personal info, i.e. don't
# literally type in "Your Name".
# Also note that there should be quotations around your name and your email
# address.
git config --global user.name "Your Name"
git config --global user.email "youremail@sandiego.edu"

You can check that these configurations were set correctly by running the following two commands, and checking that they print out your name and your email.

# enter the following commands literally, i.e. don't replace anything
git config user.name
git config user.email

While we are at it, we will set one additional Git setting.

# again, you only ever need to run this command once.
git config --global push.default simple

Now that we have our name and e-mail set, we can proceed with the commit as follows:

git commit -m "added hello.txt and foo.txt"

The -m indicates what message we want associated with this version. If you leave it off, it will open vim automatically where you will have to put your message. I know you all love Vim, but here it’s probably easier to just use the -m option and avoid Vim.

Let’s work on creating a new commit/version with the following changes:

  1. Create a new file named goodbye.txt with the contents “bye bye”.
  2. Add a new line to hello.txt that reads “hola mundo”.

Make the two changes listed above.

After making those changes, stage the modified files and then commit these changes to your repository. Remember: you need to use git add to add/stage files and git commit to commit staged files them.

That’s all there is to it. Run the git status command once more to confirm that there is nothing else to commit.

We can have a look at the history of our repository versions using the git log command. If we run git log with no options, it will give us an overview of all the past versions of the repository. When viewing the log, type in q to quit.

Q6: What information is given with each commit when running the git log command?

Share your answer in our CampusWire #labs chatroom, including the question number.

If you specify git log -p, it will give you even more detailed information, including the exact changes to the files. You likely won’t be able to decipher how this information is formatted: we’ll learn about that later this semester.

Note that all the changes you have made to the repository are in your local copy of the repository. If you wanted to synchronize your repository with the remote server, you would use the git pull (to download any new changes that the server has) and git push (to upload your changes to the remote server) commands. For this lab, you don’t have access to write to the repository you cloned, so you won’t be able to push your changes.

We’ll explore pushing and pulling more in a future lab.

IMPORTANT: At this point, you should wait for your partner to complete this section and then join them to do pair programming for the rest of the lab. Remember that in pair programming, you will be sharing a single computer, with one person (called the driver) operating the computer and the other person (called the navigator) helping decide what to do.

4 Debugger C Programs With GDB

The GNU debugger, GDB, is the C debugger we will use in this class. It is an “industrial strength” debugger with many features to help software developers track down errors in their programs.

To start GDB, we will specify the name of the program executable file that we want to debug.

To get started, use make to compile the example programs and run one of them (first-program) as follows.

make
./fun-program 5 wiggle

Q7: What did the program do?

Q8: Open the fun-program.c file. What functions are defined in this file?

Now let’s run the same program, but using GDB.

gdb fun-program

After printing some basic information (e.g. the version and license information), you will be greeted with a GDB prompt: “(gdb)”. When you see this prompt, it means you can enter in GDB commands.

A reasonable place to start is to run the program using the run command. Enter the following into GDB.

run 5 wiggle

This should have run the program, just like if you had run it directly (i.e. without starting GDB first). Go ahead and enter the inputs like you did before to run the program to completion.

When you are actually debugging a program, you will usually want to set breakpoints so that your program will stop when it reaches a specific part of your program. In GDB, the break command is used to set breakpoints. You can tell GDB to set a breakpoint either at the beginning of a function or at a specific line number in the file.

Let’s set breakpoints at the beginning of the two functions defined in fun-program.

break main
break fun_facts

We can see which breakpoints we currently have set using the info command. Type the following into GDB now.

info break

Q9: Based on the output of this command, at which line numbers are the two breakpoints we have set?

Now let’s restart the program using the run command again.

run woohoo

It should indicate you are at breakpoint 1 (i.e. the beginning of main). To continue on to the next breakpoint, use the continue command.

continue

After the program asks for your inputs, you should be at the breakpoint at the beginning of the fun_facts function.

While debugging, you will likely want to know the values of specific variables. GDB makes this simple with the print command. Test this command out now on the n parameter in the fun_facts function.

print n

Q10: What was printed out after entering this command?

When printing, the value being printed is assigned to a variable. This variable starts with a “$”.

Q11: What was the variable assigned to the value you printed?

You can use that variable throughout the rest of your GDB session (e.g. print $7); however, you likely won’t need to use this feature frequently.

The print command has a few tricks up its sleeve though. Enter the following commands now.

print/t n
print/x n

Q12: What was different when adding “/t” to the end of the print? What about “/x”?

Share your answer in our CampusWire #labs chatroom, including the question number.

In addition to moving from breakpoint to breakpoint using run/continue, you can execute one line at a time using either the next or step command (don’t enter either of these yet though…). We’ll explore the difference between these two in more detail later, but for now we will stick with the next command. Before entering that command, let’s get a reminder of where we are in the program using the where command.

where

For the current function (#0), the line number is line of code where executing is currently paused. For all other functions, the listed line number is the line where the callee was called from (e.g. #1 lists where we called #0 from).

Examine the output of the previous command.

Q13: What line are we currently on and what line was the current function called from in main?

Now let’s execute the current line using the following command:

next

While the line of code we moved to will be printed out, sometimes it can be confusing to know what is going on without more context. To see the source code of the lines around your current line, try the following command:

list

Q14: Which line numbers were printed?

If you need to print more lines past the ones just printed, simply rerun the command:

list

You can also tell the list command to print the lines around a specific line number (e.g. list 7) or at the beginning of a specific function (e.g. list main).

At this point, let’s finish executing the fun_facts, using the next command until GDB indicates that you are back in the main function. To verify you are actually in main, use the where command and make sure that main is now listed as frame #0.

Now that we are back in main, use the list command to see what instructions will be coming up next.

Q15: What is the name of the variable where we will store the single character that the user types in?

Use the next command until you get to the point where the program has asked you to enter that single character. After entering that character, use the print command now to print out the contents of the variable with that character. This will print both the text representation as well as the numeric representation of the character.

Q16: Which character did you enter and what was its numeric value?

Share your answer in our CampusWire #labs chatroom, including the question number.

At this point, we are done so finish program execution using the continue command. Now let’s quit GDB using the quit command:

quit

4.1 Debugging a Program with a Segmentation Fault

A very common error when dealing with C programs is the dreaded segmentation fault. This type of fault usually occurs when you are trying to access (either read or write) some location in memory that you don’t have permission to access. Often this results from trying to dereference a NULL pointer.

As its name implies, the segfaulter program (source: segfaulter.c) contains a segmentation fault. Run this program now to see what happens.

./segfaulter

Q17: What error message was printed out?

Let’s use GDB to figure out the source of this error.

Start by loading the program in gdb.

gdb segfaulter

Run the program using the run command.

This should tell you the line of code where the program ended with the segmentation fault. We would like a bit more info though, namely the trace of function calls that lead to this point in the code. Luckily, we can easily locate this using the where command, which you should enter into GDB now.

Q18: What function are we currently in? Who is the caller of this function and on what line was the call made?

Let’s get some more information by printing out the code around the place where the segmentation fault occurred.

(gdb) list

There seems to be a problem with array. Let’s try to print the first element in the array.

Q19: What happens when you try to print the value stored in array[0]?

That ran into a problem so let’s try to print out the value of array, which should be the address of the start of the array.

Q20: Print the value of the array variable. What value did this pointer/array contain?

Share your answer in our CampusWire #labs chatroom, including the question number.

Let’s switch back to the calling function to see what we passed in: We can do this easily using the frame command and giving it the frame number. Enter the following command in now.

frame 1

Now that we’re in the caller, print out the value of the array to see if it is NULL. You may need to use the list command to see what the array is called in this function (it won’t necessarily be named array).

Q21: What is the value of the array in the caller?

If you just used list, examine the output to see if you can identify what went wrong in the caller. (If you haven’t run list yet, run that now.)

Q22: What was the problem? Hint: Did you allocate (:wink:) time for this problem?

Share your answer in our CampusWire #labs chatroom, including the question number.

Quit GDB, fix the program (in the file segfaulter.c), recompile using make, then rerun the program and see if everything works now.

Q23: What change did you make to get rid of the error?

Share your answer in our CampusWire #labs chatroom, including the question number.

Q24: Imagine that your project 1 program (employee_db) runs into a segmentation fault. What steps would you take to figure out where the error occurred, using GDB?

IMPORTANT: Before continuing on, check in with the instructor or lab assistant.

5 Submission

For this lab, either you or your partner will need to submit your answers to the lab questions through Blackboard. The other person will also do a Blackboard submission, but the only submission will be a “comment” saying with whom they worked.

6 Project Work Time

After you have finished the tasks above, you should spend the rest of the lab session working on your current project. Unless you demonstrate that you finished with Project 1, I expect you to spend the rest of the lab session working on that project. Leaving early without approval will result in you being marked “absent” for the lab session (and you don’t want that, right?).