obtaining callstack level

S

seannakasone

Is there a way to get the callstack level in c++? for example, take
the following code:

void call3() {
//callstack level would be 3
}

void call2() {
//callstack level would be 2
call3();
}

int main() {
//callstack level would be 1
call2();
return 0;
}

And I don't mean obtaining it from using a debugger and looking at the
callstack. I'm mean obtaining it programmatically.
 
A

Alf P. Steinbach

* (e-mail address removed):
Is there a way to get the callstack level in c++? for example, take
the following code:

void call3() {
//callstack level would be 3
}

void call2() {
//callstack level would be 2
call3();
}

int main() {
//callstack level would be 1
call2();
return 0;
}

And I don't mean obtaining it from using a debugger and looking at the
callstack. I'm mean obtaining it programmatically.

Instrument the functions, that is, add code that maintains a call stack
level count.

That can easily be done via the construction and destruction of a local
object in each function.

For example, off the cuff,

class CallTracer
{
private:
static std::size_t& theLevel()
{
static std::size_t theLevelVariable = 0;
return theLevelVariable;
}

public:
CallTracer(){ ++theLevel(); }
~CallTracer(){ --theLevel(); }
static std::size_t level() { return theLevel(); }
};

which you'd use like

void foo3()
{
CallTracer tracer;
std::cout
<< "foo3, at call level " << CallTracer::level()
<< std::endl;
}

This counts the calls of functions instrumented this way; if that's not
enough, use a decent debugger.
 
D

Diomidis Spinellis

Is there a way to get the callstack level in c++? for example, take
the following code:
[...]

And I don't mean obtaining it from using a debugger and looking at the
callstack. I'm mean obtaining it programmatically.

If you're not interested in a precise number, but can accept a measure
that would indicate the stack depth in bytes you can obtain the address
of one of the function's plain local variables through the & operator
and cast it to a char * pointer. I've used this technique to track the
use of stack space during the runtime of a program; see the snapshots at
<http://www.spinellis.gr/codequality/stack.gif?clcpp>. This technique
is not portable, but will work on most computer architectures.
 
M

Michiel.Salters

If you're not interested in a precise number, but can accept a measure
that would indicate the stack depth in bytes you can obtain the address
of one of the function's plain local variables through the & operator
and cast it to a char * pointer. I've used this technique to track the
use of stack space during the runtime of a program; see the snapshots at
<http://www.spinellis.gr/codequality/stack.gif?clcpp>. This technique
is not portable, but will work on most computer architectures.

The Itanium of course being one of those where it doesn't work; it has
two
stacks. C++ doesn't care; you can still implement Standard C++ on it.
Yet such features explain why Standard C++ doesn't have "a" callstack
level; it doesn't really make sense.

HTH,
Michiel Salters
 
A

Alf P. Steinbach

* (e-mail address removed):
The Itanium of course being one of those where it doesn't work; it has
two
stacks. C++ doesn't care; you can still implement Standard C++ on it.
Yet such features explain why Standard C++ doesn't have "a" callstack
level; it doesn't really make sense.

Although a bit off-topic, would you care to elaborate? It's a long time
since I did any assembly level programming, but the only case of /two/
stacks I'm familiar with is the normal call stack versus system
exception handling stack (as on e.g. MC68K). I can't see how ordinary
function execution could utilize two different stacks, except if the
Itanium has one stack for return addresses and another for arguments?
 
I

Ian Collins

The Itanium of course being one of those where it doesn't work; it has
two
stacks. C++ doesn't care; you can still implement Standard C++ on it.
Yet such features explain why Standard C++ doesn't have "a" callstack
level; it doesn't really make sense.
That's assuming the Itanium user is subscribed to this group :)
 
D

Diomidis Spinellis

Alf said:
* (e-mail address removed):

Although a bit off-topic, would you care to elaborate? It's a long time

My understanding is that the Itanium has SPARC-like register windows
backed by a hardware assisted so-called register stack engine (RSE).
When a function calls another arguments are placed into the general
purpose registers Gr32-Gr128. Through an alloc instruction the called
function specifies the layout of its stack frame (input registers, local
registers, and output registers). This has the effect of mapping the
calling function's output registers to the called function's input
registers, and thus passing the function arguments through the registers
without any overhead. The registers get renamed at the point of the
call, so that all functions will always see their registers starting at
Gr32. When the 96 general purpose registers used for implementing the
scheme run out, a spill will burst-write registers to the special
register stack; later when the stack is unwound a fill will read the
contents back from memory.

