• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

STEllAR-GROUP / hpx / #882

31 Aug 2023 07:44PM UTC coverage: 41.798% (-44.7%) from 86.546%
#882

push

19442 of 46514 relevant lines covered (41.8%)

126375.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.57
/libs/core/debugging/src/backtrace.cpp
1
//  Copyright (c) 2011 Bryce Lelbach
2
//  Copyright (c) 2011-2025 Hartmut Kaiser
3
//  Copyright (c) 2010 Artyom Beilis (Tonkikh)
4
//
5
//  SPDX-License-Identifier: BSL-1.0
6
//  Distributed under the Boost Software License, Version 1.0. (See
7
//  accompanying file LICENSE_1_0.txt or copy at
8
//  http://www.boost.org/LICENSE_1_0.txt)
9

10
#include <hpx/config.hpp>
11

12
#if defined(HPX_HAVE_STACKTRACES)
13

14
#include <hpx/debugging/backtrace/backtrace.hpp>
15

16
#if (defined(__linux) || defined(__APPLE__) || defined(__sun)) &&              \
17
    (!defined(__ANDROID__) || !defined(ANDROID))
18
#if defined(__GLIBC__)
19
#define HPX_HAVE_EXECINFO
20
#endif
21
#define HPX_HAVE_DLFCN
22
#if defined(__GNUC__) && !defined(__clang__)
23
#define HPX_HAVE_UNWIND
24
#endif
25
#endif
26

27
#if defined(__GNUC__) && !defined(__bgq__)
28
#define HPX_HAVE_ABI_CXA_DEMANGLE
29
#endif
30

31
#ifdef HPX_HAVE_EXECINFO
32
#include <execinfo.h>
33
#endif
34

35
#ifdef HPX_HAVE_ABI_CXA_DEMANGLE
36
#include <cxxabi.h>
37
#endif
38

39
#ifdef HPX_HAVE_DLFCN
40
#include <dlfcn.h>
41
#endif
42
#ifdef HPX_HAVE_UNWIND
43
#include <unwind.h>
44
#endif
45

46
#include <cstddef>
47
#include <cstdint>
48
#include <cstdlib>
49
#include <cstring>
50
#include <iomanip>
51
#include <ostream>
52
#include <sstream>
53
#include <string>
54
#include <utility>
55
#include <vector>
56

57
#if defined(HPX_MSVC)
58
#include <windows.h>
59

60
#include <dbghelp.h>
61
#endif
62

63
namespace hpx::util::stack_trace {
64

65
    [[nodiscard]] std::size_t trace(void** addresses, std::size_t size);
66
    void write_symbols(void* const* addresses, std::size_t size, std::ostream&);
67
    [[nodiscard]] std::string get_symbol(void* address);
68
    [[nodiscard]] std::string get_symbols(
69
        void* const* address, std::size_t size);
5✔
70

5✔
71
#if defined(HPX_HAVE_EXECINFO) && defined(HPX_HAVE_UNWIND)
5✔
72
    struct trace_data
5✔
73
    {
74
        constexpr trace_data(void** array, std::size_t size) noexcept
75
          : array_(array)
76
          , size_(size)
77
          , cfa_(0)
78
          , count_(std::size_t(-1))
79
        {
80
        }
81

82
        void** array_;         // storage for the stack trace
90✔
83
        std::size_t size_;     // number of frames
84
        std::uint64_t cfa_;    // canonical frame address
85
        std::size_t count_;
90✔
86
    };
87

88
    [[nodiscard]] _Unwind_Reason_Code trace_callback(
89
        _Unwind_Context* ctx, void* ptr)
90
    {
91
        if (!ptr)
90✔
92
            return _URC_NO_REASON;
93

94
        trace_data& d = *(reinterpret_cast<trace_data*>(ptr));
85✔
95

96
        // First call.
97
        if (std::size_t(-1) != d.count_)
85✔
98
        {
99
            // Get the instruction pointer for this frame.
100
            d.array_[d.count_] = reinterpret_cast<void*>(_Unwind_GetIP(ctx));
85✔
101

80✔
102
            // Get the CFA.
×
103
            std::uint64_t cfa = _Unwind_GetCFA(ctx);
104

105
            // Check if we're at the end of the stack.
106
            if ((0 < d.count_) &&
107
                (d.array_[d.count_ - 1] == d.array_[d.count_]) &&
85✔
108
                (cfa == d.cfa_))
109
            {
110
                return _URC_END_OF_STACK;
90✔
111
            }
112

113
            d.cfa_ = cfa;
114
        }
115

116
        if (++d.count_ == d.size_)
5✔
117
            return _URC_END_OF_STACK;
118

119
        return _URC_NO_REASON;
120
    }
5✔
121

5✔
122
    [[nodiscard]] std::size_t trace(void** array, std::size_t n)
123
    {
5✔
124
        trace_data d(array, n);
1✔
125

126
        if (1 <= n)
5✔
127
            _Unwind_Backtrace(trace_callback, reinterpret_cast<void*>(&d));
128

129
        if ((1 < d.count_) && d.array_[d.count_ - 1])
130
            --d.count_;
131

132
        return (std::size_t(-1) != d.count_) ? d.count_ : 0;
133
    }
134

135
#elif defined(HPX_HAVE_EXECINFO)
136

137
    [[nodiscard]] std::size_t trace(void** array, std::size_t n)
138
    {
139
        return ::backtrace(array, static_cast<int>(n));
140
    }
141

142
#elif defined(HPX_MSVC)
143

144
    [[nodiscard]] std::size_t trace(void** array, std::size_t n)
145
    {
146
#if _WIN32_WINNT < 0x0600
147
        // for Windows XP/Windows Server 2003
148
        if (n >= 63)
149
            n = 62;
150
#endif
151
        return RtlCaptureStackBackTrace(
152
            static_cast<ULONG>(0), static_cast<ULONG>(n), array, nullptr);
153
    }
154

155
#else
156

157
    [[nodiscard]] std::size_t trace(void** /*array*/, std::size_t /*n*/)
158
    {
159
        return 0;
45✔
160
    }
161

45✔
162
#endif
163

164
#if defined(HPX_HAVE_EXECINFO)
45✔
165
    [[nodiscard]] std::string get_symbol_exec_info(void* address)
×
166
    {
45✔
167
        char** ptr = backtrace_symbols(&address, 1);
45✔
168
        try
169
        {
45✔
170
            if (ptr == nullptr)
171
                return std::string("???");
×
172
            std::string res = ptr[0];
173
            // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
×
174
            free(ptr);
×
175
            ptr = nullptr;
×
176
            return res;
177
        }
178
        catch (...)
179
        {
180
            // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
74✔
181
            free(ptr);
182
            throw;
74✔
183
        }
184
    }
185
#endif
186

70✔
187
#if defined(HPX_HAVE_DLFCN) && defined(HPX_HAVE_ABI_CXA_DEMANGLE)
70✔
188
    [[nodiscard]] std::string get_symbol(void* ptr)
189
    {
70✔
190
        if (!ptr)
70✔
191
            return {};
70✔
192

193
        bool need_offset = true;
194
        std::ostringstream res;
195
        res.imbue(std::locale::classic());
196
        res << std::left << std::setw(sizeof(void*) * 2) << std::setfill(' ')
×
197
            << ptr << ": ";
198
        Dl_info info = {nullptr, nullptr, nullptr, nullptr};
199
        if (dladdr(ptr, &info) == 0)
200
        {
201
#if !defined(HPX_HAVE_EXECINFO)
202
            res << "???";
70✔
203
#else
204
            res << get_symbol_exec_info(ptr);
205
            need_offset = false;
25✔
206
#endif
25✔
207
        }
208
        else
25✔
209
        {
210
            if (info.dli_sname)
25✔
211
            {
25✔
212
#if defined(HPX_HAVE_STACKTRACES_DEMANGLE_SYMBOLS)
213
                int status = 0;
214
                char* demangled = abi::__cxa_demangle(
215
                    info.dli_sname, nullptr, nullptr, &status);
×
216
                if (demangled)
217
                {
218
                    res << demangled;
219
                    free(demangled);
220
                }
221
                else
222
                {
223
                    res << info.dli_sname;
224
                }
225
#else
226
                res << info.dli_sname;
90✔
227
#endif
228
            }
229
            else
230
            {
231
#if !defined(HPX_HAVE_EXECINFO)
70✔
232
                res << "???";
70✔
233
#else
70✔
234
                res << get_symbol_exec_info(ptr);
235
                need_offset = false;
25✔
236
#endif
237
            }
238

70✔
239
            std::ptrdiff_t offset = reinterpret_cast<char*>(ptr) -
240
                reinterpret_cast<char*>(info.dli_saddr);
70✔
241
            if (need_offset)
242
            {
243
                res << std::hex << " [0x" << offset << "]";
244
            }
70✔
245

246
            if (info.dli_fname)
5✔
247
            {
248
                res << " in " << info.dli_fname;
249
            }
250
        }
5✔
251
        return res.str();
252
    }
5✔
253

5✔
254
    [[nodiscard]] std::string get_symbols(
255
        void* const* addresses, std::size_t size)
256
    {
257
        // the first two stack frames are from the back tracing facility itself
10✔
258
        if (size > 2)
79✔
259
        {
260
            addresses += 2;
74✔
261
            size -= 2;
74✔
262
        }
263

264
        std::string res =
265
            std::to_string(size) + ((1 == size) ? " frame:" : " frames:");
266
        for (std::size_t i = 0; i < size; i++)
267
        {
5✔
268
            std::string tmp = get_symbol(addresses[i]);
269
            if (!tmp.empty())
270
            {
×
271
                res += '\n';
272
                res += tmp;
273
            }
×
274
        }
×
275
        return res;
276
    }
×
277

×
278
    void write_symbols(
279
        void* const* addresses, std::size_t size, std::ostream& out)
×
280
    {
281
        out << size << ((1 == size) ? " frame:" : " frames:");
282
        for (std::size_t i = 0; i < size; i++)
283
        {
×
284
            std::string tmp = get_symbol(addresses[i]);
285
            if (!tmp.empty())
286
            {
287
                out << '\n' << tmp;
288
            }
289
        }
290
        out << std::flush;
291
    }
292

293
#elif defined(HPX_HAVE_EXECINFO)
294

295
    [[nodiscard]] std::string get_symbol(void* address)
296
    {
297
        return get_symbol_exec_info(address);
298
    }
299

300
    [[nodiscard]] std::string get_symbols(
301
        void* const* address, std::size_t size)
302
    {
303
        // the first two stack frames are from the back tracing facility itself
304
        if (size > 2)
305
        {
306
            addresses += 2;
307
            size -= 2;
308
        }
309

310
        char** ptr = backtrace_symbols(address, size);
311
        try
312
        {
313
            if (ptr == nullptr)
314
                return {};
315
            std::string res =
316
                std::to_string(size) + ((1 == size) ? " frame:" : " frames:");
317
            for (std::size_t i = 0; i < size; i++)
318
            {
319
                res += '\n';
320
                res += ptr[i];
321
            }
322
            free(ptr);
323
            ptr = nullptr;
324
            return res;
325
        }
326
        catch (...)
327
        {
328
            free(ptr);
329
            throw;
330
        }
331
    }
332

333
    void write_symbols(
334
        void* const* addresses, std::size_t size, std::ostream& out)
335
    {
336
        char** ptr = backtrace_symbols(addresses, size);
337
        out << size << ((1 == size) ? " frame:" : " frames:");
338
        try
339
        {
340
            if (ptr == nullptr)
341
                return;
342
            for (std::size_t i = 0; i < size; i++)
343
                out << '\n' << ptr[i];
344
            free(ptr);
345
            ptr = nullptr;
346
            out << std::flush;
347
        }
348
        catch (...)
349
        {
350
            free(ptr);
351
            throw;
352
        }
353
    }
354

355
#elif defined(HPX_MSVC)
356

357
    namespace {
358

359
        HANDLE hProcess = nullptr;
360
        bool syms_ready = false;
361

362
        void init()
363
        {
364
            if (hProcess == nullptr)
365
            {
366
                hProcess = GetCurrentProcess();
367
                SymSetOptions(SYMOPT_DEFERRED_LOADS);
368

369
                if (SymInitialize(hProcess, nullptr, TRUE))
370
                {
371
                    syms_ready = true;
372
                }
373
            }
374
        }
375
    }    // namespace
376

377
    [[nodiscard]] std::string get_symbol(void* ptr)
378
    {
379
        if (ptr == nullptr)
380
            return {};
381

382
        init();
383
        std::ostringstream ss;
384
        ss << std::left << std::setw(sizeof(void*) * 2) << std::setfill(' ')
385
           << ptr;
386
        if (syms_ready)
387
        {
388
            DWORD64 dwDisplacement = 0;
389
            auto const dwAddress = reinterpret_cast<DWORD64>(ptr);
390

391
            std::vector<char> buffer(sizeof(SYMBOL_INFO) + MAX_SYM_NAME);
392
            auto const pSymbol =
393
                reinterpret_cast<PSYMBOL_INFO>(&buffer.front());
394

395
            pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
396
            pSymbol->MaxNameLen = MAX_SYM_NAME;
397

398
            if (SymFromAddr(hProcess, dwAddress, &dwDisplacement, pSymbol))
399
            {
400
                ss << ": " << pSymbol->Name << std::hex << " +0x"
401
                   << dwDisplacement;
402
            }
403
            else
404
            {
405
                ss << ": ???";
406
            }
407
        }
408
        return ss.str();
409
    }
410

411
    [[nodiscard]] std::string get_symbols(
412
        void* const* addresses, std::size_t size)
413
    {
414
        // the first two stack frames are from the back tracing facility itself
415
        if (size > 2)
416
        {
417
            addresses += 2;
418
            size -= 2;
419
        }
420

421
        std::string res =
422
            std::to_string(size) + ((1 == size) ? " frame:" : " frames:");
423
        for (std::size_t i = 0; i < size; i++)
424
        {
425
            std::string tmp = get_symbol(addresses[i]);
426
            if (!tmp.empty())
427
            {
428
                res += '\n';
429
                res += tmp;
430
            }
431
        }
432
        return res;
433
    }
434

435
    void write_symbols(
436
        void* const* addresses, std::size_t size, std::ostream& out)
437
    {
438
        out << size << ((1 == size) ? " frame:" : " frames:");    //-V128
439
        for (std::size_t i = 0; i < size; i++)
440
        {
441
            std::string tmp = get_symbol(addresses[i]);
442
            if (!tmp.empty())
443
            {
444
                out << '\n' << tmp;
445
            }
446
        }
447
        out << std::flush;
448
    }
449

450
#else
451

452
    [[nodiscard]] std::string get_symbol(void* ptr)
453
    {
454
        if (!ptr)
455
            return {};
456
        std::ostringstream res;
457
        res.imbue(std::locale::classic());
458
        res << std::left << std::setw(sizeof(void*) * 2) << std::setfill(' ')
459
            << ptr;
460
        return res.str();
461
    }
462

463
    std::string get_symbols(void* const* ptrs, std::size_t size)
464
    {
465
        if (!ptrs)
466
            return {};
467

468
        // the first two stack frames are from the back tracing facility itself
469
        if (size > 2)
470
        {
471
            ptrs += 2;
472
            size -= 2;
473
        }
474

475
        std::ostringstream res;
476
        res.imbue(std::locale::classic());
477
        write_symbols(ptrs, size, res);
478
        return res.str();
479
    }
480

481
    void write_symbols(
482
        void* const* addresses, std::size_t size, std::ostream& out)
483
    {
484
        out << size << ((1 == size) ? " frame:" : " frames:");    //-V128
485
        for (std::size_t i = 0; i < size; i++)
486
        {
487
            if (addresses[i] != nullptr)
488
                out << '\n'
489
                    << std::left << std::setw(sizeof(void*) * 2)
490
                    << std::setfill(' ') << addresses[i];
491
        }
492
        out << std::flush;
493
    }
494

495
#endif
496
}    // namespace hpx::util::stack_trace
497

498
namespace hpx::util {
499

500
    backtrace::backtrace(std::size_t frames_no)
501
    {
502
        if (frames_no == 0)
503
            return;
504
        frames_no += 2;    // we omit two frames from printing
505
        frames_.resize(frames_no, nullptr);
506

507
        std::size_t const size =
508
            stack_trace::trace(&frames_.front(), frames_no);
509
        if (size != 0)
510
            frames_.resize(size);
511
    }
512

513
    void backtrace::trace_line(std::size_t frame_no, std::ostream& out) const
514
    {
515
        if (frame_no < frames_.size())
516
            stack_trace::write_symbols(&frames_[frame_no], 1, out);
517
    }
518

519
    std::string backtrace::trace_line(std::size_t frame_no) const
520
    {
521
        if (frame_no < frames_.size())
522
            return stack_trace::get_symbol(frames_[frame_no]);
523
        return {};
524
    }
525

526
    std::string backtrace::trace() const
527
    {
528
        if (frames_.empty())
529
            return {};
530
        return stack_trace::get_symbols(&frames_.front(), frames_.size());
531
    }
532

533
    void backtrace::trace(std::ostream& out) const
534
    {
535
        if (frames_.empty())
536
            return;
537
        stack_trace::write_symbols(&frames_.front(), frames_.size(), out);
538
    }
539

540
    namespace detail {
541

542
        std::ostream& trace_manip::write(std::ostream& out) const
543
        {
544
            if (tr_)
545
                tr_->trace(out);
546
            return out;
547
        }
548

549
        inline std::ostream& operator<<(
550
            std::ostream& out, detail::trace_manip const& t)
551
        {
552
            return t.write(out);
553
        }
554
    }    // namespace detail
555

556
    std::string trace(std::size_t frames_no)
557
    {
558
        return backtrace(frames_no).trace();
559
    }
560
}    // namespace hpx::util
561

562
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc