{"id":10755,"date":"2021-06-21T15:57:58","date_gmt":"2021-06-21T13:57:58","guid":{"rendered":"https:\/\/sii.pl\/blog\/?p=10755"},"modified":"2023-01-17T11:00:19","modified_gmt":"2023-01-17T10:00:19","slug":"call-stack-logger-function-instrumentation-as-a-way-to-trace-programs-flow-of-execution","status":"publish","type":"post","link":"https:\/\/sii.pl\/blog\/en\/call-stack-logger-function-instrumentation-as-a-way-to-trace-programs-flow-of-execution\/","title":{"rendered":"Call Stack Logger &#8211; Function instrumentation as a way to trace program&#8217;s flow of execution"},"content":{"rendered":"\n<p>Have you ever worked on the C++ project with codebase so huge that it is hard to understand what\u2019s really going on when the program executes?&nbsp;Which functions are called from what place? Have you ever been assigned to solve a bug in such a project, being given the logs which by no means bring you any closer to find the root cause of the problem? And the following reproductions fail to provide you with any useful information? If the answer is yes \u2013 please bear with me.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Seeking the solution<\/strong><\/h2>\n\n\n\n<p>I started mulling over what traits of the tool could help me better understand the nature of the problems and bugs that arise. Someone here could ask \u2013 \u201cIsn\u2019t it what debuggers were invented for?\u201d. Of course \u2013 debuggers are a huge help providing that you have an access to the environment on which you can freely run the faulty code and debug it line by line which, to my experience, is not always so evident. In many cases the problem is reproducible only on customer\u2019s infrastructure and they won\u2019t allow you to experiment on a living organism. Or you lack the necessary test setup. Or the source of the problem lies in a third-party library and you don\u2019t have access to its source code. Or the problem is simply too complex to debug it traditionally. Whatever the reason is, the alternative approach may be inevitable. I decided to impose the following constraints on the tool that could satisfy my needs:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>every time the user-defined function is being called, it should be logged to the file;<\/li><li>functions are demangled and presented in logs in a user-friendly way;<\/li><li>logging should be performed out of the box \u2013 without the need for a developer to pollute the code with some extra macros at the beginning of every function;<\/li><li>functions from standard library should be excluded from logging \u2013 we want to avoid the situation when we print hundreds of lines when performing simple operations on the containers;<\/li><li>log the filename and line number from where given function is called;<\/li><li>each function nesting adds an ident, whereas returning from a function removes it \u2013 as a result call stack tree is produced at the runtime giving the visual understanding which function calls which;<\/li><li>the logging mechanism may be activated only if compiled under some debug flag \u2013 this way we will avoid expensive overhead during normal program\u2019s execution;<\/li><li>Linux based.<\/li><\/ul>\n\n\n\n<p>As painstakingly as I browsed the web, I could only find the solutions that met just the single points of my requirements, none of them had them all, so I decided to write my own micro-framework and call it\u00a0<strong>Call Stack Logger.<\/strong><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Starting point<\/strong><\/h2>\n\n\n\n<p>Firstly, we will have to start from the mechanism that will allow us to invoke certain functions every time we enter or exit any function. This may sound a little similar to Aspect-oriented programming but the difference here is that on-enter and on-exit functions have the signatures already defined by the compiler. I\u2019m talking here about gcc feature called\u00a0<strong>function instrumentation<\/strong>. In order to activate it, we have to pass the following compilation flag to gcc:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n-finstrument-functions\n<\/pre><\/div>\n\n\n<p>Now we are free to define the profiling functions in whatever place suitable for us. I just created\u00a0<strong>trace.cpp<\/strong>\u00a0file and put them there:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nextern \"C\" __attribute__((no_instrument_function))\nvoid __cyg_profile_func_enter(void *callee, void *caller) {\n\/\/ Code to be executed just after function entry.\n}\n \nextern \"C\" __attribute__((no_instrument_function))\nvoid __cyg_profile_func_exit(void *callee, void *caller) {\n\/\/ Code to be executed just before function exit.\n}\n<\/pre><\/div>\n\n\n<p>Both functions have two void pointers as the arguments \u2013\u00a0<em>callee<\/em>\u00a0is a pointer to the function that is currently being called, whereas\u00a0<em>caller<\/em>\u00a0is a call site which points to the location (line of code) from where the function is called. The\u00a0<strong>__attribute__((no_instrument_function))<\/strong>\u00a0in the function signature is responsible for excluding the given function from instrumentation. In other words \u2013 we don\u2019t want profiling functions to be instrumented to avoid infinite calls leading to seg faults. To make it look a little more friendly, we can define a macro:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#ifndef NO_INSTRUMENT\n#define NO_INSTRUMENT __attribute__((no_instrument_function))\n#endif\n<\/pre><\/div>\n\n\n<p>As the next thing, we will add a simple logging mechanism to save the history of the functions that are being called.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#include &lt;stdio.h&gt;\n \nstatic FILE *fp_trace;\n \n__attribute__ ((constructor))\nNO_INSTRUMENT\nvoid trace_begin() {\nfp_trace = fopen(&quot;trace.out&quot;, &quot;a&quot;);\n}\n \n__attribute__ ((destructor))\nNO_INSTRUMENT\nvoid trace_end() {\nif(fp_trace != nullptr) {\nfclose(fp_trace);\n}\n}\n<\/pre><\/div>\n\n\n<p>After we have our file pointer initialized we can use it inside\u00a0<strong>__cyg_profile_func_enter<\/strong>\u00a0function. Let\u2019s just log there the address of the function being called:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nif(fp_trace != nullptr) {\nfprintf(fp_trace, \"enter %p\\n\", callee);\n}\n<\/pre><\/div>\n\n\n<p>And some basic example:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#include &lt;iostream&gt;\n#include &lt;vector&gt;\n#include &lt;algorithm&gt;\n \nclass A {\npublic:\nstatic void foo() {\nstd::cout &lt;&lt; &quot;static foo \\n&quot;;\nstd::vector&lt;int&gt; vec { 1, 55, 78, 3, 11, 7, 90 };\nstd::sort(vec.begin(), vec.end());\n}\n};\n \nint main() {\n\/\/ Test logging static member methods.\nA::foo();\nreturn 0;\n}\n<\/pre><\/div>\n\n\n<p>The\u00a0<em>trace.out<\/em>\u00a0file will contain the following:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nenter 0x5602d9622be5\nenter 0x5602d9622f1c\nenter 0x5602d9623084\nenter 0x5602d96233b2\nenter 0x5602d96230fc\nenter 0x5602d962342a\nenter 0x5602d96239b6\nenter 0x5602d9623fa6\n\u2026\n\u2026\n\u2026\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\"><strong>Decoding the addresses<\/strong><\/h2>\n\n\n\n<p>From the output above we are not able to obtain much information as we get only the address of the function that is being called which is not very useful in terms of debugging. Apart from that we can notice a lot of entries in the log file (actually I got 232 lines of those) instead of only 2 \u2013 for&nbsp;<strong>main<\/strong>&nbsp;and&nbsp;<strong>A::foo()<\/strong>. Why is that? To figure it out we will have to decode those addresses. For that purpose we will need&nbsp;<strong>binutils<\/strong>&nbsp;as a prerequisite:<\/p>\n\n\n\n<p><em>$ sudo apt install binutils-dev<\/em><\/p>\n\n\n\n<p>Also we need to add another gcc option in order to have dynamic symbol information in the executable:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n-rdynamic\n<\/pre><\/div>\n\n\n<p>And link against those two:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n-ldl -lbfd\n<\/pre><\/div>\n\n\n<p><em>libdl<\/em>&nbsp;is a Dynamic Link Library which is an interface to the dynamic loader and allows us, among others, to look up the symbols,&nbsp;<em>libbfd<\/em>&nbsp;is Binary File Descriptor library which is used to operate on object files and will be used later.<\/p>\n\n\n\n<p>Let\u2019s firstly do a simple decoding. Include the headers:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#include &lt;dlfcn.h&gt; \/\/ for dladdr\n#include &lt;cxxabi.h&gt; \/\/ for __cxa_demangle\n<\/pre><\/div>\n\n\n<p>and in\u00a0<strong>__cyg_profile_func_enter<\/strong>\u00a0define now the following body instead of previous naive logging of the function address:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nif (fp_trace != nullptr) {\nDl_info info;\nif (dladdr(func, &amp;info)) {\nint status;\nconst char* name;\nchar* demangled = abi::__cxa_demangle(info.dli_sname, nullptr, 0, &amp;status);\nif (status == 0) {\nname = demangled ? demangled : \"&#x5B;not demangled]\";\n} else {\nname = info.dli_sname ? info.dli_sname : \"&#x5B;no dli_sname]\";\n}\nfprintf(fp_trace, \"%s (%s)\\n\", name, info.dli_fname);\nif (demangled) {\ndelete demangled;\ndemangled = nullptr;\n}\n} else {\nfprintf(fp_trace, \"%s\\n\", \"unknown\");\n}\n}\n<\/pre><\/div>\n\n\n<p>After compiling and running the program you\u2019ll see now the following in\u00a0<em>trace.out<\/em>\u00a0file:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nmain (build\/runDemo)\nA::foo() (build\/runDemo)\nstd::allocator::allocator() (build\/runDemo)\n__gnu_cxx::new_allocator::new_allocator() (build\/runDemo)\nstd::vector&lt;int, std::allocator &gt;::vector(std::initializer_list, std::allocator const&amp;) (build\/runDemo)\nstd::_Vector_base&lt;int, std::allocator &gt;::_Vector_base(std::allocator const&amp;) (build\/runDemo)\nstd::_Vector_base&lt;int, std::allocator &gt;::_Vector_impl::_Vector_impl(std::allocator const&amp;) (build\/runDemo)\nstd::allocator::allocator(std::allocator const&amp;) (build\/runDemo)\n__gnu_cxx::new_allocator::new_allocator(__gnu_cxx::new_allocator const&amp;) (build\/runDemo)\nstd::_Vector_base&lt;int, std::allocator &gt;::_Vector_impl_data::_Vector_impl_data() (build\/runDemo)\nstd::initializer_list::end() const (build\/runDemo)\nstd::initializer_list::begin() const (build\/runDemo)\nstd::initializer_list::size() const (build\/runDemo)\nstd::initializer_list::begin() const (build\/runDemo)\nvoid std::vector&lt;int, std::allocator &gt;::_M_range_initialize(int const*, int const*, std::forward_iterator_tag) (build\/runDemo)\nstd::iterator_traits::difference_type std::distance(int const*, int const*) (build\/runDemo)\n\u2026\n\u2026\n\u2026\n<\/pre><\/div>\n\n\n<p>And again in total 232 lines of decoded functions. All those functions come from a standard library and are linked to all of the operations that are invoked during the creation, memory allocation, initialization, sorting and destruction of the vector that we used in our example. We only wrote and called a 3-line function and got such a huge output in return. That\u2019s definitely too much clutter, we have to do something with it because it totally obfuscates our log file. Besides, in most cases we are not interested in details of what standard functions do, as we trust they do exactly what their names suggest. We want to focus on a business logic that our user-defined functions introduce and confine logging mechanism only to them.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Exclude standard library from instrumentation<\/strong><\/h2>\n\n\n\n<p>Happily, gcc provides us with a special instrumentation option:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n-finstrument-functions-exclude-file-list\n<\/pre><\/div>\n\n\n<p>in which we can list the locations of all the headers from C++ standard that we\u2019d like to exclude from instrumentation, like&nbsp;<em>\/usr\/include, \/usr\/include\/c++, \/usr\/include\/x86_64-linux-gnu\/c++\/10<\/em>, etc.<\/p>\n\n\n\n<p>Unfortunately those paths may differ depending on Linux OS or gcc version, so to make it easier I wrote the piece of shell script inside Makefile to automatically find and list all of the locations of C++ standard library headers and pass them to\u00a0<strong>-finstrument-functions-exclude-file-list:<\/strong><\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n# Find C++ Standard Library header files to exclude them from instrumentation\nCPP_STD_INCLUDES=$(shell ( `gcc -print-prog-name=cc1plus` -v &lt; \/dev\/null 2&gt;&amp;1 \\\n| LC_ALL=C sed -ne &#039;\/starts here\/,\/End of\/p&#039; \\\n| grep -o &#039;\/&#x5B;^&quot;]*&#039; ; \\\n`gcc -print-prog-name=cpp` -v &lt; \/dev\/null 2&gt;&amp;1 \\\n| LC_ALL=C sed -ne &#039;\/starts here\/,\/End of\/p&#039; \\\n| grep -o &#039;\/&#x5B;^&quot;]*&#039; ) \\\n| cat | sort | uniq | tr &#039;\\n&#039; &#039;,&#039; | sed &#039;s\/\\(.*\\),\/\\1 \/&#039; | xargs )\n \n# Exclude tracing functions themselves from instrumentation\nCPP_STD_INCLUDES := &quot;${CPP_STD_INCLUDES},include\/callStack.h&quot;\n \nCXXFLAGS = -g -Wall -rdynamic -std=c++17 -finstrument-functions -finstrument-functions-exclude-file-list=$(CPP_STD_INCLUDES)\n<\/pre><\/div>\n\n\n<p>After doing so, recompiling and rerunning the program, we will get the following output:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nmain (build\/runDemo)\nA::foo() (build\/runDemo)\n<\/pre><\/div>\n\n\n<p>which is much closer to what we want to achieve.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Improving the solution<\/strong><\/h2>\n\n\n\n<p>Now we will modify the current solution to introduce further enhancements. Instead of printing binary name we will print the filename and line number from where the function was called as it seems to be much more meaningful information during problem solving. We can also add a timestamp when a particular function is being called. Here is the code responsible for the core mechanism of Call Stack Logger:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#include &quot;callStack.h&quot;\n#include &quot;prettyTime.h&quot;\n#include &quot;unwinder.h&quot;\n#include &lt;bfd.h&gt;\n#include &lt;cxxabi.h&gt; \/\/ for __cxa_demangle\n#include &lt;dlfcn.h&gt; \/\/ for dladdr\n#include &lt;fstream&gt;\n#include &lt;map&gt;\n#include &lt;memory&gt;\n#include &lt;stdexcept&gt;\n#include &lt;unistd.h&gt;\n \nnamespace {\n \nNO_INSTRUMENT\nstd::string demangle_cxa(const std::string&amp; _cxa) {\nint status;\nstd::unique_ptr&lt;char, void (*)(void*)&gt; realname(\nabi::__cxa_demangle(_cxa.data(), nullptr, nullptr, &amp;status), &amp;free);\nif (status != 0) {\nreturn _cxa;\n}\n \nreturn realname ? std::string(realname.get()) : &quot;&quot;;\n}\n \n} \/\/ namespace\n \nnamespace instrumentation {\n \nstruct ResolvedFrame {\nstd::string timestamp;\nstd::optional&lt;void*&gt; callee_address;\nstd::string callee_function_name;\nstd::string caller_filename;\nstd::optional&lt;unsigned int&gt; caller_line_number;\n};\n \nstd::optional&lt;ResolvedFrame&gt; resolve(void* callee_address, void* caller_address) {\nreturn bfdResolver::resolve(callee_address, caller_address);\n}\n \nstd::optional&lt;ResolvedFrame&gt; bfdResolver::resolve(void* callee_address, void* caller_address) {\ncheck_bfd_initialized();\n \nauto maybe_func_name = resolve_function_name(callee_address);\nif (!maybe_func_name) {\nreturn std::nullopt;\n}\nResolvedFrame resolved;\nresolved.callee_function_name = *maybe_func_name;\n \n\/\/ If the code is not changed 6th frame is constant as the execution flow\n\/\/ starting from 6th frame to the top of the stack will look e.g. as follows:\n\/\/ * 6th - instrumentation::FrameUnwinder::unwind_nth_frame\n\/\/ * 5th - bfdResolver::resolve instrumentation::unwind_nth_frame\n\/\/ * 4th - instrumentation::bfdResolver::resolve\n\/\/ * 3rd - instrumentation::resolve\n\/\/ * 2nd - __cyg_profile_func_enter\n\/\/ * 1st - A::foo() --&gt; function we are interested in\n\/\/\n\/\/ Otherwise, if this call flow is altered, frame number must be recalculated.\nCallback callback(caller_address);\nunwind_nth_frame(callback, 6);\n \nauto pair = resolve_filename_and_line(callback.caller);\nresolved.caller_filename = pair.first;\nresolved.caller_line_number = pair.second;\nresolved.timestamp = utils::pretty_time();\n \nreturn std::make_optional(resolved);\n}\n \nstd::optional&lt;std::string&gt; bfdResolver::resolve_function_name(void* address) {\nDl_info info;\ndladdr(address, &amp;info);\nif (info.dli_fbase == nullptr) {\nreturn &quot;&lt;address to object not found&gt;&quot;;\n}\n \nif (!ensure_bfd_loaded(info) || s_bfds.find(info.dli_fbase) == s_bfds.end()) {\nreturn &quot;&lt;could not open object file&gt;&quot;;\n}\nstoredBfd&amp; currBfd = s_bfds.at(info.dli_fbase);\n \nasection* section = currBfd.abfd-&gt;sections;\nconst bool relative = section-&gt;vma &lt; static_cast&lt;uintptr_t&gt;(currBfd.offset);\n \nwhile (section != nullptr) {\nconst intptr_t offset = reinterpret_cast&lt;intptr_t&gt;(address) - (relative ? currBfd.offset :              0) - static_cast&lt;intptr_t&gt;(section-&gt;vma);\n \nif (offset &lt; 0 || static_cast&lt;size_t&gt;(offset) &gt; section-&gt;size) {\nsection = section-&gt;next;\ncontinue;\n}\n \nconst char* file;\nconst char* func;\nunsigned line;\nif (bfd_find_nearest_line(currBfd.abfd.get(), section, currBfd.symbols.get(), offset, &amp;file,                &amp;func, &amp;line))\n{\nauto demangled = demangle_cxa(func);\nreturn demangled.empty() ? std::nullopt : std::make_optional(demangled);\n}\nreturn demangle_cxa(info.dli_sname != nullptr ? info.dli_sname : &quot;&quot;) + &quot; &lt;bfd_error&gt;&quot;;\n}\nreturn &quot;&lt;not sectioned address&gt;&quot;;\n}\n \nstd::pair&lt;std::string, std::optional&lt;unsigned int&gt;&gt; bfdResolver::resolve_filename_and_line(void* address)\n{\n\/\/ Get path and offset of shared object that contains caller address.\nDl_info info;\ndladdr(address, &amp;info);\nif (info.dli_fbase == nullptr) {\nreturn std::make_pair(&quot;&lt;caller address to object not found&gt;&quot;, std::nullopt);\n}\n \nif (!ensure_bfd_loaded(info) || s_bfds.find(info.dli_fbase) == s_bfds.end()) {\nreturn std::make_pair(&quot;&lt;could not open caller object file&gt;&quot;, std::nullopt);\n}\nstoredBfd&amp; currBfd = s_bfds.at(info.dli_fbase);\n \nasection* section = currBfd.abfd-&gt;sections;\nconst bool relative = section-&gt;vma &lt; static_cast&lt;uintptr_t&gt;(currBfd.offset);\n \nwhile (section != nullptr) {\nconst intptr_t offset = reinterpret_cast&lt;intptr_t&gt;(address) - (relative ? currBfd.offset :              0) - static_cast&lt;intptr_t&gt;(section-&gt;vma);\n \nif (offset &lt; 0 || static_cast&lt;size_t&gt;(offset) &gt; section-&gt;size) {\nsection = section-&gt;next;\ncontinue;\n}\nconst char* file;\nconst char* func;\nunsigned int line = 0;\nif (bfd_find_nearest_line(currBfd.abfd.get(), section, currBfd.symbols.get(), offset, &amp;file,                &amp;func, &amp;line))\n{\nif (file != nullptr) {\nreturn std::make_pair(std::string(file), std::make_optional(line));\n}\nreturn std::make_pair(demangle_cxa(func), std::nullopt);\n}\nif (info.dli_sname != nullptr) {\nreturn std::make_pair(demangle_cxa(info.dli_sname) + &quot; &lt;bfd_error&gt;&quot;, std::nullopt);\n}\n}\nreturn std::make_pair(&quot;&lt;not sectioned address&gt;&quot;, std::nullopt);\n}\n \n} \/\/ namespace instrumentation\n<\/pre><\/div>\n\n\n<p>In the above-provided code some of the parts are omitted such as BFD initialization, unwinder implementation, formatting the resolved frame or generating timestamp. It\u2019s because I didn\u2019t want to litter the article with copy-pasting too much code \u2013 the clue is to show the general idea and how it is realised.<\/p>\n\n\n\n<p>The\u00a0<strong>__cyg_profile_func_enter<\/strong>\u00a0function should also be altered:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nextern \"C\" NO_INSTRUMENT\nvoid __cyg_profile_func_enter(void *callee, void *caller) {\nif(fp_trace != nullptr) {\nauto maybe_resolved = instrumentation::resolve(callee, caller);\nif (!maybe_resolved) { return; }\nfprintf(fp_trace, \"%s\\n\", utils::format(*maybe_resolved).c_str());\n}\n}\n<\/pre><\/div>\n\n\n<p>We start from&nbsp;<strong>resolve<\/strong>&nbsp;function taking both callee and caller addresses. Underneath we in turn call separate functions for callee address (<strong>resolve_function_name<\/strong>) and caller address (<strong>resolve_filename_and_line<\/strong>) \u2013 since from the first one we can decode class and function name being called and the second one lets us pull out the information about the call site \u2013 the exact place from where the function was called. If we passed callee instead of caller address to&nbsp;<strong>resolve_filename_and_line<\/strong>&nbsp;function, it would point to the first line of definition of the called function.<\/p>\n\n\n\n<p>Both functions use BFD objects to extract vital information. For the efficiency reasons they are stored in a static map and whenever any of them is required, they can be looked up and accessed without the necessity of creating those objects from scratch. Only when no suitable BFD object is found, it is constructed and inserted into the map.<\/p>\n\n\n\n<p>If&nbsp;<strong>resolve_function_name<\/strong>&nbsp;fails to decode function name we are not proceeding to decode filename and line as well as we back off from logging the whole entry.<\/p>\n\n\n\n<p>It\u2019s also worth mentioning that caller address cannot be resolved in a standard way. There is some kind of bug in instruction pointer value resulting in a wrong call site being returned. Having said that, before passing caller address to&nbsp;<strong>resolve_filename_and_line<\/strong>&nbsp;function we firstly have to unwind nth frame, extract its address and only then pass it to&nbsp;<strong>resolve_filename_and_line<\/strong>. Fortunately the unwinding depth is constant and for current implementation it is 6. Unwinder uses&nbsp;<em>libunwind<\/em>&nbsp;library to achieve that.<\/p>\n\n\n\n<p>Now our trace.out file will look as follows:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n&#x5B;13-06-2021 20:33:49.544] main  (called from: \/build\/glibc-S9d2JN\/glibc-2.27\/csu\/..\/csu\/libc-start.c:310)\n&#x5B;13-06-2021 20:33:49.600] A::foo()  (called from: \/home\/ubuntu\/Desktop\/call-stack-logger.8-final-with-identation-ready\/src\/main.cpp:39)\n<\/pre><\/div>\n\n\n<p>But there is still some room for improvement. To make it much easier to notice the actual flow of execution, we will introduce indentations. We will modify<strong>\u00a0__cyg_profile_func_enter<\/strong>\u00a0and add implementation for<strong>\u00a0__cyg_profile_func_exit<\/strong>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nstatic int current_stack_depth = -1;\nstatic bool last_frame_was_resolved = false;\n \nextern \"C\" NO_INSTRUMENT\nvoid __cyg_profile_func_enter(void *callee, void *caller) {\nif(fp_trace != NULL) {\nlast_frame_was_resolved = false;\nauto maybe_resolved = instrumentation::resolve(callee, caller);\nif (!maybe_resolved) { return; }\nlast_frame_was_resolved = true;\ncurrent_stack_depth++;\nfprintf(fp_trace, \"%s\\n\", utils::format(*maybe_resolved, current_stack_depth).c_str());\n}\n}\n \nextern \"C\" NO_INSTRUMENT\nvoid __cyg_profile_func_exit(void *callee, void *caller) {\nif(fp_trace != nullptr && last_frame_was_resolved) {\ncurrent_stack_depth--;\n}\n}\n<\/pre><\/div>\n\n\n<p>We declare static variable&nbsp;<strong>current_stack_depth<\/strong>&nbsp;which is incremented every time a frame is resolved successfully. We also store the information whether the last frame was resolved which is used to decrement&nbsp;<strong>current_stack_depth<\/strong>&nbsp;variable when we exit from a function.<\/p>\n\n\n\n<p>Now we only need to additionally pass this variable into\u00a0<strong>utils::format<\/strong>\u00a0function and format the string whatever way we would like it to be printed in the log file.<br>Only one more thing \u2013 let\u2019s modify a little bit our sample code to better visualize how the\u00a0<strong>Call Stack Logger<\/strong>\u00a0prints the entries in the log file:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nclass A {\npublic:\nstatic void foo() { std::cout &lt;&lt; &quot;static foo \\n&quot;; }\n};\n \nclass B {\npublic:\nvoid foo() {\nstd::cout &lt;&lt; &quot;non-static foo \\n&quot;;\nstd::vector&lt;int&gt; vec{ 1, 55, 78, 3, 11, 7, 90 };\nstd::sort(vec.begin(), vec.end());\nA::foo();\n}\n};\n \nint fibonacci(int n) {\nif (n &lt;= 1)\nreturn n;\nreturn fibonacci(n - 1) + fibonacci(n - 2);\n}\n \nint main() {\n\/\/ Test logging static member methods.\nA::foo();\n \n\/\/ Test logging non-static member methods with calls to std\n\/\/ functions\/containers (which should not be instrumented).\nB b;\nb.foo();\nfibonacci(6);\nA::foo();\n}\n<\/pre><\/div>\n\n\n<p>And here is the final result<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/06\/call-stack-logger-capture-1.gif\"><img decoding=\"async\" width=\"1024\" height=\"778\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/06\/call-stack-logger-capture-1-1024x778.gif\" alt=\"\" class=\"wp-image-18356\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/06\/call-stack-logger-capture-1-1024x778.gif 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/06\/call-stack-logger-capture-1-300x228.gif 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/06\/call-stack-logger-capture-1-768x584.gif 768w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Summary<\/strong><\/h2>\n\n\n\n<p>As we can see, the output from the framework clearly visualizes the program\u2019s flow of execution in the form of a tree structure. You may have noticed that as far as caller address is concerned, we only retrieve a filename and a line number from it. We are not decoding the name of the caller function, only the name of the callee function as the tree structure explicitly shows us the whole call hierarchy of the program, so we don\u2019t need to additionally decode it and we are still able to say which function called which.<\/p>\n\n\n\n<p>The solution introduced in&nbsp;<strong>Call Stack Logger<\/strong>&nbsp;may constitute an alternative to the standard debugging in many situations like the one when access to the production environment is limited and it is worth having such a mechanism as a backup in our pool of problem-solving tools.You can access&nbsp;<strong>Call Stack Logger<\/strong>&nbsp;on Github under:<a href=\"https:\/\/github.com\/TomaszAugustyn\/call-stack-logger\" target=\"_blank\" rel=\"noreferrer noopener\" rel=\"nofollow\" >https:\/\/github.com\/TomaszAugustyn\/call-stack-logger<\/a><\/p>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;10755&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;11&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;5&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;11&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;5\\\/5 ( votes: 11)&quot;,&quot;size&quot;:&quot;18&quot;,&quot;title&quot;:&quot;Call Stack Logger - Function instrumentation as a way to trace program\\u0026#039;s flow of execution&quot;,&quot;width&quot;:&quot;139.5&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} ( {votes}: {count})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 139.5px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 14.4px;\">\n            5\/5 ( votes: 11)    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>Have you ever worked on the C++ project with codebase so huge that it is hard to understand what\u2019s really &hellip; <a class=\"continued-btn\" href=\"https:\/\/sii.pl\/blog\/en\/call-stack-logger-function-instrumentation-as-a-way-to-trace-programs-flow-of-execution\/\">Continued<\/a><\/p>\n","protected":false},"author":292,"featured_media":10780,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","inline_featured_image":false,"footnotes":""},"categories":[1320],"tags":[1377,1385,1375,1379,1376,1383,1381],"class_list":["post-10755","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-hard-development","tag-binutils-en","tag-cpp-en","tag-embedded-competency-center-en","tag-function-instrumentation-en","tag-gcc-en","tag-logging-en","tag-trace-en"],"acf":[],"aioseo_notices":[],"republish_history":[],"featured_media_url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/06\/cpp.jpg","category_names":["Hard development"],"_links":{"self":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts\/10755"}],"collection":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/users\/292"}],"replies":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/comments?post=10755"}],"version-history":[{"count":3,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts\/10755\/revisions"}],"predecessor-version":[{"id":18359,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts\/10755\/revisions\/18359"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/media\/10780"}],"wp:attachment":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/media?parent=10755"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/categories?post=10755"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/tags?post=10755"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}