Discussion:
throw std::exception with stack trace (portable)
(too old to reply)
s***@gmail.com
2016-04-15 06:02:39 UTC
Permalink
To find the root cause of bugs easier, it is very helpful to have stack
trace from the location where the exception is thrown.

Surely I can throw an exception class derived from std:exception and use
backtrace on Linux or StackWalk64 on Windows, with the appropriate symbols
(=> not portable),
or have a try catch "everywhere" and add file and line information (=> too
much code to add),
or not to catch the exception at all and let the Operation System write the
dump file(=>not portable, some exceptions can be handled inside the
program).

I thought there should be an easy portable way to add stack information to
a std::exception. Does anybody have some ideas?

best regards,
Stefan
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Andrew Marlow
2016-04-15 06:46:19 UTC
Permalink
I have been wanting this for years but there seems to be little interest
unfortunately. We all know the challenges of doing it portably including
demangling challenges but these are well known problems that have been
solved before.
Post by s***@gmail.com
To find the root cause of bugs easier, it is very helpful to have stack
trace from the location where the exception is thrown.
Surely I can throw an exception class derived from std:exception and use
backtrace on Linux or StackWalk64 on Windows, with the appropriate symbols
(=> not portable),
or have a try catch "everywhere" and add file and line information (=> too
much code to add),
or not to catch the exception at all and let the Operation System write
the dump file(=>not portable, some exceptions can be handled inside the
program).
I thought there should be an easy portable way to add stack information to
a std::exception. Does anybody have some ideas?
best regards,
Stefan
--
---
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
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
Regards,

Andrew Marlow
http://www.andrewpetermarlow.co.uk
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Richard Hodges
2016-04-15 06:59:23 UTC
Permalink
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
Post by Andrew Marlow
I have been wanting this for years but there seems to be little interest
unfortunately. We all know the challenges of doing it portably including
demangling challenges but these are well known problems that have been
solved before.
Post by s***@gmail.com
To find the root cause of bugs easier, it is very helpful to have stack
trace from the location where the exception is thrown.
Surely I can throw an exception class derived from std:exception and use
backtrace on Linux or StackWalk64 on Windows, with the appropriate symbols
(=> not portable),
or have a try catch "everywhere" and add file and line information (=>
too much code to add),
or not to catch the exception at all and let the Operation System write
the dump file(=>not portable, some exceptions can be handled inside the
program).
I thought there should be an easy portable way to add stack information
to a std::exception. Does anybody have some ideas?
best regards,
Stefan
--
---
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
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
Regards,
Andrew Marlow
http://www.andrewpetermarlow.co.uk
--
---
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
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nathan Ernst
2016-04-15 08:16:48 UTC
Permalink
Post by Richard Hodges
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
I wouldn't necessarily call doing this manually easy - although, not
difficult, either. It would require every call to be "annotated" , which
is a very easy mistake to make.

I've implemented similar backtraces under linux, but I'd posit a reason
it's not standard: I would think that in order to properly implement this,
there may be several dynamic memory allocations (i.e. maintain the stack of
functions that were involved), which could, in turn, cause other failures
and just mask the original problem. Additionally, how would this play with
inlined functions?

Additionally, I think this could fly in the face of the underlying C++
philosophy of only pay for that which you use.

I would generally agree that such a facility would be useful, however.

Regards
Post by Richard Hodges
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
Post by Andrew Marlow
I have been wanting this for years but there seems to be little interest
unfortunately. We all know the challenges of doing it portably including
demangling challenges but these are well known problems that have been
solved before.
Post by s***@gmail.com
To find the root cause of bugs easier, it is very helpful to have stack
trace from the location where the exception is thrown.
Surely I can throw an exception class derived from std:exception and use
backtrace on Linux or StackWalk64 on Windows, with the appropriate symbols
(=> not portable),
or have a try catch "everywhere" and add file and line information (=>
too much code to add),
or not to catch the exception at all and let the Operation System write
the dump file(=>not portable, some exceptions can be handled inside the
program).
I thought there should be an easy portable way to add stack information
to a std::exception. Does anybody have some ideas?
best regards,
Stefan
--
---
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
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
Regards,
Andrew Marlow
http://www.andrewpetermarlow.co.uk
--
---
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
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
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
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Dilip Ranganathan
2016-04-15 14:06:57 UTC
Permalink
This is not a trivial undertaking if its need to be made portable. My
employer has open sourced
this effort here:

<https://github.com/bloomberg/bde/tree/master/groups/bal/balst>

You might need to poke around a bit to figure how to compose a rich
Exception object filled with
backtrace information, but I'd start with:
<
https://github.com/bloomberg/bde/blob/master/groups/bal/balst/balst_stacktraceutil.h
It does require certain amount of buy-in into other parts of the library
(dependencies and such) but
I haven't seen this problem addressed anywhere else in quite the way we
have.
Post by Richard Hodges
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
Post by Andrew Marlow
I have been wanting this for years but there seems to be little interest
unfortunately. We all know the challenges of doing it portably including
demangling challenges but these are well known problems that have been
solved before.
Post by s***@gmail.com
To find the root cause of bugs easier, it is very helpful to have stack
trace from the location where the exception is thrown.
Surely I can throw an exception class derived from std:exception and use
backtrace on Linux or StackWalk64 on Windows, with the appropriate symbols
(=> not portable),
or have a try catch "everywhere" and add file and line information (=>
too much code to add),
or not to catch the exception at all and let the Operation System write
the dump file(=>not portable, some exceptions can be handled inside the
program).
I thought there should be an easy portable way to add stack information
to a std::exception. Does anybody have some ideas?
best regards,
Stefan
--
---
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
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
Regards,
Andrew Marlow
http://www.andrewpetermarlow.co.uk
--
---
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
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
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
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Richard Hodges
2016-04-15 14:19:53 UTC
Permalink
what could be more portable that the use of standard libraries? We use this paradigm in the two (large!) projects that I run. It works perfectly well on all platforms, is extremely efficient (stack trace is only constructed during an exception) and does not pollute program logic in any way.

It’s so effective at tracing bugs that we no longer need to produce megabytes of logs on the off-chance that there may be a bug to track down.


void foo()
try
{
// do something
}
catch(
)
{
std::throw_with_nested(std::runtime_error(__func__));
}

in fact, we use a variadic macro to build the exception so that arguments can be captured during the rollback.

e.g.

void foo(int arg1, std::string& const arg2)
try
{
// do something
}
UTILS_CATCH_ARGS(__func__, arg1, arg2)

This is better than a stack trace since it allows us to capture extra information (such as a digest of the contents of each *this in the exception chain) and it all gets pushed into a log file.

Almost all bugs are fixed without even resorting to a debugger.
This is not a trivial undertaking if its need to be made portable. My employer has open sourced
<https://github.com/bloomberg/bde/tree/master/groups/bal/balst <https://github.com/bloomberg/bde/tree/master/groups/bal/balst>>
You might need to poke around a bit to figure how to compose a rich Exception object filled with
<https://github.com/bloomberg/bde/blob/master/groups/bal/balst/balst_stacktraceutil.h <https://github.com/bloomberg/bde/blob/master/groups/bal/balst/balst_stacktraceutil.h>>
It does require certain amount of buy-in into other parts of the library (dependencies and such) but
I haven't seen this problem addressed anywhere else in quite the way we have.
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 <http://cppreference.com/>
I have been wanting this for years but there seems to be little interest unfortunately. We all know the challenges of doing it portably including demangling challenges but these are well known problems that have been solved before.
To find the root cause of bugs easier, it is very helpful to have stack trace from the location where the exception is thrown.
Surely I can throw an exception class derived from std:exception and use backtrace on Linux or StackWalk64 on Windows, with the appropriate symbols (=> not portable),
or have a try catch "everywhere" and add file and line information (=> too much code to add),
or not to catch the exception at all and let the Operation System write the dump file(=>not portable, some exceptions can be handled inside the program).
I thought there should be an easy portable way to add stack information to a std::exception. Does anybody have some ideas?
best regards,
Stefan
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/ <https://groups.google.com/a/isocpp.org/group/std-discussion/>.
--
Regards,
Andrew Marlow
http://www.andrewpetermarlow.co.uk <http://www.andrewpetermarlow.co.uk/>
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/ <https://groups.google.com/a/isocpp.org/group/std-discussion/>.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/ <https://groups.google.com/a/isocpp.org/group/std-discussion/>.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/ <https://groups.google.com/a/isocpp.org/group/std-discussion/>.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Matthew Woehlke
2016-04-15 14:19:59 UTC
Permalink
Post by s***@gmail.com
To find the root cause of bugs easier, it is very helpful to have stack
trace from the location where the exception is thrown.
I thought there should be an easy portable way to add stack information to
a std::exception. Does anybody have some ideas?
It sounds like you are asking for an opt-in (i.e. at the point where the
exception is created) mechanism for adding a stack trace. Is that correct?

If yes, I would drop everything about exceptions and just ask for a
portable way of obtaining a stack trace. I thought about writing a paper
for this once, but haven't done so. I think it would be a good addition
to the standard. If phrased such that only the API must be available,
but it isn't required to do anything, I think this would be useful to
stave off possible objections such as out of memory situations or
platforms that can't implement it reasonably. Then, if anyone is slow to
adopt it (i.e. platforms that don't implement it even though they
could), you can just file QoI bugs against those implementations.
--
Matthew
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Viacheslav Usov
2016-04-15 17:13:49 UTC
Permalink
Post by Matthew Woehlke
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 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.

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; "pointers" can be made lightweight, too. Another mechanism could
translate those pointers into human readable stack traces, if appropriate
symbolic info is available.

Cheers,
V.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Tony V E
2016-04-15 17:42:29 UTC
Permalink
<html><head></head><body lang="en-US" style="background-color: rgb(255, 255, 255); line-height: initial;"> <div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);">Why are we conflating bugs with exceptions? (sure the venn diagram might overlap but there is also a big gap)</div><div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);"><br></div><div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);">Exceptions are for *coping* with the non-happy path‎ of execution. &nbsp;And coping means that pre-written code is going to execute to cope. And then probably the program can continue along its way.&nbsp;</div><div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);"><br></div><div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);">We cope with bugs via future code, not pre-written code.&nbsp;</div><div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);"><br></div><div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);">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...</div><div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);"><br></div><div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);">A stack trace can be useful in many cases. </div><div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);"><br></div><div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);">---</div><div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);">*bad state detection is why exceptions and bugs are often conflated. Both deal with 'non happy' state.&nbsp;</div><div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);"><br></div><div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);">*panic save is where you save the user's document (to a different filename, don't overwrite the original) ‎and then (attempt to) tell the user "sorry, hope you can recover something from here" and then exit</div><div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);"><br></div> <div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);"><br></div> <div style="font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);">Sent&nbsp;from&nbsp;my&nbsp;BlackBerry&nbsp;portable&nbsp;Babbage&nbsp;Device</div> <table width="100%" style="background-color:white;border-spacing:0px;"> <tbody><tr><td colspan="2" style="font-size: initial; text-align: initial; background-color: rgb(255, 255, 255);"> <div style="border-style: solid none none; border-top-color: rgb(181, 196, 223); border-top-width: 1pt; padding: 3pt 0in 0in; font-family: Tahoma, 'BB Alpha Sans', 'Slate Pro'; font-size: 10pt;"> <div><b>From: </b>Viacheslav Usov</div><div><b>Sent: </b>Friday, April 15, 2016 1:13 PM</div><div><b>To: </b>std-***@isocpp.org</div><div><b>Reply To: </b>std-***@isocpp.org</div><div><b>Subject: </b>Re: [std-discussion] Re: throw std::exception with stack trace (portable)</div></div></td></tr></tbody></table><div style="border-style: solid none none; border-top-color: rgb(186, 188, 209); border-top-width: 1pt; font-size: initial; text-align: initial; background-color: rgb(255, 255, 255);"></div><br><div id="_originalContent" style=""><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Fri, Apr 15, 2016 at 4:19 PM, Matthew Woehlke <span dir="ltr">&lt;<a href="mailto:***@gmail.com" target="_blank">***@gmail.com</a>&gt;</span> wrote:<br><div><br></div><div>&gt; If yes, I would drop everything about exceptions and just ask for a portable way of obtaining a stack trace.</div><div><br></div><div>I would say that supporting a stack trace without an exception is more of an effort 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.</div><div><br></div><div>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; "pointers" can be made lightweight, too. Another mechanism could translate those pointers into human readable stack traces, if appropriate symbolic info is available.</div><div><br></div><div>Cheers,</div><div>V.</div><div><br></div></div></div></div>

