Add unwinding usage
This commit is contained in:
parent
be47fefd98
commit
36de838a89
3 changed files with 70 additions and 4 deletions
|
@ -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
|
often stored among the debugging informations of a program, and one common
|
||||||
format of debugging data is DWARF\@.
|
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}
|
\subsection{DWARF format}
|
||||||
|
|
||||||
|
@ -168,10 +211,6 @@ drastically the unwinding process.
|
||||||
\subsection{How big are FDEs?}
|
\subsection{How big are FDEs?}
|
||||||
\todo{}
|
\todo{}
|
||||||
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
\subsection{Unwinding usage and frequency}
|
|
||||||
\todo{}
|
|
||||||
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
\subsection{Unwinding state-of-the-art}
|
\subsection{Unwinding state-of-the-art}
|
||||||
\todo{}
|
\todo{}
|
||||||
|
|
10
report/src/segfault/gdb_session
Normal file
10
report/src/segfault/gdb_session
Normal 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
|
||||||
|
|
17
report/src/segfault/segfault.c
Normal file
17
report/src/segfault/segfault.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue