The old solution to that, which worked very well, was coredumps. The assertion fires and your program is taken down, but just before that we save out the entire memory area of your program. That way you can come in with a debugger later and poke around.
People would often leave some memory areas (typically circular buffers) with debug values that would be useful in debugging. They'd never be used anywhere in the program, unless the programmer had to poke around manually.
I've often wished this workflow was still considered high priority on modern runtimes.