<p></p>

-- <br>
<br>
--- <br>
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.<br>
To unsubscribe from this group and stop receiving emails from it, send an email to <a href="mailto:std-discussion+***@isocpp.org">std-discussion+***@isocpp.org</a>.<br>
To post to this group, send email to <a href="mailto:std-***@isocpp.org">std-***@isocpp.org</a>.<br>
Visit this group at <a href="https://groups.google.com/a/isocpp.org/group/std-discussion/">https://groups.google.com/a/isocpp.org/group/std-discussion/</a>.<br>
<br><!--end of _originalContent --></div></body></html>

<p></p>

-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &quot;ISO C++ Standard - Discussion&quot; group.<br />
To unsubscribe from this group and stop receiving emails from it, send an email to <a href="mailto:std-discussion+***@isocpp.org">std-discussion+***@isocpp.org</a>.<br />
To post to this group, send email to <a href="mailto:std-***@isocpp.org">std-***@isocpp.org</a>.<br />
Visit this group at <a href="https://groups.google.com/a/isocpp.org/group/std-discussion/">https://groups.google.com/a/isocpp.org/group/std-discussion/</a>.<br />
Viacheslav Usov
2016-04-15 18:35:30 UTC
Permalink
Post by Tony V E
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.
I did not debate that (nor did I conflate). My remarks was based on the
premise that stack unwinding is solely specified in an exception context.
That is something that every (conformant) implementation must have.

It is quite possible that certain implementation can do more.

Cheers,
V.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Tony V E
2016-04-16 00:35:14 UTC
Permalink
<html><head></head><body lang="en-US" style="background-color: rgb(255, 255, 255); line-height: initial;"> <div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);">Sorry, I should mention that my 'conflate' remarks were more for the whole thread, not just a reply to you. I was originally typing a response to the OP, but then also wanted to respond to your post... sorry. </div> <div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);"><br style="display:initial"></div> <div style="font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);">Sent&nbsp;from&nbsp;my&nbsp;BlackBerry&nbsp;portable&nbsp;Babbage&nbsp;Device</div> <table width="100%" style="background-color:white;border-spacing:0px;"> <tbody><tr><td colspan="2" style="font-size: initial; text-align: initial; background-color: rgb(255, 255, 255);"> <div style="border-style: solid none none; border-top-color: rgb(181, 196, 223); border-top-width: 1pt; padding: 3pt 0in 0in; font-family: Tahoma, 'BB Alpha Sans', 'Slate Pro'; font-size: 10pt;"> <div><b>From: </b>Viacheslav Usov</div><div><b>Sent: </b>Friday, April 15, 2016 2:35 PM</div><div><b>To: </b>std-***@isocpp.org</div><div><b>Reply To: </b>std-***@isocpp.org</div><div><b>Subject: </b>Re: [std-discussion] Re: throw std::exception with stack trace (portable)</div></div></td></tr></tbody></table><div style="border-style: solid none none; border-top-color: rgb(186, 188, 209); border-top-width: 1pt; font-size: initial; text-align: initial; background-color: rgb(255, 255, 255);"></div><br><div id="_originalContent" style=""><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Fri, Apr 15, 2016 at 7:42 PM, Tony V E <span dir="ltr">&lt;<a href="mailto:***@gmail.com" target="_blank">***@gmail.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div lang="en-US" style="background-color:rgb(255,255,255);line-height:initial"> <div style="width:100%;font-size:initial;font-family:Calibri,'Slate Pro',sans-serif,sans-serif;color:rgb(31,73,125);text-align:initial;background-color:rgb(255,255,255)">Why are we conflating bugs with exceptions? (sure the venn diagram might overlap but there is also a big gap)</div><div style="width:100%;font-size:initial;font-family:Calibri,'Slate Pro',sans-serif,sans-serif;color:rgb(31,73,125);text-align:initial;background-color:rgb(255,255,255)"><br></div><div style="width:100%;font-size:initial;font-family:Calibri,'Slate Pro',sans-serif,sans-serif;color:rgb(31,73,125);text-align:initial;background-color:rgb(255,255,255)">Exceptions are for *coping* with the non-happy path‎ of execution.&nbsp; And coping means that pre-written code is going to execute to cope. And then probably the program can continue along its way.&nbsp;</div><div style="width:100%;font-size:initial;font-family:Calibri,'Slate Pro',sans-serif,sans-serif;color:rgb(31,73,125);text-align:initial;background-color:rgb(255,255,255)"><br></div><div style="width:100%;font-size:initial;font-family:Calibri,'Slate Pro',sans-serif,sans-serif;color:rgb(31,73,125);text-align:initial;background-color:rgb(255,255,255)">We cope with bugs via future code, not pre-written code.&nbsp;</div><div style="width:100%;font-size:initial;font-family:Calibri,'Slate Pro',sans-serif,sans-serif;color:rgb(31,73,125);text-align:initial;background-color:rgb(255,255,255)"><br></div><div style="width:100%;font-size:initial;font-family:Calibri,'Slate Pro',sans-serif,sans-serif;color:rgb(31,73,125);text-align:initial;background-color:rgb(255,255,255)">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...</div><div style="width:100%;font-size:initial;font-family:Calibri,'Slate Pro',sans-serif,sans-serif;color:rgb(31,73,125);text-align:initial;background-color:rgb(255,255,255)"><br></div><div style="width:100%;font-size:initial;font-family:Calibri,'Slate Pro',sans-serif,sans-serif;color:rgb(31,73,125);text-align:initial;background-color:rgb(255,255,255)">A stack trace can be useful in many cases.</div></div></blockquote><div><br></div><div>I did not debate that (nor did I conflate). My remarks was based on the premise that stack unwinding is solely specified in an exception context. That is something that every (conformant) implementation must have.</div><div><br></div><div>It is quite possible that certain implementation can do more.</div><div><br></div><div>Cheers,</div><div>V.</div></div></div></div>

<p></p>

-- <br>
<br>
--- <br>
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.<br>
To unsubscribe from this group and stop receiving emails from it, send an email to <a href="mailto:std-discussion+***@isocpp.org">std-discussion+***@isocpp.org</a>.<br>
To post to this group, send email to <a href="mailto:std-***@isocpp.org">std-***@isocpp.org</a>.<br>
Visit this group at <a href="https://groups.google.com/a/isocpp.org/group/std-discussion/">https://groups.google.com/a/isocpp.org/group/std-discussion/</a>.<br>
<br><!--end of _originalContent --></div></body></html>

<p></p>

-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &quot;ISO C++ Standard - Discussion&quot; group.<br />
To unsubscribe from this group and stop receiving emails from it, send an email to <a href="mailto:std-discussion+***@isocpp.org">std-discussion+***@isocpp.org</a>.<br />
To post to this group, send email to <a href="mailto:std-***@isocpp.org">std-***@isocpp.org</a>.<br />
Visit this group at <a href="https://groups.google.com/a/isocpp.org/group/std-discussion/">https://groups.google.com/a/isocpp.org/group/std-discussion/</a>.<br />
Matthew Woehlke
2016-04-15 17:57:05 UTC
Permalink
Post by Viacheslav Usov
Post by Matthew Woehlke
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.
Post by Viacheslav Usov
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...
Post by Viacheslav Usov
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.
Post by Viacheslav Usov
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
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Dilip Ranganathan
2016-04-15 18:05:47 UTC
Permalink
Post by Matthew Woehlke
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.
This is possible with the library I posted else thread.
Post by Matthew Woehlke
Post by Viacheslav Usov
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.
Post by Viacheslav Usov
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
1. (Optional) Obtain count of available stack pointers¹.
2. Obtain up to N stack pointers.
3. Translate arbitrary stack pointers to symbol names.
All 3 of the above are also possible with the library posted else thread.

It could be an useful starting point for standardization.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Viacheslav Usov
2016-04-15 18:47:24 UTC
Permalink
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,
That is technically a library feature, although compiler magic is required.
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.

I am not seeing why that was called moot; I think we are in a violent
agreement here. The code that throws may be symbol-stripped, but it can
still collect those pointers and save/forward them to some other code that
has the symbolic info.

Cheers,
V.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Matthew Woehlke
2016-04-15 19:23:59 UTC
Permalink
Post by Viacheslav Usov
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".
Sounds like a good reason to standardize it :-).
Post by Viacheslav Usov
Stack unwinding, however, is a language feature.
...which I can *turn off*, and many users do. For this and other
reasons, I don't want to limit obtaining traces to exception contexts.

Encouraging implementations to leverage unwinding to produce better
traces *when available* is fine, and even — pardon the repetition —
encouraged :-).
Post by Viacheslav Usov
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,
That is technically a library feature, although compiler magic is required.
If you're going to hide the language feature ("compiler magic") behind a
library facade, is there a benefit to restricting it to an exception
context?
Post by Viacheslav Usov
Post by Viacheslav Usov
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.
I am not seeing why that was called moot; I think we are in a violent
agreement here.
It may also be that I misunderstood your original point. As I read it,
you implied that separating obtaining pointers and resolving names is
useful for "proprietary" code in order to *prevent* leakage of
proprietary information. But separation isn't needed for that reason,
because if the symbols exist, I can resolve them "by hand" anyway, and
if they don't, the automatic mechanism will anyway fail to resolve them.
So separation for that reason is pointless ("moot").

But maybe you meant that separation is useful *because* resolution will
not be possible. I didn't read your comment this way because I would
expect any reasonable implementation to still report the pointer in that
case; even with a non-separated implementation, the pointers would still
be available (if in a less machine-readable format) for later analysis.
(That's not to disagree with your point, with which I actually agree;
just to explain why I read your comment as I did.)
Post by Viacheslav Usov
The code that throws may be symbol-stripped, but it can still collect
those pointers and save/forward them to some other code that has the
symbolic info.
Okay, so really you want separation in order to collect a trace now, but
delay resolution. Most of the reasons I can think for separation are the
same in essence, though e.g. to delay memory allocation in case the
reason I need a trace has to do with memory problems (low, corrupt,
etc.), or because I might be collecting a trace that may or may not be
thrown out (e.g. hand-written code to do something like what
valgrind-memcheck does) and don't want to pay (time, memory) for resolution.

All good reasons :-).
--
Matthew
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Viacheslav Usov
2016-04-18 08:50:12 UTC
Permalink
Post by Matthew Woehlke
Okay, so really you want separation in order to collect a trace now, but
delay resolution.

Yes. I think this is more powerful and more flexible than just spitting
out (or not) a text blob.

Cheers,
V.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2016-04-15 23:56:41 UTC
Permalink
Post by Matthew Woehlke
Post by Matthew Woehlke
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.

If a function gets inlined, it probably disappears from the executed output
and the stack trace. But by the rules of the standard, the *effect* of
stack unwinding through an inlined call must still function. Which is fine.
Post by Matthew Woehlke
Post by Matthew Woehlke
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,
... 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".
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Viacheslav Usov
2016-04-18 09:15:37 UTC
Permalink
Post by Nicol Bolas
Post by Matthew Woehlke
Post by Matthew Woehlke
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
Post by Nicol Bolas
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.

Cheers,
V.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2016-04-18 17:10:27 UTC
Permalink
Post by Viacheslav Usov
Post by Nicol Bolas
Post by Matthew Woehlke
Post by Matthew Woehlke
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.
Your assumption is that "exception machinery" would be capable of
rebuilding stack frame data. You have yet to justify this assumption.

So stack unwinding is closest to stack tracing both standard-wise and
Post by Viacheslav Usov
implementation-wise.
... why? That all rather depends on how stack unwinding is implemented.
Post by Nicol Bolas
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.
That doesn't make sense.

You introduce the concept of "stack pointers", without actually saying what
they are, mean, or do. You then require every implementation to have them,
even though implementations may not necessarily need them for anything.
Then you say that these "stack pointers" can be converted into
implementation-defined strings. But we have no idea what these "stack
pointers" actually represent.

In which case, you're left with a feature that defines *nothing*. You may
as well say that you can call a function that returns an
implementation-defined string. In fact, it'd be far better to do it that
way.

After all, if exception machinery is truly capable of rebuilding the stack
even when it has been optimized out, then surely it must be possible for
the implementation to use this exception machinery to reconstruct the stack
*without* actually throwing an exception. The machinery is *there*, whether
you throw the exception or not, after all. It simply inaccessible. So why
not just provide a function that can access it and assemble it into a
coherent stack trace?

I see no reason why the feature of getting a stack trace *needs to be*
associated with exception handling, even if it uses exception machinery to
create that trace.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Viacheslav Usov
2016-04-19 07:57:12 UTC
Permalink
Post by Nicol Bolas
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.
Post by Nicol Bolas
In which case, you're left with a feature that defines *nothing*.
Since when is "implementation-defined" equivalent to "nothing"?
Post by Nicol Bolas
The machinery is *there*, whether you throw the exception or not, after
all

Justify *that*. Note that I said in my very first message in this thread: "it
will have some supporting code for that, which can only be optimized away
if the the throw is optimized away".

Cheers,
V.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2016-04-19 15:59:47 UTC
Permalink
Post by Nicol Bolas
Post by Nicol Bolas
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.
vtable pointers are "a fact in certain implementations." But we don't *standardize
them*. The standard does not enforce or even *assume* that virtual objects
are implemented using vtable pointers. Oh, the standard has language that
makes it *possible* to use vtable pointers. But the standard does nothing
to favor or disfavor that implementation.

Nor should it do so for exception handling.
Post by Nicol Bolas
In which case, you're left with a feature that defines *nothing*.
Since when is "implementation-defined" equivalent to "nothing"?
A function which returns a string who's nature, format, and contents are
implementation defined is pretty meaningless.

Do you see a lot of code making use of `type_info::name`?
Post by Nicol Bolas
The machinery is *there*, whether you throw the exception or not, after
all
Justify *that*.
Because the compiler cannot tell, in most cases, if a function will
certainly not participate in stack unwinding. C++ isn't Java; we aren't
required to annotate each function call with the list of exceptions that
can propagate through it. As such, most functions will have to have
exception machinery built for them, because the compiler will be unable to
tell if stack unwinding will happen to them. So that code needs to be
there, just in case.

Note that I'm referring to the *compile-time* machinery, not the runtime
machinery that might be built when execution enters a `try` block.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Viacheslav Usov
2016-04-19 17:00:01 UTC
Permalink
Post by Nicol Bolas
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?
Post by Nicol Bolas
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?
Post by Nicol Bolas
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"? But, you know,
"we don't *standardize them"*.

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.

Cheers,
V.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2016-04-19 17:59:07 UTC
Permalink
Post by Nicol Bolas
Post by Nicol Bolas
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?
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. That's why I brought up vtable
pointers. They're both implementation details that the standard does not,
and *should not* require implementations to use.
Post by Nicol Bolas
Post by Nicol Bolas
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?
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.
Post by Nicol Bolas
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"?
No, I am referring to *reality*.

Here's a function:

using FuncPtr = void(*)();

void SomeFunc(FuncPtr ptr)
{
ptr();
}

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*.

So long as the flow of execution through a function is not static, the
compiler cannot know for certain if a function will need stack unwinding
machinery.

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.
Post by Nicol Bolas
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.
What "real fact" are we talking about? 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).

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.

It should also be noted that the primary purpose of /EHa is not about C++
exceptions (which by the standard, *cannot possibly happen* outside of an
explicit `throw` statement or a non-`noexcept` function call). /EHa is, as
the text you posted shows, primarily about dealing with Microsoft
exceptions which can be generated asynchronously at any time.

So the behavior that /EHs provides is really just standard C++.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Viacheslav Usov
2016-04-20 09:50:32 UTC
Permalink
Post by Nicol Bolas
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.
Post by Nicol Bolas
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?
Post by Nicol Bolas
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.
Post by Nicol Bolas
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.
Post by Nicol Bolas
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.
Post by Nicol Bolas
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.
Post by Nicol Bolas
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.

Even though I find your participation in this discussion nothing but
militantly illogical nitpicking, it has had a positive effect just by
keeping me thinking about it longer. So I thank you for that. As I
described in another message, I no longer think it necessary to tie stack
tracing with exceptions.

Cheers,
V.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2016-04-20 14:20:00 UTC
Permalink
Post by Nicol Bolas
Post by Nicol Bolas
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.
Well, std::exception would be able to provide a list of stack pointers,
That is technically a library feature, although compiler magic is required.

That would be an addition to the standard.
Post by Nicol Bolas
Post by Nicol Bolas
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?
Post by Nicol Bolas
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.
Post by Nicol Bolas
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.
Since you wish to be pedantic about it, what I meant was to force the
generation of stack unwinding machinery for every function between that and
`main` in the call graph. If the compiler is able to search through the
call graph in order to remove unwinding machinery from functions that call
functions that don't throw, then the compiler would be able to add
unwinding machinery to functions that call functions that call this library
function (or throw exceptions).
Post by Nicol Bolas
Post by Nicol Bolas
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.
... what? The parenthetical is essentially irrelevant to the overall point.

Also, any C++11 compiler worth its salt wouldn't bother looking through a
function marked `noexcept` to see if it throws exceptions. I would only be
wrong if VS were a *stupid* compiler.
Post by Nicol Bolas
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.
Post by Nicol Bolas
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.
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

Now, explain to me this. If you're implementing stack unwinding logic, why
would you need to have "code for tracking the *lifetime* of many unwindable
objects"? Those objects' lifetimes are static properties, not runtime ones.
Exceptions can only be thrown at a function call or at an explicit `throw`,
after all. So for every point of an exception, the lifetimes of the objects
is a static property. So why would you need code to track those lifetimes?

You would only need to track lifetimes if exceptions could be thrown at
*other* times. Which is exactly what /EHa is all about permitting. That's a *language
extension*; /EHs is simply forcing the standard-conforming behavior, thus
eliminating all of the stuff that /EHa would have added.

/EHs is more optimized, but only *relative* to /EHa. It does not promise
that it will remove unwinding logic from most things; it only promises that
it will remove the complex logic that /EHa would have *added* to most
things.

General unwinding logic is different from what this paragraph is talking
about.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Viacheslav Usov
2016-04-20 15:17:38 UTC
Permalink
I do not believe this branch of the discussion is of interest for third
parties. If you would like to continue it privately, let me know.

Cheers,
V.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Miro Knejp
2016-04-20 19:24:35 UTC
Permalink
Here's an idea: why not stop arguing about whether a certain word is
compatible with standard terminology and instead focus your energy on
the actual feature? This whole bikeshedding nonsense is completely
counterproductive. Design the feature first, analyse if it's compatible
with implementations, and *then* worry about how to integrate it with
standard terminology. Until then it's a waste of time. Just use terms
everybody knows and understands.

So here's my contribution:

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.
3. A developer might chose not to include human-readable strings for
symbols in order to reduce binary size or enforce code obfuscation.
4. I'd like a simple abstraction that doesn't require me to deal with
pointers or arrays or stuff like that.

From these points I suggest a range-based approach:

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 ""
}

Implementations that do not support certain features (or were disabled
by the developer) are acounted for and you don't need to change the code
to handle it.

Making this a range to iterate over gives the implementation the freedom
to traverse the stack frame list only on-demand if it makes sense for
that particular implementation to do so. It also allows the
implementation to not allocate any temporary memory for storing the
stack frames for this particular trace. If the code is compiled with
compiler flags that disable features like function or source names the
corresponding functions can be made nop in the library.

It also gives the developer the most freedom to do with the stack frames
whatevery they need.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Matthew Woehlke
2016-04-20 20:06:50 UTC
Permalink
Post by Miro Knejp
1. We certainly don't want the runtime to insert any additional
instructions into functions to track the current activation frame,
Definitely agreed.
Post by Miro Knejp
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.
Agreed. From my experience, it *might* be possible to detect if an entry
is an inline call, and if so, determine the parent call point (or at
least something close). This may be expensive, and may require loading
debugging symbols.
Post by Miro Knejp
4. I'd like a simple abstraction that doesn't require me to deal with
pointers or arrays or stuff like that.
So... you want only iterator based access? Why? Maybe I don't understand
why dealing with arrays is bad. Please see also the API I proposed
elsewhere in the thread which (optionally) uses a std::vector.

I think an API that takes a previously allocated block of data is an
important feature. As a debugging API, it may be that the program needs
to generate a trace in a situation where memory allocation could be
problematic.
Post by Miro Knejp
Making this a range to iterate over gives the implementation the freedom
to traverse the stack frame list only on-demand if it makes sense for
that particular implementation to do so.
This suggests that the trace cannot be copied and/or is invalidated if
not used immediately. IMHO there are enough problems with such a design
that it's not even worth discussing.

Note that you probably can't implement `depth()` without walking the
stack, so even in your API as presented there is very little actual
benefit to such an approach.
Post by Miro Knejp
It also allows the implementation to not allocate any temporary
memory for storing the stack frames for this particular trace.
Or... you could pre-allocate the memory, as in my API.

I don't see how your implementation would even work *without* allocating
memory; at least the trace object itself would need to hold a pointer to
where in the stack is currently being traversed (two, if it isn't
read-once, which I would consider another non-starter). This is on top
of whatever scratch space is needed for operation, which won't be
reusable if stack walking is interspersed with other operations e.g.
symbol name resolution.
Post by Miro Knejp
If the code is compiled with compiler flags that disable features
like function or source names the corresponding functions can be made
nop in the library.
This won't work. I can have any combination of libraries loaded
dynamically at runtime that do and don't have symbols, and can request
traces from either. There is no way (without forbidding runtime dynamic
library loading, at least) to know for sure if symbols will be present
or not at compile time. I don't see such a limitation being worthwhile.
It's also somewhat moot if you separate tracing and symbol resolution.
Post by Miro Knejp
It also gives the developer the most freedom to do with the stack frames
whatevery they need.
Like... save them for later use? Sorry, the only benefit I see to this
design is having resolution return a struct (or even a class). I'd
already suggested doing so in my API.
--
Matthew
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Miro Knejp
2016-04-20 22:29:36 UTC
Permalink
Post by Matthew Woehlke
Post by Miro Knejp
1. We certainly don't want the runtime to insert any additional
instructions into functions to track the current activation frame,
Definitely agreed.
Post by Miro Knejp
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.
Agreed. From my experience, it *might* be possible to detect if an entry
is an inline call, and if so, determine the parent call point (or at
least something close). This may be expensive, and may require loading
debugging symbols.
Post by Miro Knejp
4. I'd like a simple abstraction that doesn't require me to deal with
pointers or arrays or stuff like that.
So... you want only iterator based access? Why? Maybe I don't understand
why dealing with arrays is bad. Please see also the API I proposed
elsewhere in the thread which (optionally) uses a std::vector.
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. The range
interface doesn't limit the implementation's capabilities: if it has to
create a block of memory for internal use, it can do so and store it in
the range. If it can traverse the data structures freely, let it do so.
My question is: why arbitrarily restrict your options?

copy(stacktrace(), back_inserter(my_vector));
copy(stacktrace() | transformed([] (const auto& frame) { return
frame.function_name(); }), ostream_iterator<string>(cout, "\n"));

Do you see where this is going? If you want to deal with an array, you
can chose to do so. If you want something else, you're free to do that, too.
Post by Matthew Woehlke
I think an API that takes a previously allocated block of data is an
important feature. As a debugging API, it may be that the program needs
to generate a trace in a situation where memory allocation could be
problematic.
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.
Post by Matthew Woehlke
Post by Miro Knejp
Making this a range to iterate over gives the implementation the freedom
to traverse the stack frame list only on-demand if it makes sense for
that particular implementation to do so.
This suggests that the trace cannot be copied and/or is invalidated if
not used immediately. IMHO there are enough problems with such a design
that it's not even worth discussing.
Note that you probably can't implement `depth()` without walking the
stack, so even in your API as presented there is very little actual
benefit to such an approach.
Then let it return 0 if it doesn't have the information available just
as in your API. After iterating the range you know how many elements
there are.
Post by Matthew Woehlke
Post by Miro Knejp
It also allows the implementation to not allocate any temporary
memory for storing the stack frames for this particular trace.
Or... you could pre-allocate the memory, as in my API.
Yes, my interface gives you *and* the runtime the freedom to do either,
depending on what is most efficient for you *and* the runtime.
Post by Matthew Woehlke
I don't see how your implementation would even work *without* allocating
memory; at least the trace object itself would need to hold a pointer to
where in the stack is currently being traversed (two, if it isn't
read-once, which I would consider another non-starter). This is on top
of whatever scratch space is needed for operation, which won't be
reusable if stack walking is interspersed with other operations e.g.
symbol name resolution.
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.
Post by Matthew Woehlke
Post by Miro Knejp
If the code is compiled with compiler flags that disable features
like function or source names the corresponding functions can be made
nop in the library.
This won't work. I can have any combination of libraries loaded
dynamically at runtime that do and don't have symbols, and can request
traces from either. There is no way (without forbidding runtime dynamic
library loading, at least) to know for sure if symbols will be present
or not at compile time. I don't see such a limitation being worthwhile.
It's also somewhat moot if you separate tracing and symbol resolution.
Right, scratch that then. It was a QoI point anyway.
Post by Matthew Woehlke
Post by Miro Knejp
It also gives the developer the most freedom to do with the stack frames
whatevery they need.
Like... save them for later use? Sorry, the only benefit I see to this
design is having resolution return a struct (or even a class). I'd
already suggested doing so in my API.
You also acknowledge the need for an iterator based approach with the
next() function. In your API the only *actually* required operations for
traversing the stack are "trace(&frame, 1)" and "frame = next(frame)"
and "offset = base(frame)". Everything else can be trivially implemented
in terms of those with simple algorithms. So why not go the full mile
and give it a proper interface that doesn't attempt to hide what it
actually is?

The point is the runtime can't possibly know what anyone might want to
do with the information, neither do you or I. Not seeing another benefit
and thus calling it a day is an argumentum ad ignorantium. My approach
presents a separation of concerns:
1. The runtime gives you the algorithm to traverse the call stack
2. You use the algorithm to get the data and do with it what you want
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
'Edward Catmur' via ISO C++ Standard - Discussion
2016-04-21 09:23:52 UTC
Permalink
Post by Miro Knejp
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.
Or on a truly resource-constrained system, you can even push minimal
information out over a wire to somewhere that has the space to process it
properly.
Post by Miro Knejp
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.
I think the issue here with inlined functions points to a more general
problem, which is that the physical stack trace (pcs of return locations)
do not necessarily correspond 1-1 with logical stack frames (as in
experimental/source_location). In addition to inline functions (1-n) there
are trampolines/thunks that should not appear in the logical stack trace;
meanwhile function/COMDAT folding implies that a single physical frame
could correspond to one of multiple logical frames that can (at least in
principle) be distinguished by the address of their caller. To my thinking
this points to tracing as a range generator and symbol resolution as a
range transformer.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Matthew Woehlke
2016-04-21 15:34:43 UTC
Permalink
Post by 'Edward Catmur' via ISO C++ Standard - Discussion
I think the issue here with inlined functions points to a more general
problem, which is that the physical stack trace (pcs of return locations)
do not necessarily correspond 1-1 with logical stack frames (as in
experimental/source_location). In addition to inline functions (1-n) there
are trampolines/thunks that should not appear in the logical stack trace;
meanwhile function/COMDAT folding implies that a single physical frame
could correspond to one of multiple logical frames that can (at least in
principle) be distinguished by the address of their caller. To my thinking
this points to tracing as a range generator and symbol resolution as a
range transformer.
I have to *strongly* disagree with this, at least with the notion that
"tracing" (i.e. the initial process used to collect a trace) involves
resolving these issues.

For some use cases, it is *imperative* that collecting the trace be as
efficient as possible. This means doing a walk of the assembly-level
call stack *and nothing else*, and deferring any translation between
this "physical stack trace" and a logical stack trace until later. (This
is specifically why I decided to keep `next` as a separate function in
my notional API, rather than declaring its function to be built into
`trace`.) Symbol resolution is separated for similar reasons.

Specifically, I may need to collect (and retain copies of!) a *lot* of
stack traces, but only very rarely if ever actually do anything with
them. (Example: a library collects a stack trace every time an object¹
is allocated in order to dump those traces if it detects any objects not
freed at end of program execution. *Many* objects will be allocated, so
collecting the trace must be cheap, but a well behaved program will leak
few or none, so only few or no traces will be displayed and accordingly
will ever need to be "cleaned up".)

(¹ i.e. some base class from which most or all classes of the library
derive.)
--
Matthew
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Matthew Woehlke
2016-04-21 15:20:41 UTC
Permalink
Post by Miro Knejp
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.
Post by Miro Knejp
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.
Post by Miro Knejp
Post by Matthew Woehlke
Note that you probably can't implement `depth()` without walking the
stack, so even in your API as presented there is very little actual
benefit to such an approach.
Then let it return 0 if it doesn't have the information available just
as in your API. After iterating the range you know how many elements
there are.
Unacceptable. `depth()` may return 0 iff a stack trace cannot be
produced. The criteria for returning 0 are "not supported" (read: 'not
possible') and "cannot be completed", *not* "inefficient". Artificially
not supporting the operation, or having its value change for an already
collected trace, are not acceptable.
Post by Miro Knejp
Post by Matthew Woehlke
Post by Miro Knejp
It also allows the implementation to not allocate any temporary
memory for storing the stack frames for this particular trace.
I don't see how your implementation would even work *without* allocating
memory; at least the trace object itself would need to hold a pointer to
where in the stack is currently being traversed (two, if it isn't
read-once, which I would consider another non-starter). This is on top
of whatever scratch space is needed for operation, which won't be
reusable if stack walking is interspersed with other operations e.g.
symbol name resolution.
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.
You need two items of state: the original IP when the trace was
collected, and the IP of the current iteration. You need the latter so
that when you increment the iterator, it knows where it was and where it
was going. You need the former so that your iterator isn't read-once,
which would be horrible.

Yes, it's less memory than storing the whole trace (unless your trace
only has two entries; then it would be the same), but it's memory you
still have to keep around until you're doing with the trace, above and
beyond whatever the trace functions need for scratch memory... which
also has to be reallocated every time you step through the stack. (Note:
I'm assuming this is stack memory, but still...)
Post by Miro Knejp
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.
Post by Miro Knejp
Post by Matthew Woehlke
Post by Miro Knejp
It also gives the developer the most freedom to do with the stack frames
whatevery they need.
Like... save them for later use? Sorry, the only benefit I see to this
design is having resolution return a struct (or even a class). I'd
already suggested doing so in my API.
You also acknowledge the need for an iterator based approach with the
next() function.
No, I don't. Please *read* the documentation of that function (or look
carefully at the API, for that matter; it is a *stateless* function).
The `next` I present is a *static* operation that attempts to determine
the caller of inlined code. It does not require an active program stack.
It *may* be very expensive (e.g. on at least some platforms, likely
requires loading debug symbols). It *will* return a null `frame` for any
input `frame` that is not part of an inlined function. It *may* just
return a null `frame`, period.

("Next" is probably not the best name, but I couldn't come up with
anything better when I wrote it. Suggestions?)
Post by Miro Knejp
In your API the only *actually* required operations for
traversing the stack are "trace(&frame, 1)" and "frame = next(frame)"
and "offset = base(frame)".
No; you need `trace(&frames, n)`, and *maybe*, depending on how symbol
resolution is implemented (and if you e.g. want to save a trace to use
in a different process instance) `offset`. You most certainly *do not*
need `next`, and some platforms may well have an implementation of
`next` that does nothing. If you only collect the top of stack, *at
best* you can resolve its static callers if it is an inlined function.

The only functions in my API that use non-global state are `size` and
`trace`. Also, unlike your API, nothing gives you back any data that is
in any way dependent on mutable program state (aside from unloading
libraries).
--
Matthew
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
'Edward Catmur' via ISO C++ Standard - Discussion
2016-04-21 16:07:10 UTC
Permalink
Post by Matthew Woehlke
Post by Miro Knejp
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.
Taking a backtrace is essentially the same as walking a linked list;
there's not much about that that suggests that an array is a natural
interface. Sure, existing platform-specific implementations are specified
in terms of arrays, but that's because that's they're written to C, which
doesn't have the expressive power of C++.
Post by Matthew Woehlke
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.
A stacktrace range generator is the same idea as istream_iterator or
filesystem::directory_iterator; it's a concept that programmers should be
reasonably well acquainted with.
Post by Matthew Woehlke
Post by Miro Knejp
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.
copy(stacktrace() | sliced(0, n), target);
Post by Matthew Woehlke
Post by Miro Knejp
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.
Copying the stacktrace into a container shouldn't be the responsibility of
the API, since you can do it using external facilities, e.g.:
boost::copy_range<vector<stack_entry>>(stacktrace())
Post by Matthew Woehlke
Post by Miro Knejp
I think the issue here with inlined functions points to a more general
problem, which is that the physical stack trace (pcs of return locations)
do not necessarily correspond 1-1 with logical stack frames (as in
experimental/source_location). In addition to inline functions (1-n)
there
Post by Miro Knejp
are trampolines/thunks that should not appear in the logical stack trace;
meanwhile function/COMDAT folding implies that a single physical frame
could correspond to one of multiple logical frames that can (at least in
principle) be distinguished by the address of their caller. To my
thinking
Post by Miro Knejp
this points to tracing as a range generator and symbol resolution as a
range transformer.
I have to *strongly* disagree with this, at least with the notion that
"tracing" (i.e. the initial process used to collect a trace) involves
resolving these issues.
For some use cases, it is *imperative* that collecting the trace be as
efficient as possible. This means doing a walk of the assembly-level
call stack *and nothing else*, and deferring any translation between
this "physical stack trace" and a logical stack trace until later. (This
is specifically why I decided to keep `next` as a separate function in
my notional API, rather than declaring its function to be built into
`trace`.) Symbol resolution is separated for similar reasons.
Specifically, I may need to collect (and retain copies of!) a *lot* of
stack traces, but only very rarely if ever actually do anything with
them. (Example: a library collects a stack trace every time an object¹
is allocated in order to dump those traces if it detects any objects not
freed at end of program execution. *Many* objects will be allocated, so
collecting the trace must be cheap, but a well behaved program will leak
few or none, so only few or no traces will be displayed and accordingly
will ever need to be "cleaned up".)
(¹ i.e. some base class from which most or all classes of the library
derive.)
I think we're in violent agreement here; I'm advocating separating the
collection stage ("tracing") from the translation stage - I called the
latter "symbol resolution", but that was me being lazy as it involves other
forms of translation e.g. inline call expansion, thunk elimination, fold
expansion. I'm not sure I see a need to separate inline call expansion from
other forms of translation - it seems to all be very
implementation-specific - but I could be persuaded otherwise.

The power of presenting collection as a range generator and translation as
a range transformer is that it's entirely up to the end user how to compose
them, thereby encompassing the majority of use cases.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Andrew Marlow
2016-04-20 20:20:18 UTC
Permalink
Post by Miro Knejp
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.
I think there is no need to worry about this. I come back to what debuggers
are capable of doing. When the compilation is in debug mode the debugger
stack trace often contains source level information such as file and line
number. But without this info it might just give addresses. Again, so what?
These limitations are perfectly acceptable and understood in the context of
the debugger behaviour. I would like a stack trace facility in C++ that
simply provides the same.
--
Regards,

Andrew Marlow
http://www.andrewpetermarlow.co.uk
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Viacheslav Usov
2016-04-21 10:04:49 UTC
Permalink
On Wed, Apr 20, 2016 at 9:24 PM, Miro Knejp <***@gmail.com> wrote:

auto stacktrace = std::capture_stack()
Post by Miro Knejp
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.

Cheers,
V.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Viacheslav Usov
2016-04-21 12:12:43 UTC
Permalink
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. A binary capture could still be amenable to offline analysis.
And again, because the code calling the stack trace API cannot know whether
the stack is good or bad, it should better only use what is more likely to
work. I realize that this consideration is very deep in the domain of
unspecified behaviour, but, practically, I can easily imagine that dumping
the stack is the only thing available for post mortem debugging, and stack
corruption is one of the most common bugs in C++ programs..

Cheers,
V.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Andrew Marlow
2016-04-21 12:20:33 UTC
Permalink
Post by Viacheslav Usov
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.
--
Regards,

Andrew Marlow
http://www.andrewpetermarlow.co.uk
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
'Edward Catmur' via ISO C++ Standard - Discussion
2016-04-21 12:55:04 UTC
Permalink
Post by Viacheslav Usov
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.
Returning a "simple string" requires memory allocation, so is unsuitable
for a worst-case fault handler.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Viacheslav Usov
2016-04-21 13:18:12 UTC
Permalink
Post by Viacheslav Usov
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.

Oh. I think I did not get that initially. Just to make sure: do you mean
the string can contain anything, say just a bunch of non-printable bytes?
Basically a glorified binary buffer?

Cheers,
V.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Miro Knejp
2016-04-21 13:39:16 UTC
Permalink
Post by Miro Knejp
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?
Not exactly sure what you mean by "parse". As mentioned in a previous
message, the stacktrace object hides the algorithm to traverse the call
stack and you use it to collect the information you need. A
range/iterator interface has the big benefit that it can work lazily if
the runtime supports it and avoid additional resources costs that would
otherwise be necessary for a temporary array/vector/etc.
Post by Miro Knejp
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.
I'm not attached to the has_X methods, and the more I think about it
they are probably unreliable in the presence of dynamically loaded
libraries that were built with different levels of debug information.
However if a get_name() method is codified in the interface then it also
works *everywhere always*, it simply may return an empty string and you
have to be prepared to deal with it, but the code is still perfectly
portable giving you base() and offset() always.
Post by Miro Knejp
Your code also seems to assume that it can /always/ iterate over stack
frames; that is generally not granted without access to debugging
information.
If it cannot iterate over stack frames then how is it supposed to
provide a stack trace for arbitrary points in your program? Remember
it's a range with a begin and end iterator. If the implementation can
only find 2 stack frames and then gets lost then the loop simply exits
after 2 iterations and you're done. If it cannot provide any information
at all the loop body never runs. Any argument against the iterability of
the stack walking algorithm is an argument against the very feature
regardless of the interface, since the implementation *must* do *some*
work at runtime unless you have a program where every function has only
one statically known unique call stack. In cases where the
implementation cannot walk the stack interspersed with user calls it can
chose to do it all at once in the range's constructor and stash all the
state it needs in the range object. The point is to let the
implementation decide what the most efficient/safe way of walking the
stack is, and let you decide what the most efficient way is for you to
make use of the information said algorithm delivers.
Post by Miro Knejp
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.
Now you're contradicting yourself. So is stack walking *always* possible
to be iterator based or not? My code assumes exactly that and you didn't
seem to accept that. If it ends up to be accepted that an iterator-based
approach is desireable then not making it a range would be a big mistake
IMHO.
Post by Miro Knejp
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.
If the runtime has the information at hand why not make it available to
the user?
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Viacheslav Usov
2016-04-21 16:51:13 UTC
Permalink
Post by Miro Knejp
Not exactly sure what you mean by "parse". As mentioned in a previous
message, the stacktrace object hides the algorithm to traverse the call
stack and you use it to collect the information you need.

Traverse and collect could mean a few different things. Let's just use x86
as a hopefully universally understandably example. "Traversing and
collecting" could be as trivial as just copying the entire byte range from
the stack's bottom to its current top. And can be as complicated as
singling out call frames and return addresses, and translating all that
into the symbolic names and source file locations. On x86, there is a
conventional frame pointer register that makes frame iteration quite
straightforward without symbolic info - when a certain protocol is followed
by the program, which is not mandatory, and when the stack has not been
corrupted, which is unknown.

Roughly speaking, anything beyond "just copying" can be considered parsing
without a guaranteed success, but I would be willing to grant the
implementation the right to determine what it thinks it can capture
efficiently with a reasonable hope for success.
Post by Miro Knejp
If it cannot iterate over stack frames then how is it supposed to provide
a stack trace for arbitrary points in your program?

Earlier in this thread I indicated that "arbitrary points" may be harder
than "exception points". More generally, it may be impossible to iterate
*when* the stack is being captured, but may be possible *later* in a
different environment. In the example above, if the program is built with
"frame pointer omission", iterating over stack frames is only possible with
debugging information. If the latter is stripped from the executable, that
is not possible. But it may be possible if the stack is captured in a
binary form and later analysed in an environment that has the debugging
info.
Post by Miro Knejp
Now you're contradicting yourself. So is stack walking *always* possible
to be iterator based or not?

"Iterator-based" does not necessarily mean "stack-walking". The
implementation may need to capture a few different regions of memory to
make stack walking possible (with debugging info present at a later stage),
hence the need to iterate over them. But they do not need to correspond
directly to stack frames.
Post by Miro Knejp
If the runtime has the information at hand why not make it available to
the user?

To make all the information available, normally some configuration will be
required in the general case. In some cases all that is required is
embedded into the program itself and any modules it uses. But, in my
opinion, we need to regard that as an exception and API must be
configurable - and portable. And I have certain doubts as to whether it can
*really* be portable, not just superficially. I am talking about using
portable API *in this way *for platform A and *in that way* for platform B,
or, worse, *in this way* for platform X and toolchain settings Y & Z.

Cheers,
V.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Tony V E
2016-04-21 17:14:26 UTC
Permalink
<html><head></head><body lang="en-US" style="background-color: rgb(255, 255, 255); line-height: initial;"> <div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);">Don't forget coroutines. The stack might not be contiguous (IIUC)</div> <div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);"><br style="display:initial"></div> <div style="font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);">Sent&nbsp;from&nbsp;my&nbsp;BlackBerry&nbsp;portable&nbsp;Babbage&nbsp;Device</div> <table width="100%" style="background-color:white;border-spacing:0px;"> <tbody><tr><td colspan="2" style="font-size: initial; text-align: initial; background-color: rgb(255, 255, 255);"> <div style="border-style: solid none none; border-top-color: rgb(181, 196, 223); border-top-width: 1pt; padding: 3pt 0in 0in; font-family: Tahoma, 'BB Alpha Sans', 'Slate Pro'; font-size: 10pt;"> <div><b>From: </b>Viacheslav Usov</div><div><b>Sent: </b>Thursday, April 21, 2016 12:51 PM</div><div><b>To: </b>std-***@isocpp.org</div><div><b>Reply To: </b>std-***@isocpp.org</div><div><b>Subject: </b>Re: [std-discussion] Re: throw std::exception with stack trace (portable)</div></div></td></tr></tbody></table><div style="border-style: solid none none; border-top-color: rgb(186, 188, 209); border-top-width: 1pt; font-size: initial; text-align: initial; background-color: rgb(255, 255, 255);"></div><br><div id="_originalContent" style=""><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Thu, Apr 21, 2016 at 3:39 PM, Miro Knejp <span dir="ltr">&lt;<a href="mailto:***@gmail.com" target="_blank">***@gmail.com</a>&gt;</span> wrote:</div><div class="gmail_quote"><br></div><div class="gmail_quote">&gt; Not exactly sure what you mean by "parse". As mentioned in a
previous message, the stacktrace object hides the algorithm to
traverse the call stack and you use it to collect the information
you need.</div><div class="gmail_quote"><br></div><div class="gmail_quote">Traverse and collect could mean a few different things. Let's just use x86 as a hopefully universally understandably example. "Traversing and collecting" could be as trivial as just copying the entire byte range from the stack's bottom to its current top. And can be as complicated as singling out call frames and return addresses, and translating all that into the symbolic names and source file locations. On x86, there is a conventional frame pointer register that makes frame iteration quite straightforward without symbolic info - when a certain protocol is followed by the program, which is not mandatory, and when the stack has not been corrupted, which is unknown.</div><div class="gmail_quote"><br></div><div class="gmail_quote">Roughly speaking, anything beyond "just copying" can be considered parsing without a guaranteed success, but I would be willing to grant the implementation the right to determine what it thinks it can capture efficiently with a reasonable hope for success.</div><div class="gmail_quote"><br></div><div class="gmail_quote">&gt; If it cannot iterate over stack frames then how is it supposed to
provide a stack trace for arbitrary points in your program?</div><div class="gmail_quote"><br></div><div class="gmail_quote">Earlier in this thread I indicated that "arbitrary points" may be harder than "exception points". More generally, it may be impossible to iterate <i>when</i> the stack is being captured, but may be possible <i>later</i>&nbsp;in a different environment. In the example above, if the program is built with "frame pointer omission", iterating over stack frames is only possible with debugging information. If the latter is stripped from the executable, that is not possible. But it may be possible if the stack is captured in a binary form and later analysed in an environment that has the debugging info.</div><div class="gmail_quote"><br></div><div class="gmail_quote">&gt; Now you're contradicting yourself. So is stack walking *always*
possible to be iterator based or not?</div><div class="gmail_quote"><br></div><div class="gmail_quote">"Iterator-based" does not necessarily mean "stack-walking". The implementation may need to capture a few different regions of memory to make stack walking possible (with debugging info present at a later stage), hence the need to iterate over them. But they do not need to correspond directly to stack frames.</div><div class="gmail_quote"><div><br></div><div>&gt; If the runtime has the information at hand why not make it available
to the user?</div><div><br></div><div>To make all the information available, normally some configuration will be required in the general case. In some cases all that is required is embedded into the program itself and any modules it uses. But, in my opinion, we need to regard that as an exception and API must be configurable - and portable. And I have certain doubts as to whether it can <i>really</i>&nbsp;be portable, not just superficially. I am talking about using portable API <i>in this way </i>for platform A and <i>in that way</i>&nbsp;for platform B, or, worse,&nbsp;<i>in this way</i>&nbsp;for platform X and toolchain settings Y &amp; Z.</div><div><br></div><div>Cheers,</div><div>V.</div></div></div></div>

<p></p>

-- <br>
<br>
--- <br>
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.<br>
To unsubscribe from this group and stop receiving emails from it, send an email to <a href="mailto:std-discussion+***@isocpp.org">std-discussion+***@isocpp.org</a>.<br>
To post to this group, send email to <a href="mailto:std-***@isocpp.org">std-***@isocpp.org</a>.<br>
Visit this group at <a href="https://groups.google.com/a/isocpp.org/group/std-discussion/">https://groups.google.com/a/isocpp.org/group/std-discussion/</a>.<br>
<br><!--end of _originalContent --></div></body></html>

<p></p>

-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &quot;ISO C++ Standard - Discussion&quot; group.<br />
To unsubscribe from this group and stop receiving emails from it, send an email to <a href="mailto:std-discussion+***@isocpp.org">std-discussion+***@isocpp.org</a>.<br />
To post to this group, send email to <a href="mailto:std-***@isocpp.org">std-***@isocpp.org</a>.<br />
Visit this group at <a href="https://groups.google.com/a/isocpp.org/group/std-discussion/">https://groups.google.com/a/isocpp.org/group/std-discussion/</a>.<br />
Thiago Macieira
2016-04-21 17:32:39 UTC
Permalink
Post by Tony V E
Don't forget coroutines. The stack might not be contiguous (IIUC)
Just like threads: you have one stack per thread. In an environment with
coroutines, I'd like to know the state of all coroutines, running or not.
That's very discontiguous.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Patrice Roy
2016-04-22 00:55:51 UTC
Permalink
Yeah, stack traces with coroutines might look like stack traces in
callback-laden systems such as those based on Node.js. That doesn't
discredit the idea of stack traces, though (it might just be the wrong
use-case).

On a technical note: I hope any stack trace proposal will make it
program-readable (something we can translate into a human-readable format
on demand, but program-readable first and foremost).
Post by Tony V E
Don't forget coroutines. The stack might not be contiguous (IIUC)
Sent from my BlackBerry portable Babbage Device
*From: *Viacheslav Usov
*Sent: *Thursday, April 21, 2016 12:51 PM
*Subject: *Re: [std-discussion] Re: throw std::exception with stack trace
(portable)
Post by Miro Knejp
Not exactly sure what you mean by "parse". As mentioned in a previous
message, the stacktrace object hides the algorithm to traverse the call
stack and you use it to collect the information you need.
Traverse and collect could mean a few different things. Let's just use x86
as a hopefully universally understandably example. "Traversing and
collecting" could be as trivial as just copying the entire byte range from
the stack's bottom to its current top. And can be as complicated as
singling out call frames and return addresses, and translating all that
into the symbolic names and source file locations. On x86, there is a
conventional frame pointer register that makes frame iteration quite
straightforward without symbolic info - when a certain protocol is followed
by the program, which is not mandatory, and when the stack has not been
corrupted, which is unknown.
Roughly speaking, anything beyond "just copying" can be considered parsing
without a guaranteed success, but I would be willing to grant the
implementation the right to determine what it thinks it can capture
efficiently with a reasonable hope for success.
Post by Miro Knejp
If it cannot iterate over stack frames then how is it supposed to
provide a stack trace for arbitrary points in your program?
Earlier in this thread I indicated that "arbitrary points" may be harder
than "exception points". More generally, it may be impossible to iterate
*when* the stack is being captured, but may be possible *later* in a
different environment. In the example above, if the program is built with
"frame pointer omission", iterating over stack frames is only possible with
debugging information. If the latter is stripped from the executable, that
is not possible. But it may be possible if the stack is captured in a
binary form and later analysed in an environment that has the debugging
info.
Post by Miro Knejp
Now you're contradicting yourself. So is stack walking *always* possible
to be iterator based or not?
"Iterator-based" does not necessarily mean "stack-walking". The
implementation may need to capture a few different regions of memory to
make stack walking possible (with debugging info present at a later stage),
hence the need to iterate over them. But they do not need to correspond
directly to stack frames.
Post by Miro Knejp
If the runtime has the information at hand why not make it available to
the user?
To make all the information available, normally some configuration will be
required in the general case. In some cases all that is required is
embedded into the program itself and any modules it uses. But, in my
opinion, we need to regard that as an exception and API must be
configurable - and portable. And I have certain doubts as to whether it can
*really* be portable, not just superficially. I am talking about using
portable API *in this way *for platform A and *in that way* for platform
B, or, worse, *in this way* for platform X and toolchain settings Y & Z.
Cheers,
V.
--
---
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
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
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
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Myriachan
2016-04-26 01:33:46 UTC
Permalink
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.

In x86-32 Windows, it is not possible in a fully-generic way to make a
stack trace without extra information that not all executables today have
(even when they don't play tricks). However, it is possible to walk the
SEH exception chain, which covers the a) and b) cases I mentioned.
Functions that meet neither a) nor b) are usually not in the SEH chain,
which is why I suggest it that way.

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".

Note that x86-64 Windows and ARM Windows are quite different in this
regard: on x86-64 and ARM Windows, the assembly language ABI is strict and
there is additional metadata that can used to make a "perfect" stack trace
(aside from the effects of inlining and tail-call optimizations).

Melissa
Post by Patrice Roy
Yeah, stack traces with coroutines might look like stack traces in
callback-laden systems such as those based on Node.js. That doesn't
discredit the idea of stack traces, though (it might just be the wrong
use-case).
On a technical note: I hope any stack trace proposal will make it
program-readable (something we can translate into a human-readable format
on demand, but program-readable first and foremost).
Post by Tony V E
Don't forget coroutines. The stack might not be contiguous (IIUC)
Sent from my BlackBerry portable Babbage Device
*From: *Viacheslav Usov
*Sent: *Thursday, April 21, 2016 12:51 PM
*Subject: *Re: [std-discussion] Re: throw std::exception with stack
trace (portable)
Post by Miro Knejp
Not exactly sure what you mean by "parse". As mentioned in a previous
message, the stacktrace object hides the algorithm to traverse the call
stack and you use it to collect the information you need.
Traverse and collect could mean a few different things. Let's just use
x86 as a hopefully universally understandably example. "Traversing and
collecting" could be as trivial as just copying the entire byte range from
the stack's bottom to its current top. And can be as complicated as
singling out call frames and return addresses, and translating all that
into the symbolic names and source file locations. On x86, there is a
conventional frame pointer register that makes frame iteration quite
straightforward without symbolic info - when a certain protocol is followed
by the program, which is not mandatory, and when the stack has not been
corrupted, which is unknown.
Roughly speaking, anything beyond "just copying" can be considered
parsing without a guaranteed success, but I would be willing to grant the
implementation the right to determine what it thinks it can capture
efficiently with a reasonable hope for success.
Post by Miro Knejp
If it cannot iterate over stack frames then how is it supposed to
provide a stack trace for arbitrary points in your program?
Earlier in this thread I indicated that "arbitrary points" may be harder
than "exception points". More generally, it may be impossible to iterate
*when* the stack is being captured, but may be possible *later* in a
different environment. In the example above, if the program is built with
"frame pointer omission", iterating over stack frames is only possible with
debugging information. If the latter is stripped from the executable, that
is not possible. But it may be possible if the stack is captured in a
binary form and later analysed in an environment that has the debugging
info.
Post by Miro Knejp
Now you're contradicting yourself. So is stack walking *always*
possible to be iterator based or not?
"Iterator-based" does not necessarily mean "stack-walking". The
implementation may need to capture a few different regions of memory to
make stack walking possible (with debugging info present at a later stage),
hence the need to iterate over them. But they do not need to correspond
directly to stack frames.
Post by Miro Knejp
If the runtime has the information at hand why not make it available to
the user?
To make all the information available, normally some configuration will
be required in the general case. In some cases all that is required is
embedded into the program itself and any modules it uses. But, in my
opinion, we need to regard that as an exception and API must be
configurable - and portable. And I have certain doubts as to whether it can
*really* be portable, not just superficially. I am talking about using
portable API *in this way *for platform A and *in that way* for platform
B, or, worse, *in this way* for platform X and toolchain settings Y & Z.
Cheers,
V.
--
---
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
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
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
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Myriachan
2016-04-26 01:41:56 UTC
Permalink
I just thought of a change to what I said:

a) have objects that need to be destructed during an unwind
This should probably should say:

a) have objects that need to call a destructor that has potential side
effects that may affect the observable behavior of the program

The reason for the change is that compilers would still be able to omit
unwind code if the only destructor(s) to be called would optimize to
nothing.
Post by Myriachan
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.
In x86-32 Windows, it is not possible in a fully-generic way to make a
stack trace without extra information that not all executables today have
(even when they don't play tricks). However, it is possible to walk the
SEH exception chain, which covers the a) and b) cases I mentioned.
Functions that meet neither a) nor b) are usually not in the SEH chain,
which is why I suggest it that way.
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".
Note that x86-64 Windows and ARM Windows are quite different in this
regard: on x86-64 and ARM Windows, the assembly language ABI is strict and
there is additional metadata that can used to make a "perfect" stack trace
(aside from the effects of inlining and tail-call optimizations).
Melissa
Post by Patrice Roy
Yeah, stack traces with coroutines might look like stack traces in
callback-laden systems such as those based on Node.js. That doesn't
discredit the idea of stack traces, though (it might just be the wrong
use-case).
On a technical note: I hope any stack trace proposal will make it
program-readable (something we can translate into a human-readable format
on demand, but program-readable first and foremost).
Post by Tony V E
Don't forget coroutines. The stack might not be contiguous (IIUC)
Sent from my BlackBerry portable Babbage Device
*From: *Viacheslav Usov
*Sent: *Thursday, April 21, 2016 12:51 PM
*Subject: *Re: [std-discussion] Re: throw std::exception with stack
trace (portable)
Post by Miro Knejp
Not exactly sure what you mean by "parse". As mentioned in a previous
message, the stacktrace object hides the algorithm to traverse the call
stack and you use it to collect the information you need.
Traverse and collect could mean a few different things. Let's just use
x86 as a hopefully universally understandably example. "Traversing and
collecting" could be as trivial as just copying the entire byte range from
the stack's bottom to its current top. And can be as complicated as
singling out call frames and return addresses, and translating all that
into the symbolic names and source file locations. On x86, there is a
conventional frame pointer register that makes frame iteration quite
straightforward without symbolic info - when a certain protocol is followed
by the program, which is not mandatory, and when the stack has not been
corrupted, which is unknown.
Roughly speaking, anything beyond "just copying" can be considered
parsing without a guaranteed success, but I would be willing to grant the
implementation the right to determine what it thinks it can capture
efficiently with a reasonable hope for success.
Post by Miro Knejp
If it cannot iterate over stack frames then how is it supposed to
provide a stack trace for arbitrary points in your program?
Earlier in this thread I indicated that "arbitrary points" may be harder
than "exception points". More generally, it may be impossible to iterate
*when* the stack is being captured, but may be possible *later* in a
different environment. In the example above, if the program is built with
"frame pointer omission", iterating over stack frames is only possible with
debugging information. If the latter is stripped from the executable, that
is not possible. But it may be possible if the stack is captured in a
binary form and later analysed in an environment that has the debugging
info.
Post by Miro Knejp
Now you're contradicting yourself. So is stack walking *always*
possible to be iterator based or not?
"Iterator-based" does not necessarily mean "stack-walking". The
implementation may need to capture a few different regions of memory to
make stack walking possible (with debugging info present at a later stage),
hence the need to iterate over them. But they do not need to correspond
directly to stack frames.
Post by Miro Knejp
If the runtime has the information at hand why not make it available
to the user?
To make all the information available, normally some configuration will
be required in the general case. In some cases all that is required is
embedded into the program itself and any modules it uses. But, in my
opinion, we need to regard that as an exception and API must be
configurable - and portable. And I have certain doubts as to whether it can
*really* be portable, not just superficially. I am talking about using
portable API *in this way *for platform A and *in that way* for
platform B, or, worse, *in this way* for platform X and toolchain
settings Y & Z.
Cheers,
V.
--
---
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
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
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
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Christopher Jefferson
2016-04-27 17:51:45 UTC
Permalink
Post by Myriachan
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".
I think a simpler rule would just be to say "some functions can be
missing". This has the advantage that it provides an easy, valid,
implementation for compilers which don't know how to do any stack traces
(just miss everything), and makes life easier when optimisation levels
increase and stack trace functions get lost (as they often are when
debugging real code with high optimisation levels).

Of course, as a QoI issue, I would expect with optimisations off/low, most
compilers would produce good quality backtraces.

Chris
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Richard Hodges
2016-04-16 09:58:47 UTC
Permalink
The c++ standard makes no mention of a stack. The memory model makes no
assumption that there is one.

In fact, in optimised code, there is often no stack use at all because of
extensive inlining.

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.
Post by Matthew Woehlke
Post by Matthew Woehlke
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.
Post by Matthew Woehlke
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,
That is technically a library feature, although compiler magic is required.
Post by Matthew Woehlke
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.
I am not seeing why that was called moot; I think we are in a violent
agreement here. The code that throws may be symbol-stripped, but it can
still collect those pointers and save/forward them to some other code that
has the symbolic info.
Cheers,
V.
--
---
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
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Philipp Stephani
2016-04-16 10:30:21 UTC
Permalink
Post by Richard Hodges
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.
Post by Richard Hodges
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.
Post by Richard Hodges
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.
Post by Richard Hodges
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.
Post by Richard Hodges
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.
That typically (e.g. in the case of Boost.Exception) requires cooperation
by all parties involved and significant boilerplate and/or macros.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Edward Catmur
2016-04-16 11:34:36 UTC
Permalink
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.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
FrankHB1989
2016-04-18 02:48:09 UTC
Permalink
圚 2016幎4月16日星期六 UTC+8䞋午7:34:36Edward Catmur写道
Post by Edward Catmur
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.
I am not against to the feature but I don't think it should be mandatory in
every cases which violates the zero cost abstract principle.

To make it usable in a debugger should be a correct direction. (In fact I
do need such standardized interface to reduce some daily work.)

To make this possible in the level of the specification, it can be
conditionally-supported, or only mandated for hosted implementations when
appropriate (really implementable in a lightweight enough way). Otherwise,
it can be equipped with a runtime with some replacement mechanism like
"operator new" and let users decide when to use. Both choices are not easy
to be reasonably portable without the aid of the standard.



.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Andrew Marlow
2016-04-16 11:51:49 UTC
Permalink
Post by Philipp Stephani
Post by Richard Hodges
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.
Post by Richard Hodges
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.
Hear, hear!
Post by Philipp Stephani
In the light of this, I don't see that it is feasible , desirable (or even
Post by Richard Hodges
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.
Indeed. I think it would be perfectly ok for the stack trace produced to
omit functions that had been inlined.
--
Regards,

