• 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

34.09
/libs/core/runtime_local/src/custom_exception_info.cpp
1
//  Copyright (c) 2007-2025 Hartmut Kaiser
2
//  Copyright (c)      2011 Bryce Lelbach
3
//
4
//  SPDX-License-Identifier: BSL-1.0
5
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
6
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7

8
#include <hpx/config.hpp>
9
#include <hpx/assert.hpp>
10
#include <hpx/modules/debugging.hpp>
11
#include <hpx/modules/errors.hpp>
12
#include <hpx/modules/format.hpp>
13
#include <hpx/modules/futures.hpp>
14
#include <hpx/modules/lock_registration.hpp>
15
#include <hpx/modules/logging.hpp>
16
#include <hpx/modules/threading.hpp>
17
#include <hpx/modules/threading_base.hpp>
18
#include <hpx/modules/threadmanager.hpp>
19
#include <hpx/runtime_local/config_entry.hpp>
20
#include <hpx/runtime_local/custom_exception_info.hpp>
21
#include <hpx/runtime_local/debugging.hpp>
22
#include <hpx/runtime_local/get_locality_id.hpp>
23
#include <hpx/runtime_local/get_worker_thread_num.hpp>
24
#include <hpx/runtime_local/runtime_local.hpp>
25
#include <hpx/runtime_local/state.hpp>
26
#include <hpx/version.hpp>
27

28
#if defined(HPX_WINDOWS)
29
#include <process.h>
30
#elif defined(HPX_HAVE_UNISTD_H)
31
#include <unistd.h>
32
#endif
33

34
#include <algorithm>
35
#include <atomic>
36
#include <cstddef>
37
#include <cstdint>
38
#include <exception>
39
#include <iostream>
40
#include <memory>
41
#include <sstream>
42
#include <stdexcept>
43
#include <string>
44
#include <system_error>
45
#include <typeinfo>
46
#include <utility>
47
#include <vector>
48

49
#if defined(HPX_WINDOWS)
50
#include <excpt.h>
51
#undef exception_info
52
#endif
53

