Hacker News new | past | comments | ask | show | jobs | submit login
Debugging C++ Coroutines [pdf] (open-std.org)
79 points by rbanffy 17 days ago | hide | past | web | favorite | 26 comments



In .NET it took a couple of VS releases and iterations until tooling really caught up with async/await.

C++ has the additional problem that each iteration takes about three years and only a couple of compilers are developed with tooling in mind.


The state of async await tooling is shocking IMO.

For anyone curious how bad it was, here’s a StackOverflow post from 2013 asking about intelligible async stack traces in C# with a proper solution not delivered until 2018 https://stackoverflow.com/questions/15410661/is-it-possible-...

When it was finally delivered it was done by an amazing individual(Ben Adams) picking up the slack for Microsoft.

Last time I used Node in anger, last year, their async stack traces were even worse, losing the stack traces at the await point. Hopefully they’ve fixed it by now.


Python does it pretty well. At least I don't find myself wondering what is happening.



Mind that in .NET or JVM tooling has total control over underlying machine, which is also the same on any platform. So VMs have a lot of sense nowadays, especially with multithreading. No one can now dismiss them because they are "heavy" or "unnecessary", unlike, say, 10 years ago.


C and C++ also have a runtime, that should take that role.

In fact, bare metal deployments without runtime are non-conforming from ISO point of view, if I am not mistaken.

Ironically managed environments go back to Burroughs Large Systems, Xerox, UCSD, ETHZ, it is just that we had a couple of decades where focus went elsewhere, and just like containers and VMs they are being rediscovered.

EDIT: typo "with" => "without"


> In fact, bare metal deployments without runtime are non-conforming from ISO point of view, if I am not mistaken.

a freestanding implementation is conforming.


Thanks for the correction.


> C and C++ also have a runtime

What do you mean by "runtime?" libc just makes it so you're not writing your own system calls. Things like attaching a profiler post-hoc, hot-replacing code, optimizing code, remote debugging, etc. aren't really things in C-land.


I mean startup code that runs before main(), code that runs registered atexit() functions at application exit, floating point emulation for ISO C float semantics regardless of hardware support, thread local storage, threading (as of C11).

And keeping all of the above when targeting bare metal deployments as well.

All of that falls into the language runtime from compiler design point of view.


> in .NET or JVM tooling has total control over underlying machine

I don't understand at all which kind of control which is different from something else related to C or C++. Can you please be a bit more cpecific in describing what you think that .NET and JVM have?

Edit: to the reply below, in full "A virtual machine." Duh. Of course .NET or JVM have a virtual machine each. But what's the support for the rest of the claim:

> VMs have a lot of sense nowadays, especially with multithreading. No one can now dismiss them because they are "heavy" or "unnecessary", unlike, say, 10 years ago

I failed to see how that follows from the starting statement.


A virtual machine.


Aka language runtime in AOT compiled languages.


>C++ has the additional problem that each iteration takes about three years

I don't see how the iteration of the C++ standard is relevant for the development of debug tooling.


Introduction of features or standard library improvements that help to support debugging scenarios, just like the paper being discussed here.


The paper being discussed here doesn't mention or imply any changes to the standard. Neither the language nor the standard library. This is entirely a tooling problem.


aye, the paper is just highlighting the need of proper debugging support. In particular it is important that implementations add any required metadata to the coroutine runtime before their ABI is frozen.


ABI is an interesting question. My impression is that the additional debug metadata should be in the form of debug symols and shouldn't affect the ABI.


I'm not even sure if that is being claimed to be too fast, or too slow.


This is a good time to reiterate my idea that I call "stacking the stack" (like stacking the deck in card games). The idea is that you transform a simple alternation between coroutineA and coroutineB into nested method calls, methodA(continuationB) and methodB(continuationA). This way the local context of A and B are live on the stack for debugging.

Of course there would be a limit of depth, but there's always the option to pass an empty continuation and allow the stack to unwind. Since they are coroutines they don't care if you unwind the stack and wind it back up. What I mean is the stack could look like "A-B-A-B-A-B" or "A-B .. A-B .. A-B" or "A .. B .. A .." it doesn't matter. For release build (not debugging) you would just set the depth to 1 and have normal non-nested coroutines.

I can implement this with macros (you wrap your return statements and your co_xxxx statements with a macro) using hand-rolled coroutines. I haven't yet gotten familiar enough into C++ std coroutines to implement it there or see if they allow it to be done without macros.


Aka, tracing.


One can always add print statement. And for asynchronous code this is often the only realistic way to debug. If you pause some thread in a debugger you might end up with other threads noticing it is no longer responding, firing timeouts and so on.


I just want proper stack traces in my core dumps.


Imagine if C++ had a proper repl.



    g++ -Og -ggdb3 wtf.cpp
    gdb ./a.out
    help call




Applications are open for YC Summer 2020

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: