Add unwinding usage

This commit is contained in:
Théophile Bastian 2018-08-02 13:28:36 +02:00
parent be47fefd98
commit 36de838a89
3 changed files with 70 additions and 4 deletions

View file

@ -87,6 +87,49 @@ necessary to have additional data to perform stack unwinding. This data is
often stored among the debugging informations of a program, and one common
format of debugging data is DWARF\@.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{Unwinding usage and frequency}
Stack unwinding is a more common operation that one might think at first. The
most commonly thought use-case is simply to get a stack trace of a program, and
provide a debugger with the information it needs: for instance, when inspecting
a stack trace in \prog{gdb}, it is quite common to jump to a previous frame:
\lstinputlisting{src/segfault/gdb_session}
To be able to do this, \texttt{gdb} must be able to restore \lstc{fct_a}'s
context, by unwinding \lstc{fct_b}'s frame.
\medskip
Yet, stack unwinding (and thus debugging data) \emph{is not limited to
debugging}.
Another common usage is profiling. A profiling tool, such as \prog{perf} under
Linux, is used to measure and analyze in which functions a program spends its
time, identify bottlenecks and find out which parts are critical to optimize.
To do so, modern profilers pause the traced program at regular, short
intervals, inspect their stack, and determine which function is currently being
run. They also often perform a stack unwinding to determine the call path to
this function, to determine which function indirectly takes time: \eg, a
function \lstc{fct_a} can call both \lstc{fct_b} and \lstc{fct_c}, which are
quite heavy; spend practically no time directly in \lstc{fct_a}, but spend a
lot of time in calls to the other two functions that were made by \lstc{fct_a}.
Exception handling also requires a stack unwinding mechanism in most languages.
Indeed, an exception is completely different from a \lstc{return}: while the
latter returns to the previous function, the former can be caught by virtually
any function in the call path, at any point of the function. It is thus
necessary to be able to unwind frames, one by one, until a suitable
\lstc{catch} block is found. The C++ language, for one, includes a
stack-unwinding library similar to \prog{libunwind} in its runtime.
In both of these two previous cases, performance \emph{can} be a problem. In
the latter, a slow unwinding directly impacts the overall program performance,
particularly if a lot of exceptions are thrown and caught far away in their
call path. In the former, profiling \emph{is} performance-heavy and often quite
slow when analyzing large programs anyway.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{DWARF format}
@ -168,10 +211,6 @@ drastically the unwinding process.
\subsection{How big are FDEs?}
\todo{}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{Unwinding usage and frequency}
\todo{}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{Unwinding state-of-the-art}
\todo{}

View file

@ -0,0 +1,10 @@
(gdb) backtrace
#0 0x0000555555554625 in fct_b (m=0x5c) at segfault.c:5
#1 0x0000555555554663 in fct_a (n=42) at segfault.c:10
#2 0x0000555555554674 in main () at segfault.c:14
(gdb) frame 1
#1 0x0000555555554663 in fct_a (n=42) at segfault.c:10
10 fct_b((int*)(some_fct_a_var + 8));
(gdb) print some_fct_a_var
$1 = 84

View file

@ -0,0 +1,17 @@
#include <stdio.h>
#include <stdint.h>
void fct_b(int* m) {
printf("%l\n", *m);
}
void fct_a(int n) {
uintptr_t some_fct_a_var = n * 2;
fct_b((int*)(some_fct_a_var + 8));
}
int main(void) {
fct_a(42);
return 0;
}