Backtrace function

D

dj3vande

Dear all,

can any one suggest ways of implementing a backtrace function in ANSI/
standard C as given in the following link ?

http://pramode.net/2006/09/12/getting-a-backtrace-from-running-c-code/#more-144

Divine Guidance.

(Well, either that or stepping well outside what the Standard defines
to grovel through the implementation's internal structures and finding
the information you want that way. It's probably there somewhere
(though there are cases where it can be left out without breaking
anything), so actually getting to it is a simple matter of finding out
where to look and how to interpret what you see, and then hoping it
doesn't change when you upgrade your compiler.)


dave
 
R

Richard Heathfield

sophia said:
Dear all,

can any one suggest ways of implementing a backtrace function in ANSI/
standard C as given in the following link ?
http://pramode.net/2006/09/12/getting-a-backtrace-from-running-c-code/#more-144

When implementing functions such as backtrace(), backtrace_symbols(), and
backtrace_symbols_fd(), the best place to be sitting is the desk of the
compiler-writer. You *can* do something similar in a portable ISO C
program, but AFAIK the only way to do so is to set up a voluntary
mechanism whereby each function, on entry, pushes its name onto a
stack-like data structure provided for the purpose - and which then pops
the name off again just before returning. I've done this myself on two or
three sites, and seen it done on several more. It's not terribly difficult
- the trickiest bit is making sure that it still works even in low-memory
situations, which means either steering clear of malloc&co or getting your
memory request in nice and early.
 
K

Kaz Kylheku

Dear all,

can any one suggest ways of implementing a backtrace function in ANSI/
standard C as given in the following link ?

What do you think?

Where in standard C do you see a way for a function to determine even
its immediate caller, never mind that caller's caller and so on?
 
J

jacob navia

Kaz said:
Also, there is no nonportable way to do that in standard C, either.

:)

No, In non portable C you can do it

1) Read the frame pointer (This is the only assembly yo need)

2) See at what offset from the frame pointer is the pushed return
address

3) The value stored in the saved frame pointer position points to
the next frame.

Here you have the next frame and the address of some point in the
calling procedure.

4) Read the debug information.

5) Find out into which function the return address points to.

6) Is that the function "main"?

7) If not, get the next frame pointer and go to (2)
---------------

This supposes that

(1) you have a frame pointer
(2) The calling function has a frame pointer

GDB is written in C, and the lcc-win debugger is
written in C. So there is OBVIOUSLY a way of doing
this in C!
 
F

Flash Gordon

jacob navia wrote, On 28/03/08 21:39:
No, In non portable C you can do it

Sometimes. Sometimes you can't.
1) Read the frame pointer (This is the only assembly yo need)

If there is one.
2) See at what offset from the frame pointer is the pushed return
address

If there is anything to tell you other than the function prologue. Of
course the return address might not have been saved to RAM either due to
function inlining (as an optimisation) or because it did not need to for
some other reason.
3) The value stored in the saved frame pointer position points to
the next frame.

If there is a saved frame pointer. Not all implementations use a
separate frame pointer.
Here you have the next frame and the address of some point in the
calling procedure.

Maybe, if all your assumtions are true so far.
4) Read the debug information.

If there is any which there might not be.
5) Find out into which function the return address points to.

6) Is that the function "main"?

7) If not, get the next frame pointer and go to (2)

A problem if main was called recursively.
---------------

This supposes that

(1) you have a frame pointer
(2) The calling function has a frame pointer

GDB is written in C, and the lcc-win debugger is
written in C. So there is OBVIOUSLY a way of doing
this in C!

No, obviously under some specific circumstances it is possible but it is
not always possible or if it is then might be more complex than you have
suggested.
 
R

Richard Heathfield

jacob navia said:
No, In non portable C you can do it

You mis-read what he wrote, which is that there is no way (even a
non-portable way) to do it in *standard* C.
1) Read the frame pointer (This is the only assembly yo need)

