so today I want to talk about debugging
magic and why printf might break your
program
[Music]
good morning everyone welcome back sorry
I have missed a couple of weeks life
just got really crazy and I just wasn't
able to get him out hopefully things are
going to calm down a little bit and
we'll have a lot of great videos coming
up soon but today I want to talk about a
scenario that comes up at least once or
twice a semester where someone comes to
me and they say hey I've got this
program and is magically breaking
whenever I add a printf statement or
maybe it breaks when I remove a printf
statement whatever the case may be it
seems magical and they're like I'm sure
printf isn't broken but what's going on
so today I wanted to talk about that see
if I could give you a little bit of
debugging help just some insight into
what might be going on and hopefully
that's something that'll help you in a
future debugging experience before we
jump into it I just want to thank
everyone for all your help all your
support of this channel I really
appreciate it I couldn't do it without
you also I just want to recognize we
just hit 100 000 subscribers which was
awesome honestly this is a milestone
that I didn't really imagine we would
ever even hit and it's really humbling
so thank thank you so much for your help
in making this channel possible and now
without further Ado let's jump into the
code okay so this is not this and code
that I talked about but this is an
example that I created that mimics this
Behavior so I have a really simple
program maybe the kind of thing that you
might see in an intro class so basically
what it's going to do is it's going to
read in some input from standard in so
I'm just using fgets right here to read
in input a line at a time and then what
it's going to do is it's going to come
through here and it's going to try to
split them into key value pairs now this
isn't all that critical the whole point
of this exercise is not this test
program the exercise is really looking
at what happens when you have magical
failures so really quick just if I want
to point it out
let's just compile and so if we run
example it's just going to wait for
input but so let's say that we have an
input file so I have an input file here
this is basically just the the test
inputs that I'm going to use so this
passes a bunch of key value pairs so in
this case you know first name last name
and age we're going to pass that into
our program so I might say something
like uh cat inputs.txt and pipe that
into my example program and so in this
case you can see well okay it looks like
it kind of works in this case I'll just
say yeah this is this is fine this is
what I expected is each time it gets a
key value pair it's going to print out
the current status of the person and so
it just updates it with each one I get
so the first one it just adds the first
name and then the second one adds my
last name and then the third one adds
the age 123. so that looks like it's
working now the thing that is the thing
I want to focus on is if you come here
there's this printf and often this is a
situation if you have an auto grader or
something that's that's machine grading
this code maybe you didn't you weren't
supposed to have this so I come in here
and I remove it and I come down here and
I compile and then I decide to run that
exact same input and now it's seg faults
so this is where students start to go
hey wait what happened there because
that printf that was that wasn't
changing anything right it's not even
printing out any variables so it can't
be a bad pointer or something so I just
thought we'd just talk a little bit
about what's going on here because this
happens pretty frequently and yeah so
when you have a printf call that makes
or breaks your program here there's a
few things that you can look at first
before you panic and think that your
code is possessed by demons so if you're
seg faulting in a printf the first place
you would look is are we printing out
any variables now I know I just said
we're not we're not in this case so
that's not the case but you might be
printing out a variable you might be
dereferencing a pointer in this call
that is maybe it's a bad pointer so that
could result in a seg fault but in this
case we know that's not the case because
the SEC fault happens when I remove the
print app right so it's it's the absence
of the printf that's causing things to
break okay now the reason for this is
simple and it's also something that's
not portable you may not see this on
your machine I'm seeing this on my Linux
machine in just a minute I'll show you
on my Mac how it works a little
differently but the point is with this
example we are in undefined Behavior
territory so we may see different
results on different machines the
problem the actual bug in this program
is right here the problem is is that I
did not allocate enough space for the
values that I wanted right so I actually
allocated here the size of values which
is a pointer to a struct and so what
that did is it allocated the size of a
pointer okay so it allocated eight bytes
for this thing not the uh you know what
it looks like a little over 1K that I
actually needed for this struct okay so
what I should have done here is said you
know size of struct my values or
something like this you know something
that actually allocates enough space for
the struct if I do this then you're
gonna see that things work just fine
without the printf no problem so the
question is why did the printf actually
caused this issue so if I come back
let's put our bug back in here in this
situation the thing that changes when
you add the printf is that printf
allocates memory okay I mentioned this
briefly in a previous video I think but
there's a buffer inside printf that it
allocates it usually allocates it on the
first printf call and generally
depending on your buffering mode that's
either 1K or 8K in this case it's 1K and
what that does is if you think about it
this way when we request memory for the
Heap we always think of the Heap growing
as we allocate more memory we request
that memory in pages of memory those are
like four kilobyte chunks from the OS
usually and so we have these pages and
our Heap is growing but it grows in
chunks of 4K now the trick here is that
if I do not give a block enough space I
don't allocate enough space I have an
eight byte block and I'm overwriting
that I'm basically clobbering memory
after that the outcome is not always
predictable sometimes it might seg fault
if we actually walk off the end of a
page but if we don't walk off the end of
the page we might just be corrupting
memory that we have no business touching
and that's what was happening in this
case but when I add the printf printf
happens to allocate an extra 1K so that
shifts things around and either changes
what I'm corrupting with my memory
corruption bug or it shifts my memory
allocation just a little further into
memory so that now I do end up walking
off the end of the page in either case
really what it's doing is it's just
adjusting the layout of the Heap just
enough that it is causing me to seg
fault actually walk off into an unmapped
page where without the printf I was not
walking off that page okay so now I told
you we would do this let's jump over
into now I'm in my Mac so I just jumped
out of the VM let's recompile this okay
so now we have this program again and
we'll just do the same thing we did
before so let's cat our inputs into
example and you can see okay this this
works fine with out the printf and
because I forgot to save this so now if
we come in here and we compile it so we
still have the bug in here and we
compile it with the printf in this case
it's not side faulting so again like I
said my Mac and Linux happen to be using
different allocators there you know
things are lining up differently against
these pages and so in this case even
with the printf or without the printf
it's not seg faulting you know the bug
is still there so for example if I made
this struct slightly bigger then in this
case we might have a segfault so anyway
really simple example but I hope that
helps you in the future when you see
some kind of magic situation where
you're adding or moving or removing
something that seems like it shouldn't
have any impact on your program and all
of a sudden you're getting a seg fault
always think about memory corruption
because that's a really common culprit
is you might have a space you might have
a block that you didn't give enough
space to it's a Memory that you
shouldn't be writing to or shouldn't be
writing as much as you are writing into
it and it's just changing the Heap
layout just enough to cause you to break
so the problem likely isn't the printf
call itself it's just having an impact
on the rest of program and that's
bringing another bug to the surface
which is great because now you can
reproduce your bug and so hopefully you
can get it fixed so I hope this helps
you hope you learned something new and
until next week I'll see you later