Earlier quoted context omitted.
Hard to break into the debugger for a production application running on hundreds of servers.
One can argue whether stack traces should be enabled for production (at least on all servers) given they're relatively expensive to create. Which isn't a problem if they're exceptional, but in a lot of cases they aren't.
Stack Traces Are Underrated
41–50 of 58 posts
Re: Stack Traces Are Underrated
#42Way before I consistently used step debuggers and would just "print-debug" println("why are you here?") or "raise-debug" raise new Error("huh?"), I tinkered with a step debugger, but found it too complex and hard. But I remember that it also allowed me to move backwards in the stack. It allowed me to go some frames back - lines up, up in the stack. I don't recall the name of this debugger, nor what language it was. B…
Time travel debugging is the category, but I can’t help you much more than that with the tool names.
They are most likely describing the much simpler feature of having the debugger drop stack frames (by force nuking the stack) and then starting over (with all the other side effects that occurred in the “prior” execution still present). This is a fairly common feature for exploratory debugging, but has the obvious downsides of leaving lingering side effects so is only fit for use in non-production environments at best like other “edit-and-continue” features.
Re: Stack Traces Are Underrated
#43> But Rust has a better workaround to create stack traces: the backtrace module, which allows capturing stack traces that you can then add to the errors you return. The main problem with this approach is that you still have to add the stack trace to each error and also trust library authors to do so. That's technically true, but the situation is not as dire. Many errors do not need stack traces. That so few carry a b…
Python asyncio supports meaningful stack traces through async functions just fine. import asyncio async def baz(): await asyncio.sleep(.1) raise RuntimeError() async def bar(): await asyncio.sleep(.1) await baz() async def foo(): await asyncio.sleep(.1) await bar() async def main(): await asyncio.sleep(.1) await foo() if __name__ == "__main__": loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) main_task =…
[1]: the important line is "main_task = loop.create_task(main())"
Re: Stack Traces Are Underrated
#44Re: Stack Traces Are Underrated
#45Re: Stack Traces Are Underrated
#46Earlier quoted context omitted.
Python asyncio supports meaningful stack traces through async functions just fine. import asyncio async def baz(): await asyncio.sleep(.1) raise RuntimeError() async def bar(): await asyncio.sleep(.1) await baz() async def foo(): await asyncio.sleep(.1) await bar() async def main(): await asyncio.sleep(.1) await foo() if __name__ == "__main__": loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) main_task =…
Note how also in this stack trace you lose the information about where the main task was scheduled [1]. While you can stitch together the await points, it's much harder to find where tasks are originating. This is also true for `TaskGroup` where the actual call that schedules a task is lost. You will just find the eventual await, which might be the task group (which is good, since that would be structural concurrency…
At that point you have something like a "coroutine frame tree" instead of a "stack trace", where you potentially store multiple parent frames and source lines per coroutine frame. Could be presented something like:
$ python3 test_stacktrace.py
Traceback (most recent call last):
* File "/home/user/tmp/test_stacktrace.py", line 22, in
| main_task = loop.create_task(main())
| * File "/home/user/tmp/test_stacktrace.py", line 24, in
|/ loop.run_until_complete(main_task)
* File "/usr/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
| return future.result()
* File "/home/user/tmp/test_stacktrace.py", line 17, in main
| await foo()
* File "/home/user/tmp/test_stacktrace.py", line 13, in foo
| await bar()
* File "/home/user/tmp/test_stacktrace.py", line 9, in bar
| await baz()
* File "/home/user/tmp/test_stacktrace.py", line 5, in baz
raise RuntimeError()
RuntimeErrorRe: Stack Traces Are Underrated
#47Stack traces are your #1 ally when supporting someone else's legacy production pile. Once you get comfortable with how they work and what information they contain, you can hit the ground running anywhere. Stack traces will teach you about the product architecture faster than anyone on the team can. As you embrace them, you take the little bit of extra time to make sure they go well. For example, re-throwing exception…
> A broader outcome of this enlightenment is preference for monolithic products. Stack traces fare poorly across web service and API boundaries. If you've only ever worked with microservice architectures, the notion of a stack trace may seem distracting. Yes. People forget that the original concept of microservices, the AWS "everything must have an API", was to put in an accountability boundary across teams. Either t…
Re: Stack Traces Are Underrated
#48Also, for "normal" errors, you shouldn't need a stack trace. For example, "file not found" is, from the point of view of the developer an expected situation and should be handled with the same amount of care as it the file was present. You don't dump the internals to the user when you have successfully opened the file, so don't dump them when you haven't.
For unexpected errors (i.e. bugs), the crash, abort, panic, or whatever it is called in your language. These will usually give you a stack trace, or a core dump from where you can extract the stack trace and more.
What I would wish for however would be a standard feature in languages to display a stack trace on command. Many languages have it, but even when they do, they could be more prominent. This way, if you encounter an unexpected situation you want to debug without crashing and without a debugger attached, you can call it.
Re: Stack Traces Are Underrated
#49Earlier quoted context omitted.
> A broader outcome of this enlightenment is preference for monolithic products. Stack traces fare poorly across web service and API boundaries. If you've only ever worked with microservice architectures, the notion of a stack trace may seem distracting. Yes. People forget that the original concept of microservices, the AWS "everything must have an API", was to put in an accountability boundary across teams. Either t…
Accountability and transparency go hand in hand, though. Teams would be able to debug together much easier if stack traces were propagated across RPCs, and good RPC frameworks can do that. Unfortunately when (ab)using HTTP+JSON for RPCs, good cross-service debugging is one of the first casualities.
Re: Stack Traces Are Underrated
#50> But Rust has a better workaround to create stack traces: the backtrace module, which allows capturing stack traces that you can then add to the errors you return. The main problem with this approach is that you still have to add the stack trace to each error and also trust library authors to do so. That's technically true, but the situation is not as dire. Many errors do not need stack traces. That so few carry a b…
You need to use the actual `await` syntax to get an async stack trace in node. Callbacks and raw promise work can't be seen by the async stack trace implementation which hooks into `await` points.