And Step 1 is where it stops being standard C.

5) Find out into which function the return address points to.

6) Is that the function "main"?

7) If not, get the next frame pointer and go to (2)

This fails to handle recursive calls to main.
---------------

This supposes that

(1) you have a frame pointer
(2) The calling function has a frame pointer

GDB is written in C, and the lcc-win debugger is
written in C. So there is OBVIOUSLY a way of doing
this in C!

And equally obviously, both gdb and the lcc-win debugger need extensions in
order to do this.
 
J

jacob navia

Richard said:
jacob navia said:


You mis-read what he wrote, which is that there is no way (even a
non-portable way) to do it in *standard* C.


And Step 1 is where it stops being standard C.

Why?

It means calling

fp = GetFramePointer();

And that is standard C.
This fails to handle recursive calls to main.

No. It will just stop at the first of those recursive calls
since it will look no further up. Please read carefully what I
write.
And equally obviously, both gdb and the lcc-win debugger need extensions in
order to do this.

No extensions needed. In C you can call any external function written
in assembly since maybe 30 years...

When you write:

if (!strcmp(str,"hello"))

you are likely calling an assembler routine anyway.
 
I

Ian Collins

Richard said:
jacob navia said:

You mis-read what he wrote, which is that there is no way (even a
non-portable way) to do it in *standard* C.


And Step 1 is where it stops being standard C.



This fails to handle recursive calls to main.
Which occur how often?

I know it isn't standard C, but I've added code to a couple of embedded
systems that does almost exactly what Jacob describes. It may not be
standard C, but it might help someone who wants to know how.
 
J

jacob navia

Ian said:
Which occur how often?

But it DOES handle recursive calls. It will just stop at the
first of those recursive calls since it looks no further up!
I know it isn't standard C, but I've added code to a couple of embedded
systems that does almost exactly what Jacob describes. It may not be
standard C, but it might help someone who wants to know how.

Exactly, that is what I wanted to show. The frame pointers are just
a linked list. Immediately above (in most cases)
is the return address, and there you go!
 
R

Richard Heathfield

jacob navia said:
Why?

It means calling

fp = GetFramePointer();

And that is standard C.

Ha! Yes, we're right on the border here. Clearly, the *call* is standard.
The implementation of GetFramePointer() is not, *but* the claim was that
there was no way to do this non-portably in standard C. Standard C
implementations can legally offer extensions, and non-portable programs
can take advantage of them whilst remaining syntactically
standard-conforming. I think you win this round. Nice one.

<snip>
 
I

Ian Collins

Flash said:
jacob navia wrote, On 28/03/08 21:39:


If there is anything to tell you other than the function prologue. Of
course the return address might not have been saved to RAM either due to
function inlining (as an optimisation) or because it did not need to for
some other reason.
Interesting point, if your coding style is anything like mine, you will
have lots of small functions that do get inlined, leaving a pretty
useless call stack
If there is a saved frame pointer. Not all implementations use a
separate frame pointer.
x64 is a prime example, very difficult even for a debugger to work out
the callstack in optimised code.
 
J

jacob navia

Flash said:
jacob navia wrote, On 28/03/08 21:39:

Sometimes. Sometimes you can't.


If there is one.

Mr "Gordon":
I said this several lines below. Please read the whole message
before replying.

I have the feeling that you do not red the message and try to
understand what I propose but that you jump into each sentence
trying to find out something to comment negatively on.
If there is anything to tell you other than the function prologue. Of
course the return address might not have been saved to RAM either due to
function inlining (as an optimisation) or because it did not need to for
some other reason.

The function address is always saved, or, sometimes held in a
designated register. In both cases it is at a fixed point.

Obviously, if your function is *inlined* it will NOT appear
in a backtrace of the stack!

But that is "by design" isn't it?

:)

If there is a saved frame pointer. Not all implementations use a
separate frame pointer.

See the prerequisites that I wrote below...

