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.
clone
,
add
, commit
, status
, and
log
commands.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:
It allows programmers to share code in a more systematic way than simply emailing files or using shared folders (like Google Drive).
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.
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:
goodbye.txt
with the contents
“bye bye”.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.
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
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.
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.
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?).