Why are two stacks needed? My guess is that it was simpler to optimize
the RSE hardware (for example caching or the burst mode alignment
requirements) if the return addresses and the normal stack push/pop
instructions didn't interfere with the register spill stack.

You can find a few more details in this article:
http://www.intel.com/cd/ids/developer/asmo-na/eng/20314.htm?page=1
 
I

Ian Collins

Diomidis said:
Is there a way to get the callstack level in c++? for example, take
the following code:

[...]

And I don't mean obtaining it from using a debugger and looking at the
callstack. I'm mean obtaining it programmatically.


If you're not interested in a precise number, but can accept a measure
that would indicate the stack depth in bytes you can obtain the address
of one of the function's plain local variables through the & operator
and cast it to a char * pointer. I've used this technique to track the
use of stack space during the runtime of a program; see the snapshots at
<http://www.spinellis.gr/codequality/stack.gif?clcpp>. This technique
is not portable, but will work on most computer architectures.

Do you get artificially high values?

I was thinking of the case where the compiler could use registers for
local variables. Until you start taking their address which would
defeat that optimisation.
 
A

Alf P. Steinbach

* Diomidis Spinellis:

Thanks. It's, well, perplexing. They have cached the stack, but turned
the conceptual picture 180 degrees around so that it seems main memory
is used to relieve what actually is the cache, and instead of making
this transparent they require direct support from the machine code, and
therefore (one must presume) limited the mechanism to 64-bit code.

Hm.

Hopefully it won't influence ordinary C++ programming, but I saw nothing
in that article that discussed how the architecture meshed with common
C++ techniques such as using a pointer or reference to a local variable.
 
D

Diomidis Spinellis

Ian said:
Diomidis said:
Is there a way to get the callstack level in c++? for example, take
the following code:
[...]

And I don't mean obtaining it from using a debugger and looking at the
callstack. I'm mean obtaining it programmatically.

If you're not interested in a precise number, but can accept a measure
that would indicate the stack depth in bytes you can obtain the address
of one of the function's plain local variables through the & operator
and cast it to a char * pointer. I've used this technique to track the
use of stack space during the runtime of a program; see the snapshots at
<http://www.spinellis.gr/codequality/stack.gif?clcpp>. This technique
is not portable, but will work on most computer architectures.

Do you get artificially high values?

I was thinking of the case where the compiler could use registers for
local variables. Until you start taking their address which would
defeat that optimisation.

I assume by "high values" you mean high memory addresses, i.e. low stack
usage values. At most the measure will be off by the number of the
processor's registers, because even when the compiler allocates local
variables to registers these have to be stored on the stack when another
function gets called. In the particular case I wanted to profile the
stack's growth across millions of function invocations; an offset of a
few tens of bytes would hardly matter. Function inlining is also
affecting the results (I used a modified call graph profiler function
prologue to write the values to a file, so inlined functions weren't
tracked at all.)
 
I

Ian Collins

Diomidis said:
Ian said:
Diomidis said:
(e-mail address removed) wrote:

Is there a way to get the callstack level in c++? for example, take
the following code:


[...]

And I don't mean obtaining it from using a debugger and looking at the
callstack. I'm mean obtaining it programmatically.


If you're not interested in a precise number, but can accept a measure
that would indicate the stack depth in bytes you can obtain the address
of one of the function's plain local variables through the & operator
and cast it to a char * pointer. I've used this technique to track the
use of stack space during the runtime of a program; see the snapshots at
<http://www.spinellis.gr/codequality/stack.gif?clcpp>. This technique
is not portable, but will work on most computer architectures.


Do you get artificially high values?

I was thinking of the case where the compiler could use registers for
local variables. Until you start taking their address which would
defeat that optimisation.


I assume by "high values" you mean high memory addresses, i.e. low stack
usage values. At most the measure will be off by the number of the
processor's registers, because even when the compiler allocates local
variables to registers these have to be stored on the stack when another
function gets called. In the particular case I wanted to profile the
stack's growth across millions of function invocations; an offset of a
few tens of bytes would hardly matter. Function inlining is also
affecting the results (I used a modified call graph profiler function
prologue to write the values to a file, so inlined functions weren't
tracked at all.)
That's a good point, I forgot about calling other functions.

The only place this would be a problem would be on a Sparc like
processor that uses register wheels to pass parameters.

I've used similar techniques to this, along with high water marks.
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top