Maybe, if all your assumtions are true so far.


If there is any which there might not be.

If there is no debug information, a backtrace can still be
printed, but it is a backrace like:

0x4477ADD()
0x4477665()

etc

The address can be still useful if you have a linker map.

A problem if main was called recursively.

No. As I explained to Mr Heathfield, that raised the same objection,
my algorithm just stops at the first possible recursive call of main
It doesn't fail at all. It will not follow recursive calls to "main".

That is all.

Obviously, calling "main" recurisvely is very popular here.

:)

You see?

If you would have read till here, you would have saved yourself
the remarks above.
No, obviously under some specific circumstances it is possible but it is
not always possible or if it is then might be more complex than you have
suggested.

Mr "Gordon"
Writing a debugger is more complex than what I outlined above.

True.

And so what?

The purpose of my algorithm description is not to provide you
with a ready made debugger implementation!

Specially

"Read all debug information" is quite complex as an undertaking,
I can tell you.
 
J

jacob navia

Ian said:
Interesting point, if your coding style is anything like mine, you will
have lots of small functions that do get inlined, leaving a pretty
useless call stack

x64 is a prime example, very difficult even for a debugger to work out
the callstack in optimised code.

Yes, my debugger has still problems even with slightly
optimized code.

The first 4 arguments are not in the stack, but in
RCX,RDX,R8,and R9.

Sometimes I optimize the writing of those registers
and skip any stores. This confuses the debugger completely.

I tried to emit in the compiler, records to tell the debugger
in which registers the variable lives, but since I alias
a register to several variables at the same time if possible
(when the variables have disjoint lifetimes) that is a further
complication.

In any case for the straight backtrace there is no problem since
lcc-win ALWAYS emits a frame pointer.

I thought that for the time being, I will emit frame pointers until
I find a way of telling the debugger where the return address should
be. If not, you just can't debug optimized code!

There was recently a discussion in the gcc mailing list about the
updating of the debug information to support optimized code.

I had the impression that the poor guy that tried to do that didn't
receive much support from the compiler people, maybe because everyone
ws convinced that the task is just too difficult and would
imply a lot of modifications in the compiler.

I will try to avoid that situation. I prefer slower code but
code that can be debugged, sorry. Specially in x64, the
machines are so fast that making the code undebuggable is
just not worth the price you pay for it in undiscovered bugs!
 
R

Richard Heathfield

jacob navia said:
No. As I explained to Mr Heathfield, that raised the same objection,
my algorithm just stops at the first possible recursive call of main
It doesn't fail at all. It will not follow recursive calls to "main".

That is all.

Obviously, calling "main" recurisvely is very popular here.

Well, not really, but recursive main *is* legal. It seems to me that a
non-portable solution would be free to learn main's caller (e.g. _startup,
or whatever), and probe for that instead. This would allow recursive main
calls to be backtraced properly.

<snip>
 
J

jacob navia

Richard said:
jacob navia said:


Well, not really, but recursive main *is* legal. It seems to me that a
non-portable solution would be free to learn main's caller (e.g. _startup,
or whatever), and probe for that instead. This would allow recursive main
calls to be backtraced properly.

<snip>

In principle you are right, and I considered that. Problem is, my
startup is obviously not written in C but in assembler... It would
have been quite impossible to write it in C actually...

In my context, My assembler doesn't know how to emit debug information,
so there is no debug info.

For startups written in C, your idea would work.
 
C

CBFalconer

jacob said:
Why? It means calling

fp = GetFramePointer();

And that is standard C.

I think you had better return your C book, and your copy of the C
standard, for refunds. Someone has been passing off invalid copies
on you.
 
R

Richard Heathfield

CBFalconer said:
jacob navia wrote:


I think you had better return your C book, and your copy of the C
standard, for refunds. Someone has been passing off invalid copies
on you.

Consider the following code:

x = y();

Is that standard C? If not, why not?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top