489: Constructive Cat
Transcript from 489: Constructive Cat with Christopher White and Elecia White.
EW (00:00:06):
Hello and welcome to Embedded. I am Elecia White, here with Christopher White. This week it is just us, and that is all I have. So, "Winnie-the-Pooh"?
CW (00:00:20):
Yep. It is 4:15 in the afternoon, so we are late, and this will not drop until another day. Also, it is late enough that I should have brought my reading glasses to look at this computer, so I do not even know if we are recording right now.
EW (00:00:35):
Well, that part does seem important.
CW (00:00:37):
It looks like numbers are going up.
EW (00:00:39):
Okay. We have a contest going on, or we did, for folks who were interested in getting a copy of "Looks Good to Me" by Adrienne Tacke. Ben Combee, Nathan Olff and Ralph Hempel have won. I will be sending you all emails. Thank you for the nice things you said about Memfault.
CW (00:01:06):
Wait, what? Oh, right.
EW (00:01:08):
Right. That was-
CW (00:01:09):
Confusing.
EW (00:01:10):
Yeah, I know.
CW (00:01:11):
<laugh>
EW (00:01:11):
Nathan also asked a question, "Last episode about code reviews was interesting." At his company, they often have only one embedded software engineer working on a project. "It gets more difficult to get a review, or give a review. Do you have any advice on how to code review, when projects are worked on by a single developer?"
CW (00:01:40):
There are no other developers? This is just a single developer?
EW (00:01:43):
Let us say that it is one developer in a team of developers, but they only ever work on their own thing.
CW (00:01:52):
<sigh>
EW (00:01:55):
Like you and I.
CW (00:01:55):
Yeah.
EW (00:01:55):
Where we do not really work on the same projects right now. So asking you to code review one of my things, would mean that I have to spend ten hours explaining what it is I was supposed to be doing, what the code is trying to do.
CW (00:02:12):
I do not think it would take ten hours. But that was going to be my suggestion, is to incorporate a small design review at the head of code reviews. Because if people are always unfamiliar with code, then the kinds of things they are going to be looking for, are not as deep as the kinds of things you want to be looked for.
EW (00:02:30):
Right. Walking into a code review where you do not know anything about the system, makes you far less effective.
CW (00:02:38):
But that is pretty common anyway, even on teams that are more tightly knit. Because sometimes there are the areas of the code that you just do not- The only times you see them is during code reviews.
(00:02:48):
I think it is a common problem. It is just made worse by the fact that if there are fewer developers, or only one developer in a particular area. Maybe all the other developers are app developers or something. Right? Then it gets even trickier if like, "Oh, this is not even a language I am really familiar with." Hmm.
EW (00:03:08):
So I have been working some, trying to figure out code reviews better.
CW (00:03:15):
For a client.
EW (00:03:16):
For a client. But also just because- This is the question I have had. Do I talk to Phillip over at Embedded Artistry? And ask, maybe he code reviews for me sometimes, and I code review for him? But then we have to do NDAs, and...
CW (00:03:34):
And then you cannot do NDAs on behalf of another company. Yeah, that does not really work.
EW (00:03:38):
And then we run into the problem of either we have to do it often, or you do not really remember what the project was. One of the things- There are a couple of things that have helped me. One is to...
(00:03:54):
Okay. Number one. Do the whole Git branching thing. I did not used to be in favor of this, but I am becoming more in favor of it. You have the main or the dev branch, and then you pull a branch in called "feature blah blah, blah," and then you merge back. And you have a PR to merge back into dev.
CW (00:04:21):
Mm-hmm.
EW (00:04:21):
That PR is actually really useful.
CW (00:04:26):
Pull request.
EW (00:04:27):
Thank you. The pull request.
CW (00:04:28):
Which does not actually help explain what it is at all.
EW (00:04:30):
No. No.
CW (00:04:32):
Because it is all Git terminology. It makes zero sense at all, because actually it should be a "push request."
EW (00:04:38):
No! You are requesting other people pull your code.
CW (00:04:41):
But you are requesting to put your code in!
EW (00:04:44):
You are right. It should be a "push request."
CW (00:04:45):
Anyway, this is one of my... <laugh>
EW (00:04:48):
But it is at this point, as you are doing this PR, which we are not going to define now...
CW (00:04:57):
<laugh> A "PR." It is your collection of-
EW (00:04:58):
It is a peer review.
CW (00:04:59):
Sure. But it is your code in a format on a website, that people can look at and approve or make comments on.
EW (00:05:06):
And it may be one commit, or it may be enough commits to make this feature mostly done.
CW (00:05:12):
Right.
EW (00:05:14):
Okay. So you have this thing, you have collected a couple of commits, you have pushed them to the server on a branch. Now you would normally, if you were in a team, send that out. You would fill out a PR, and then other people would come and review your code. Okay.
(00:05:31):
But you are by yourself. If you still do this, you do a couple of the checks for the PR system. Usually as part of a PR, you compile and test on the unit. Well, hopefully you did that. You look at the differences, and make sure they are as intended.
(00:05:53):
If you had not already done that during your commits, sometimes you are like, "Oh, right. I have a TODO in there." Or a FIXME. Or a DO NOT CHECK IN. This is the point where you actually have- You can do your own reviews for those things.
CW (00:06:10):
I have found that looking at my code in that format versus in the editor, where you can see the changes. They are highlighted differently. It is not in your editor that you are used to, and you gloss over things because it looks like your editor. Sometimes just changing the context can let you see things that you would not normally see when you are coding.
(00:06:29):
So yeah, I totally agree that just going through that process gets a lot of things noticed, by yourself.
EW (00:06:38):
I have been looking at Adrienne's PR templates, so whenever you are adding a feature, you have to fill out this little form. Like, "Does this feature cause any backward compatibility problems?" Of course you should have been thinking about that, but a checklist really is quite helpful here.
(00:07:00):
It also makes you think of this feature as a single feature, and not as three weeks worth of code. You are committing an enclosed thingybob and not randomness. Which that sort of structure is good for programming. It means that we are not just randomly running around the code changing whatever we want.
(00:07:24):
And then you set up your PR for Friday, and on Monday you come back and review that code and wonder, "What idiot wrote this!" But then you have to admit that you are the only person on the project.
(00:07:37):
It is always surprising to me how quickly I forget what I am working on <laugh>. I would normally say if you can wait a couple of weeks, that is plenty. But you cannot probably, so even a weekend is enough for you to get a little bit of distance from your code, and then you do your own code review.
(00:08:02):
Again, going back to Adrienne's book, because that was where this conversation came from, there are check boxes, a checklist of what you would do as a reviewer. You can pick and choose what is in her book.
(00:08:17):
Whether or not a reviewer is supposed to test it or compile it themselves. Whether a reviewer has some automated tool to make sure that you conform to standards, which hopefully Jenkins is doing for you, instead of some other person or even yourself. It is just...
CW (00:08:37):
Yeah, no. Along those lines, I would recommend setting up, even if it is just you, some static stuff. Some static checking on the pull request system, whether it is GitHub Actions or Jenkins or whatever, that do a build if you can. Maybe some unit tests, and maybe do Clang-Format or something like that.
EW (00:08:59):
A lot of this is more important because you are by yourself.
CW (00:09:02):
But it is nice, because if you set that stuff up, what you are saying about the checklist, is some of those things just happen. It will not let you put it in if some of those things fail. And you do not have to worry about, "Oh, did I do that checklist item." Just setting some of those things up- If you are by yourself, yeah, just get the easy things.
(00:09:21):
The hardest thing is to find the deeper weirder bugs, that somebody coming with fresh eyes will see. That is going to be less likely, unless you just do a mind wipe, like you said. Or have a head injury between pushing the PR and reviewing it.
(00:09:40):
Maybe there the rubber duck analogy is the best thing to do, is to, "All right, if I am going to review this, and nobody else is going to look at it, maybe I should do a presentation to nobody about this code."
EW (00:09:55):
Definitely you could have two or three stuffed animals, and each one could have their own little quirk.
CW (00:10:01):
That is going a-
EW (00:10:02):
Grumpy triceratops.
CW (00:10:04):
That is going a little too far, but sure <laugh>.
EW (00:10:07):
The nitpicking mongoose. You can have whatever you want.
CW (00:10:12):
<laugh> The "No" narwhal.
EW (00:10:15):
Yeah. This sounds silly.
CW (00:10:18):
It is not!
EW (00:10:19):
And it is.
CW (00:10:19):
It is not!
EW (00:10:19):
But it also is not. There is value to having to articulate your inside ideas to the outside world.
CW (00:10:29):
But I am going to say that it takes a great deal of self-discipline, which I do not have.
EW (00:10:33):
So much!
CW (00:10:37):
So if there is a way to find- If you are on a team that does not have- There is nobody else. Well then there is nothing else you can do besides these things we are describing.
(00:10:46):
If there are other people who are at least developers of some kind, I think it is valuable to give a presentation to them, instead of stuffed animals. Even if they are not familiar with the codebase for the code. Because then- Usually they are smart people, and they will have questions about just how things work and, "Did you think of this?" Things you might not have thought of, because everyone has blinders.
(00:11:08):
So it is worth having at least a design review or a walk-through. Maybe not a formal code review, if they are not up to reading the subsystem in detail. But just a walk-through and, "Are there any questions?" or, "Have I missed anything?" kind of things.
(00:11:27):
Because the goal here is not to perfect code. Because guess what? There is not any perfect code. The goal is to find obvious things, find things that are going to cause problems later, that you might miss on a cursory walk-through. To find the cursory walk-through stuff, typos and gross errors and compile errors and unit test failures.
(00:11:54):
And then beyond that, just have a different set of eyes on the problem, so that people can maybe notice something you did not notice. Because that is the key, is we are all focused on what we think is a full understanding of things, but we tend to get very narrow. So outside input of any kind is useful, as long as it is constructive.
EW (00:12:21):
Constructive cat.
CW (00:12:23):
Constructive cat.
EW (00:12:24):
Yeah. So I think it is possible. I think it is worth doing. I tend to think that if you want comments from me that are about spaces- If a comment can be made by a robot, I actually refused.
CW (00:12:44):
There should not be comments about spaces, because you should have a formatter that just does that.
EW (00:12:48):
Yeah.
CW (00:12:49):
We should be beyond-
EW (00:12:50):
We should be beyond that.
CW (00:12:50):
Code style. Unless it is egregious stuff. ClangFormat does a good job.
EW (00:12:59):
So if you can find somebody else. It is going to be a trade, and it is not going to be a worthwhile trade in the beginning.
CW (00:13:07):
I guess. I do not think of it that way. If you are in an organization, it is not a trade. It is a "This is your job."
EW (00:13:15):
Yes. But I am asking you to stop doing what is obvious on your job, and to work on my job for a little while.
CW (00:13:20):
Well, that is management's job to say, "You have to do this," because-
EW (00:13:23):
It is easier to trade with someone.
(00:13:24):
<music> Thank you to Memfault for sponsoring this show. We appreciate their sponsorship, and the work that they do.
(00:13:35):
Memfault is a leading embedded device observability platform, that empowers teams to build better IoT products faster. What that means is that if you have just realized that you are going to build five, ten, a hundred, 50,000 units and you need to keep track of them, they will let you create your own dashboard to observe how your system is doing in the field.
(00:13:59):
Memfault gives developers a more scalable and sustainable process. This accelerates the time to market and de-risks product launches. You can cut product costs and deliver more high quality software.
(00:14:11):
Trusted by leading brands such as Bose, Lyft, Logitech, Panasonic, and Augury, Memfault improves the reliability of devices across consumer electronics. And across mission critical systems, such as access control, point of sale, energy and healthcare.
(00:14:28):
Thanks again to Memfault for sponsoring this show. Check out memfault.com, and the Interrupt blog which is filled with incredible amounts of information. <music>
(00:14:37):
Okay, so let us see. What else did I have on my list? Seriously, we should just go straight- I mean, we left off on how to get rid of Kanga and Roo.
(00:14:54):
Let us see. Origami! Origami! That is why we had to do a show. Actually, I had somebody scheduled and then I messed it up. But that is all fine. It will all happen soon. But we are gonna have three shows Thanksgiving weekend. Do not tell Christopher. But I have an origami show going on.
CW (00:15:18):
Tell me about your origami show.
EW (00:15:20):
You went. Did you not go?
CW (00:15:23):
Several times.
EW (00:15:25):
Yeah, I need to go by again today.
CW (00:15:26):
<laugh>
EW (00:15:26):
So I have two bookcases in the library, our local Aptos library. I filled it with an origami octopus garden, in which I have a bunch of octopus and jellyfish and random weird curvy coral sponge wave things, and books.
(00:15:55):
On the left hand side is order, which are all the symmetric things. And on the right hand side is chaos, which is all of the- Where the octopus start lifting their legs, and get all squiggly and stuff. On the order side, I have natural history books that they are all reading. Because it is a library thing and I was really excited to add books to it. On the right hand side, it is art books, origami books really. And my book. But that is kind of hidden in there.
(00:16:25):
On the top there are the light colors, and on the bottom there are the dark colors, so that it has the effect of going down the ocean. It is really better if you see it from the side, because that is where the kids would see it. Those are the ones who came in and would get excited about it.
(00:16:43):
And it is all designed and folded by me. I am not the first person to put sine patterns together. But definitely everything was stuff I made out of not other people's patterns. There was one pattern I actually really wanted to put in, and then I realized, "Oh, this is actually somebody else's pattern. I cannot put it in." Instead I just took that origami book, flipped it to that page and-
CW (00:17:13):
<laugh>
EW (00:17:16):
Put an octopus on top of it.
CW (00:17:17):
<laugh>
EW (00:17:20):
Because it looked like starfish.
CW (00:17:22):
Mhm. Oh, right. That one.
EW (00:17:22):
I could only do minor mods. And then I was like, "This is somebody else's pattern." I got cards. I decided-
(00:17:31):
Somebody actually offered me money for one of the jellyfish recently, and I was just like, "No! Absolutely not! It does not matter how nice you are trying to be about this. Or if you have somebody that you want to give it to. Just ask me for another one. You are a friend and a neighbor. Just fine."
CW (00:17:53):
Mm-hmm.
EW (00:17:53):
Because they cannot pay me what it would be my engineering rate to make them.
CW (00:18:01):
Well-
EW (00:18:01):
That would make them cost prohibitively amount.
CW (00:18:06):
Guess what about art? <laugh>
EW (00:18:15):
<sigh> I do not want to make this business.
CW (00:18:18):
No, you already went down that road with something else, and did not enjoy it afterwards. Huh?
EW (00:18:22):
It meant- When I give something to somebody now it is like, "Here, I made this!" If you sell something, you have to look and say, "Oh, the legs are not quite perfect."
CW (00:18:33):
Well, there is that. And there is you are doing it for your own- Not your own enjoyment, exactly. But it is a-
EW (00:18:39):
It totally is for my own enjoyment!
(00:18:40):
But it is a different pathway. Right? It turns it into a job, instead of- I do not know. We all like to get compensated for things that we do like this, but...
(00:18:53):
But compensation comes in different ways, and it is not always money.
CW (00:18:55):
Yeah. Right.
EW (00:18:55):
I went to the library last week, last Friday afternoon, which was not a hopping time at the library, basically just to snoop. So I sat in a chair that was a ways from where the display case was, and I just watched people walk in. I folded a jellyfish while I was there.
(00:19:19):
I just wanted to see if anybody actually noticed. Because when I was there with other people, or- People noticed, but I did not know if they were noticing because other people were standing around it. You know?
CW (00:19:33):
Sure. Yeah.
EW (00:19:33):
So people came in. It was kind of interesting people-watching.
(00:19:38):
But then this little girl came in. She must have been about ten. She walked in. The display case is near the door. She walked in and she stopped. She put up her foot, and she put both hands on her cheeks and she smiled and just was so gobsmacked. I loved it. So I gave her the jellyfish I was working on, which if I was selling them, I do not know if I could have.
CW (00:20:08):
No. Yeah. Well, I mean, yeah. I think that is great. I think it is fun you went down there to see what people- <laugh> I would be- Not embarrassed. I would be...
EW (00:20:21):
I felt totally like a creeper. But it was-
CW (00:20:23):
Not like a creeper, but I would just be hesitant. Because I would worry about like, "Oh, what if-" I do not know.
EW (00:20:28):
They are library kids.
CW (00:20:30):
I know. I know.
EW (00:20:31):
They are the kids that are excited about things.
(00:20:32):
But I did do- I made postcards. I was not going to, because why? But I wanted to feel like this was real. How do you feel something is real?
CW (00:20:52):
Just make it real. What would be differently real? You have an art installation in a public place. That is it. That is real.
EW (00:21:05):
But it is not, because- I hesitate to say this, because one of our listeners said, "Oh, it must be really nice that somebody accepted this." So the way that I got an art installation at the library was I went into the library and said, "So those display cases. How do people fill them?" And they gave me a form.
CW (00:21:22):
Yeah, yeah. I know.
EW (00:21:23):
Then I wrote in the little form and she said, "Oh, that sounds like fun." And then she put it in the folder and said, "How about November?" It was not a juried art show.
CW (00:21:31):
That is fine. But there is a lot of stuff like that.
EW (00:21:35):
But that makes it feel less like an art show.
CW (00:21:38):
No, I get that. But if you had held a reception at your house or your studio, would that have been less? Imagine you had a studio, and you held a reception and invited people.
EW (00:21:48):
I know! It is why-
CW (00:21:49):
Would that feel any different? You set that up.
EW (00:21:50):
One of my artist-
CW (00:21:51):
You bought the studio.
EW (00:21:52):
Friends did suggest having a closing...
CW (00:21:56):
No, but you get what I am saying, right?
EW (00:21:57):
Oh. Oh, if I had an opening at my- If I had a studio, if I had- Yeah, that would feel fake too.
CW (00:22:06):
Well, I bet most artists do not- <laugh>
EW (00:22:08):
Sorry.
CW (00:22:09):
Do not feel that way. Anyway, I think it is cool.
EW (00:22:12):
And I did a little artist statement, and I did a bio. I do not know, it was kind of cool. And Chris came and took pictures, and got lots of moody shots that are really fun.
CW (00:22:25):
Moody? I was not shooting for moody, but okay.
EW (00:22:29):
Well yeah, but- You got one octopus really well in focus, and some of the others not in focus. So it makes it look like that one is more alive than the others.
CW (00:22:39):
Okay.
EW (00:22:39):
And you got some of the curvy things. I do not know. I liked the pictures.
(00:22:45):
So what have you been up to?
CW (00:22:52):
Work. Drumming.
EW (00:22:54):
I worked a lot today! I billed more hours today than I usually do.
CW (00:22:57):
Civic duty.
EW (00:22:59):
I am so glad your civic duty is over.
CW (00:23:03):
And trying to not go insane. Some of those things are going okay. I will leave it to the listeners to figure out which.
EW (00:23:15):
How is your drum class going?
CW (00:23:16):
It is good. It is a lot of work and it is slow progress. Focus on very minute details of technique and stuff. So it is a challenge. I have to do a cover this- It is divided into modules. There are ten modules for the whole course. It is supposed to take anywhere from six months to longer than six months. It has taken me longer than six months, because I am on module six. I have already been for six months.
EW (00:23:38):
Well, it was six months to a year.
CW (00:23:41):
Yeah. In this module, module six, there is a midterm project and that is to do a drum cover. I think I have picked the wrong song, so <laugh> I need to spend some more time thinking about maybe changing it.
EW (00:23:50):
Did I hear you say it was an old Satriani song?
CW (00:23:52):
Mm-hmm.
EW (00:23:52):
I am telling you, Doctor Worm was way better.
CW (00:23:57):
I mean old, as in newer than Doctor Worm?
EW (00:23:59):
Oh. Fine.
CW (00:24:03):
<laugh> But it is a little too fast for me to endure-
EW (00:24:09):
<laugh>
CW (00:24:11):
For the four and a half minutes, without building up some stamina on my right arm. So. Yeah. No, it is fine. The drum thing is good. Working on some new songs. Still working on new songs for the band. Someday we will have a new record. Not maybe this year. Maybe. No, not this year. Maybe- What year is it?
EW (00:24:32):
It is currently 2024. We are speeding into 2025.
CW (00:24:36):
Right. For some reason I thought it was like April. Probably next year.
EW (00:24:39):
<laugh>
CW (00:24:39):
Probably next year. Definitely not this year.
(00:24:41):
And I have ramped up on an older client, because they have more stuff for me to do. And adjusting time with the others, so things are kind of balanced. But yeah.
EW (00:25:01):
Do you still hate computers?
CW (00:25:03):
I do not know that they were- I am becoming less and less happy about computers, as the weeks go on. Yeah. But I have to work on them.
(00:25:16):
The stuff for this client is kind of fun. Working on UAVs again, and doing some CAD stuff which- The problem is I do not do very much of it. So if I do it once a year, I have to climb the learning curve every damn time. So luckily for this, I did not have to- I thought I was going to have to modify a lot, but I did not have to modify too much. But I have to figure out some camera mounts, to mount to this UAV. That is a bit tricky.
EW (00:25:46):
Are there skills for you that are more slippery than others?
CW (00:25:50):
Oh, yeah. Oh, yeah. Stuff like that where I only pick it up a couple times a year, or something like that.
EW (00:25:58):
But there are some things I can pick up a couple times a year, and still be fine with them. I am not sure they are simpler, they are just-
CW (00:26:05):
Like what?
EW (00:26:07):
Stickier. Anything to do with cooking is not slippery to me. Like, I can make a recipe in April. And in October I can make the same recipe from memory-
CW (00:26:20):
Yeah? Okay.
EW (00:26:20):
Or from one quick read of the recipe, if I used a recipe.
CW (00:26:27):
Yeah, I do not know how. The stuff that tends to- That sounds- Do not have to be flip, but that sounds a little simpler than the things that I lose. I do not know that if I have anything like that, that- I am trying to think of something that I- Like Python, I have to go look up Python stuff all the time again. Every time. What?
EW (00:26:47):
Python is stickier to me.
CW (00:26:48):
Python is stickier, but there are still aspects of it- Like if you asked me to go work with Pandas. I have lost all my Pandas, even though I spent many, many hours working with it. It is going to be faster this time. But if you asked me to sit down in an interview and write some Pandas code, I would not be able to do it from...
EW (00:27:05):
You draw a black and white bear. <laugh>
CW (00:27:07):
Right. Yeah, I do not know. Certainly physical things like musical drums and that kind of stuff, some parts of it are sticky and some are very not sticky. Like, if I do not do something every week, I will lose it. Certain techniques and things.
EW (00:27:25):
I wonder if it is just complexity, and whether or not it builds off existing knowledge well.
CW (00:27:31):
The things I tend to lose are like, I mean really, really badly, like applications that have a thousand buttons and stuff.
EW (00:27:37):
Ohh. Yes.
CW (00:27:37):
So the CAD program, it has got a row of a hundred icons at the top, and all these modes and stuff, or keyboard shortcuts. There is just a lot of stuff to remember, just to use.
EW (00:27:50):
There is an origami program that I want to use. I opened it and I looked at it. There were more buttons than Photoshop. Then I just closed it. I want to learn it, but I do not want to learn it enough, to have to look at all of that while I am trying to learn it.
CW (00:28:07):
Yeah. It is tough to want to know how to do a lot of things, because to some degree you have to keep refreshing that knowledge <laugh>. Once you know how to do too many things, you would run out of time to refresh.
EW (00:28:19):
Yeah, I have been thinking about that with some robotics. Some robotics is sticky and some of it is just- I just keep- I do not know why I cannot make it stick. And I cannot make it stick from week to week. So if I get interrupted, I get a little-
(00:28:36):
Well, I guess maybe I do not truly understand some of it, and then I get interrupted to do something else and then I believe I understood it better. <laugh> Then I cannot do it, and I wonder why.
CW (00:28:53):
I also find that there are two aspects of stickiness. Just to use an example, if you ask me any of the 26 letters in Morse code, I can tell you. I know them all by heart, it is fine. But my speed with copying them, if I listen, if I do not practice a lot, it goes away completely. Suddenly I cannot keep up at a very slow rate. And so yes, I know-
EW (00:29:18):
That is why you have to use the ASCII chart to communicate.
CW (00:29:21):
Why would that be more helpful?
EW (00:29:23):
It is just easier to remember with A at 41 and 61.
CW (00:29:28):
Okay, can you do that at 12 words per minute? Anyway <laugh>, what was I talking about?
EW (00:29:37):
<laugh> Morse code. I am sorry.
CW (00:29:39):
Morse code. So I have the knowledge of Morse code, where I can rattle off any of the characters. But my proficiency with it as a skill, which is separate from the knowledge, goes away quickly without practice.
(00:29:55):
That is true of a bunch of stuff I think, like music and code and other things like that. Yes, I can tell you all about all the keywords in C and how C works. But if you ask me to do a particular small container class of some kind in C++ or something, I might not remember how to do that. Anyway, I do not know if that made any sense, but I think there are multiple aspects to this.
EW (00:30:19):
I think this is how people feel about function pointers.
CW (00:30:21):
<laugh> Function point- It is a syntax. It just does not- It is horrible.
EW (00:30:24):
It is really bad syntax.
CW (00:30:27):
Although I think I finally memorized it, after 20, 30 years. And I no longer use them anymore.
EW (00:30:36):
Exactly. Ooh, Ooh! I want to go back to origami. I am sorry. Is that okay?
CW (00:30:39):
I do not care. This show is about nothing.
EW (00:30:42):
I am really sorry. Usually- It will be better next time, I promise.
CW (00:30:46):
<laugh> That is fine.
EW (00:30:47):
So a friend came from out of town, and asked about and was surprised by the display. Because it was jellyfish and octopus.
(00:31:01):
I mostly have talked on the show about snails, which I had been working on for a long time. I had a lot of Python scripts to generate the snails. Then I had the biology in there, if you cut the origami snails in half, they look like if you cut a snail shell in half, which was all very intentional. And yet there were only a couple of snail shells hiding in my display.
(00:31:28):
She was surprised by that, and thought and asked about coding, and what this had for coding. I do have quite a bit of software that goes with generating-
CW (00:31:40):
Mm-hmm.
EW (00:31:42):
But it is more in line with, "How do you make patterns change?"
CW (00:31:49):
Okay.
EW (00:31:49):
So a lot of things start with sine waves. If you put a sine wave- You have three parallel sine waves, and you fold mountain fold, valley fold, mountain fold, and then you end up with this nice undulating curve sort of thing. Okay.
(00:32:11):
How far apart do you put those sine waves? How close together can they be? How do the undulations of the paper change? And I am not modeling anything with this. I mean, I could use the origami simulator to model some of it. I just have an easy way to change these things and keep track of them.
CW (00:32:33):
Hmm.
EW (00:32:34):
And to make sine waves, or to make different forms of Gaussian-based waves. So not quite sine, just a little bit different. More spread out, less spread out.
CW (00:32:48):
Okay.
EW (00:32:48):
And it is not just about amplitude. Anyway. Most of the software around these has to do with being able to make repetitive patterns in a repeatable way with lots of different variations.
CW (00:33:06):
Okay.
EW (00:33:09):
It means I can have a sine wave and then a straight line and then a sine wave. It gives me totally different pattern, depending on how far apart those are.
CW (00:33:15):
Sure.
EW (00:33:17):
And whether or not the sine waves are in phase or out of phase, or just a little bit out of phase. I did a lot with just a little bit out of phase. That is fun because it makes the paper compress, which makes the paper kind of shiny.
(00:33:31):
Anyway, there is software, there are Python scripts. If anybody is interested in those Python scripts, I am always happy to share. That one in particular is a parsing problem, plus the kernel shapes.
CW (00:33:45):
Kernel shapes?
EW (00:33:47):
So like I said, Gaussian versus sine versus...
CW (00:33:50):
The basis functions of I guess what you are doing. Okay.
EW (00:33:55):
And then sometimes I do- I can multiply functions too, so I can have a sine wave that is slowly getting larger.
CW (00:34:03):
All right.
EW (00:34:05):
That makes a nice pattern. It makes a really nice pattern, though. Yeah. I was trying to remember what people had said when they saw it, and yeah, it is not snails. Although if I was going to do this again, the temptation to do the art and science of snail shells would be so high!
(00:34:23):
But I also want to work on flower patterns. I know it sounds girly girl, but I think there is some maybe interesting science with the flower patterns. But it would still be one piece of paper. Anyway, anyway. This is what I think about before I go to sleep is, "How do I make flower patterns in origami?"
CW (00:34:49):
<laugh>
EW (00:34:49):
"How do I make them structurally similar to actual?"
CW (00:34:55):
You include the stem too?
EW (00:34:57):
I was not.
CW (00:34:58):
Sounds harder.
EW (00:35:00):
I kind of know how to, but I- Yeah. Anyway.
CW (00:35:04):
Anyway.
EW (00:35:05):
Anyway.
CW (00:35:05):
Anyway.
EW (00:35:06):
Should I read "Winnie-the-Pooh"?
CW (00:35:10):
I do not think so. We got a lot of time left to fill.
EW (00:35:14):
How much time do we have?
CW (00:35:15):
25 minutes at least.
EW (00:35:17):
We are going to need another technical question, are we not?
CW (00:35:19):
<laugh>
EW (00:35:19):
Okay. Do you want to talk about debugging or pull requests somewhere?
CW (00:35:29):
It depends on the debugging. What is the debugging question? "How to debug?"
EW (00:35:34):
"What exactly is GDB?"
CW (00:35:37):
No! No! I do not know what GDB is. It is a mystery <laugh>. Uhhh. Boy, I do not know if I can speak to this off the cuff. I do not even remember how this stuff works. Do you remember how they work? At the lowest level?
EW (00:35:54):
Well, that is one of the things that has changed, over our career.
CW (00:36:00):
I am not including hardware debugging. Even just on a desktop, debugging an application.
EW (00:36:06):
Oh! Oh, I was thinking the hardware debugging.
CW (00:36:09):
Maybe hardware debugging is easier to explain.
EW (00:36:11):
No, definitely not.
CW (00:36:12):
Oh.
EW (00:36:12):
Okay. So GDB- When you compile your program- You are right, let us just start with just a program. You start out with a .c file, "Hello, World!" That goes to an object file.
CW (00:36:33):
With an intermediate step, but yes.
EW (00:36:37):
And then the object file has all that code. But it does not have printf. That comes in a library. It gets linked by the linker, which a lot of us call GCC the linker, but it is not. But GCC can invoke the linker, so we do not really care. Okay, so you have the linker.
(00:36:57):
The linker takes this "Hello, World!" code, and it takes the printf standard library code, and it puts them in the same file. Then it tells them how to find each other. The first thing it says is where to start, "You want to start at main." Okay, so we are going to go to this address, because this is where main was defined. And then you step down, and it says printf, and-
CW (00:37:26):
Who says? What says?
EW (00:37:28):
Oh, your program counter. Now we are running.
CW (00:37:31):
Yes. Okay.
EW (00:37:33):
We are running without debugging. We are just running. So you invoke your program. It goes to main, because that is where it says to go.
CW (00:37:44):
And it goes through- Your object file has the actual machine code of your processor.
EW (00:37:50):
The executable has the- Yeah.
CW (00:37:51):
Well so does the object file. But the executable has the machine code, which is a list of instructions, which are opcodes, which are very small...
EW (00:38:02):
Push/pop.
CW (00:38:02):
Instructions that run natively on the processor that you-
EW (00:38:07):
"Push H into this register, and then take this register and put it on the screen."
CW (00:38:13):
Or do- No.
EW (00:38:16):
I am taking the screen as an entirely-
CW (00:38:17):
<laugh> Okay.
EW (00:38:17):
That is its own-
CW (00:38:20):
But they do math, they do arithmetic, they do comparisons, and they can do conditionals. There are a lot of these instructions. Early computers had fewer of them.
EW (00:38:34):
I feel like we are pretty far from GDB now.
CW (00:38:36):
We will get there. It is very important, because this is what GDB is stepping through.
EW (00:38:39):
Okay. Okay.
CW (00:38:41):
And your program is stepping through. Your program is not stepping through C code.
EW (00:38:44):
No, it is stepping through assembly code.
CW (00:38:46):
And it is going to be very confusing. Yeah.
EW (00:38:49):
Okay, okay. Go ahead.
CW (00:38:50):
So it steps through the machine code instructions. Well the processor does this. It is incrementing the program counter and doing it. So there is nothing else happening. The operating system is not walking through your program. The program is running on the processor. And so it is following these instructions, that are these opcodes that are run on the CPU. The CPU is doing its thing. Okay. Sorry.
EW (00:39:16):
Okay. Now next time you compile, you compile with -g to get debugging available to you. When it did not have debugging, it did not put the name "printf" in there.
CW (00:39:29):
Right.
EW (00:39:30):
It just put the address. But now that you are going to have debugging, it is going to say, "You know what? Not only do you want to go to this address, by the way, it is called this."
CW (00:39:38):
Right.
EW (00:39:38):
And it may include additional information like, "And it is defined in this file."
CW (00:39:46):
"And it is this line of code" in C code.
EW (00:39:48):
"And it is this line of code."
CW (00:39:50):
This is debug information. You can debug without that.
EW (00:39:54):
But let us not.
CW (00:39:55):
But you better know how to read assembly code, and know what all those- Yeah, yeah. Because it is not going to have any hints for you. It is just going to be the raw code.
EW (00:40:05):
So your debug enabled executable has extra information, that tells GDB all this other stuff. Otherwise GDB only has access to the list of low level commands.
CW (00:40:20):
All right.
EW (00:40:21):
Not C, but much lower level. When you have debug enabled, GDB can go through and say, "Ohh. Okay, yeah. So I am going to print on the screen that you are at the line of printf."
CW (00:40:34):
Wait. Wait.
EW (00:40:34):
"I am going to show on the-"
CW (00:40:36):
What is GDB?
EW (00:40:37):
GDB is a program-
CW (00:40:38):
<laugh>
EW (00:40:40):
That you can send your executable to. So you say, "gdb my_program.exe" or "gdb a.out" and now GDB is taking the place of your processor. It does not act as a VM though.
CW (00:40:58):
No, it is not taking the place of the processor, but I think it halts between- If you are in step mode, it halts. Because when you are running in continuous mode, I do not think there is a performance hit, because it just sets-
EW (00:41:11):
Not on a regular computer.
CW (00:41:13):
Not on a regular computer. What GDB does- <laugh> It is the GNU debugger. It allows you to run your code, to break inside your code when it is running to stop it, resume from there, or to step. Plus a bunch of other stuff, but those are the major kind of operations.
(00:41:35):
And while stepping through, it is going to tell you where you are in the code, by showing you what line from the C source file, or the assembly, depending on how you view it. And you can also interrogate things like, "What does this variable value have? What does this register value have?" Okay. Is that okay?
EW (00:41:55):
Okay. So we have compiled the code. We have linked the code. We have compiled and linked the code, with the debug enable option. We have used GDB to run the code, and then stepped through the code, because GDP allows us to put in breakpoints.
(00:42:13):
And even when it is not obvious there is a breakpoint, like it automatically breaks on main, there is still the stuff going on. You can do-
CW (00:42:33):
Objdump.
EW (00:42:34):
O B J dump. Object dump, and it will show you the information that is in your executable for debugging. So you can see what GDB sees, if you want to. Of course GDB is a program and it has a lot of memory, and you will probably get bored looking at all those numbers. Whatever. It has the map file that I am always going on and on about. It is all in that objdump.
(00:43:01):
Okay, so that is on your computer. Probably on your Linux computer, because it is GDB, which is-
CW (00:43:08):
Did we say what a breakpoint was? A breakpoint is a thing where you can say, "I want to stop at this line."
EW (00:43:14):
"Hey processor, stop here and let me think for a minute."
CW (00:43:18):
Or this function. So you could say, "Okay, main, just go. But as soon as you get to my function do_stuff, it stops at do_stuff." And then you can step through do_stuff or continue from there or whatever.
(00:43:33):
You can do all sorts of powerful things, like sometimes watchpoints like, if this variable is 42 then stop. Or you can switch between threads, if you are on multi-threaded program. All kinds of stuff. It is very powerful. You can even script it with Python. Okay. There. Done.
EW (00:43:50):
And the scripting with Python actually is important, because you can do some really cool stuff with that. I have not ever done it, but I have seen really cool stuff.
(00:43:58):
Okay, so now we have GDB for embedded systems. This is where everything goes wrong.
CW (00:44:06):
Well one thing that goes wrong, is you cannot fit all that debug information on your 8K RAM processor, or with 2K of flash or whatever. It is bigger than the program. Oh well, I guess we are doomed.
EW (00:44:21):
<laugh> We are so doomed. Yeah, so that is one of the biggest problems, is it that all of that debugging information, it cannot be in your executable if you are executable is size limited and has to be downloaded or burned or programmed.
CW (00:44:40):
How am I going to run GDB on my PIC?
EW (00:44:43):
That is the other problem.
CW (00:44:44):
It does not fit on there.
EW (00:44:46):
Instead of running and compiling on the same device, you have to do cross compilers, cross debuggers. Where you run your GDB, you run your compiler, on your computer, but you run your executable over on your little processor.
CW (00:45:03):
How does that work? My processor is over here and my computer is over here, they cannot-
EW (00:45:09):
Magic dust.
CW (00:45:10):
Okay. Good show, everyone.
EW (00:45:12):
<laugh> Sorry. I do not know why...
CW (00:45:16):
No, no, I want to leave it there. Magic dust. I was being silly, so-
EW (00:45:22):
No, I mean...
CW (00:45:24):
There is a piece of hardware. <laugh>
EW (00:45:24):
Usually there is a piece of hardware between them.
CW (00:45:27):
Yes.
EW (00:45:28):
Which is often called a "JTAG" or a "JTAG programmer."
CW (00:45:32):
It usually has nothing to do with JTAG.
EW (00:45:33):
Sometimes it is called an "ICE." Sometimes it is called an "ICD," for in-circuit debugger.
CW (00:45:38):
Sometimes it is called a "USB cable."
EW (00:45:41):
<laugh> It is called the "USB cable." Sometimes the dev boards you get have these built into the dev board, and then you can break them off and use them in other places. And what this piece of hardware does, is it on one side it talks to the processor.
CW (00:46:01):
Very deeply.
EW (00:46:02):
And has a special relationship with the processor. And on the other side it talks to your computer, and pretends that it is running locally so that you can "GDB it." And I am using quotes here, "GDB it." GDB, the executable that is running on your processor. So it is pretending to provide an interface.
CW (00:46:30):
Usually there is a little piece of software in between. There is a little proxy that talks to the hardware, and then that talks to GDB, called "GDB server."
EW (00:46:37):
Right.
CW (00:46:39):
That does that translation. That piece of hardware is conveying all the stuff that normally GDB would be able to see. Like where the program counter is, what is this variable equal to, setting breakpoints, or that kind of thing.
EW (00:46:54):
Actually want to say that again. You said it fine, but I want to say it again just because it is so important. You have GDB and when you are using it for cross development, you have a GDB server. This server talks to your programmer hardware.
(00:47:10):
Usually comes from your programmer hardware vendor, which is J-Link or ST-Link or OpenOCD. This GDB server is from them. You cannot use an ST-Link GDB server with an OpenOCD programmer. You might be able to use an OpenOCD GDB server with a J-Link programmer, but it would be better if you use the native one.
CW (00:47:47):
Usually you do not have to think about these things. You just install the tools that <laugh> come with your programmer.
EW (00:47:52):
Understanding that there is a server.
CW (00:47:53):
Yeah, yeah. No, that is important, because it can be confusing.
EW (00:47:57):
Yeah. So you have GDB, and then you have this GDB server that talks to your hardware. You might consider thinking of it as like an overblown USB driver, but since you usually have to type it and make it start yourself, you cannot just ignore it like you do with USB drivers.
CW (00:48:16):
There is a lot of additional complexity too, because all this stuff has to know what your processor is, exactly.
EW (00:48:23):
Right. Your programmer needs to know what your processor is exactly. Your GDB server needs to know what your processor exactly is. Your GDB does not really.
CW (00:48:35):
Right. Well, GDB needs to be the right architecture.
EW (00:48:38):
Right. It needs to be ARM or MIPS or something.
CW (00:48:40):
You might be running the ARM GDB on your Intel thing, even though it is running Intel. The GDB is compiled for Intel, but it knows about the ARM architecture and how to debug it.
EW (00:48:51):
Right. GDB is running on your computer, which in this case we are going to say is Intel. Because if we use Apple ARM then it is all super confusing. So we are going to say it is Intel.
CW (00:48:59):
It is still probably a different ARM.
EW (00:49:01):
It is a different ARM. So GDB runs on your computer, it is compiled for a computer. GDB server runs on your computer, it is compiled for a computer. These know how to talk to your processor.
CW (00:49:15):
Yeah. And what it is saying. <laugh>
EW (00:49:18):
And the GDB that you are using is not just GDB. It is ARM GDB, because it is doing this cross compiler talking thing.
CW (00:49:27):
Or MIPS, or whatever you are happy to be- MIPS. MIPS? Anybody use MIPS anymore?
EW (00:49:32):
I said MIPS a second ago.
CW (00:49:33):
Or RISC-V or whatever.
EW (00:49:36):
And the ARM GDB knows how to read your executable, and associated debug files.
CW (00:49:45):
Ahh! So that is the important key. Remember my first question, "How does it fit?"
EW (00:49:49):
It does not. Your compiler says, "Oh, here is the little tiny itty-bitty thing you wanted, in a .hex file. And here is a giant thing that has all the information in it, in a .elf file."
CW (00:50:04):
We usually call those "symbols," debug symbols.
EW (00:50:06):
Debug symbols. And the ELF file has the executable and the debug symbols. A .out file does not have the debug symbols. A .hex does not. SREC does not. BIN does not.
CW (00:50:19):
So what GDB is doing is saying, "Well, that thing over there that is running on that hardware that I am getting info about, it does not know anything about what these addresses mean. But I have a reference table over here on this big computer, and I can smash them both together and tell you what source line this came from."
EW (00:50:35):
And when the processor says, "I am at address 8,312," the GDB server tells GDB, "The processor is here," and then GDB says, "Oh, I am just going to look that up in my table here. Oh, you are at main, the fifth line down."
CW (00:50:56):
Right.
EW (00:50:56):
And then depending on if you are in VSCode, or something else that is using GDB server-
CW (00:51:02):
It will highlight that.
EW (00:51:03):
Or GDB, it will highlight that line. ST Cube IDE uses GDB underneath. It is funny, I did not use to have to, I guess maybe because I always used ST-Link or I always used an IDE like IAR plus, a debugger that was supported by IAR, so I did not have to worry so much about the server, it just happened.
(00:51:32):
But these days, not only do I think about the server, I sometimes have to think about the debugger itself. Because they know so many processors, but then they almost never know the latest processors. So if you are working on cutting edge things, your debugger may not know everything about your processor.
(00:51:52):
Either you have to make a file, usually an SVD file, that describes the processor. Or you will have to do something specific with the processor. You can say, "Hey, GDB, I want to talk to the programmer. This next statement, it is not for you." And so you type "monitor reset" or "mon res," and suddenly you are talking to the debugger itself, and not to GDB.
(00:52:25):
It is really complicated. It did not use to feel this complicated, because we were using pay for IDEs, which only supported one programmer.
CW (00:52:40):
Oh really? Let me tell you a story about the 1990s. How do you think GDB worked on a router, from my computer at Cisco on my desk, that was sitting in a data center a couple floors down?
EW (00:52:57):
With GDB servers and all this? Really?
CW (00:52:59):
Yep, yep. Over TCP/IP. Or serial. You could do serial too. But yeah, there was a J-Link. It was not J-Link. There was a GDB and you would put in the address, and-
EW (00:53:10):
What kind of programmer did it have? What was the hardware?
CW (00:53:13):
Hardware was a MIPS computer. It was running an operating system. I do not remember exactly how the device side worked. But it had enough code that could run a server on itself, the shim layer to speak whatever GDB spoke over TCP/IP, and you would proxy in, and you could step through stuff. Yeah.
EW (00:53:40):
And that is how the GDB servers kind of work now.
CW (00:53:42):
And that works for Android too. For Android, if you are debugging on Android and you want a GDB, you hook the phone up to your thing. Then it does, I do not remember what, either USB or TCP/IP to that. Because in that case you are debugging an application that is running on it. So the operating system can do the shim layer.
(00:54:00):
But yeah, there are all kinds of ways to connect GDB to things.
EW (00:54:06):
Well I think that is why we call it the "GDB server" now, is because it used to actually be a server on the programmer device.
CW (00:54:13):
Maybe. Yeah.
EW (00:54:18):
The old JTAGs used to come in ethernet variants, before they came in USB variants.
CW (00:54:28):
Oh, the expensive ones still have ethernet-
EW (00:54:28):
A lot of them still have ethernet.
CW (00:54:29):
Which is nice, because it means you can stick it on a network with a device.
EW (00:54:36):
But I do not know if it runs the GDB server, or if it runs something that talks to the GDB server.
CW (00:54:43):
I bet the latter. Yeah, I bet there is like a GDB server-
EW (00:54:46):
The GDB server used to be something that-
CW (00:54:48):
Ran on the device.
EW (00:54:49):
Ran somewhere else.
CW (00:54:51):
Yeah. I do not know still how- I know there are ways to debug the Linux kernel remotely, and stuff like that. So there are still other weird ways to connect and do GDB.
EW (00:55:01):
Oh, there are lots of GDB remote options. That is another thing, setting up your GDB server, sometimes you have to tell it where its remote is. Even if it is running on the same computer, because you have this remote debugging thing. Imagine that the GDB server used to run somewhere else, and we just kept the terminology.
CW (00:55:28):
Maybe. <laugh> All right, as my drum instructor would say, "Did we do a thing?"
EW (00:55:31):
We did in fact do a thing. We did not finish this question, though.
CW (00:55:37):
Oh! Oh. I am sorry.
EW (00:55:40):
"What would you tell a recent EE or CS grad, who has never used a debugger on an embedded system before?" "Well, now is your chance to never do it."
CW (00:55:51):
No, I would find-
EW (00:55:52):
<laugh>
CW (00:55:56):
I would find a nice tutorial somewhere, on a dev board kind of thing, and in VSCode or a nice IDE try it out. I think there are plenty of them. We will probably find one and put them in the show notes.
(00:56:09):
But with VSCode or STM32Cube is probably the easiest to get started with, you could set up a simple program. It is really easy with an integrated IDE that is- Integrated IDE with an IDE, <laugh> they make it easy. Once you have got stuff connected, you build your little application even if it is a Blinky, and you set run and push play or stop and it is all in there. And you do not really think about GDB so much.
EW (00:56:45):
I think this is the way to go. If you ran STM32Cube with a Nucleo board. Nucleo board has a debugger and ST-Link on the board.
CW (00:56:57):
Debugging hardware.
EW (00:56:58):
Debugging hardware on the board.
CW (00:57:00):
Do not want them to get confused.
EW (00:57:01):
Thank you. The JTAG programmer on the board, even though JTAG is not the thing. So the IDE on your computer, the ST Cube IDE, already knows what kind of-
CW (00:57:19):
Processor and board.
EW (00:57:21):
Processor and hardware programmer on the board, because it is in their list. So it can configure the GDB server, without telling you it runs the GDB server when you debug. You never really see GDB or the GDB server, unless you look for them in that bottom tab, where it talks about the debugger or the debugger console.
CW (00:57:49):
Yeah.
EW (00:57:52):
If I was talking to a new college grad who had not used cross compilers or cross debuggers before, I would hide all of the details, because they are awful.
CW (00:58:03):
Yeah. I started out with command line GDB. GDB, if you just run it on your desktop in a terminal, it comes up and it is a terminal program. It is text based, and you type commands to it and stuff happens, and there is no graphical element.
(00:58:21):
It is from I think the eighties or earlier. There were predecessors of GDB. There were other kinds of debuggers, or other debuggers that are very similar, that were terminal based. GDB is still terminal based, and you can use it that way. I know how to use it that way, and I like it sometimes.
(00:58:37):
But that is not how I would recommend most people use GDB. It is probably not actually the right way for most people to use GDB. Graphical source level debugging is much more friendly.
EW (00:58:50):
It really is much more friendly. Okay, that does complete that question.
CW (00:58:54):
<laugh> Okay.
EW (00:58:54):
The next one is, "What exactly are the compiler and the linker?" But let us not talk about that, because the Nand to Tetris episode is coming.
CW (00:59:05):
The compiler takes your source code in some language, and turns it into...
EW (00:59:10):
A different language.
CW (00:59:10):
A different language.
EW (00:59:11):
And then.
CW (00:59:12):
And then another language. And then maybe some more languages, that you do not see, and that nobody even knows what they are.
EW (00:59:16):
<laugh>
CW (00:59:16):
And then eventually it turns it into assembly language. And then the assembler, which nobody ever talks about either, takes the assembly language and turns into an object file, which has the machine instructions. And then the linker takes your object file, and hooks it up to all the other crap that you did not write.
EW (00:59:35):
Object file crap. Library crap.
CW (00:59:38):
And puts it in the right memory locations and stuff. There. Done. Everyone forgets about the assembler.
EW (00:59:45):
Yeah, I did too. Yeah. Yeah. Thank you for joining us today. We hope we have amused you, and maybe given you something to think about. Whether it is code reviews, origami or GDB.
(01:00:00):
Thank you to Memfault for sponsoring the show. If you have any comments or questions, or, gosh I do not know, things you need to tell us, origami articles, the address is show@embedded.fm or hit the contact link at embedded.fm.
(01:00:24):
And now some "Winnie-the-Pooh."
(01:00:26):
[Winnie the Pooh excerpt]