54
namespace hpx {
55

56
    char const* get_runtime_state_name(state st) noexcept;
57

×
58
    ///////////////////////////////////////////////////////////////////////////
59
    // For testing purposes we sometimes expect to see exceptions, allow those
×
60
    // to go through without attaching a debugger.
61
    namespace {
62

63
        std::atomic<bool> expect_exception_flag(false);
64
    }
65

×
66
    bool expect_exception(bool flag)
67
    {
×
68
        return expect_exception_flag.exchange(flag);
×
69
    }
70

×
71
    ///////////////////////////////////////////////////////////////////////////
×
72
    // Extract the diagnostic information embedded in the given exception and
73
    // return a string holding a formatted message.
74
    std::string default_diagnostic_information(std::exception_ptr const& e)
×
75
    {
76
        try
×
77
        {
78
            if (e)
79
                std::rethrow_exception(e);
×
80
        }
81
        catch (std::exception const& ex)
82
        {
83
            return {ex.what()};
84
        }
85
        return {"<unknown>"};
×
86
    }
87

88
    std::string diagnostic_information(hpx::exception_info const& xi)
89
    {
×
90
        int const verbosity = util::from_string<int>(
91
            get_config_entry("hpx.exception_verbosity", "2"));
92

×
93
        std::ostringstream strm;
94
        strm << "\n";
95

×
96
        // add full build information
97
        if (verbosity >= 2)
98
        {
×
99
            strm << full_build_string();
100

101
            if (std::string const* env = xi.get<hpx::detail::throw_env>();
102
                env && !env->empty())
103
            {
×
104
                strm << "{env}: " << *env;
×
105
            }
106
        }
107

×
108
        if (verbosity >= 1)
×
109
        {
110
            std::string const* back_trace =
111
                xi.get<hpx::detail::throw_stacktrace>();
112
            if (back_trace && !back_trace->empty())
×
113
            {
114
                // FIXME: add indentation to stack frame information
×
115
                strm << "{stack-trace}: " << *back_trace << "\n";
116
            }
×
117

118
            if (std::uint32_t const* locality =
119
                    xi.get<hpx::detail::throw_locality>())
120
            {
×
121
                strm << "{locality-id}: " << *locality << "\n";
×
122
            }
×
123

124
            std::string const* hostname_ =
×
125
                xi.get<hpx::detail::throw_hostname>();
×
126
            if (hostname_ && !hostname_->empty())
127
                strm << "{hostname}: " << *hostname_ << "\n";
128

129
            std::int64_t const* pid_ = xi.get<hpx::detail::throw_pid>();
×
130
            if (pid_ && -1 != *pid_)
131
                strm << "{process-id}: " << *pid_ << "\n";
×
132

×
133
            bool thread_info = false;
134
            constexpr auto thread_prefix = "{os-thread}: ";
135
            if (std::size_t const* shepherd =
136
                    xi.get<hpx::detail::throw_shepherd>();
137
                shepherd && static_cast<std::size_t>(-1) != *shepherd)
×
138
            {
×
139
                strm << thread_prefix << *shepherd;
140
                thread_info = true;
×
141
            }
×
142

143
            std::string const thread_name = hpx::get_thread_name();
×
144
            if (!thread_info)
145
                strm << thread_prefix;
146
            else
×
147
                strm << ", ";
148
            strm << thread_name << "\n";
149

150
            std::size_t const* thread_id =
×
151
                xi.get<hpx::detail::throw_thread_id>();
×
152
            if (thread_id && *thread_id)
153
            {
×
154
                strm << "{thread-id}: ";
×
155
                hpx::util::format_to(strm, "{:016x}\n", *thread_id);
156
            }
×
157

×
158
            std::string const* thread_description =
159
                xi.get<hpx::detail::throw_thread_name>();
160
            if (thread_description && !thread_description->empty())
161
                strm << "{thread-description}: " << *thread_description << "\n";
×
162

×
163
            if (std::string const* state = xi.get<hpx::detail::throw_state>())
164
                strm << "{state}: " << *state << "\n";
×
165

×
166
            if (std::string const* auxinfo =
167
                    xi.get<hpx::detail::throw_auxinfo>())
168
            {
169
                strm << "{auxinfo}: " << *auxinfo << "\n";
170
            }
171
        }
172

5✔
173
        if (std::string const* file = xi.get<hpx::detail::throw_file>())
174
            strm << "{file}: " << *file << "\n";
175

176
        if (long const* line = xi.get<hpx::detail::throw_line>())
5✔
177
            strm << "{line}: " << *line << "\n";
178

179
        if (std::string const* function = xi.get<hpx::detail::throw_function>())
180
            strm << "{function}: " << *function << "\n";
181

5✔
182
        // Try a cast to std::exception - this should handle boost.system
183
        // error codes in addition to the standard library exceptions.
184
        if (auto const* se = dynamic_cast<std::exception const*>(&xi))
10✔
185
            strm << "{what}: " << se->what() << "\n";
5✔
186

187
        return strm.str();
×
188
    }
189
}    // namespace hpx
190

191
namespace hpx::util {
10✔
192

193
    // This is a local helper used to get the backtrace on a new stack if
194
    // possible.
195
    static std::string trace_on_new_stack(
196
        std::size_t frames_no = HPX_HAVE_THREAD_BACKTRACE_DEPTH)
197
    {
198
#if defined(HPX_HAVE_STACKTRACES)
5✔
199
        if (frames_no == 0)
5✔
200
        {
×
201
            return {};
202
        }
203

5✔
204
        backtrace bt(frames_no);
205

10✔
206
        // avoid infinite recursion on handling errors
207
        if (auto const* self = threads::get_self_ptr(); nullptr == self ||
208
            self->get_thread_id() == threads::invalid_thread_id)
209
        {
5✔
210
            return bt.trace();
211
        }
212

213
        lcos::local::futures_factory<std::string()> p(
214
            [&bt] { return bt.trace(); });
×
215

216
        error_code ec(throwmode::lightweight);
×
217
        threads::thread_id_ref_type const tid =
218
            p.post("hpx::util::trace_on_new_stack",
×
219
                launch::fork_policy(threads::thread_priority::default_,
220
                    threads::thread_stacksize::medium),
×
221
                ec);
222
        if (ec)
223
            return "<couldn't retrieve stack backtrace>";
224

×
225
        // make sure this thread is executed last
226
        hpx::this_thread::yield_to(thread::id(tid));
×
227

228
        return p.get_future().get(ec);
×
229
#else
×
230
        return {};
231
#endif
×
232
    }
233
}    // namespace hpx::util
×
234

