Sometimes, it is a compiler bug (2022)
quick-lint-js.com
quick-lint-js.com
I'm honestly a little surprised that someone would stream this -- so much potential for egg on face for the debugger, and had it been me, much time would have been spent scratching my head, which I don't think would make for compelling viewing.
Bonus snark: Fractional line numbers sounds like something Larry Wall would add to Perl, deliberately, as a "feature".
At the end of the day compilers are also just software and it's just a matter of how many features you end up being exposed to.
Kind of the same thing as reporting memory usage for programs. You want to be able to say "A uses 3MB, B uses 2MB", but then there's shared memory, file caches, fragmentation, etc. So much complication that "how much memory is this process using?" isn't even a valid question anymore.
for (i = 0; i < 4; i++) ...
If the compiler unrolls the loop four times the eliminates the induction variable altogether, then combines the four iteration loop into a single vector instruction, what debug info should it emit for the value of i in the loop body?
You can argue that it should emit what i would have had at a given point, but now you have four different values for i all at that one vector instruction. Reasoning about that is quite difficult.
There are things that do exist, though, right? For example, the compiler knows which values are live at every point of the code, and in which registers and stack slots they are located. In most cases it even knows the SSA that produced them, and—again, in most cases—that SSA is expressible in the source language in terms of user-visible values. (How to display that ergonomically in the presence of inlining is a problem, though.)
None of this will fit in DWARF, but honestly DWARF is enough of a slog to work with that I’m not really sorry about that. (And PDB is not a contender until actual first-party documentation and library code exists.) The design work is thorny, don’t get me wrong, but I don’t think this is impossible.
No because "point of the code" is not well defined after optimisation. The compiler can reorder statements, split them, merge them, inline them, outline them, eliminate them entirely etc.
> After a minute, I realized that binutils was being installed in /ucrt64, not into my temporary directory. Oh no!
Tip: to install Autotools- or otherwise GNU-Coding-Standards-compliant projects (e.g. CMake-based ones using GNUInstallDirs) into places you haven’t configured them to go to, on the hope they will kinda sorta work, use DESTDIR[1]. The value of PREFIX (et al.) is set at configuration time, and overriding it via make does not work, while DESTDIR is there specifically to be overridden. It’s what Linux package managers use to create a parallel bin/, lib/, usr/, etc. hierarchy that’s then archived up into a package. (The value of DESTDIR is prepended to all destination paths, it does not override them.)
Also, support DESTDIR in your build systems. Distro maintainers will thank you.
[1] https://www.gnu.org/prep/standards/html_node/DESTDIR.html
The manual maybe (?) hints at this behavior:
> Using a Character Pointer
> The second method you can use to define strings is a character pointer. Edit your program to look like this:
main ()
{
char *msg;
msg = "Hello, world";
puts(msg);
}
> The asterisk (*) in front of msg tells the compiler that msg is a pointer to a character; in other words, msg can hold the address of some character. However, the compiler sets aside no space to store characters and does not initialize msg to any particular value.> When the compiler finds the statement
msg = "Hello, world\n";
it does two things:- As before, it creates the string "Hello, world\n", followed by a null character somewhere within the object code file.
- It assigns to the starting address of that string-the address of the character H-to msg.
> The command puts(msg) works just as it did before, printing characters until it encounters the null character.
The workaround is of course to use s1 = &s2[0]
1: https://learn.microsoft.com/en-us/cpp/build/reference/linker...
Kind of like experience gained as a plumber working with dirty clogged real pipes in a basement, vs doing exercises in plumbing school with new pipes and theory about pipe sizes, gradients etc. In other words, school can only give part of the picture.
And great parties as well.
It's a waste of a student's time. Deep debugging isn't just one set of skills; each problem is different. It's usually very time-consuming, and you will need to learn new skills and tools.
That is: you have to encounter a problem that is a blocker, so that your motivation is that you have to solve the problem.
My experience was a long time ago: I had linked a library compiled with Borland C with code compiled with Microsoft C. It wouldn't work. I wss only using one function from the Borland library; that's where the error was occurring. It turned out that the Microsoft compiler required the callee to restore the flag register; the Borland compiler required the caller to do that. Therefore the carry bit wasn't being restored correctly, causing the bug. Took several days to figure out.
That's how I got there at least...
The execution engine works in discrete[a] operations, hence uops. The microcode is a sequencer that tears apart ISA instructions into those uops.
[a]: I'm considering fused operations (like CMP+Jcc) as single "operations" for simplicity.
Spend some time learning gdb and other classic cli tools.
Don't be afraid of reading books!
It you don't rush and focus on making progress, you will find it eventually. There is no wizardry involved, skills will help you find the solution faster, but that's your ability to focus and keep your calm that will get to it in the first place.
If you are at work, ignore your boss trying to pressure you (that's not necessary easy), your boss only has the "right" to tell you if you should work or the problem or not, if you are asked to work on the problem, do it as if you have your entire life to do it. With experience, you can make estimates that can help decide if the problem is worth trying to solve or not, but with the correct mindset, one doesn't need experience to solve the problem itself, instead one builds experience by solving the problem.
Nevertheless, I recommend learning the details of computer architecture by learning some assembly programming. Higher level languages are an abstraction meant to shield the developer from the details of the H/W but it remains useful to understand the details, particularly if the abstraction is broken.
Sometimes that can be turning an intermittent problem into something I can reproduce consistently, and sometimes it can be constructing new test cases and seeing if their results match my hypothesis.
It helps to either have a good memory, or to keep notes so you know what you’ve already explored and tested, and it’s something you get better at with practice. I spent several years on third line product support working out what was going wrong for customers, producing hot fixes, and working with devs to turn those into proper fixes in the next version.
Or that old saying….
Any science well done, is as good as magic.