From 36de838a892c8c5e7924c0991cab268f7a85ab21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Thu, 2 Aug 2018 13:28:36 +0200 Subject: [PATCH] Add unwinding usage --- report/report.tex | 47 ++++++++++++++++++++++++++++++--- report/src/segfault/gdb_session | 10 +++++++ report/src/segfault/segfault.c | 17 ++++++++++++ 3 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 report/src/segfault/gdb_session create mode 100644 report/src/segfault/segfault.c diff --git a/report/report.tex b/report/report.tex index 11c73d7..d1dcbdb 100644 --- a/report/report.tex +++ b/report/report.tex @@ -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{} diff --git a/report/src/segfault/gdb_session b/report/src/segfault/gdb_session new file mode 100644 index 0000000..3a3187b --- /dev/null +++ b/report/src/segfault/gdb_session @@ -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 + diff --git a/report/src/segfault/segfault.c b/report/src/segfault/segfault.c new file mode 100644 index 0000000..681e45a --- /dev/null +++ b/report/src/segfault/segfault.c @@ -0,0 +1,17 @@ +#include +#include + +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; +} +