BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bsls_stackaddressutil.h
Go to the documentation of this file.
1/// @file bsls_stackaddressutil.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bsls_stackaddressutil.h -*-C++-*-
8#ifndef INCLUDED_BSLS_STACKADDRESSUTIL
9#define INCLUDED_BSLS_STACKADDRESSUTIL
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup bsls_stackaddressutil bsls_stackaddressutil
15/// @brief Provide a utility for obtaining return addresses from the stack.
16/// @addtogroup bsl
17/// @{
18/// @addtogroup bsls
19/// @{
20/// @addtogroup bsls_stackaddressutil
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bsls_stackaddressutil-purpose"> Purpose</a>
25/// * <a href="#bsls_stackaddressutil-classes"> Classes </a>
26/// * <a href="#bsls_stackaddressutil-description"> Description </a>
27/// * <a href="#bsls_stackaddressutil-usage"> Usage </a>
28/// * <a href="#bsls_stackaddressutil-example-1-obtaining-return-addresses-and-verifying-their-validity"> Example 1: Obtaining Return Addresses and Verifying Their Validity </a>
29/// * <a href="#bsls_stackaddressutil-example-2-obtaining-a-cheapstack"> Example 2: Obtaining a "Cheapstack" </a>
30///
31/// # Purpose {#bsls_stackaddressutil-purpose}
32/// Provide a utility for obtaining return addresses from the stack.
33///
34/// # Classes {#bsls_stackaddressutil-classes}
35///
36/// - bsls::StackAddressUtil: utilities for obtaining addresses from the stack
37///
38/// @see balst_stacktraceutil
39///
40/// # Description {#bsls_stackaddressutil-description}
41/// This component defines a `struct`, `bsls::StackAddressUtil`,
42/// that provides a namespace for a function, `getStackAddresses`, that
43/// populates an array with an ordered sequence of return addresses from the
44/// current thread's function call stack. Each return address points to the
45/// (text) memory location of the first instruction to be executed upon
46/// returning from a called routine.
47///
48/// This component also provides a function, `formatCheapStack`, that builds a
49/// current stack trace and formats it with instructions on how to use
50/// `showfunc.tsk` to print out a stack trace matching where this function was
51/// called. This is a Bloomberg standard "cheapstack" output.
52///
53/// ## Usage {#bsls_stackaddressutil-usage}
54///
55///
56/// In this section we show the intended usage of this component.
57///
58/// ### Example 1: Obtaining Return Addresses and Verifying Their Validity {#bsls_stackaddressutil-example-1-obtaining-return-addresses-and-verifying-their-validity}
59///
60///
61/// In the following example we demonstrate how to obtain the sequence of
62/// function return addresses from the stack using `getStackAddresses`.
63///
64/// First, we define `AddressEntry`, which will contain a pointer to the
65/// beginning of a function and an index corresponding to the function. The `<`
66/// operator is defined so that a vector of address entries can be sorted in the
67/// order of the function addresses. The address entries will be populated so
68/// that the entry containing `&funcN` when `N` is an integer will have an index
69/// of `N`.
70/// @code
71/// struct AddressEntry {
72/// void *d_funcAddress;
73/// int d_index;
74///
75/// // CREATORS
76/// AddressEntry(void *funcAddress, int index)
77/// : d_funcAddress(funcAddress)
78/// , d_index(index)
79/// // Create an 'AddressEntry' object and initialize it with the
80/// // specified 'funcAddress' and 'index'.
81/// {}
82///
83/// bool operator<(const AddressEntry& rhs) const
84/// // Return 'true' if the address stored in the object is lower than
85/// // the address stored in 'rhs' and 'false' otherwise. Note that
86/// // this is a member function for brevity, it only exists to
87/// // facilitate sorting 'AddressEntry' objects in a vector.
88/// {
89/// return d_funcAddress < rhs.d_funcAddress;
90/// }
91/// };
92/// @endcode
93/// Then, we define `entries`, a vector of address entries. This will be
94/// populated such that a given entry will contain function address `&funcN` and
95/// index `N`. The elements will be sorted according to function address.
96/// @code
97/// bsl::vector<AddressEntry> entries;
98/// @endcode
99/// Next, we define `findIndex`:
100/// @code
101/// static int findIndex(const void *retAddress)
102/// // Return the index of the address entry whose function uses an
103/// // instruction located at specified 'retAddress'. The behavior is
104/// // undefined unless 'retAddress' is the address of an instruction in
105/// // use by a function referred to by an address entry in 'entries'.
106/// {
107/// unsigned int u = 0;
108/// while (u < entries.size()-1 &&
109/// retAddress >= entries[u+1].d_funcAddress) {
110/// ++u;
111/// }
112/// assert(u < entries.size());
113/// assert(retAddress >= entries[u].d_funcAddress);
114///
115/// int ret = entries[u].d_index;
116///
117/// if (veryVerbose) {
118/// P_(retAddress) P_(entries[u].d_funcAddress) P(ret);
119/// }
120///
121/// return ret;
122/// }
123/// @endcode
124/// Then, we define a volatile global variable that we will use in calculation
125/// to discourage compiler optimizers from inlining:
126/// @code
127/// volatile unsigned int volatileGlobal = 1;
128/// @endcode
129/// Next, we define a set of functions that will be called in a nested fashion
130/// -- `func5` calls `func4` who calls `fun3` and so on. In each function, we
131/// will perform some inconsequential instructions to prevent the compiler from
132/// inlining the functions.
133///
134/// Note that we know the `if` conditions in these 5 subroutines never evaluate
135/// to `true`, however, the optimizer cannot figure that out, and that will
136/// prevent it from inlining here.
137/// @code
138/// static unsigned int func1();
139/// static unsigned int func2()
140/// {
141/// if (volatileGlobal > 10) {
142/// return (volatileGlobal -= 100) * 2 * func2(); // RETURN
143/// }
144/// else {
145/// return volatileGlobal * 2 * func1(); // RETURN
146/// }
147/// }
148/// static unsigned int func3()
149/// {
150/// if (volatileGlobal > 10) {
151/// return (volatileGlobal -= 100) * 2 * func3(); // RETURN
152/// }
153/// else {
154/// return volatileGlobal * 3 * func2(); // RETURN
155/// }
156/// }
157/// static unsigned int func4()
158/// {
159/// if (volatileGlobal > 10) {
160/// return (volatileGlobal -= 100) * 2 * func4(); // RETURN
161/// }
162/// else {
163/// return volatileGlobal * 4 * func3(); // RETURN
164/// }
165/// }
166/// static unsigned int func5()
167/// {
168/// if (volatileGlobal > 10) {
169/// return (volatileGlobal -= 100) * 2 * func5(); // RETURN
170/// }
171/// else {
172/// return volatileGlobal * 5 * func4(); // RETURN
173/// }
174/// }
175/// static unsigned int func6()
176/// {
177/// if (volatileGlobal > 10) {
178/// return (volatileGlobal -= 100) * 2 * func6(); // RETURN
179/// }
180/// else {
181/// return volatileGlobal * 6 * func5(); // RETURN
182/// }
183/// }
184/// @endcode
185/// Next, we define the macro FUNC_ADDRESS, which will take a parameter of
186/// `&<function name>` and return a pointer to the actual beginning of the
187/// function's code, which is a non-trivial and platform-dependent exercise.
188/// Note: this doesn't work on Windows for global routines.
189/// @code
190/// #if defined(BSLS_PLATFORM_OS_AIX)
191/// # define FUNC_ADDRESS(p) (((void **) (void *) (p))[0])
192/// #else
193/// # define FUNC_ADDRESS(p) ((void *) (p))
194/// #endif
195/// @endcode
196/// Then, we define `func1`, the last function to be called in the chain of
197/// nested function calls. `func1` uses
198/// `bsls::StackAddressUtil::getStackAddresses` to get an ordered sequence of
199/// return addresses from the current thread's function call stack and uses the
200/// previously defined `findIndex` function to verify those address are correct.
201/// @code
202/// unsigned int func1()
203/// // Call 'getAddresses' and verify that the returned set of addresses
204/// // matches our expectations.
205/// {
206/// @endcode
207/// Next, we populate and sort the `entries` table, a sorted array of
208/// `AddressEntry` objects that will allow `findIndex` to look up within which
209/// function a given return address can be found.
210/// @code
211/// entries.clear();
212/// entries.push_back(AddressEntry(0, 0));
213/// entries.push_back(AddressEntry(FUNC_ADDRESS(&func1), 1));
214/// entries.push_back(AddressEntry(FUNC_ADDRESS(&func2), 2));
215/// entries.push_back(AddressEntry(FUNC_ADDRESS(&func3), 3));
216/// entries.push_back(AddressEntry(FUNC_ADDRESS(&func4), 4));
217/// entries.push_back(AddressEntry(FUNC_ADDRESS(&func5), 5));
218/// entries.push_back(AddressEntry(FUNC_ADDRESS(&func6), 6));
219/// bsl::sort(entries.begin(), entries.end());
220/// @endcode
221/// Then, we obtain the stack addresses with `getStackAddresses`.
222/// @code
223/// enum { BUFFER_LENGTH = 100 };
224/// void *buffer[BUFFER_LENGTH];
225/// bsl::memset(buffer, 0, sizeof(buffer));
226/// int numAddresses = bsls::StackAddressUtil::getStackAddresses(
227/// buffer,
228/// BUFFER_LENGTH);
229/// assert(numAddresses >= (int) entries.size());
230/// assert(numAddresses < BUFFER_LENGTH);
231/// assert(0 != buffer[numAddresses-1]);
232/// assert(0 == buffer[numAddresses]);
233/// @endcode
234/// Finally, we go through several of the first addresses returned in `buffer`
235/// and verify that each address corresponds to the routine we expect it to.
236///
237/// Note that on some, but not all, platforms there is an extra "narcissistic"
238/// frame describing `getStackAddresses` itself at the beginning of `buffer`.
239/// By starting our iteration through `buffer` at `k_IGNORE_FRAMES`, we
240/// guarantee that the first address we examine will be in `func1` on all
241/// platforms.
242/// @code
243/// int funcIdx = 1;
244/// int stackIdx = bsls::StackAddressUtil::k_IGNORE_FRAMES;
245/// for (; funcIdx < (int) entries.size(); ++funcIdx, ++stackIdx) {
246/// assert(stackIdx < numAddresses);
247/// assert(funcIdx == findIndex(buffer[stackIdx]));
248/// }
249///
250/// if (testStatus || veryVerbose) {
251/// Q(Entries:);
252/// for (unsigned int u = 0; u < entries.size(); ++u) {
253/// P_(u); P_((void *) entries[u].d_funcAddress);
254/// P(entries[u].d_index);
255/// }
256///
257/// Q(Stack:);
258/// for (int i = 0; i < numAddresses; ++i) {
259/// P_(i); P(buffer[i]);
260/// }
261/// }
262///
263/// return volatileGlobal;
264/// }
265/// @endcode
266///
267/// ### Example 2: Obtaining a "Cheapstack" {#bsls_stackaddressutil-example-2-obtaining-a-cheapstack}
268///
269///
270/// In this example we demonstrate how to use `formatCheapStack` to generate a
271/// string containing the current stack trace and instructions on how to print
272/// it out from `showfunc.tsk`. Note that `showfunc.tsk` is a Bloomberg tool
273/// that, when given an executable along with a series of function addresses
274/// from a process that was running that executable, will print out a
275/// human-readable stack trace with the names of the functions being called in
276/// that stack trace.
277///
278/// First, we define our function where we want to format the stack:
279/// @code
280/// struct MyTest {
281/// static void printCheapStack()
282/// {
283/// char str[128];
284/// bsls::StackAddressUtil::formatCheapStack(str, 128);
285/// printf("%s", str);
286/// }
287/// };
288/// @endcode
289/// Calling this function will then result in something like this being printed
290/// to standard output:
291/// @code
292/// Please run "/bb/bin/showfunc.tsk <binary_name_here> 403308 402641 ...
293/// ... 3710C1ED1D 400F49" to see the stack trace.
294/// @endcode
295/// Then, if you had encountered this output running the binary "mybinary.tsk",
296/// you could see your stack trace by running this command:
297/// @code
298/// /bb/bin/showfunc.tsk mybinary.tsk 403308 402641 3710C1ED1D 400F49
299/// @endcode
300/// This will produce output like this:
301/// @code
302/// 0x403308 _ZN6MyTest15printCheapStackEv + 30
303/// 0x402641 main + 265
304/// 0x3710c1ed1d ???
305/// 0x400f49 ???
306/// @endcode
307/// telling you that `MyTest::printCheapStack` was called directly from `main`.
308/// Note that if you had access to the binary name that was invoked, then that
309/// could be provided as the optional last argument to `printCheapStack` to get
310/// a `showfunc.tsk` command that can be more easily invoked, like this:
311/// @code
312/// struct MyTest2 {
313/// static void printCheapStack()
314/// {
315/// char str[128];
316/// bsls::StackAddressUtil::formatCheapStack(str, 128, "mybinary.tsk");
317/// printf("%s", str);
318/// }
319/// };
320/// @endcode
321/// resulting in output that looks like this:
322/// @code
323/// Please run "/bb/bin/showfunc.tsk mybinary.tsk 403308 402641 3710C1ED1D ...
324/// ... 400F49" to see the stack trace.
325/// @endcode
326/// @}
327/** @} */
328/** @} */
329
330/** @addtogroup bsl
331 * @{
332 */
333/** @addtogroup bsls
334 * @{
335 */
336/** @addtogroup bsls_stackaddressutil
337 * @{
338 */
339
340#include <bsls_platform.h>
341
342 // ============================
343 // class bsls::StackAddressUtil
344 // ============================
345
346
347namespace bsls {
348
349/// This struct provides a namespace for the function to obtain return
350/// addresses from the stack.
352
353 // On some platforms, 'getStackAddresses' finds a frame representing
354 // 'getStackAddresses' itself. This frame is usually unwanted.
355 // 'k_IGNORE_FRAMES' instructs the caller as to whether the first frame is
356 // such an unwanted frame.
357
358#if defined(BSLS_PLATFORM_OS_LINUX) || defined(BSLS_PLATFORM_OS_DARWIN)
359 enum { k_IGNORE_FRAMES = 1 };
360#else
361 enum { k_IGNORE_FRAMES = 0 };
362#endif
363
364 // CLASS METHODS
365
366 /// Get an sequence of return addresses from the current thread's
367 /// function call stack, ordered from most recent call to least recent,
368 /// and load them into the specified array `*buffer`, which is at least
369 /// the specified `maxFrames` in length. A return address is an address
370 /// stored on the stack that points to the first instruction that will
371 /// be executed after the called subroutine returns. If there are more
372 /// than `maxFrames` frames on the stack, only the return addresses for
373 /// the `maxFrames` most recent routine calls are stored. When this
374 /// routine completes, `buffer` will contain an ordered sequence of
375 /// return addresses, sorted such that recent calls occur in the array
376 /// before calls that took place before them. Return the number of
377 /// stack frames stored into `buffer` on success, and a negative value
378 /// otherwise. The behavior is undefined unless `maxFrames >= 0` and
379 /// `buffer` has room for at least `maxFrames` addresses. Note that
380 /// this routine may fill `buffer` with garbage if the stack is corrupt,
381 /// or on Windows if some stack frames represent optimized routines.
382 static
383 int getStackAddresses(void **buffer,
384 int maxFrames);
385
386 /// Load the specified `output` buffer having the specified `length`
387 /// with the Bloomberg standard "cheapstack" contents as a
388 /// null-terminated string. On successfully obtaining the current call
389 /// stack, this will be instructions on how to run the Bloomberg tool
390 /// `showfunc.tsk` (with the optionally specified `taskname`, otherwise
391 /// with an attempt to obtain the system-specific process name) to get
392 /// details of the current call stack where `formatCheapStack` was
393 /// called. On failure, text indicating that the call stack was not
394 /// obtainable will be written to `output`. If `length` is not long
395 /// enough for the entire output it will be truncated. The behavior is
396 /// undefined unless `0 <= length` and `output` has the capacity for at
397 /// least `length` bytes.
398 static
399 void formatCheapStack(char *output, int length, const char *taskname = 0);
400};
401
402} // close package namespace
403
404
405#endif
406
407// ----------------------------------------------------------------------------
408// Copyright 2018 Bloomberg Finance L.P.
409//
410// Licensed under the Apache License, Version 2.0 (the "License");
411// you may not use this file except in compliance with the License.
412// You may obtain a copy of the License at
413//
414// http://www.apache.org/licenses/LICENSE-2.0
415//
416// Unless required by applicable law or agreed to in writing, software
417// distributed under the License is distributed on an "AS IS" BASIS,
418// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
419// See the License for the specific language governing permissions and
420// limitations under the License.
421// ----------------------------- END-OF-FILE ----------------------------------
422
423/** @} */
424/** @} */
425/** @} */
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
Definition bdlt_iso8601util.h:691
Definition bsls_stackaddressutil.h:351
static int getStackAddresses(void **buffer, int maxFrames)
@ k_IGNORE_FRAMES
Definition bsls_stackaddressutil.h:361
static void formatCheapStack(char *output, int length, const char *taskname=0)