235
namespace hpx::detail {
×
236

×
237
    void pre_exception_handler()
238
    {
×
239
        if (!expect_exception_flag.load(std::memory_order_relaxed))
240
        {
×
241
            hpx::util::may_attach_debugger("exception");
242
        }
×
243
    }
×
244

245
    ///////////////////////////////////////////////////////////////////////////
×
246
    // report an early or late exception and abort
247
    void report_exception_and_continue(std::exception const& e)
×
248
    {
×
249
        pre_exception_handler();
250

251
        std::cerr << e.what() << "\n" << std::flush;
×
252
    }
253

×
254
    void report_exception_and_continue(std::exception_ptr const& e)
×
255
    {
256
        pre_exception_handler();
257

×
258
        std::cerr << diagnostic_information(e) << "\n" << std::flush;
259
    }
×
260

×
261
    void report_exception_and_continue(hpx::exception const& e)
262
    {
263
        pre_exception_handler();
×
264

265
        std::cerr << diagnostic_information(e) << "\n" << std::flush;
266
    }
267

268
    void report_exception_and_terminate(std::exception const& e)
269
    {
270
        report_exception_and_continue(e);
271
        std::abort();
×
272
    }
×
273

×
274
    void report_exception_and_terminate(std::exception_ptr const& e)
×
275
    {
×
276
        report_exception_and_continue(e);
×
277
        std::abort();
×
278
    }
×
279

×
280
    void report_exception_and_terminate(hpx::exception const& e)
×
281
    {
×
282
        report_exception_and_continue(e);
×
283
        std::abort();
284
    }
285

286
    hpx::exception_info construct_exception_info(std::string const& func,
×
287
        std::string const& file, long line, std::string const& back_trace,
288
        std::uint32_t node, std::string const& hostname, std::int64_t pid,
289
        std::size_t shepherd, std::size_t thread_id,
290
        std::string const& thread_name, std::string const& env,
291
        std::string const& config, std::string const& state_name,
292
        std::string const& auxinfo)
293
    {
×
294
        return hpx::exception_info().set(
295
            hpx::detail::throw_stacktrace(back_trace),
×
296
            hpx::detail::throw_locality(node),
297
            hpx::detail::throw_hostname(hostname), hpx::detail::throw_pid(pid),
×
298
            hpx::detail::throw_shepherd(shepherd),
299
            hpx::detail::throw_thread_id(thread_id),
300
            hpx::detail::throw_thread_name(thread_name),
301
            hpx::detail::throw_function(func), hpx::detail::throw_file(file),
302
            hpx::detail::throw_line(line), hpx::detail::throw_env(env),
303
            hpx::detail::throw_config(config),
304
            hpx::detail::throw_state(state_name),
305
            hpx::detail::throw_auxinfo(auxinfo));
306
    }
307

308
    template <typename Exception>
309
    std::exception_ptr construct_exception(
310
        Exception const& e, hpx::exception_info info)
311
    {
312
        // create a std::exception_ptr object encapsulating the Exception to
313
        // be thrown and annotate it with all the local information we have
314
        try
315
        {
316
            throw_with_info(e, HPX_MOVE(info));
317
        }
318
        catch (...)
319
        {
320
            return std::current_exception();
321
        }
322

323
        HPX_UNREACHABLE;    // -V779
324

325
        // need this return to silence a warning with icc
326
        return {};
327
    }
328

329
    template HPX_CORE_EXPORT std::exception_ptr construct_exception(
330
        hpx::exception const&, hpx::exception_info info);
331
    template HPX_CORE_EXPORT std::exception_ptr construct_exception(
332
        std::system_error const&, hpx::exception_info info);
333
    template HPX_CORE_EXPORT std::exception_ptr construct_exception(
334
        std::exception const&, hpx::exception_info info);
335
    template HPX_CORE_EXPORT std::exception_ptr construct_exception(
336
        hpx::detail::std_exception const&, hpx::exception_info info);
337
    template HPX_CORE_EXPORT std::exception_ptr construct_exception(
338
        std::bad_exception const&, hpx::exception_info info);
339
    template HPX_CORE_EXPORT std::exception_ptr construct_exception(
340
        hpx::detail::bad_exception const&, hpx::exception_info info);
341
    template HPX_CORE_EXPORT std::exception_ptr construct_exception(
342
        std::bad_typeid const&, hpx::exception_info info);
343
    template HPX_CORE_EXPORT std::exception_ptr construct_exception(
5✔
344
        hpx::detail::bad_typeid const&, hpx::exception_info info);
345
    template HPX_CORE_EXPORT std::exception_ptr construct_exception(
810✔
346
        std::bad_cast const&, hpx::exception_info info);
805✔
347
    template HPX_CORE_EXPORT std::exception_ptr construct_exception(
348
        hpx::detail::bad_cast const&, hpx::exception_info info);
349
    template HPX_CORE_EXPORT std::exception_ptr construct_exception(
350
        std::bad_alloc const&, hpx::exception_info info);
351
    template HPX_CORE_EXPORT std::exception_ptr construct_exception(
5✔
352
        hpx::detail::bad_alloc const&, hpx::exception_info info);
353
    template HPX_CORE_EXPORT std::exception_ptr construct_exception(
5✔
354
        std::logic_error const&, hpx::exception_info info);
355
    template HPX_CORE_EXPORT std::exception_ptr construct_exception(
356
        std::runtime_error const&, hpx::exception_info info);
357
    template HPX_CORE_EXPORT std::exception_ptr construct_exception(
358
        std::out_of_range const&, hpx::exception_info info);
359
    template HPX_CORE_EXPORT std::exception_ptr construct_exception(
360
        std::invalid_argument const&, hpx::exception_info info);
361

5✔
362
    ///////////////////////////////////////////////////////////////////////////
5✔
363
    //  Figure out the size of the given environment
5✔
364
    static std::size_t get_arraylen(char** array)
365
    {
366
        std::size_t count = 0;
367
        if (nullptr != array)
368
        {
369
            while (nullptr != array[count])
370
                ++count;    // simply count the environment strings
371
        }
372
        return count;
373
    }
374

375
    std::string get_execution_environment()
376
    {
377
        std::vector<std::string> env;
5✔
378

379
#if defined(HPX_WINDOWS)
380
        std::size_t len = get_arraylen(_environ);
381
        env.reserve(len);
5✔
382
        std::copy(&_environ[0], &_environ[len], std::back_inserter(env));
810✔
383
#elif defined(linux) || defined(__linux) || defined(__linux__) ||              \
384
    defined(__AIX__)
805✔
385
        std::size_t len = get_arraylen(environ);
1,610✔
386
        env.reserve(len);
1,610✔
387
        std::copy(&environ[0], &environ[len], std::back_inserter(env));
388
#elif defined(__FreeBSD__)
389
        std::size_t len = get_arraylen(freebsd_environ);
1,610✔
390
        env.reserve(len);
391
        std::copy(&freebsd_environ[0], &freebsd_environ[len],
392
            std::back_inserter(env));
5✔
393
#elif defined(__APPLE__)
5✔
394
        std::size_t len = get_arraylen(environ);
395
        env.reserve(len);
5✔
396
        std::copy(&environ[0], &environ[len], std::back_inserter(env));
397
#else
398
#error "Don't know, how to access the execution environment on this platform"
5✔
399
#endif
400

401
        std::sort(env.begin(), env.end());
5✔
402

403
        static constexpr char const* ignored_env_patterns[] = {
404
            "DOCKER", "GITHUB_TOKEN"};
405
        std::string retval = hpx::util::format("{} entries:\n", env.size());
5✔
406
        for (std::string const& s : env)
407
        {
5✔
408
            if (std::all_of(std::begin(ignored_env_patterns),
409
                    std::end(ignored_env_patterns), [&s](auto const e) {
5✔
410
                        return s.find(e) == std::string::npos;
411
                    }))
5✔
412
            {
5✔
413
                retval += "  " + s + "\n";
414
            }
5✔
415
        }
416
        return retval;
10✔
417
    }
418

419
    hpx::exception_info custom_exception_info(std::string const& func,
420
        std::string const& file, long line, std::string const& auxinfo)
421
    {
422
        std::int64_t const pid = ::getpid();
423

5✔
424
        auto const trace_depth =
425
            util::from_string<std::size_t>(get_config_entry(
426
                "hpx.trace_depth", HPX_HAVE_THREAD_BACKTRACE_DEPTH));
5✔
427

428
        std::string const back_trace(
429
            hpx::util::trace_on_new_stack(trace_depth));
5✔
430

5✔
431
        std::string state_name("not running");
432
        std::string hostname;
5✔
433
        if (hpx::runtime const* rt = get_runtime_ptr())
5✔
434
        {
435
            state const rts_state = rt->get_state();
5✔
436
            state_name = get_runtime_state_name(rts_state);
5✔
437

438
            if (rts_state >= state::initialized && rts_state < state::stopped)
439
            {
5✔
440
                hostname = get_runtime().here();
5✔
441
            }
442
        }
10✔
443

5✔
444
        // if this is not a HPX thread we do not need to query neither for
5✔
445
        // the shepherd thread nor for the thread id
×
446
        error_code ec(throwmode::lightweight);
5✔
447
        std::uint32_t const node = get_locality_id(ec);
5✔
448

5✔
449
        auto shepherd = static_cast<std::size_t>(-1);
10✔
450
        threads::thread_id_type thread_id;
5✔
451
        threads::thread_description thread_name;
5✔
452

5✔
453
        threads::thread_self const* self = threads::get_self_ptr();
5✔
454
        if (nullptr != self)
10✔
455
        {
456
            if (threads::threadmanager_is(hpx::state::running))
457
                shepherd = hpx::get_worker_thread_num();
458

459
            thread_id = threads::get_self_id();
460
            thread_name = threads::get_thread_description(thread_id);
461
        }
×
462

463
        std::string const env(get_execution_environment());
464
        std::string const config(configuration_string());
×
465

466
        return hpx::exception_info().set(
467
            hpx::detail::throw_stacktrace(back_trace),
468
            hpx::detail::throw_locality(node),
469
            hpx::detail::throw_hostname(hostname), hpx::detail::throw_pid(pid),
470
            hpx::detail::throw_shepherd(shepherd),
×
471
            hpx::detail::throw_thread_id(
472
                reinterpret_cast<std::size_t>(thread_id.get())),
×
473
            hpx::detail::throw_thread_name(threads::as_string(thread_name)),
474
            hpx::detail::throw_function(func), hpx::detail::throw_file(file),
475
            hpx::detail::throw_line(line), hpx::detail::throw_env(env),
×
476
            hpx::detail::throw_config(config),
477
            hpx::detail::throw_state(state_name),
478
            hpx::detail::throw_auxinfo(auxinfo));
479
    }
480
}    // namespace hpx::detail
481

