• 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

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

7
#include <hpx/config.hpp>
8
#include <hpx/assert.hpp>
9
#include <hpx/command_line_handling_local/command_line_handling_local.hpp>
10
#include <hpx/command_line_handling_local/parse_command_line_local.hpp>
11
#include <hpx/modules/asio.hpp>
12
#include <hpx/modules/debugging.hpp>
13
#include <hpx/modules/format.hpp>
14
#include <hpx/modules/functional.hpp>
15
#include <hpx/modules/logging.hpp>
16
#include <hpx/modules/program_options.hpp>
17
#include <hpx/modules/runtime_configuration.hpp>
18
#include <hpx/modules/string_util.hpp>
19
#include <hpx/modules/topology.hpp>
20
#include <hpx/modules/util.hpp>
21
#include <hpx/version.hpp>
22
#if defined(HPX_HAVE_MAX_CPU_COUNT)
23
#include <hpx/modules/preprocessor.hpp>
24
#endif
25

26
#include <algorithm>
27
#include <cstddef>
28
#include <fstream>
29
#include <iostream>
30
#include <iterator>
31
#include <sstream>
32
#include <string>
33
#include <utility>
34
#include <vector>
35

36
#if defined(HPX_HAVE_UNISTD_H)
37
#include <unistd.h>
38
#endif
39

40
namespace hpx::local::detail {
41

42
    std::string runtime_configuration_string(command_line_handling const& cfg)
43
    {
44
        std::ostringstream strm;
×
45

46
        // default scheduler used for this run
×
47
        strm << "  {scheduler}: " << cfg.queuing_ << "\n";
48

49
        // amount of threads and cores configured for this run
×
50
        strm << "  {os-threads}: " << cfg.num_threads_ << "\n";
51
        strm << "  {cores}: " << cfg.num_cores_ << "\n";
52

×
53
        return strm.str();
×
54
    }
55

×
56
    ///////////////////////////////////////////////////////////////////////
×
57
    int print_version(std::ostream& out)
58
    {
59
        out << std::endl << hpx::copyright() << std::endl;
×
60
        out << hpx::complete_version() << std::endl;
61
        return 1;
×
62
    }
×
63

×
64
    int print_info(std::ostream& out, command_line_handling const& cfg)
65
    {
66
        out << "Static configuration:\n---------------------\n";
×
67
        out << hpx::configuration_string() << std::endl;
68

×
69
        out << "Runtime configuration:\n----------------------\n";
×
70
        out << runtime_configuration_string(cfg) << std::endl;
71

×
72
        return 1;
×
73
    }
74

×
75
    ///////////////////////////////////////////////////////////////////////
76
    inline void encode(
77
        std::string& str, char s, char const* r, std::size_t inc = 1ull)
78
    {
275✔
79
        std::string::size_type pos = 0;
80
        while ((pos = str.find_first_of(s, pos)) != std::string::npos)
81
        {
82
            str.replace(pos, 1, r);
275✔
83
            pos += inc;
84
        }
×
85
    }
×
86

87
    inline std::string encode_string(std::string str)
275✔
88
    {
89
        encode(str, '\n', "\\n");
90
        return str;
91
    }
×
92

×
93
    inline std::string encode_and_enquote(std::string str)
94
    {
95
        encode(str, '\"', "\\\"", 2);
275✔
96
        return enquote(HPX_MOVE(str));
97
    }
275✔
98

550✔
99
    ///////////////////////////////////////////////////////////////////////
100
    std::string convert_to_log_file(std::string const& dest)
101
    {
102
        if (dest.empty())
×
103
            return "cout";
104

×
105
        if (dest == "cout" || dest == "cerr" || dest == "console")
×
106
            return dest;
107
#if defined(ANDROID) || defined(__ANDROID__)
×
108
        if (dest == "android_log")
109
            return dest;
110
#endif
111
        // everything else is assumed to be a file name
112
        return "file(" + dest + ")";
113
    }
114

×
115
    std::string handle_queuing(util::manage_config const& cfgmap,
116
        hpx::program_options::variables_map const& vm,
117
        std::string const& default_)
128✔
118
    {
119
        // command line option is used preferred
120
        if (vm.count("hpx:queuing"))
121
        {
122
            std::string queuing = vm["hpx:queuing"].as<std::string>();
256✔
123
            if (!queuing.empty() && queuing[0] == '!')
×
124
                queuing.erase(0);
125
            return queuing;
126
        }
256✔
127

128
        // use either cfgmap value or default
129
        return cfgmap.get_value<std::string>("hpx.scheduler", default_);
128✔
130
    }
131

132
    std::string handle_affinity(util::manage_config const& cfgmap,
133
        hpx::program_options::variables_map const& vm,
134
        std::string const& default_)
256✔
135
    {
×
136
        // command line options is used preferred
137
        if (vm.count("hpx:affinity"))
138
            return vm["hpx:affinity"].as<std::string>();
256✔
139

140
        // use either cfgmap value or default
141
        return cfgmap.get_value<std::string>("hpx.affinity", default_);
128✔
142
    }
143

144
    std::string handle_affinity_bind(util::manage_config const& cfgmap,
145
        hpx::program_options::variables_map const& vm,
146
        std::string const& default_)
256✔
147
    {
148
        // command line options is used preferred
149
        if (vm.count("hpx:bind"))
150
        {
151
            std::string affinity_desc;
348✔
152

232✔
153
            std::vector<std::string> const bind_affinity =
154
                vm["hpx:bind"].as<std::vector<std::string>>();
116✔
155
            for (std::string const& s : bind_affinity)
156
            {
157
                if (!affinity_desc.empty())
158
                    affinity_desc += ";";
159
                affinity_desc += s;
116✔
160
            }
116✔
161

162
            return affinity_desc;
163
        }
24✔
164

165
        // use either cfgmap value or default
166
        return cfgmap.get_value<std::string>("hpx.bind", default_);
128✔
167
    }
168

169
    std::size_t handle_pu_step(util::manage_config const& cfgmap,
170
        hpx::program_options::variables_map const& vm, std::size_t default_)
256✔
171
    {
×
172
        // command line options is used preferred
173
        if (vm.count("hpx:pu-step"))
174
            return vm["hpx:pu-step"].as<std::size_t>();
256✔
175

176
        // use either cfgmap value or default
177
        return cfgmap.get_value<std::size_t>("hpx.pu_step", default_);
128✔
178
    }
179

180
    std::size_t handle_pu_offset(util::manage_config const& cfgmap,
181
        hpx::program_options::variables_map const& vm, std::size_t default_)
256✔
182
    {
×
183
        // command line options is used preferred
184
        if (vm.count("hpx:pu-offset"))
185
            return vm["hpx:pu-offset"].as<std::size_t>();
256✔
186

187
        // use either cfgmap value or default
188
        return cfgmap.get_value<std::size_t>("hpx.pu_offset", default_);
128✔
189
    }
190

191
    std::size_t handle_numa_sensitive(util::manage_config const& cfgmap,
256✔
192
        hpx::program_options::variables_map const& vm, std::size_t default_)
193
    {
194
        if (vm.count("hpx:numa-sensitive") != 0)
×
195
        {
×
196
            std::size_t const numa_sensitive =
197
                vm["hpx:numa-sensitive"].as<std::size_t>();
198
            if (numa_sensitive > 2)
199
            {
×
200
                throw hpx::detail::command_line_error(
201
                    "Invalid argument value for --hpx:numa-sensitive. Allowed "
202
                    "values are 0, 1, or 2");
203
            }
204
            return numa_sensitive;
205
        }
256✔
206

207
        // use either cfgmap value or default
208
        return cfgmap.get_value<std::size_t>("hpx.numa_sensitive", default_);
209
    }
192✔
210

211
    ///////////////////////////////////////////////////////////////////////
192✔
212
    std::size_t get_number_of_default_threads(bool use_process_mask)
213
    {
×
214
        if (use_process_mask)
×
215
        {
216
            threads::topology const& top = threads::create_topology();
192✔
217
            return threads::count(top.get_cpubind_mask());
218
        }
219
        return static_cast<std::size_t>(threads::hardware_concurrency());
384✔
220
    }
221

384✔
222
    std::size_t get_number_of_default_cores(bool use_process_mask)
384✔
223
    {
224
        threads::topology const& top = threads::create_topology();
384✔
225
        std::size_t const num_cores = top.get_number_of_cores();
226

×
227
        if (use_process_mask)
228
        {
229
            threads::mask_type const proc_mask = top.get_cpubind_mask();
×
230
            std::size_t num_cores_proc_mask = 0;
231

232
            for (std::size_t num_core = 0; num_core < num_cores; ++num_core)
×
233
            {
×
234
                threads::mask_type core_mask =
235
                    top.init_core_affinity_mask_from_core(num_core);
×
236
                if (threads::bit_and(core_mask, proc_mask))
237
                {
238
                    ++num_cores_proc_mask;
239
                }
240
            }
241

242
            return num_cores_proc_mask;
243
        }
244

245
        return num_cores;
246
    }
128✔
247

248
    ///////////////////////////////////////////////////////////////////////
249
    std::size_t handle_num_threads(util::manage_config const& cfgmap,
250
        hpx::util::runtime_configuration const& rtcfg,
251
        hpx::program_options::variables_map const& vm, bool use_process_mask)
252
    {
253
        // If using the process mask we override "cores" and "all" options but
128✔
254
        // keep explicit numeric values.
255
        std::size_t const init_threads =
128✔
256
            get_number_of_default_threads(use_process_mask);
257
        std::size_t const init_cores =
128✔
258
            get_number_of_default_cores(use_process_mask);
512✔
259

260
        auto threads_str = cfgmap.get_value<std::string>("hpx.os_threads",
261
            rtcfg.get_entry("hpx.os_threads", std::to_string(init_threads)));
128✔
262

263
        std::size_t threads;
124✔
264
        if ("cores" == threads_str)
265
        {
4✔
266
            threads = init_cores;
267
        }
4✔
268
        else if ("all" == threads_str)
269
        {
270
            threads = init_threads;
271
        }
×
272
        else
273
        {
274
            threads = cfgmap.get_value<std::size_t>("hpx.os_threads",
275
                hpx::util::from_string<std::size_t>(threads_str));
256✔
276
        }
277

256✔
278
        if (vm.count("hpx:threads"))
128✔
279
        {
280
            threads_str = vm["hpx:threads"].as<std::string>();
×
281
            if ("all" == threads_str)
282
            {
128✔
283
                threads = init_threads;
284
            }
×
285
            else if ("cores" == threads_str)
286
            {
287
                threads = init_cores;
288
            }
128✔
289
            else
290
            {
291
                threads = hpx::util::from_string<std::size_t>(threads_str);
128✔
292
            }
293

294
            if (threads == 0)
×
295
            {
296
                throw hpx::detail::command_line_error(
297
                    "Number of --hpx:threads must be greater than 0");
298
            }
299

300
#if defined(HPX_HAVE_MAX_CPU_COUNT)
301
            if (threads > HPX_HAVE_MAX_CPU_COUNT)
302
            {
303
                // clang-format off
304
                throw hpx::detail::command_line_error("Requested more than "
305
                    HPX_PP_STRINGIZE(HPX_HAVE_MAX_CPU_COUNT)" --hpx:threads "
306
                    "to use for this application, use the option "
307
                    "-DHPX_WITH_MAX_CPU_COUNT=<N> when configuring HPX.");
308
                // clang-format on
309
            }
310
#endif
311
        }
312

128✔
313
        // make sure minimal requested number of threads is observed
314
        auto min_os_threads =
128✔
315
            cfgmap.get_value<std::size_t>("hpx.force_min_os_threads", threads);
316

317
        if (min_os_threads == 0)
×
318
        {
319
            throw hpx::detail::command_line_error(
320
                "Number of hpx.force_min_os_threads must be greater than 0");
321
        }
322

323
#if defined(HPX_HAVE_MAX_CPU_COUNT)
324
        if (min_os_threads > HPX_HAVE_MAX_CPU_COUNT)
325
        {
326
            // clang-format off
327
            throw hpx::detail::command_line_error(
328
                "Requested more than " HPX_PP_STRINGIZE(HPX_HAVE_MAX_CPU_COUNT)
329
                " hpx.force_min_os_threads to use for this application, use the "
330
                "option -DHPX_WITH_MAX_CPU_COUNT=<N> when configuring HPX.");
331
            // clang-format on
128✔
332
        }
333
#endif
128✔
334
        threads = (std::max) (threads, min_os_threads);
335

336
        return threads;
192✔
337
    }
338

339
    std::size_t handle_num_cores_default(util::manage_config& cfgmap,
340
        hpx::program_options::variables_map const& vm, std::size_t num_threads,
384✔
341
        std::size_t num_default_cores)
192✔
342
    {
343
        auto cores_str = cfgmap.get_value<std::string>("hpx.cores", "");
×
344
        if ("all" == cores_str)
345
        {
346
            cfgmap.config_["hpx.cores"] = std::to_string(num_default_cores);
347
        }
192✔
348

384✔
349
        auto num_cores =
350
            cfgmap.get_value<std::size_t>("hpx.cores", num_threads);
×
351
        if (vm.count("hpx:cores"))
×
352
        {
353
            cores_str = vm["hpx:cores"].as<std::string>();
354
            if ("all" == cores_str)
355
            {
356
                num_cores = num_default_cores;
357
            }
×
358
            else
359
            {
360
                num_cores = hpx::util::from_string<std::size_t>(cores_str);
361
            }
192✔
362
        }
363

364
        return num_cores;
×
365
    }
366

367
    std::size_t handle_num_cores(util::manage_config& cfgmap,
368
        hpx::program_options::variables_map const& vm, std::size_t num_threads,
128✔
369
        bool use_process_mask)
×
370
    {
371
        return handle_num_cores_default(cfgmap, vm, num_threads,
372
            get_number_of_default_cores(use_process_mask));
×
373
    }
374

×
375
    void print_config(std::vector<std::string> const& ini_config)
×
376
    {
×
377
        std::cerr << "Configuration before runtime start:\n";
378
        std::cerr << "-----------------------------------\n";
379
        for (std::string const& s : ini_config)
380
        {
×
381
            std::cerr << s << std::endl;
×
382
        }
383
        std::cerr << "-----------------------------------\n";
384
    }
64✔
385

386
    ///////////////////////////////////////////////////////////////////////
387
    command_line_handling::command_line_handling(
64✔
388
        hpx::util::runtime_configuration rtcfg,
64✔
389
        std::vector<std::string> ini_config,
390
        hpx::function<int(hpx::program_options::variables_map& vm)> hpx_main_f)
391
      : rtcfg_(HPX_MOVE(rtcfg))
64✔
392
      , ini_config_(HPX_MOVE(ini_config))
64✔
393
      , hpx_main_f_(HPX_MOVE(hpx_main_f))
64✔
394
      , num_threads_(1)
64✔
395
      , num_cores_(1)
64✔
396
      , pu_step_(1)
64✔
397
      , pu_offset_(static_cast<std::size_t>(-1))
64✔
398
      , numa_sensitive_(0)
64✔
399
      , use_process_mask_(false)
64✔
400
      , cmd_line_parsed_(false)
401
      , info_printed_(false)
64✔
402
      , version_printed_(false)
403
    {
128✔
404
    }
405

128✔
406
    void command_line_handling::check_affinity_domain() const
407
    {
×
408
        if (affinity_domain_ != "pu")
×
409
        {
×
410
            if (0 != std::string("pu").find(affinity_domain_) &&
×
411
                0 != std::string("core").find(affinity_domain_) &&
412
                0 != std::string("numa").find(affinity_domain_) &&
413
                0 != std::string("machine").find(affinity_domain_))
414
            {
×
415
                throw hpx::detail::command_line_error(
416
                    "Invalid command line option --hpx:affinity, value must be "
417
                    "one of: pu, core, numa, or machine.");
128✔
418
            }
419
        }
128✔
420
    }
421

128✔
422
    void command_line_handling::check_affinity_description() const
423
    {
424
        if (affinity_bind_.empty())
425
        {
426
            return;
128✔
427
        }
128✔
428

128✔
429
        if (!(pu_offset_ == static_cast<std::size_t>(-1) ||
430
                pu_offset_ == static_cast<std::size_t>(0)) ||
431
            pu_step_ != 1 || affinity_domain_ != "pu")
432
        {
×
433
            throw hpx::detail::command_line_error(
434
                "Command line option --hpx:bind should not be used with "
435
                "--hpx:pu-step, --hpx:pu-offset, or --hpx:affinity.");
436
        }
128✔
437
    }
438

128✔
439
    void command_line_handling::check_pu_offset() const
440
    {
×
441
        if (pu_offset_ != static_cast<std::size_t>(-1) &&
442
            pu_offset_ >=
443
                static_cast<std::size_t>(hpx::threads::hardware_concurrency()))
444
        {
×
445
            throw hpx::detail::command_line_error(
446
                "Invalid command line option --hpx:pu-offset, value must be "
128✔
447
                "smaller than number of available processing units.");
448
        }
128✔
449
    }
450

128✔
451
    void command_line_handling::check_pu_step() const
128✔
452
    {
128✔
453
        if (hpx::threads::hardware_concurrency() > 1 &&
128✔
454
            (pu_step_ == 0 ||
455
                pu_step_ >= static_cast<std::size_t>(
456
                                hpx::threads::hardware_concurrency())))
457
        {
458
            throw hpx::detail::command_line_error(
×
459
                "Invalid command line option --hpx:pu-step, value must be "
460
                "non-zero and smaller than number of available processing "
128✔
461
                "units.");
462
        }
192✔
463
    }
464

465
    void command_line_handling::handle_high_priority_threads(
466
        hpx::program_options::variables_map const& vm,
384✔
467
        std::vector<std::string>& ini_config) const
468
    {
469
        if (vm_.count("hpx:high-priority-threads"))
×
470
        {
×
471
            std::size_t const num_high_priority_queues =
×
472
                vm["hpx:high-priority-threads"].as<std::size_t>();
473
            if (num_high_priority_queues != static_cast<std::size_t>(-1) &&
474
                num_high_priority_queues > num_threads_)
475
            {
476
                throw hpx::detail::command_line_error(
×
477
                    "Invalid command line option: number of high priority "
478
                    "threads (--hpx:high-priority-threads), should not be "
479
                    "larger than number of threads (--hpx:threads)");
×
480
            }
481

482
            if (!(queuing_ == "local-priority" || queuing_ == "abp-priority" ||
483
                    queuing_.find("local-workrequesting") != 0))
484
            {
×
485
                throw hpx::detail::command_line_error(
486
                    "Invalid command line option --hpx:high-priority-threads, "
487
                    "valid for --hpx:queuing=local-priority, "
×
488
                    "--hpx:queuing=local-workrequesting-fifo, "
×
489
                    "--hpx:queuing=local-workrequesting-lifo, "
490
                    "--hpx:queuing=local-workrequesting-mc, "
192✔
491
                    "and --hpx:queuing=abp-priority only");
492
            }
493

128✔
494
            ini_config.emplace_back("hpx.thread_queue.high_priority_queues!=" +
495
                std::to_string(num_high_priority_queues));
496
        }
497
    }
128✔
498

499
    ///////////////////////////////////////////////////////////////////////////
256✔
500
    bool command_line_handling::handle_arguments(util::manage_config& cfgmap,
501
        hpx::program_options::variables_map& vm,
502
        std::vector<std::string>& ini_config)
36✔
503
    {
504
#if !defined(HPX_HAVE_DISTRIBUTED_RUNTIME)
12✔
505
        bool const debug_clp = vm.count("hpx:debug-clp");
12✔
506

507
        // fill logging default
128✔
508
        enable_logging_settings(vm, ini_config);
128✔
509

384✔
510
        // handle command line arguments after logging defaults
511
        if (vm.count("hpx:ini"))
512
        {
513
            std::vector<std::string> cfg =
514
                vm["hpx:ini"].as<std::vector<std::string>>();
515
            std::copy(cfg.begin(), cfg.end(), std::back_inserter(ini_config));
516
            cfgmap.add(cfg);
517
        }
518
#endif
519

520
        use_process_mask_ =
521
            (cfgmap.get_value<int>("hpx.use_process_mask", 0) > 0) ||
522
            (vm.count("hpx:use-process-mask") > 0);
128✔
523

128✔
524
#if defined(__APPLE__)
525
        if (use_process_mask_)
526
        {
128✔
527
            std::cerr
128✔
528
                << "Warning: enabled process mask for thread binding, but "
529
                   "thread binding is not supported on macOS. Ignoring option."
128✔
530
                << std::endl;
128✔
531
            use_process_mask_ = false;
532
        }
128✔
533
#endif
534

256✔
535
        ini_config.emplace_back(
128✔
536
            "hpx.use_process_mask!=" + std::to_string(use_process_mask_));
537

538
        // handle setting related to schedulers
539
        queuing_ = detail::handle_queuing(cfgmap, vm, "local-priority-fifo");
540
        ini_config.emplace_back("hpx.scheduler=" + queuing_);
541

542
        affinity_domain_ = detail::handle_affinity(cfgmap, vm, "pu");
543
        ini_config.emplace_back("hpx.affinity=" + affinity_domain_);
544

232✔
545
        check_affinity_domain();
546

547
        affinity_bind_ = detail::handle_affinity_bind(cfgmap, vm, "");
548
        if (!affinity_bind_.empty())
128✔
549
        {
550
#if defined(__APPLE__)
551
            std::cerr << "Warning: thread binding set to \"" << affinity_bind_
552
                      << "\" but thread binding is not supported on macOS. "
553
                         "Ignoring option."
554
                      << std::endl;
555
            affinity_bind_ = "";
556
#else
557
            ini_config.emplace_back("hpx.bind!=" + affinity_bind_);
558
#endif
559
        }
256✔
560

561
        pu_step_ = detail::handle_pu_step(cfgmap, vm, 1);
128✔
562
#if defined(__APPLE__)
563
        if (pu_step_ != 1)
128✔
564
        {
128✔
565
            std::cerr << "Warning: PU step set to \"" << pu_step_
566
                      << "\" but thread binding is not supported on macOS. "
128✔
567
                         "Ignoring option."
568
                      << std::endl;
569
            pu_step_ = 1;
570
        }
571
#endif
572
        ini_config.emplace_back("hpx.pu_step=" + std::to_string(pu_step_));
573

574
        check_pu_step();
575

576
        pu_offset_ =
×
577
            detail::handle_pu_offset(cfgmap, vm, static_cast<std::size_t>(-1));
×
578

579
        if (pu_offset_ != static_cast<std::size_t>(-1))
580
        {
581
#if defined(__APPLE__)
582
            std::cerr << "Warning: PU offset set to \"" << pu_offset_
128✔
583
                      << "\" but thread binding is not supported on macOS. "
584
                         "Ignoring option."
585
                      << std::endl;
128✔
586
            pu_offset_ = std::size_t(-1);
587
            ini_config.emplace_back("hpx.pu_offset=0");
244✔
588
#else
589
            ini_config.emplace_back(
128✔
590
                "hpx.pu_offset=" + std::to_string(pu_offset_));
128✔
591
#endif
592
        }
593
        else
594
        {
128✔
595
            ini_config.emplace_back("hpx.pu_offset=0");
596
        }
597

598
        check_pu_offset();
599

600
        numa_sensitive_ = detail::handle_numa_sensitive(
601
            cfgmap, vm, affinity_bind_.empty() ? 0 : 1);
602
        ini_config.emplace_back(
24✔
603
            "hpx.numa_sensitive=" + std::to_string(numa_sensitive_));
604

605
        // default affinity mode is now 'balanced' (only if no pu-step or
128✔
606
        // pu-offset is given)
607
        if (pu_step_ == 1 && pu_offset_ == static_cast<std::size_t>(-1) &&
608
            affinity_bind_.empty())
128✔
609
        {
128✔
610
#if defined(__APPLE__)
256✔
611
            affinity_bind_ = "none";
128✔
612
#else
613
            affinity_bind_ = "balanced";
614
#endif
128✔
615
            ini_config.emplace_back("hpx.bind!=" + affinity_bind_);
128✔
616
        }
256✔
617

618
        check_affinity_description();
619

128✔
620
        // handle number of cores and threads
621
        num_threads_ =
128✔
622
            detail::handle_num_threads(cfgmap, rtcfg_, vm, use_process_mask_);
623
        num_cores_ = detail::handle_num_cores(
128✔
624
            cfgmap, vm, num_threads_, use_process_mask_);
625

×
626
        // Set number of cores and OS threads in configuration.
627
        ini_config.emplace_back(
628
            "hpx.os_threads=" + std::to_string(num_threads_));
128✔
629
        ini_config.emplace_back("hpx.cores=" + std::to_string(num_cores_));
630

631
        // handle high-priority threads
632
        handle_high_priority_threads(vm, ini_config);
256✔
633

634
#if !defined(HPX_HAVE_DISTRIBUTED_RUNTIME)
635
        if (debug_clp)
636
        {
637
            print_config(ini_config);
512✔
638
        }
639
#endif
×
640

×
641
        return true;
×
642
    }
×
643

×
644
    ///////////////////////////////////////////////////////////////////////////
×
645
    void command_line_handling::enable_logging_settings(
×
646
        hpx::program_options::variables_map& vm,
×
647
        [[maybe_unused]] std::vector<std::string>& ini_config)
648
    {
649
#if defined(HPX_HAVE_LOGGING)
650
        if (vm.count("hpx:debug-hpx-log"))
512✔
651
        {
652
            ini_config.emplace_back("hpx.logging.console.destination=" +
×
653
                detail::convert_to_log_file(
×
654
                    vm["hpx:debug-hpx-log"].as<std::string>()));
×
655
            ini_config.emplace_back("hpx.logging.destination=" +
×
656
                detail::convert_to_log_file(
×
657
                    vm["hpx:debug-hpx-log"].as<std::string>()));
×
658
            ini_config.emplace_back("hpx.logging.console.level=5");
×
659
            ini_config.emplace_back("hpx.logging.level=5");
×
660
        }
661

662
#if defined(HPX_LOGGING_HAVE_SEPARATE_DESTINATIONS)
663
        if (vm.count("hpx:debug-timing-log"))
664
        {
665
            ini_config.emplace_back("hpx.logging.console.timing.destination=" +
666
                detail::convert_to_log_file(
667
                    vm["hpx:debug-timing-log"].as<std::string>()));
668
            ini_config.emplace_back("hpx.logging.timing.destination=" +
669
                detail::convert_to_log_file(
670
                    vm["hpx:debug-timing-log"].as<std::string>()));
512✔
671
            ini_config.emplace_back("hpx.logging.console.timing.level=1");
672
            ini_config.emplace_back("hpx.logging.timing.level=1");
×
673
        }
×
674
#else
×
675
        if (vm.count("hpx:debug-timing-log"))
×
676
        {
×
677
            throw hpx::detail::command_line_error(
×
678
                "Command line option error: can't enable logging while it was "
×
679
                "disabled at configuration time. Please re-configure HPX using "
×
680
                "the option -DHPX_LOGGING_WITH_SEPARATE_DESTINATIONS=On.");
×
681
        }
682
#endif
683
        if (vm.count("hpx:debug-app-log"))
684
        {
685
            ini_config.emplace_back(
686
                "hpx.logging.console.application.destination=" +
687
                detail::convert_to_log_file(
688
                    vm["hpx:debug-app-log"].as<std::string>()));
689
            ini_config.emplace_back("hpx.logging.application.destination=" +
690
                detail::convert_to_log_file(
691
                    vm["hpx:debug-app-log"].as<std::string>()));
692
            ini_config.emplace_back("hpx.logging.console.application.level=5");
693
            ini_config.emplace_back("hpx.logging.application.level=5");
694
        }
256✔
695
#else
696
        if (vm.count("hpx:debug-hpx-log") || vm.count("hpx:debug-timing-log") ||
697
            vm.count("hpx:debug-app-log"))
64✔
698
        {
699
            // clang-format off
700
            throw hpx::detail::command_line_error(
701
                "Command line option error: can't enable logging while it "
702
                "was disabled at configuration time. Please re-configure "
703
                "HPX using the option -DHPX_WITH_LOGGING=On.");
273✔
704
            // clang-format on
705
        }
706
#endif
418✔
707
    }
708

709
    ///////////////////////////////////////////////////////////////////////////
209✔
710
    void command_line_handling::store_command_line(int argc, char** argv)
711
    {
712
        // Collect the command line for diagnostic purposes.
713
        std::string command;
714
        std::string cmd_line;
715
        std::string options;
145✔
716
        for (int i = 0; i < argc; ++i)
717
        {
718
            // quote only if it contains whitespace
209✔
719
            std::string arg = detail::encode_and_enquote(argv[i]);    //-V108
720

721
            cmd_line += arg;
722
            if (i == 0)
723
            {
724
                command = arg;
725
            }
64✔
726
            else
64✔
727
            {
128✔
728
                options += " " + arg;
209✔
729
            }
730

731
            if ((i + 1) != argc)
64✔
732
            {
733
                cmd_line += " ";
734
            }
735
        }
736

737
        // Store the program name and the command line.
64✔
738
        ini_config_.emplace_back("hpx.cmd_line!=" + cmd_line);
739
        ini_config_.emplace_back("hpx.commandline.command!=" + command);
740
        ini_config_.emplace_back("hpx.commandline.options!=" + options);
2✔
741
    }
742

743
    ///////////////////////////////////////////////////////////////////////////
2✔
744
    void command_line_handling::store_unregistered_options(
745
        std::string const& cmd_name, int argc, char* argv[],
746
        std::vector<std::string> const& unregistered_options)
2✔
747
    {
2✔
748
        std::string unregistered_options_cmd_line;
749

750
        if (!unregistered_options.empty())
751
        {
64✔
752
            auto const end = unregistered_options.end();
64✔
753
            for (auto it = unregistered_options.begin(); it != end; ++it)
256✔
754
            {
64✔
755
#if defined(HPX_GCC_VERSION) && HPX_GCC_VERSION >= 110000
64✔
756
#pragma GCC diagnostic push
757
#pragma GCC diagnostic ignored "-Wrestrict"
758
#endif
64✔
759
                unregistered_options_cmd_line +=
760
                    " " + detail::encode_and_enquote(*it);
761
#if defined(HPX_GCC_VERSION) && HPX_GCC_VERSION >= 110000
128✔
762
#pragma GCC diagnostic pop
763
#endif
×
764
            }
×
765

766
            ini_config_.emplace_back("hpx.unknown_cmd_line!=" +
767
                detail::encode_and_enquote(cmd_name) +
×
768
                unregistered_options_cmd_line);
769
        }
770

×
771
        ini_config_.emplace_back("hpx.program_name!=" + cmd_name);
772
        ini_config_.emplace_back("hpx.reconstructed_cmd_line!=" +
773
            std::string(" ") + reconstruct_command_line(argc, argv));
774
    }
×
775

×
776
    ///////////////////////////////////////////////////////////////////////////
×
777
    bool command_line_handling::handle_help_options(
×
778
        hpx::program_options::options_description const& help)
×
779
    {
×
780
        if (vm_.count("hpx:help"))
×
781
        {
782
            std::string const help_option(vm_["hpx:help"].as<std::string>());
783
            if (0 == std::string("minimal").find(help_option))
×
784
            {
785
                // print static help only
786
                std::cout << help << std::endl;
×
787
                return true;
788
            }
789
            else if (0 == std::string("full").find(help_option))
790
            {
791
                // defer printing help until after dynamic part has been
792
                // acquired
64✔
793
                std::ostringstream strm;
794
                strm << help << std::endl;
795
                ini_config_.emplace_back(
128✔
796
                    "hpx.cmd_line_help!=" + detail::encode_string(strm.str()));
797
                ini_config_.emplace_back(
798
                    "hpx.cmd_line_help_option!=" + help_option);
×
799
            }
×
800
            else
×
801
            {
802
                throw hpx::detail::command_line_error(hpx::util::format(
803
                    "Invalid argument for option --hpx:help: '{1}', allowed "
804
                    "values: 'minimal' (default) and 'full'",
805
                    help_option));
806
            }
807
        }
808
        return false;
809
    }
810

811
    void command_line_handling::handle_attach_debugger()
×
812
    {
×
813
#if defined(_POSIX_VERSION) || defined(HPX_WINDOWS)
814
        if (vm_.count("hpx:attach-debugger"))
×
815
        {
816
            std::string const option =
817
                vm_["hpx:attach-debugger"].as<std::string>();
818
            if (option != "off" && option != "startup" &&
64✔
819
                option != "exception" && option != "test-failure")
820
            {
821
                // clang-format off
822
                std::cerr <<
64✔
823
                    "hpx::init: command line warning: --hpx:attach-debugger: "
824
                    "invalid option: " << option << ". Allowed values are "
825
                    "'off', 'startup', 'exception' or 'test-failure'" << std::endl;
64✔
826
                // clang-format on
64✔
827
            }
828
            else
829
            {
830
                if (option == "startup")
831
                    util::attach_debugger();
64✔
832

833
                ini_config_.emplace_back("hpx.attach_debugger!=" + option);
834
            }
835
        }
64✔
836
#endif
837
    }
838

839
    ///////////////////////////////////////////////////////////////////////////
64✔
840
    // separate command line arguments from configuration settings
841
    std::vector<std::string> command_line_handling::preprocess_config_settings(
842
        int argc, char** argv)
×
843
    {
844
        std::vector<std::string> options;
×
845
        options.reserve(static_cast<std::size_t>(argc) + ini_config_.size());
846

847
        // extract all command line arguments from configuration settings and
×
848
        // remove them from this list
849
        auto const it =
850
            std::stable_partition(ini_config_.begin(), ini_config_.end(),
851
                [](std::string const& e) { return e.find("--hpx:") != 0; });
209✔
852

853
        std::move(it, ini_config_.end(), std::back_inserter(options));
145✔
854
        ini_config_.erase(it, ini_config_.end());
855

856
        // store the command line options that came from the configuration
64✔
857
        // settings in the registry
×
858
        if (!options.empty())
859
        {
860
            std::string config_options;
64✔
861
            for (auto const& option : options)
862
            {
863
                config_options += " " + option;
64✔
864
            }
865

866
            rtcfg_.add_entry("hpx.commandline.config_options", config_options);
867
        }
868

×
869
        // now append all original command line options
870
        for (int i = 1; i != argc; ++i)
871
        {
×
872
            options.emplace_back(argv[i]);
873
        }
874

×
875
        return options;
876
    }
877

64✔
878
    ///////////////////////////////////////////////////////////////////////////
879
    std::vector<std::string> prepend_options(
880
        std::vector<std::string>&& args, std::string&& options)
881
    {
128✔
882
        if (options.empty())
192✔
883
        {
884
            return HPX_MOVE(args);
×
885
        }
886

887
        hpx::string_util::escaped_list_separator sep('\\', ' ', '\"');
888
        hpx::string_util::tokenizer const tok(options, sep);
64✔
889

128✔
890
        std::vector<std::string> result(tok.begin(), tok.end());
891
        std::move(args.begin(), args.end(), std::back_inserter(result));
12✔
892
        return result;
6✔
893
    }
894

895
    ///////////////////////////////////////////////////////////////////////////
896
    void command_line_handling::reconfigure(
897
        util::manage_config& cfgmap, hpx::program_options::variables_map& prevm)
898
    {
899
        // re-initialize runtime configuration object
900
        if (prevm.count("hpx:config"))
64✔
901
            rtcfg_.reconfigure(prevm["hpx:config"].as<std::string>());
64✔
902
        else
903
            rtcfg_.reconfigure("");
904

905
        // Make sure any aliases defined on the command line get used for the
906
        // option analysis below.
64✔
907
        std::vector<std::string> cfg;
64✔
908
        if (prevm.count("hpx:ini"))
909
        {
32✔
910
            cfg = prevm["hpx:ini"].as<std::vector<std::string>>();
911
            cfgmap.add(cfg);
912
        }
913

914
        // append ini options from command line
32✔
915
        std::copy(
916
            ini_config_.begin(), ini_config_.end(), std::back_inserter(cfg));
917

32✔
918
        // enable logging if invoked requested from command line
919
        std::vector<std::string> ini_config_logging;
32✔
920
        enable_logging_settings(prevm, ini_config_logging);
921

922
        std::copy(ini_config_logging.begin(), ini_config_logging.end(),
34✔
923
            std::back_inserter(cfg));
4✔
924

925
        rtcfg_.reconfigure(cfg);
926
    }
927

928
    int command_line_handling::call(
64✔
929
        hpx::program_options::options_description const& desc_cmdline, int argc,
930
        char** argv)
931
    {
932
        // set the flag signaling that command line parsing has been done
933
        cmd_line_parsed_ = true;
934

935
        // separate command line arguments from configuration settings
936
        std::vector<std::string> args = preprocess_config_settings(argc, argv);
32✔
937

938
        util::manage_config cfgmap(ini_config_);
32✔
939

940
        // insert the pre-configured ini settings before loading modules
941
        for (std::string const& e : ini_config_)
942
            rtcfg_.parse("<user supplied config>", e, true, false);
943

944
        // support re-throwing command line exceptions for testing purposes
945
        util::commandline_error_mode error_mode =
946
            util::commandline_error_mode::allow_unregistered;
947
        if (cfgmap.get_value("hpx.commandline.rethrow_errors", 0) != 0)
32✔
948
        {
64✔
949
            error_mode |= util::commandline_error_mode::rethrow_on_error;
950
        }
951

952
        // The cfg registry may hold command line options to prepend to the
953
        // real command line.
954
        std::string prepend_command_line =
955
            rtcfg_.get_entry("hpx.commandline.prepend_options");
32✔
956

32✔
957
        args = prepend_options(HPX_MOVE(args), HPX_MOVE(prepend_command_line));
958

959
        // Initial analysis of the command line options. This is preliminary as
960
        // it will not take into account any aliases as defined in any of the
961
        // runtime configuration files.
32✔
962
        {
32✔
963
            // Boost V1.47 and before do not properly reset a variables_map when
964
            // calling vm.clear(). We work around that problems by creating a
965
            // separate instance just for the preliminary command line handling.
966
            hpx::program_options::variables_map prevm;
32✔
967
            if (!parse_commandline(
32✔
968
                    rtcfg_, desc_cmdline, argv[0], args, prevm, error_mode))
969
            {
970
                return -1;
64✔
971
            }
972

973
            // handle all --hpx:foo options
974
            std::vector<std::string> ini_config;    // discard
975
            if (!handle_arguments(cfgmap, prevm, ini_config))
976
            {
977
                return -2;
32✔
978
            }
979

980
            reconfigure(cfgmap, prevm);
32✔
981
        }
982

983
        // Re-run program option analysis, ini settings (such as aliases) will
984
        // be considered now.
985
        hpx::program_options::options_description help;
32✔
986
        std::vector<std::string> unregistered_options;
987

64✔
988
        error_mode |= util::commandline_error_mode::report_missing_config_file;
989
        if (!parse_commandline(rtcfg_, desc_cmdline, argv[0], args, vm_,
64✔
990
                error_mode, &help, &unregistered_options))
991
        {
992
            return -1;
993
        }
994

64✔
995
        // break into debugger, if requested
64✔
996
        handle_attach_debugger();
997

998
        // handle all --hpx:foo options
64✔
999
        if (!handle_arguments(cfgmap, vm_, ini_config_))
1000
        {
1001
            return -2;
64✔
1002
        }
1003

1004
        return finalize_commandline_handling(
1005
            argc, argv, help, unregistered_options);
1006
    }
1007

128✔
1008
    int command_line_handling::finalize_commandline_handling(int argc,
1009
        char** argv, hpx::program_options::options_description const& help,
×
1010
        std::vector<std::string> const& unregistered_options)
1011
    {
×
1012
        // store unregistered command line and arguments
×
1013
        store_command_line(argc, argv);
1014
        store_unregistered_options(argv[0], argc, argv, unregistered_options);
1015

×
1016
        // add all remaining ini settings to the global configuration
1017
        rtcfg_.reconfigure(ini_config_);
1018

1019
        // help can be printed only after the runtime mode has been set
128✔
1020
        if (handle_help_options(help))
1021
        {
×
1022
            return 1;    // exit application gracefully
1023
        }
×
1024

×
1025
        // print version/copyright information
1026
        if (vm_.count("hpx:version"))
1027
        {
×
1028
            if (!version_printed_)
1029
            {
1030
                detail::print_version(std::cout);
1031
                version_printed_ = true;
1032
            }
1033

1034
            return 1;
1035
        }
1036

1037
        // print configuration information (static and dynamic)
1038
        if (vm_.count("hpx:info"))
1039
        {
1040
            if (!info_printed_)
1041
            {
1042
                detail::print_info(std::cout, *this);
1043
                info_printed_ = true;
1044
            }
1045

1046
            return 1;
1047
        }
1048

1049
        // all is good
1050
        return 0;
1051
    }
1052
}    // namespace hpx::local::detail
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

© 2026 Coveralls, Inc