Andrew Marlow
http://www.andrewpetermarlow.co.uk
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Bjorn Reese
2016-04-17 09:35:41 UTC
Permalink
Post by Edward Catmur
Because stack traces are lightweight and do not require manual intervention or any special machinery to generate
How are you going to generate a stack trace on MIPS without "any
special machinery"?
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
'Edward Catmur' via ISO C++ Standard - Discussion
2016-04-17 15:47:46 UTC
Permalink
Post by Bjorn Reese
Post by Edward Catmur
Because stack traces are lightweight and do not require manual
intervention or any special machinery to generate
Post by Bjorn Reese
How are you going to generate a stack trace on MIPS without "any
special machinery"?
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.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
FrankHB1989
2016-04-18 02:36:37 UTC
Permalink
圚 2016幎4月16日星期六 UTC+8䞋午6:30:33Philipp Stephani写道
Post by Philipp Stephani
Post by Richard Hodges
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
Post by Philipp Stephani
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. It confuses users about the scope of the language, makes them
wrongly to believe the activation record should be implemented as a stack
and makes some wording harder to compose.
Post by Philipp Stephani
The only time you'll ever need a stack trace is when you don't have a
Post by Richard Hodges
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.
Post by Richard Hodges
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.
Please note it is not so easy to make it mandatory by a few assertions like
here. Java is a very different monster in this aspect: firstly it has
nothing to support freestanding implementations, second it does have at one
canonical related specification about the intermediate representation (i.e.
the JVM spec) to help make the task here being "portable" easily enough.
Post by Philipp Stephani
Post by Richard Hodges
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.
No. C does like this, though it has only the equivalence of "as-if" rules,
without some other more subtle rules aiming to gain such intent (e.g. copy
elision). Many other languages are simpler: they just fully document the
intended (including explicitly unspecified) *behavior* of the
implementations, in the language specification, in the target intermediate
representation specification. in both, or being more simple - leave them
out when there presents a reference implementation. There also exist a few
languages strictly specify the formal semantics of the language rules
without mention the intent, but seems to be similar to the "as-if" rules on
implementation when the behavior is not explicitly specified.
Post by Philipp Stephani
Post by Richard Hodges
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.
That typically (e.g. in the case of Boost.Exception) requires cooperation
by all parties involved and significant boilerplate and/or macros.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Asiri Rathnayake
2016-04-18 06:42:46 UTC
Permalink
Post by FrankHB1989
圚 2016幎4月16日星期六 UTC+8䞋午6:30:33Philipp Stephani写道
Post by Philipp Stephani
Post by Richard Hodges
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
Post by Philipp Stephani
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.
Post by FrankHB1989
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.

[1] https://mentorembedded.github.io/cxx-abi/abi.html
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
FrankHB1989
2016-04-18 11:32:44 UTC
Permalink
圚 2016幎4月18日星期䞀 UTC+8䞋午2:42:48Asiri Rathnayake写道
Post by Asiri Rathnayake
Post by FrankHB1989
圚 2016幎4月16日星期六 UTC+8䞋午6:30:33Philipp Stephani写道
Post by Richard Hodges
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.
But I don't necessarily need it *just only as a stack*. It can be a vector
or even a hash table. On the other hand, "the stack" might be too easily
confused with the stack provided by the ISA-level ABI (which should not be
in the language rules).
Post by Asiri Rathnayake
Post by FrankHB1989
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.
No, I don't. It is an issue of the lack of terminology in the language
standard to distinguish something from the corresponding entity in ABI
specifications.
Post by Asiri Rathnayake
[1] https://mentorembedded.github.io/cxx-abi/abi.html
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Matthew Woehlke
2016-04-18 14:42:49 UTC
Permalink
Post by Philipp Stephani
Post by Richard Hodges
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.
First off, I want to agree with this. Definitely the standard should not
make any assertions as to the "quality" of the trace, i.e. we expect
that inlined functions will either a) not show up, or b) "hide" the
function that called them.

That said, something just occurred to me... it seems like we could do
better here. If a function is inlined, as I understand, a call to that
function is replaced with a *copy* of that function. My experience with
stack traces is that when this happens, the resolved stack pointer gives
the name and source line of the inlined function being executed.
However, since that function is a *copy*, shouldn't we also know what
function and source line *called* the inline?

This would imply that there might exist a function that, given a "frame
pointer", can resolve a second pointer that called the first, in the
case that the first is an inlined call. (Maybe current debugging
information does not give us a way to resolve this information?) This
might potentially be an API we would want to include.
--
Matthew
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Matthew Woehlke
2016-04-18 14:42:47 UTC
Permalink
Post by Richard Hodges
In fact, in optimised code, there is often no stack use at all because of
extensive inlining.
I'm going to have to concur with Philipp in disagreeing. I see stack
traces from "optimized" code often enough to know that this isn't true.
I would guess that the number of real calls that are "hidden" by
inlining is typically 15%-50%. In particular, note that inlining is not
likely¹ across a shared library boundary. Also note that inlining is a
speed / size tradeoff; it's unlikely cache concerns will ever go away to
the extent that we can expand every possible code path fully inline.
After all, the performance improvement is something like O(N) vs. a size
cost of something more like O(N^2) or O(2^N). (I don't know the actual
numbers, I just know that the size order is higher than the speed order.)

(¹ I'm not all that familiar with "prelinking" to guess if it could
dynamically inline across library boundaries in previously compiled
code. You'd at least need to keep a copy of the original code in such
case to redo or scrap the optimization when the library changes. Also,
you're still in the low-order-speed vs. higher-order-size tradeoff
territory.)

p.s. The KDE crash reported collects stack traces (using gdb) for
inclusion in bug reports. (This feature wouldn't help there, since the
application is already crashing; I'm just mentioning it as a
counter-argument to the claim that stack traces are useless.)
Post by Richard Hodges
In the light of this, I don't see that it is feasible , desirable (or even
possible) to impose a stack trace on implementors.
(...and also...)
Post by Richard Hodges
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.
--
Matthew
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2016-04-18 17:21:18 UTC
Permalink
Post by Matthew Woehlke
Post by 'Edward Catmur' via ISO C++ Standard - Discussion
In any case, this would be a conditionally supported feature; whether
that
Post by 'Edward Catmur' via ISO C++ Standard - Discussion
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.
Hmmm.

I disagree that a null return value should be allowed as a valid
implementation. However, I also do not believe that implementations should
be required to go to extensive effort to create a quality stack trace.

Getting some form of trace should be guaranteed. Maybe not an especially
useful form, but some form. Even if all of the names and locations are just
memory addresses, something still needs to be there. We obviously can't
guarantee that any particular function call will add one more level to the
trace, and so forth. But returning an empty string shouldn't be valid.

Implementations should at least make an effort to come up with something.

I also think that having a consistent *format* for the stack trace is
important. There are certain things we ought to be able to do in a
platform-independent way:

1: Separate individual levels in the stack trace.

2: Be able to distinguish "function name", "line number" and "filename" in
each level of the trace. Note that none of these need to actually be what
they say they are. That is, "function name" could be a memory address or a
post-mangled name. It may or may not have parameter types listed. "Line
number" could be a byte offset or nothing at all. "Filename" could be an
empty string. The point is, you need to be able to parse all of these
elements from the string in a platform-neutral way.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2016-04-18 18:27:54 UTC
Permalink
Post by Nicol Bolas
1: Separate individual levels in the stack trace.
2: Be able to distinguish "function name", "line number" and "filename" in
each level of the trace. Note that none of these need to actually be what
they say they are. That is, "function name" could be a memory address or a
post-mangled name. It may or may not have parameter types listed. "Line
number" could be a byte offset or nothing at all. "Filename" could be an
empty string. The point is, you need to be able to parse all of these
elements from the string in a platform-neutral way.
Existing practice is <execinfo.h> header from glibc, which has two functions
of interest:

/* Store up to SIZE return address of the current program state in
ARRAY and return the exact number of values stored. */
extern int backtrace (void **__array, int __size) __nonnull ((1));


/* Return names of functions from the backtrace list in ARRAY in a newly
malloc()ed memory block. */
extern char **backtrace_symbols (void *const *__array, int __size)
__THROW __nonnull ((1));

If you couple that with the demangler from cxxabi.h:

char*
__cxa_demangle(const char* __mangled_name, char* __output_buffer,
size_t* __length, int* __status);

you can get pretty decent function names in a stack trace, at least for
exported functions. We're using that in Qt and you can ask each line of a
debugging output to include the stack trace by using %[backtrace] in your
QT_MESSAGE_PATTERN environment variable.

File names and line numbers aren't stored in the unwind stack. They're only
present in the debugging information, which isn't loaded into memory. Asking
for that is too much.

Finally, when I really want a stack trace, I want to see the values to the
parameters in each frame. We're not going to get that from the library. That's
a debugger's job.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Viacheslav Usov
2016-04-20 08:58:33 UTC
Permalink
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.

Cheers,
V.
Post by Matthew Woehlke
Post by Viacheslav Usov
Post by Matthew Woehlke
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.
Post by Viacheslav Usov
and probably a pessimisation than supporting a stack trace when
an exception is thrown. When an exception is thrown, the implementation
has
Post by Viacheslav Usov
to do stack unwinding. So it will have some supporting code for that,
which
Post by Viacheslav Usov
can only be optimized away if the the throw is optimized away, and it
will
Post by Viacheslav Usov
normally have some statically allocated data that could be translated
into
Post by Viacheslav Usov
a stack trace, not necessarily at runtime. None of that needs to be
present
Post by Viacheslav Usov
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...
Post by Viacheslav Usov
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.
Post by Viacheslav Usov
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
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
--
---
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
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2016-04-20 13:19:39 UTC
Permalink
Post by Viacheslav Usov
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.
What's the point of making these separate actions? Why bother with the
intermediate step of implementation-defined byte arrays? What are you going
to do, stick them in a binary log-file? It's not like they have any
inherent meaning, nor are they required to be useful between executions of
the program.

Just give us the human readable stack trace.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
'Edward Catmur' via ISO C++ Standard - Discussion
2016-04-20 13:51:08 UTC
Permalink
Post by Nicol Bolas
Post by Viacheslav Usov
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".
Post by Nicol Bolas
Post by Viacheslav Usov
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.
Post by Nicol Bolas
Post by Viacheslav Usov
We also need another mechanism to translate the byte buffer returned by
std:;stacktrace into human readable text.
Post by Nicol Bolas
What's the point of making these separate actions? Why bother with the
intermediate step of implementation-defined byte arrays? What are you going
to do, stick them in a binary log-file? It's not like they have any
inherent meaning, nor are they required to be useful between executions of
the program.

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.
Post by Nicol Bolas
Just give us the human readable stack trace.
Human readable tends to imply less useful for machines.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2016-04-20 14:16:46 UTC
Permalink
Post by 'Edward Catmur' via ISO C++ Standard - Discussion
Post by Nicol Bolas
Post by Viacheslav Usov
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".
Post by Nicol Bolas
Post by Viacheslav Usov
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.
Post by Nicol Bolas
Post by Viacheslav Usov
We also need another mechanism to translate the byte buffer returned by
std:;stacktrace into human readable text.
Post by Nicol Bolas
What's the point of making these separate actions? Why bother with the
intermediate step of implementation-defined byte arrays? What are you going
to do, stick them in a binary log-file? It's not like they have any
inherent meaning, nor are they required to be useful between executions of
the program.
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
Post by 'Edward Catmur' via ISO C++ Standard - Discussion
modules.
So, you want to have this two-step process for cases where you're not using
DLLs/SOs and you have either turned off or otherwise subverted ASLR. Oh,
and the format of such binary data is well-documented. Is this a
particularly common case?

Also, how exactly can you predict how much space a stack trace takes? It
is, after all, highly dependent on a runtime concept: where you were when
it was generated.

In any case, I believe that this can be resolved without a two-stage
process: use formats.

That is, when you get a stack trace, you specify a format like `printf`
that defines how the contents of each level of the trace are formatted.
Formatting characters would include:

%F: Function name of that level of the trace, as an implementation-defined
string. If not available, then nothing.
%L: Line number for that level of the trace, as a string. If not available,
then nothing.
%E: Filename for that level of the trace, as a string. If not available,
then nothing.
%B: Binary representation of that level of the trace, as an
implementation-defined sequence of hexidecimal characters.
%b: Binary representation of that level of the trace, as an
implementation-defined sequence of bytes.
%n: Number of bytes in the binary representation of that level of the
trace, as an `unsigned int` integer type, as though the implementation
performed `memcpy(output, &size_var, sizeof(unsigned int));`

So if you wanted a textual trace, you could do:

trace("%F(0x%B)[%L]\n");

If you want a binary trace, you could do:

trace("%n%b");

Note that the trace is generated as a single sequence of bytes, based
entirely on the format. So the implementations will not add characters
between the levels of the trace. In the first case, "\n" is used to section
the trace into levels, but you could use whatever you like. In the second
case, you have to parse it by reading the implementation-defined byte array
back.

Admittedly, reading back the binary trace is a bit inelegant. But `unsigned
int` followed by that number of bytes is sufficient for processing the
whole thing into whatever format you desire.

So there's no clumsy two-step process, and everyone can still get the data
they desire.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Viacheslav Usov
2016-04-20 15:12:04 UTC
Permalink
Post by Nicol Bolas
So, you want to have this two-step process for cases where you're not
using DLLs/SOs and you have either turned off or otherwise subverted ASLR.

Why does that exclude DLLs/SOs and ASLR? It is very trivial to produce a
list of modules, including that of the main program, complete with the base
addresses of whatever sections/segments they are transformed into memory.
You can do so without having *any *symbolic information. It is very trivial
to include that info into a binary stack trace. That is essentially what
the minidump feature in Windows does, working just fine just about always.

Cheers,
V.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Viacheslav Usov
2016-04-20 13:52:25 UTC
Permalink
Post by Nicol Bolas
What's the point of making these separate actions?
Separating those actions makes it unnecessary to embed symbolic information
information into the compiled and linked executable, as has been mentioned
more than once in his thread.
Post by Nicol Bolas
nor are they required to be useful between executions of the program.
Indeed they are not, being implementation-defined.

Cheers,
V.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Matthew Woehlke
2016-04-20 15:01:40 UTC
Permalink
Post by Nicol Bolas
Post by Viacheslav Usov
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.
What's the point of making these separate actions?
- Symbol resolution is expensive (both in time, and in necessary use of
dynamically allocated memory, as opposed to "stack pointers" which are
almost certainly of fixed size); we may be collecting a trace that may
never be used, so paying this cost up front is stupid. Even if I am
definitely going to print the trace, I may not want to do resolution
immediately.

- Symbol resolution may not be possible at run time due to missing debug
information, but may be possible "offline", so it can be useful to dump
just the "stack pointers" without resolving them.

- We might be collecting a trace for some other reason entirely for
which the resolved symbols aren't even interesting. (See for example my
other message suggesting reasons why having a separate call to *only get
the stack depth* might be interesting.)

- It's lower level. I'm not aware of any case where the implementation
would not be a two-step process anyway, so not providing direct access
to this API is unnecessarily limiting.

- I might obtain a "stack pointer" by some other means and want to
resolve it.
Post by Nicol Bolas
Why bother with the intermediate step of implementation-defined byte
arrays?
...because a "stack pointer" may not be isomorphic with `void*` on every
platform. (That said, I don't like the byte array, either; I'd propose
an opaque type.)
Post by Nicol Bolas
Just give us the human readable stack trace.
Existing implementations don't work this way, for a reason.

That said, I'd encourage providing a convenience function to combine the
operations; it just shouldn't be the *only available* API.
Post by Nicol Bolas
Also, how exactly can you predict how much space a stack trace takes?
It is, after all, highly dependent on a runtime concept: where you
were when it was generated.
Typically, when requesting a trace, you pass a limit on the number of
entries to be returned. Accordingly, it is trivial to compute an upper
bound on the required memory. (Especially if I am doing this for e.g. a
panic handler, I might want to reserve this memory in advance.)
--
Matthew
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Viacheslav Usov
2016-04-20 15:26:32 UTC
Permalink
Post by Matthew Woehlke
...because a "stack pointer" may not be isomorphic with `void*` on every
platform. (That said, I don't like the byte array, either; I'd propose an
opaque type.)

The reason I switched from "stack pointers" to a byte buffer is that the
former seem to imply that the implementation may be able to resolve a
binary chunk that the stack is into a collection of entities right there
and then. That need not be the case I believe.

Then again, insisting on a single byte buffer may also be both restrictive
and wasteful, requiring memory allocation to pack possibly disjoint data.
Therefore, I think the interface should be something like a begin/end
iterator pair, with the iterator pointing to a byte array.

Cheers,
V.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Tony V E
2016-04-15 17:31:54 UTC
Permalink
<html><head></head><body lang="en-US" style="background-color: rgb(255, 255, 255); line-height: initial;"> <div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);">I think a portable stack trace library would be a great addition to boost.&nbsp;</div><div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);"><br></div><div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);">I think making it standardized is an order of magnitude harder. </div> <div style="width: 100%; font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);"><br style="display:initial"></div> <div style="font-size: initial; font-family: Calibri, 'Slate Pro', sans-serif, sans-serif; color: rgb(31, 73, 125); text-align: initial; background-color: rgb(255, 255, 255);">Sent&nbsp;from&nbsp;my&nbsp;BlackBerry&nbsp;portable&nbsp;Babbage&nbsp;Device</div> <table width="100%" style="background-color:white;border-spacing:0px;"> <tbody><tr><td colspan="2" style="font-size: initial; text-align: initial; background-color: rgb(255, 255, 255);"> <div style="border-style: solid none none; border-top-color: rgb(181, 196, 223); border-top-width: 1pt; padding: 3pt 0in 0in; font-family: Tahoma, 'BB Alpha Sans', 'Slate Pro'; font-size: 10pt;"> <div><b>From: </b>***@gmail.com</div><div><b>Sent: </b>Friday, April 15, 2016 2:02 AM</div><div><b>To: </b>ISO C++ Standard - Discussion</div><div><b>Reply To: </b>std-***@isocpp.org</div><div><b>Subject: </b>[std-discussion] throw std::exception with stack trace (portable)</div></div></td></tr></tbody></table><div style="border-style: solid none none; border-top-color: rgb(186, 188, 209); border-top-width: 1pt; font-size: initial; text-align: initial; background-color: rgb(255, 255, 255);"></div><br><div id="_originalContent" style=""><div dir="ltr"><div>To find the root cause of bugs easier, it is very helpful to have stack trace from the location where the exception is thrown.<br></div><div><br></div><div>Surely I can throw an exception class derived from std:exception and use backtrace on Linux or StackWalk64 on Windows, with the appropriate symbols (=&gt; not portable),&nbsp;<br><div>or have a try catch "everywhere" and add file and line information (=&gt; too much code to add),&nbsp;</div></div><div>or not to catch the exception at all and let the Operation System write the dump file(=&gt;not portable, some exceptions can be handled inside the program).</div><div><br></div><div>I thought there should be an easy portable way to add stack information to a std::exception. Does anybody have some ideas?</div><div><br></div><div>best regards,</div><div>Stefan</div></div>

<p></p>

-- <br>
<br>
--- <br>
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.<br>
To unsubscribe from this group and stop receiving emails from it, send an email to <a href="mailto:std-discussion+***@isocpp.org">std-discussion+***@isocpp.org</a>.<br>
To post to this group, send email to <a href="mailto:std-***@isocpp.org">std-***@isocpp.org</a>.<br>
Visit this group at <a href="https://groups.google.com/a/isocpp.org/group/std-discussion/">https://groups.google.com/a/isocpp.org/group/std-discussion/</a>.<br>
<br><!--end of _originalContent --></div></body></html>

<p></p>

-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &quot;ISO C++ Standard - Discussion&quot; group.<br />
To unsubscribe from this group and stop receiving emails from it, send an email to <a href="mailto:std-discussion+***@isocpp.org">std-discussion+***@isocpp.org</a>.<br />
To post to this group, send email to <a href="mailto:std-***@isocpp.org">std-***@isocpp.org</a>.<br />
Visit this group at <a href="https://groups.google.com/a/isocpp.org/group/std-discussion/">https://groups.google.com/a/isocpp.org/group/std-discussion/</a>.<br />
Matthew Woehlke
2016-04-20 15:24:17 UTC
Permalink
Some of the discussion in this thread seems to be caused various
perceptions of what a portable API to obtain stack traces might look
like. To that end, I would like to submit the following for consideration.

This isn't a proposal yet, just some notes I wrote down on what the
actual API might look like. Some noteworthy features incorporated include:

- Ability to resolve the (static) call site of an inlined call.
- Ability to turn dynamically offset pointers into absolute pointers.

This is not yet complete. For example, there is no resolution function
at all. Some mechanism to format a `frame` (i.e. without resolution) is
probably needed. Methods to apply and remove offsets may be needed?
Also, there will likely be at least one "push button" convenience
function to combine operations. (As noted, however, I'm opposed to
making this the *only* available API.)

p.s. If anyone would like to co-author a proposal, please let me know.
--
Matthew
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Andrew Marlow
2016-04-20 16:46:54 UTC
Permalink
What not model the interface on what Java has done? There is the means to
get the trace as a string as well as a function that logs the trace
directly where it gets the string internally. In both cases we do not need
to get pointers to stack frame objects or anything like that. Remember, the
whole point of asking for this is as aid in debugging and error reporting.
Post by Matthew Woehlke
Some of the discussion in this thread seems to be caused various
perceptions of what a portable API to obtain stack traces might look
like. To that end, I would like to submit the following for consideration.
This isn't a proposal yet, just some notes I wrote down on what the
- Ability to resolve the (static) call site of an inlined call.
- Ability to turn dynamically offset pointers into absolute pointers.
This is not yet complete. For example, there is no resolution function
at all. Some mechanism to format a `frame` (i.e. without resolution) is
probably needed. Methods to apply and remove offsets may be needed?
Also, there will likely be at least one "push button" convenience
function to combine operations. (As noted, however, I'm opposed to
making this the *only* available API.)
p.s. If anyone would like to co-author a proposal, please let me know.
--
Matthew
--
---
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
<javascript:;>.
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
Regards,

Andrew Marlow
http://www.andrewpetermarlow.co.uk
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Viacheslav Usov
2016-04-20 17:32:45 UTC
Permalink
Post by Andrew Marlow
What not model the interface on what Java has done?
Because in this particular case the difference between C++ and Java is
significant.
Post by Andrew Marlow
There is the means to get the trace as a string as well as a function
that logs the trace directly where it gets the string internally.

Java can always obtain symbolic names through its reflection. C++ has
nothing like that, and may or may not, or may only partially be able to do
that right there and then. Being able to obtain some binary information is
just about the only thing that is truly portable in this case. Translation
in situ is not.

Cheers,
V.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
t***@viasys.com
2016-10-03 13:38:00 UTC
Permalink
Exception with stack trace sounds nice - but would it be possible as very
first step standardize stack trace itself (without any exception handling).

I can provide proposal for API - from this project:

https://sourceforge.net/projects/diagnostic/

See ResolveStack.h
<https://sourceforge.net/p/diagnostic/svn/HEAD/tree/src/ResolveStack.h> /
ResolveStack.
<https://sourceforge.net/p/diagnostic/svn/HEAD/tree/src/ResolveStack.h>cpp
/ ResolveStack
<https://sourceforge.net/p/diagnostic/svn/HEAD/tree/src/ResolveStack.h>M.h
/ ResolveStackM.
<https://sourceforge.net/p/diagnostic/svn/HEAD/tree/src/ResolveStack.h>cpp
https://sourceforge.net/p/diagnostic/svn/HEAD/tree/src/

Design: https://sourceforge.net/p/diagnostic/svn/HEAD/tree/ - see chapter 7.

It has some cross links to stack overflow forum / message / posts for it.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
tapika via ISO C++ Standard - Discussion
2016-10-03 13:39:03 UTC
Permalink
Exception with stack trace sounds nice - but would it be possible as very
first step standardize stack trace itself (without any exception handling).

I can provide proposal for API - from this project:

https://sourceforge.net/projects/diagnostic/

See ResolveStack.h
<https://sourceforge.net/p/diagnostic/svn/HEAD/tree/src/ResolveStack.h> /
ResolveStack.
<https://sourceforge.net/p/diagnostic/svn/HEAD/tree/src/ResolveStack.h>cpp
/ ResolveStack
<https://sourceforge.net/p/diagnostic/svn/HEAD/tree/src/ResolveStack.h>M.h
/ ResolveStackM.
<https://sourceforge.net/p/diagnostic/svn/HEAD/tree/src/ResolveStack.h>cpp
https://sourceforge.net/p/diagnostic/svn/HEAD/tree/src/

Design: https://sourceforge.net/p/diagnostic/svn/HEAD/tree/ - see chapter 7.

It has some cross links to stack overflow forum / message / posts for it.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Continue reading on narkive:
Loading...