482
///////////////////////////////////////////////////////////////////////////////
483
namespace hpx {
484
    /// Return the host-name of the locality where the exception was thrown.
×
485
    std::string get_error_host_name(hpx::exception_info const& xi)
486
    {
×
487
        std::string const* hostname_ = xi.get<hpx::detail::throw_hostname>();
×
488
        if (hostname_ && !hostname_->empty())
489
            return *hostname_;
490
        return {};
491
    }
492

493
    /// Return the locality where the exception was thrown.
×
494
    std::uint32_t get_error_locality_id(hpx::exception_info const& xi) noexcept
495
    {
496
        if (std::uint32_t const* locality =
×
497
                xi.get<hpx::detail::throw_locality>())
498
        {
499
            return *locality;
500
        }
501

×
502
        // same as naming::invalid_locality_id
503
        return ~static_cast<std::uint32_t>(0);
504
    }
505

×
506
    /// Return the (operating system) process id of the locality where the
507
    /// exception was thrown.
508
    std::int64_t get_error_process_id(hpx::exception_info const& xi) noexcept
509
    {
×
510
        if (std::int64_t const* pid = xi.get<hpx::detail::throw_pid>())
511
            return *pid;
512
        return -1;
513
    }
514

515
    /// Return the environment of the OS-process at the point the exception
516
    /// was thrown.
517
    std::string get_error_env(hpx::exception_info const& xi)
518
    {
519
        if (std::string const* env = xi.get<hpx::detail::throw_env>();
×
520
            env && !env->empty())
521
        {
×
522
            return *env;
×
523
        }
524

525
        return "<unknown>";
526
    }
527

528
    /// Return the stack backtrace at the point the exception was thrown.
529
    std::string get_error_backtrace(hpx::exception_info const& xi)
530
    {
531
        if (std::string const* back_trace =
×
532
                xi.get<hpx::detail::throw_stacktrace>();
533
            back_trace && !back_trace->empty())
×
534
        {
535
            return *back_trace;
×
536
        }
537

538
        return {};
539
    }
540

541
    /// Return the sequence number of the OS-thread used to execute HPX-threads
542
    /// from which the exception was thrown.
543
    std::size_t get_error_os_thread(hpx::exception_info const& xi) noexcept
544
    {
×
545
        if (std::size_t const* shepherd = xi.get<hpx::detail::throw_shepherd>();
546
            shepherd && static_cast<std::size_t>(-1) != *shepherd)
547
        {
548
            return *shepherd;
×
549
        }
550
        return static_cast<std::size_t>(-1);
551
    }
552

553
    /// Return the unique thread id of the HPX-thread from which the exception
554
    /// was thrown.
555
    std::size_t get_error_thread_id(hpx::exception_info const& xi) noexcept
556
    {
557
        if (std::size_t const* thread_id =
×
558
                xi.get<hpx::detail::throw_thread_id>();
559
            thread_id && *thread_id)
560
        {
561
            return *thread_id;
×
562
        }
563
        return static_cast<std::size_t>(-1);
564
    }
565

566
    /// Return any addition thread description of the HPX-thread from which the
567
    /// exception was thrown.
568
    std::string get_error_thread_description(hpx::exception_info const& xi)
569
    {
570
        if (std::string const* thread_description =
×
571
                xi.get<hpx::detail::throw_thread_name>();
572
            thread_description && !thread_description->empty())
573
        {
×
574
            return *thread_description;
575
        }
576
        return {};
577
    }
578

579
    /// Return the HPX configuration information point from which the
580
    /// exception was thrown.
581
    std::string get_error_config(hpx::exception_info const& xi)
582
    {
583
        if (std::string const* config_info =
584
                xi.get<hpx::detail::throw_config>();
585
            config_info && !config_info->empty())
586
        {
587
            return *config_info;
588
        }
589
        return {};
590
    }
591

592
    /// Return the HPX runtime state information at which the exception was
593
    /// thrown.
594
    std::string get_error_state(hpx::exception_info const& xi)
595
    {
596
        if (std::string const* state_info = xi.get<hpx::detail::throw_state>();
597
            state_info && !state_info->empty())
598
        {
599
            return *state_info;
600
        }
601
        return {};
602
    }
603
}    // namespace hpx
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