Hopefully, the number of frameworks at the top, and the size of your individual programs are relatively small (so that 1-3 aren't nightmares by themselves).
In my experience, 4-5 are seldomly the problem (thanks Linaro!). I suspect the ratio of C to C++ is significantly larger in embedded systems though.
In general, PowerPC/MIPS/ARM toolchains and drivers are not as mature as x86/AMD64. 6-8 tend to occur because CPU vendors usually have their own "blessed" toolchains and BSPs that have diverged from their upstream projects. Fortunately, this means that it's often the case that someone else has already fixed the problem. It's just as often that a driver has not been tested for your use-case since the last time that particular driver's infrastructure was refactored inside the kernel. Or... you wrote the driver and made the mistake (or it might be something from 9/10).
9-10 happen because we're often using hardware that is new and has not had all of its errata discovered yet.
When products need to ship, we're regularly going through this stack. I've seen every one of these, even in just the last 4 years.
Once, I used a chunk of ice to cool down a chip, and that made it work. The hardware guys were unimpressed. But hey, they've got cans of Chill and they use them a lot, and this software guy took a while to realize the reason the board worked in the morning and was dead by lunch, and worked for a little while again after lunch, was temperature related.
There were some devs who tracked down a nasty bug in a processor's TLB. I only heard about that one, wish I had been there. I only had to deal with the fallout in the hypervisor. Note: If you have to spend 20ms hunting down and killing lies with all interrupts turned off and everything basically stopped in its tracks, you are no longer a real-time operating system.
My ex-coworker has done the vanilla scope thing too and has a 400MHz scope at home. For some reason people like this are not too uncommon in Finnish oldskool[tm] IT scene. I remember how he isolated a latency and concurrency bug to an expensive interrupt handler. Rewriting isolated parts of core kernel code to make a really tricky problem go away was one of his more hardcore skills.
I'm not even near his level. My own experience is limited to slightly nibbling the edges of file system and block cache behaviour. It's a brave person who dares dive into that code. Not me.
But I do know one person who regularly works with decapped chips. He works for a company who do extremely low-level hardware investigations. Now that's hardcore.
When you find the problem it's usually a blinding flash of realization that illuminates a tiny, eensy bit of code that you tweak and make right in a couple of minutes. Invariably the mistake was pretty stupid. The glory moment is over quickly because you know all the test cases will pass and that you've just nailed another one.
You've got bragging rights during one lunch, but that's it. It's off to more mundane bugs in the mortal world, and you feel a little sad.
I need to do hardware again.
I would try it even before going for some harder software problems, because it's so easy.
I had a local-made spectrum clone, it didn't overheat, but I lost a multimeter on its power supply.
11. "Shit, where do I borrow a spectrum analyzer and a set of near field probes? These things cost an arm and a leg!"
Yes, STM32F1 MCUs generate inference that jams GPS receivers. No, it's not documented anywhere.
if (featureFlags[HN_DEBUG_HIER_FLAGS] = null) {
/* Who won't this trigger!?!?!?
*/
}
...oh god, kill me now.The Arduino software stack is not huge, there is no operating system involved. Our application is the only thing that runs on bare slow hardware with very limited memory. But this also makes debugging harder. The IDE is limited, you debug over serial output. You have to reflash the flash-memory after every re-compile, which can take a minute.
Building a IoT system for very specific tasks that has to run reliable for years without interruption, I would still use a 8-bit tiny ATMega CPU (e.g. Arduino), and to control this tiny CPU and do some networking stuff with a control center using a 32bit ARM CPU (e.g. RPi).
uh, you know that AVRs have debugWIRE (smaller parts) or JTAG (bigger parts)?
(We ended up underclocking the CPU by about 20% because there wasn't enough time for a redesign. Sigh. It's a miracle the thing even worked in the first place...)
An easy, one bit change in configuration registers of SDRAM fixed it. A week well spent!
... it's not widely known, whispers attribute it to a transcription error, unsure when it started, copied through ancient manuscripts, that the Dead Thing that lies dreaming at the bottom of the ocean, is actually named ... C++hulhu
It's a well-known syndrome. The classic motto for it is "'SELECT' isn't broken" https://blog.codinghorror.com/the-first-rule-of-programming-...
12. "Where is my electron microscope?"
[1] http://www.electronicdesign.com/eda/fib-circuit-edit-becomes...
That's a lot of stars to align there, but when they do, hell breaks lose just often enough to be sure it's not completely random, and obviously this hit one of our most finicky customers, and only in production because of course "#{customer}_production".size == 28 (and not 25, because nah that'd have been too easy to be able to reproduce the bug right away).
http://cs.dartmouth.edu/~sergey/cs258/2010/D2T1%20-%20Kris%2...
https://www.mikeash.com/pyblog/friday-qa-2013-06-28-anatomy-...
Basically we had a bug where a whole conditional branch was being skipped, and we traced it down to the branch being omitted entirely from the compiled IR.
And no, it was nothing fancy, just something like:
if (customer.country == "US") {
doSomething();
} else {
doDifferentThing();
}
The whole `else` branch was simply missing from the compiled program.If I remember correctly, we got around it by doing something like:
bool isUsCustomer = customer.country == "US"
if (isUsCustomer) {
doSomething();
} else {
doDifferentThing();
}
Anyway, the point is that the compiler fucked up it's handling of if/else statements, but only at that specific part of the code, leading to a few wasted days of effort tracking down the problem.I'm not finding it right now, but I recall seeing a few weeks ago a presentation with several of these sorts of bugs in a recent version of Java (all reported and fixed): after a number of iterations, it suddenly starts returning wrong results.
It probably was the optimiser at fault, but there wasn't anything special about this conditional, and certainly nothing that _should_ have caused the optimiser to throw away the else branch.
If memory serves right it was comparing a string field of an object to a static string, like `someObject.foo == "some string"`.
It would have to be specific. Put it this way: if this was a general bug and the "else" was always omitted, how long would it last before being found and fixed?
Related, if you were to say to me "I found the issue, the compiler isn't correctly handling if/else statements" Then my first thought would be about your medication not about the compiler.
And yet, it happened :)
And the senior engineers at the company looked at it and confirmed it was a compiler bug. Their best guess was that something about that part of the code was putting the compiler in a funny state, causing it to skip that particular `else` branch.
We reported it to Microsoft, but never heard anything back.
once it led me to discover a leak in a major travel website's purchase flow caused by Java's Thread class, related to thread groups.
most recently, I was writing some Linux auth code in C, reached a point where I could rule out my code, and found a bug in sudo. freaking sudo.
(also related to groups, though the Linux user kind, not Java threads.)
1. "It must be my code" -- minutes of debugging
2. "It must be in our codebase" -- hours of debugging
3. "Third party library or framework" -- If library use a different library, if framework accept the bug and work around it whilst cursing framework choice.
(https://bugs.launchpad.net/ubuntu/+source/linux/+bug/745836)
This got more attention that I thought possible, so I decided to pull it out as an item all by itself.
There is a corollary to development and debugging. When things break in mysterious ways, we tend to go through a familiar song and dance. As experience, skills and even personal networks grow, we can find ourselves diving ever further in the following chain.
1. "It must be in my code." -- hours of debugging
2. "Okay, it must be somewhere in our codebase." -- days of intense debugging and code spelunking
3. "It HAS TO be in the third party libraries" -- days of issue tracker excavations and never-before-enabled profiling runs
4. "It can't possibly be in stdlib..." -- more of the same, but now profiling the core runtime libraries
5. "Please let this not be a compiler bug" -- you become intensely familiar with mailing list archives
6. "I will not debug drivers. I will not debug drivers. I will not debug drivers."
7. "I don't even know anyone who could help me figure out the kernel innards."
8. "NOBODY understands filesystems!"
9. "What do you mean 'firmware edge case'?"
And the final stage, the one I have witnessed only one person ever achieve:
10. "Where is my chip lab grade oscilloscope?"
Apart from bullheadedness, this chain also highlights another trait of a good developer. Humility.