Lab FAQs
General
- How will code style be graded?
Here are some guidelines:
- Complex pieces of code should be documented in comments so that they become easier for the reader to understand—but don't overdo it with comments for trivial things.
- Common, general-purpose functionality should be decomposed into helper functions to avoid repeated code—don't overdo this either, e.g. by making it difficult to understand the flow of execution.
- Code should use consistent naming conventions and not contain commented-out code or vestiges of debugging.
- Please check the return values of all functions in which an error could occur, and handle these errors gracefully. Throwing an exception is one graceful way to handle an unrecoverable error.
- Avoid memory errors and leaks, egregious performance issues (e.g. allocating large amounts of memory for no reason) or unnecessarily complex approaches (e.g. a complicated data structure that is only marginally more efficient than something much simpler, or something available in the C++ standard library).
- You can check your code for memory leaks, invalid accesses, and undefined behavior by compiling with
cmake .. -DCMAKE_BUILD_TYPE=RelASan
and thenmake clean
,make
, thenmake check
or running any of the individual apps. - Before submitting each assignment, please run
make format
(to standardize the formatting), and make sure to remove any dummy code or comments that were included with the starter code.
- How much of my grade will come from style and to the quality of my writeup?
- How do I run an individual test?
- Can I add include lines in the header files?
- Why does 'make check' fail even though it's in the Sponge README?
- What's the procedure if I want to use some of my late days?
- How do I use
git
? - Run
$ git clone [bundle name] [unbundled name]
. cd
into the unbundled directory.- Run
$ git log
and verify that you see the commits you expect to. - Copy the commit hash of the starter code commit.
- Run
$ git diff [hash]
to make sure they see all changes you want to submit. - How do I debug?
- Install
gdb
(on the VM,sudo apt-get install gdb
). - Start
gdb
on the executable corresponding to the test you want to debug (from thebuild
directory,ls tests
to see the executables). e.g. from the build directory:gdb tests/byte_stream_one_write
. - The output of the test failure will show you the part of the test that failed. To set a breakpoint at that part of the test, break on the line in the test file where the test harness for that part is created. The test files are in
sponge/tests
(notsponge/build/tests
). e.g. if you're failing thewrite-pop2-end
test inbyte_stream_one_write
,break 83
(i.e. where thewrite-pop2-end
test harness is created). - You can set breakpoints on your functions using the function names, as usual.
- We don't recommend modifying any files in the
libsponge/util
directory, since messing up the sponge library will make debugging difficult. - If a test is timing out, but you want to check if it passes without the timeout, run the test executable individually, which won't enforce the timeout. e.g. from the build directory:
./tests/byte_stream_one_write
. gdb
may help debug timeouts. While running the test ingdb
, if it appears to hang (meaning it may be executing a slow portion of code), ctrl-C and backtrace to pause and see which code was executing.
Grading breakdown for labs: 15% style, 10% writeup, 75% functionality.
ctest -R '^test_name$'
Sure.
Short Answer: This is a typo in our documentation. You should run make check_lab0
, not make check
.
Long Answer: The make target, check
, runs the entire sponge test suite. The full test suite requires a shell script, tun.sh
, which you won't get until a later lab. The make target check_lab0
will just test lab0. You should be able to run it with the lab0 starter code (although it will return an error until you've completed the lab!).
To use a late day, please email the staff list (cs144-aut1920-staff@lists.stanford.edu) by the lab deadline, letting us know if you are using late days. With weekly labs, the CAs want to be able to get feedback to you quickly. As such, we will start grading as soon as the deadline passes, and want to avoid redundantly grading submissions that weren't intended to be final.
Here are some links to resources to learn Git.
Brief summary: Git commits store a snapshot of the current state of your code. For instance, say you break a feature that you know used to work, but aren't sure which code caused it to break. If you made a commit after getting that feature working, you can checkout that commit to see what things looked like in the good old days when it worked. So, make a commit every time you get something working! To make a commit:
First, git add the files you've changed - you must add before every commit. e.g. git add ../libsponge/byte_stream.*
Then, commit with a message describing the state of the code. e.g. git commit -m "All tests pass except many_writes times out."
You can see which files you modified by running git status
. To really understand the state of a Git repository, the command-line tig
tool, or the graphical gitk
tool, can be very helpful.
A handy shortcut to commit all modified files is git commit -am "[message]"
.
Don't git push
- when you're ready to submit, you'll use git bundle instead to package your commits.
Sanity check: If you want to verify that you have submitted exactly the files you intended to, you may find the following useful.
First, once you've committed, run git status
. You should see a message saying nothing to commit, working tree clean
. If you have uncommitted changes, you'll see this instead: Changes not staged for commit:
.
Second, after you've copied your git bundle to rice, and logged into rice:
gdb
is a great tool for debugging the labs! Check out the CS107 guide if you need a refresher.
To use gdb
to debug a test:
Lab 0
- What should the behaviour of my program be if the caller tries to peek or pop with a
len
greater than what is available?
We don't have any preference on the behavior if the caller tries to peek or pop more than is available in the stream, as long as you behave reasonably and don't crash in that situation. If you want to peek/pop the maximum available, that is fine with us. If you want to throw an exception (either for an overlength peek or an overlength pop), that is also fine with us.
Lab 1
- Which bytes should be accepted in a
push_substring
call?
Those whose index is within - Is it okay for our re-assembly data structure to store overlapping substrings? It's possible to implement an interface-correct reassembler which stores overlapping substrings. However, allowing the re-assembler to do this undermines the notion of
capacity
indices of the first unread byte in the re-assembled stream.
Pictorally:
capacity
as a memory limit, so we'll consider the storage of overlapping substrings to be a style violation when grading.
Lab 2
- Can we use
static_cast
in functions forWrappingInt32
?
Yes, these kinds of functions (those that explicitly convert between
numeric types) present exactly the kind of situation in which
- Should
TCPReceiver::segment_received()
return true if any part of the segment is inside the window, even if both the beginning and end of the segment are outside the window?
Good question—please
see our
Piazza note where we clarify this. The short answer is
that we've deleted a test in the lab2 starter code in order to
make both answers to this question (yes and no) acceptable.
static_cast
is appropriate. Note also that
you don't
need to use static_cast
if you don't
want to—you can
explicitly construct a WrappingInt32 with syntax
like WrappingInt32{x}
.
Lab 4
- In
TCPConnection::segment_received
, what are the three conditions in which the TCPConnection needs to make sure that the segment receives at least one ACK segment in reply, and may need to force the TCPSender to spit out an empty segment to make this happen?
- If the incoming segment occupies any sequence numbers
(
length_in_sequence_space() > 0
) - If the
TCPReceiver
thinks the segment is unacceptable (TCPReceiver::segment_received()
returnsfalse
) - If the
TCPSender
thinks the ackno is invalid (TCPSender::ack_received()
returnsfalse
)
- If the incoming segment occupies any sequence numbers
(
- Okay but hang on, what happens if
the TCPConnection wants to send an ACK
segment but the TCPReceiver's ackno is
missing (e.g. because it hasn't gotten an incoming SYN
yet)?
If the ackno is missing, then you can't send an ACK segment. Don't send one in this case. - When is the connection fully over (i.e., when should
active()
start returning false?There are two ways a connection can fully end:
- [Unclean shutdown] Any segment with the RST flag is sent or received, or
- [Clean shutdown] The sender is totally done (meaning, the sender's input stream is at EOF with no bytes in flight) AND the receiver is totally done (meaning, the receiver's output stream has ended) AND either
-
_linger_after_streams_finish
is false (no need to linger), or time_since_last_segment_received() >= 10 * _cfg.rt_timeout
(we've lingered long enough)
-
- I think I may be having some trouble with my
logic about when a connection is fully over (i.e., when to stop
returning
true
fromactive()
). Some of my tests are timing out. How can I debug this?Check out this Piazza thread for a debugging approach that may help. It may be easier to run the client and server manually (as described there) if you want to figure out where things are going awry.
- The test suite keeps talking about the state of the TCPReceiver and TCPSender—what are these states?
The test suite (and the tcp_state.hh and tcp_state.cc files) uses the public interface of your TCPSender and TCPReceiver to put them in broad categories or states. The tests make sure that your TCPSender/TCPReceiver are in the right state at the right time, given the actions taken.
Here is a diagram of the expected evolution (and definition of each state) for the TCPReceiver. The receiver starts with no ISN (and therefore no ackno) (LISTEN). Then it moves to a state SYN_SENT where it has received a SYN segment (so there is an ackno) but hasn't assembled a FIN segment (so the stream hasn't ended). This is where most of the connection happens. Eventually the receiver assembles a FIN segment and ends the stream (FIN_RECV).
And here is a diagram of the expected evolution (and definition of each state) for the TCPSender. The sender starts with nothing sent (CLOSED), then moves to a state SYN_SENT where it has sent something but nothing has been acknowledged. At some point, some bytes are acknowledged (SYN_ACKED). This is where most of the connection happens. Eventually, the stream ends and the sender sends a FIN segment (FIN_SENT), and some time after that, the stream is over and everything has been acknowledged (FIN_ACKED).