--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussio...@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
You can implement this easily in a portable way by adding a function try block to each function. In the exception handler use std::throw_with_nested to rethrow the exception chain. Pass the function name (__func__) as the argument to the runtime_error you throw.There is an example of unwrapping the nested exceptions on cppreference.com
From: ste.ri...@gmail.com Sent: Friday, April 15, 2016 2:02 AM To: ISO C++ Standard - Discussion Reply To: std-dis...@isocpp.org Subject: [std-discussion] throw std::exception with stack trace (portable) |
From: Viacheslav Usov Sent: Friday, April 15, 2016 1:13 PM Reply To: std-dis...@isocpp.org Subject: Re: [std-discussion] Re: throw std::exception with stack trace (portable) |
Plus, see Tony's point. I can think of many situations where I might
want a stack trace but I'm *not* throwing an exception.
> I would say having a mechanism coupled with std::exception that collects
> those "pointers", which are not yet human readable, is a good thing,
> because certain people and certain companies may have policies that would
> prohibit exposing too much symbolic information about their code to third
> parties
That's both orthogonal (see below) and also moot. If I have pointers,
either a) I can resolve those to symbols, regardless if the code did it
for me automatically, or b) I can't, period. Code that has been
symbol-stripped is just not going to be able to produce names from
pointers. Any proposal will need to take this into consideration.
> Another mechanism could translate those pointers into human readable
> stack traces, if appropriate symbolic info is available.
I do agree with this, however. In fact, I would make any such proposal
necessarily operate in 2-3 phases:
1. (Optional) Obtain count of available stack pointers¹.
2. Obtain up to N stack pointers.
3. Translate arbitrary stack pointers to symbol names.
Why are we conflating bugs with exceptions? (sure the venn diagram might overlap but there is also a big gap)Exceptions are for *coping* with the non-happy path of execution. And coping means that pre-written code is going to execute to cope. And then probably the program can continue along its way.We cope with bugs via future code, not pre-written code.If I write some bug-detection code (ie bad state detection*), I might throw an exception or I might terminate, or I might "panic save"*, or tell the test framework, or log and continue or...A stack trace can be useful in many cases.
On 2016-04-15 13:13, Viacheslav Usov wrote:
On Fri, Apr 15, 2016 at 7:57 PM, Matthew Woehlke <mwoehlk...@gmail.com> wrote:On 2016-04-15 13:13, Viacheslav Usov wrote:> I might be willing to believe this if obtaining stack traces at arbitrary points of execution was not *already supported*, at least by Windows and Linux.That is more like "arcanely supported by some implementations". Stack unwinding, however, is a language feature.
> That sounds like you want the operation to involve compiler magic, which would make it a *language* feature, rather than a library feature.Well, std::exception would be able to provide a list of stack pointers,
From: Viacheslav Usov Sent: Friday, April 15, 2016 2:35 PM |
Reply To: std-dis...@isocpp.org Subject: Re: [std-discussion] Re: throw std::exception with stack trace (portable) |
--
The c++ standard makes no mention of a stack. The memory model makes no assumption that there is one.
The only time you'll ever need a stack trace is when you don't have a debugger handy - I.e. Production code. In this case it's almost useless because of inlining.
In the light of this, I don't see that it is feasible , desirable (or even possible) to impose a stack trace on implementors.
C++ is not like other languages. It expresses intent. The compiler transforms that intent into 'as if' code.
It is wiser to focus efforts on guaranteeing that the correct intent is specified. This is (in the main) achieved through copious use of standard library constructs, idioms and patterns, static asserts, early parameter checking and good, informative exceptions.
Because stack traces are lightweight and do not require manual intervention or any special machinery to generate, they can be used in the first line of bug triage and prioritization. It doesn't matter that they're less than perfect.
Richard Hodges <hodg...@gmail.com> schrieb am Sa., 16. Apr. 2016 um 11:58 Uhr:The c++ standard makes no mention of a stack. The memory model makes no assumption that there is one.The C++ standard does mention the stack. It is the thing that gets unwound during stack unwinding.The only time you'll ever need a stack trace is when you don't have a debugger handy - I.e. Production code. In this case it's almost useless because of inlining.In practice only a few simple functions are inlined, and most functions are not (and often cannot be) inlined. Stack traces tend to be very useful and valuable in practice.
In the light of this, I don't see that it is feasible , desirable (or even possible) to impose a stack trace on implementors.Is is both feasible, desirable and possible. In fact, many implementations provide stack traces today. Implementations should be allowed to omit frames for inlined functions, just like in other programming languages, e.g. Java.
Using the unwind tables, which are required for exception support (but can be provided even if you are compiling without exceptions). It's the same on x86 with frame pointer omission.
In any case, this would be a conditionally supported feature; whether that argues against standardization is another matter.
Richard Hodges <hodg...@gmail.com> schrieb am Sa., 16. Apr. 2016 um 11:58 Uhr:The c++ standard makes no mention of a stack. The memory model makes no assumption that there is one.
The C++ standard does mention the stack. It is the thing that gets unwound during stack unwinding.
The only time you'll ever need a stack trace is when you don't have a debugger handy - I.e. Production code. In this case it's almost useless because of inlining.In practice only a few simple functions are inlined, and most functions are not (and often cannot be) inlined. Stack traces tend to be very useful and valuable in practice.In the light of this, I don't see that it is feasible , desirable (or even possible) to impose a stack trace on implementors.Is is both feasible, desirable and possible. In fact, many implementations provide stack traces today. Implementations should be allowed to omit frames for inlined functions, just like in other programming languages, e.g. Java.
C++ is not like other languages. It expresses intent. The compiler transforms that intent into 'as if' code.All other programming languages are exactly alike in this respect. Otherwise optimizations wouldn't be possible at all.
Adding to this: stack traces are useful precisely when deciding whether to open a crash in a debugger - assuming you haven't suppressed core generation, in which case they can still be used to determine whether a crash is known and whether to enable core generation.Because stack traces are lightweight and do not require manual intervention or any special machinery to generate, they can be used in the first line of bug triage and prioritization. It doesn't matter that they're less than perfect.
在 2016年4月16日星期六 UTC+8下午6:30:33,Philipp Stephani写道:Richard Hodges <hodg...@gmail.com> schrieb am Sa., 16. Apr. 2016 um 11:58 Uhr:The c++ standard makes no mention of a stack. The memory model makes no assumption that there is one.The C++ standard does mention the stack. It is the thing that gets unwound during stack unwinding.If so, show the reference.I believe not. The stack has LIFO semantics but the frames of activation record do not. I don't find any assumption of the implementation in the standard. So it only specifies the stack unwinding (which does need LIFO) but not the stack itself.
BTW I think it a potential defect to leave the specification of activation record out.
On Friday, April 15, 2016 at 2:47:26 PM UTC-4, Viacheslav Usov wrote:On Fri, Apr 15, 2016 at 7:57 PM, Matthew Woehlke <mwoehlk...@gmail.com> wrote:On 2016-04-15 13:13, Viacheslav Usov wrote:> I might be willing to believe this if obtaining stack traces at arbitrary points of execution was not *already supported*, at least by Windows and Linux.That is more like "arcanely supported by some implementations". Stack unwinding, however, is a language feature.
Stack unwinding is a language feature. But the nature of the stack itself is not.
... why? That all rather depends on how stack unwinding is implemented. I'm not sure I see a need for an implementation to have "stack pointers".
On Mon, Apr 18, 2016 at 3:36 AM, FrankHB1989 <frank...@gmail.com> wrote:
在 2016年4月16日星期六 UTC+8下午6:30:33,Philipp Stephani写道:Richard Hodges <hodg...@gmail.com> schrieb am Sa., 16. Apr. 2016 um 11:58 Uhr:The c++ standard makes no mention of a stack. The memory model makes no assumption that there is one.The C++ standard does mention the stack. It is the thing that gets unwound during stack unwinding.If so, show the reference.I believe not. The stack has LIFO semantics but the frames of activation record do not. I don't find any assumption of the implementation in the standard. So it only specifies the stack unwinding (which does need LIFO) but not the stack itself.
How else would you implement "frames of activation records" if not a stack? You need a stack, this is basic CS.
BTW I think it a potential defect to leave the specification of activation record out.You are confusing platform ABI (e.g. [1]) with the language standard.
On Sat, Apr 16, 2016 at 1:56 AM, Nicol Bolas <jmck...@gmail.com> wrote:On Friday, April 15, 2016 at 2:47:26 PM UTC-4, Viacheslav Usov wrote:On Fri, Apr 15, 2016 at 7:57 PM, Matthew Woehlke <mwoehlk...@gmail.com> wrote:On 2016-04-15 13:13, Viacheslav Usov wrote:> I might be willing to believe this if obtaining stack traces at arbitrary points of execution was not *already supported*, at least by Windows and Linux.That is more like "arcanely supported by some implementations". Stack unwinding, however, is a language feature.
Stack unwinding is a language feature. But the nature of the stack itself is not.The stack is not a language feature, I am fully aware of that and that was a primary motivation for looking at that from the stack unwinding angle, because that is something that the standard has.Another primary motivation was that, as mentioned by others, with frame pointer omission, one cannot back-trace the stack unless relying on the exception machinery.
So stack unwinding is closest to stack tracing both standard-wise and implementation-wise.... why? That all rather depends on how stack unwinding is implemented. I'm not sure I see a need for an implementation to have "stack pointers".I did not specify what "stack pointers" were. Nor should the standard, this can be implementation-defined. It is sufficient that std::exception (or something available in a catch block) has "something" that gives the caller implementation-defined stack pointers. And there should be another "something" that can, under implementation-defined conditions, convert them them to strings of an implementation-defined format. An implementation not supporting that can trivially return an empty collection of stack pointers/converted strings.
On 2016-04-17 11:47, Edward Catmur wrote:
> In any case, this would be a conditionally supported feature; whether that
> argues against standardization is another matter.
I recommended that *only the API* would be mandated. That is, an
implementation which always fails / returns no data / etc. is
conforming. Providing stubs is not much of an imposition. Always
providing the API, so that portable code doesn't need to employ
conditional compilation, is IMHO preferable. Also, it may be that
whether or not a result can be achieved is dependent on compile flags or
even run-time variables that make it difficult or maybe even impossible
to determine if the feature should be available at compile time.
Vendors that can reasonably provide a "real" implementation will likely
do so (and can be pressured by customers to do so as a matter of QoI).
Platforms that can't reasonably provide an implementation would *not* be
required to go to heroic efforts; they could just leave the
implementation as doing nothing.
And of course, as has been mentioned to death by now, those platforms
likely to provide a "real" implementation are those platforms that
*already have* an implementation. All that's really being recommended
here is to provide a standard, C++ interface.
On Mon, Apr 18, 2016 at 7:10 PM, Nicol Bolas <jmck...@gmail.com> wrote:> Your assumption is that "exception machinery" would be capable of rebuilding stack frame data. You have yet to justify this assumption.It is not an assumption, it is a fact in certain implementations, with at least one particular implementation already mentioned in this thread.
> In which case, you're left with a feature that defines nothing.Since when is "implementation-defined" equivalent to "nothing"?
> The machinery is there, whether you throw the exception or not, after allJustify that.
On Tue, Apr 19, 2016 at 5:59 PM, Nicol Bolas <jmck...@gmail.com> wrote:> vtable pointers are "a fact in certain implementations." But we don't standardize them.Two messages earlier I said: "stack unwinding is closest to stack tracing both standard-wise and implementation-wise." You singled out the latter part and generalized that into absurdity. Yes, and your point is?
> A function which returns a string who's nature, format, and contents are implementation defined is pretty meaningless.Any language linkage that is not "C" or "C++" is like that. Pretty meaningless you say? Why would the standard have such a thing?
> Because the compiler cannot tell, in most cases, if a function will certainly not participate in stack unwinding.Oh, you are referring to "a fact in certain implementation"?
using FuncPtr = void(*)();
void SomeFunc(FuncPtr ptr)
{
ptr();
}
And here is another fact for you: https://msdn.microsoft.com/en-us/library/1deeycx5.aspx(begin quote)If you use /EHa, the image may be larger and might perform less well because the compiler does not optimize a try block as aggressively. It also leaves in exception filters that automatically call the destructors of all local objects even if the compiler does not see any code that can throw a C++ exception. This enables safe stack unwinding for asynchronous exceptions as well as for C++ exceptions. When you use /EHs, the compiler assumes that exceptions can only occur at a throw statement or at a function call. This allows the compiler to eliminate code for tracking the lifetime of many unwindable objects, and this can significantly reduce code size.(end quote)Do you see that your imaginary fact cannot be reconciled with a real fact? You should be more careful with your generalizations, Nicol.
On 2016-04-15 13:13, Viacheslav Usov wrote:
> On Fri, Apr 15, 2016 at 4:19 PM, Matthew Woehlke wrote:
>> If yes, I would drop everything about exceptions and just ask for a
>> portable way of obtaining a stack trace.
>
> I would say that supporting a stack trace without an exception is more of
> an effort
I might be willing to believe this if obtaining stack traces at
arbitrary points of execution was not *already supported*, at least by
Windows and Linux.
> and probably a pessimisation than supporting a stack trace when
> an exception is thrown. When an exception is thrown, the implementation has
> to do stack unwinding. So it will have some supporting code for that, which
> can only be optimized away if the the throw is optimized away, and it will
> normally have some statically allocated data that could be translated into
> a stack trace, not necessarily at runtime. None of that needs to be present
> elsewhere.
That sounds like you want the operation to involve compiler magic, which
would make it a *language* feature, rather than a library feature.
Maybe an implementation could do that anyway as a QoI thing, but
*requiring* it to work that way (i.e. making it a language feature)
seems... ambitious. I think a library approach would have a better
chance at being accepted by the committee.
Plus, see Tony's point. I can think of many situations where I might
want a stack trace but I'm *not* throwing an exception.
I also have to wonder if your exception-based stack collection doesn't
quit working as soon as it hits a `catch`... which would be
unacceptable, of course...
> I would say having a mechanism coupled with std::exception that collects
> those "pointers", which are not yet human readable, is a good thing,
> because certain people and certain companies may have policies that would
> prohibit exposing too much symbolic information about their code to third
> parties
That's both orthogonal (see below) and also moot. If I have pointers,
either a) I can resolve those to symbols, regardless if the code did it
for me automatically, or b) I can't, period. Code that has been
symbol-stripped is just not going to be able to produce names from
pointers. Any proposal will need to take this into consideration.
> Another mechanism could translate those pointers into human readable
> stack traces, if appropriate symbolic info is available.
I do agree with this, however. In fact, I would make any such proposal
necessarily operate in 2-3 phases:
1. (Optional) Obtain count of available stack pointers¹.
2. Obtain up to N stack pointers.
3. Translate arbitrary stack pointers to symbol names.
Having (2) and (3) [available²] as separate operations is orthogonal to
whether or not exception unwinding is used to help with (2).
(2) should accept an input buffer which is filled with pointers.
(Possibly an overload that accepts a std::vector should also be
provided, but one taking a previously allocated memory block is mandatory.)
(3) should be usable on any pointer obtained in any fashion, not just by
(2). (No guarantees that it will *work*, of course...)
(1) isn't required, but it helps with (2), and I can imagine uses for
(1) that don't use (2) or (3), e.g. an algorithm that detects if it has
gone into an infinite recursion state and terminates itself gracefully
*before* crashing due to stack exhaustion.
(¹ In case of platforms where `void*` is not sufficient, let's assume
that when I say "[stack] pointer" I'm really talking about something
that the standard would specify as an "opaque" type.)
(² We might want to *additionally* provide a convenience function that
combines (2) and (3).)
--
Matthew
Top posting because I cannot see how I could write this response inline.In another message I said that "stack pointers" could be as either part of std::exception or something that can be used in a catch block. Thinking more on the latter, I like that "something that can be used in a catch block" better. But then, taking into account that as far as I can tell this entire mechanism needs to be implementation-defined anyway, what would happen if that something is used outside a catch block? Probably something implementation-defined again. Therefore, we can just say that "something" can be called anywhere to obtain implementation-defined "stack pointers".So I think we could propose either a class or a function, say std::stacktrace, that can be instantiated/called anywhere. To avoid discussing the nature of "stack pointers", we can specify that the class/function then can make available to its users a pointer into an array of chars, whose length is also provided by the class/function. The length and content of that array is implementation-defined.We also need another mechanism to translate the byte buffer returned by std:;stacktrace into human readable text.
But they are useful between executions if you don't apply aslr, or if you do you can de-relocate them, or ship the relocation table along with the binary stack trace. The advantage of a binary stack trace is that it is compact, takes a predictable amount of space and can reliably be hashed for triage and analysis.
Admittedly, it could be more complicated if you're dynamically loading modules.
> Just give us the human readable stack trace.
Human readable tends to imply less useful for machines.
On Tue, Apr 19, 2016 at 7:59 PM, Nicol Bolas <jmck...@gmail.com> wrote:> My point is the same as it was before: we should not add language to the standard that makes assumptions about how stack unwinding is implemented. Your "stack pointer" nonsense in particular.The only nonsense here is yours. Nowhere did I suggest adding any such language to the standard.
> So that you can actually use it and rely on it. So that you can do something more than stick it in a file or throw it on the screen.Oh, so something wholly implementation-defined is not nothing and may even be useful? Why are you contradicting yourself?> Can the compiler tell, in all possible cases, that `SomeFunc` will participate in stack unwinding? No. With aggressive, global optimizations, it may be able to tell in specific cases. But not generally.The compiler does not need to deal with all possible cases at any given time. Compiling a program is not equivalent to proving a theorem in CS. Not being able to prove something when compiling a particular piece of code does not nullify that ability in a million other places. Optimizing away function calls and all that comes with them is the bread and butter of contemporary C++ optimizers. Your generalization is irrelevant.> Oh and here's the thing. If we add a standard library function to generate a stack trace, and the implementer wants to make it use stack unwinding machinery to generate that trace (remember: that's not a requirement of stack traces in general, only in your personal idea)... that implementation would need nothing more than to choose to treat calls to that standard library function as equivalent to a `throw` that never gets caught."treat calls to that standard library function as equivalent to a `throw` that never gets caught" means really just that: call to std::terminate.
> According to that document, exceptions in the most optimal case are assumed to happen when `throw` is encountered or when you call a function (presumably ones without `noexcept` or similar constructs).The parenthetical conditional of yours is not what the document said. It did not specify the class of functions that the compiler assume will throw. So, you are wrong.
> I very clearly said "compiler cannot tell, in most cases". Do you write a lot of functions that only call explicitly qualified `noexcept` functions, or don't call functions at all? My point still stands: the majority of code will still have to have exception machinery, because the majority of code does not limit itself to `noexcept` functions.That is making implication from wrong premises.> So the behavior that /EHs provides is really just standard C++.Yes. And the text I posted implies very trivially that in that mode ("standard C++") the compiler does analyse exception-generating code and eliminates exception-handling machinery very aggressively. Contrary to your claims.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussio...@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
1. We certainly don't want the runtime to insert any additional instructions into functions to track the current activation frame, so whatever machinery is used to determine the stack trace is going to have to do some magic based on the current instruction pointer. This usually involves some kind of pointer chasing in tables or the stack itself.
2. It may or may not be able to detect inlined functions. For me not having them is a sacrifice I'm willing to make if I can get traces for everything else. I consider this a QoI issue.
Indeed. Debuggers can already provide stack traces,which includes demangling symbols. The code necessary to do this depends on the compiler but so what? It means the implementation must be compiler aware. But this is true for loads of other aspects of library implementation.
3. A developer might chose not to include human-readable strings for symbols in order to reduce binary size or enforce code obfuscation.
Then with a range based API it has the freedom to have a preallocated block available for the trace. But it isn't *forced* to. If you're on a system with limited memory you preallocate an array to hold just what you need and then copy as much as you can. If memory is not your concern you have the option to store the information however and where ever you wish. You can even chose a different solution depending on whether you're dealing with a std::bad_alloc or something else. I don't see how limiting your number of options is a good thing.
On x86 the most trivial form of stack tracing is using the IP to find the current function's base address for name lookup and then iteratively following the return address stored in the function prolog until it reaches a known point like main(). No allocation required, only side tables with static information. All the strings are static as well so a string_view does the job, again no allocation by the runtime required. It's an iterative algorithm that is a perfect fit for a range interface. If you have an IP -> name mapping available it can give you very usable results. This obviously skips over inlined functions, but that's where more sophisticated solutions can be applied using additional debug information and effort by the runtime. Making the stacktrace object non-copyable prevents problems with reading a stack that no longer exists, and since the stacktrace object only represents the algorithm to retrieve the information, not the actual trace data itself, that's not an issue in reality.
auto stacktrace = std::capture_stack()
cout << stacktrace.depth();
cout << stacktrace.has_source_names(); // Are human-readable file/source names present?
cout << stacktrace.has_function_names(); // Are human-readable function names present?
cout << stacktrace.has_line_numbers(); // Are line numbers for source files available?
for(const auto& frame : stracktrace) {
cout << frame.source_id(); // An id that can be used with a compiler-generated file/tool to get the source file name
cout << frame.line_number(); // Line number into source file if has_line_numbers == true otherwise 0
cout << frame.source_name(); // "/usr/project/file.cpp" if has_source_names == true otherwise ""
cout << frame.base(); // A void(*)() containing the function's address
cout << frame.offset(); // Offset in bytes/chars/addressable units from the beginning of the function
cout << frame.function_id(); // An id that can be used with a compiler-generated file/tool to get the function name
cout << frame.function_name(); // "project::foo(int, int)" if has_function_names == true otherwise ""
}
On Thu, Apr 21, 2016 at 12:04 PM, Viacheslav Usov <via....@gmail.com> wrote:> iterate over stack frames; that is generally not granted without access to debugging information.Another consideration: when stack is being captured, it can have been corrupted. In this case iteration over stack frames will be at least unreliable.
Agreed. Which is why I am in favour of a simple string being returned from the getStack function or whatever it winds up being called. My aim in being able to portably get at the stack trace is just for debugging and logging. It is not to do anything more complicated than that. I don't expect to be able to perform program introspection and or manipulate stack frames, I think all that's beyond portable c++for all the reasons people have already given.
On Wed, Apr 20, 2016 at 9:24 PM, Miro Knejp <miro....@gmail.com> wrote:
auto stacktrace = std::capture_stack()
cout << stacktrace.depth();
cout << stacktrace.has_source_names(); // Are human-readable file/source names present?
cout << stacktrace.has_function_names(); // Are human-readable function names present?
cout << stacktrace.has_line_numbers(); // Are line numbers for source files available?
for(const auto& frame : stracktrace) {
cout << frame.source_id(); // An id that can be used with a compiler-generated file/tool to get the source file name
cout << frame.line_number(); // Line number into source file if has_line_numbers == true otherwise 0
cout << frame.source_name(); // "/usr/project/file.cpp" if has_source_names == true otherwise ""
cout << frame.base(); // A void(*)() containing the function's address
cout << frame.offset(); // Offset in bytes/chars/addressable units from the beginning of the function
cout << frame.function_id(); // An id that can be used with a compiler-generated file/tool to get the function name
cout << frame.function_name(); // "project::foo(int, int)" if has_function_names == true otherwise ""
}
I do not think it was explicitly mentioned in your points 1 - 4, but your code does not have a clear separation between "capture" and "parse". Is this something that you really meant to be desirable?
My impression is that you wanted to combine those two phases within one stacktrace object, and extract information conditionally based on its has_X methods. That is, in principle, portable, but is unnecessarily baroque because truly portable code can only use the subset that works everywhere always, and that is binary stack capture, as has already been explained in this thread. The success of those has_X calls will depend on the capabilities and the configuration of the toolchain; I do not think we really want a close tie between the way we use the (theoretically) portable API and those settings.
Your code also seems to assume that it can always iterate over stack frames; that is generally not granted without access to debugging information.
I believe that a truly portable API need not have anything but a binary capture mode, which, as I mentioned earlier, can be iterator based.
Thinking more about the parse phase, I am not even sure we need a C++ API for that. Personally, having an implementation-provided tool (e.g., its standard debugger) that can parse that capture and show me the call stack would be sufficient.
On 2016-04-20 18:29, Miro Knejp wrote:
> If the API codifies a vector or array then the implementation and user
> have no other choice than to use a vector or array, even if that
> pessimizes what the implementation or user already have available. With
> a range you can still copy everything to a preallocated array, or a
> vector, or any other sequence container you may have.
...and if the implementation needs to work with an array anyway, then
you've penalized *those* implementations by forcing anyone that needs to
keep the trace around to immediately make a copy that would have been
unnecessary.
Can you give an example of an implementation for which an array would
not be efficient *and* does not have the property that the result is
invalidated if I make a copy of it for later? Because I am not a fan of
an object that needs to be transformed in order to safely retain it for
later use. That sort of subtle "gotcha" seems like a great way to
encourage bugs.
> copy(stacktrace(), back_inserter(my_vector));
How would you do this when the target is a `frame*` of fixed size? I
still think that's an important use case that needs to be handled.
> Making the stacktrace object non-copyable prevents problems with
> reading a stack that no longer exists, and since the stacktrace
> object only represents the algorithm to retrieve the information, not
> the actual trace data itself, that's not an issue in reality.
Any API that does not allow me to copy a lightweight trace is a
non-starter. "Lightweight" on at least x86 means `sizeof(void*)*k`,
where `k` is the number of trace entries. Any effort above and beyond
collecting the IP's is also strongly undesirable.
From: Viacheslav Usov Sent: Thursday, April 21, 2016 12:51 PM Reply To: std-dis...@isocpp.org Subject: Re: [std-discussion] Re: throw std::exception with stack trace (portable) |
a) have objects that need to be destructed during an unwind
Not about coroutines:
Since sometimes stack traces aren't directly possible, perhaps a rule could be that the implementation need only provide a stack trace containing functions that either a) have objects that need to be destructed during an unwind, or b) have an active try {} containing the unwind point (return address), with the exclusion that inlined functions may appear as their containing function instead. Of course, additional functions could be provided in the trace if the implementation is aware of them as a QoI issue.
....
The x86-32 Windows design can be applied generically, because all implementations must be capable of unwinding to those functions. I guess that my meaning is, "if you can unwind to it, you can trace the stack to it".