libadc-cxx 1.0.0
Structured logging for scientific computing
Loading...
Searching...
No Matches
builder.ipp
Go to the documentation of this file.
1/* Copyright 2025 NTESS. See the top-level LICENSE.txt file for details.
2 *
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5#include <pwd.h>
6#include <unistd.h>
7#include <time.h>
8#include <sys/time.h>
9#include <sys/utsname.h>
10#include <sstream>
11#include <fstream>
12#include <array>
13#include <ostream>
14#include <utility>
15#include <string>
16#include <cstdio>
17#include <cstdint>
18#include <boost/algorithm/string.hpp>
19#include <uuid/uuid.h>
20#include <version>
21#if defined(__cpp_lib_span) && __cpp_lib_span >= 202002L
22#include <span>
23#endif
24
27
28/* implementation note:
29 * do not use language features beyond c++17.
30 */
31
32namespace adc {
33
34// \return true if name is not allowed in our json protocol.
35// keys containing . and / are escapable by the related search
36// RFCs, but we may want to warn about them in future.
37// / is not banned though it interferes with search using RFC6901
38// . is not banned though it interferes with search using RFC9535
39inline bool badkey(std::string_view /* name */)
40{
41 // return (name.find_first_of("./") != std::string::npos);
42 // could put a warning here in debug mode
43 return false;
44}
45
46// \return view of s without leading or trailing character '/' (or other)
47std::string_view strip(std::string_view s, char c = '/')
48{
49 auto first = s.find_first_not_of(c);
50 if (std::string::npos == first) {
51 return "";
52 }
53 auto last = s.find_last_not_of(c);
54 auto len = last - first + 1;
55 return s.substr(first, len);
56}
57
58std::string tolower_s(std::string s)
59{
60 std::transform(s.begin(), s.end(), s.begin(),
61 [](unsigned char c){ return std::tolower(c); }
62 );
63 return s;
64}
65
66std::string get_exe(size_t bufsize)
67{
68 ssize_t ret;
69 auto buffer = std::make_unique<char[]>(bufsize);
70 ret = readlink("/proc/self/exe", buffer.get(), bufsize - 1);
71 if (ret < 0)
72 sprintf(buffer.get(),"(nullexe)");
73 else
74 buffer.get()[ret] = '\0';
75 return buffer.get();
76}
77
78// std date and formatters are still unstable; use c.
79// fixme strftime isn't 8601 tz format compliant (no xx:yy) adc::format_timespec_8601
80// fixme consider using nanoseconds instead of milli. adc::format_timespec_8601
81std::string format_timespec_8601(struct timespec& ts)
82{
83 struct tm tm;
84 char prefix[40] = "YYYY-MM-ddTHH:mm:ss";
85 char suffix[40] = "0000 ";
86 char millis[20];
87 localtime_r(&ts.tv_sec, &tm);
88 sprintf(millis, "%03ld", ts.tv_nsec / 1000000);
89 strftime(prefix, sizeof(prefix), "%Y-%m-%dT%H:%M:%S.", &tm);
90 strftime(suffix, sizeof(suffix), "%z", &tm);
91 std::string buf = std::string(prefix) + std::string(millis) + std::string(suffix);
92 return buf;
93
94}
95
96std::string format_timespec_utc_ns(struct timespec& ts)
97{
98 char str[40];
99 snprintf(str, sizeof(str), "%ld.%09ld",(long)ts.tv_sec, (long)ts.tv_nsec);
100 return std::string(str);
101}
102
103std::string get_lscpu(){
104 pipe_data lscpu = out_pipe::run("lscpu -J");
105 if (lscpu.rc) {
106 return "{}";
107 }
108 return lscpu.output;
109}
110
111#define CPUINFO_FILE "/proc/cpuinfo"
113{
114 static std::string affinity_all;
115 if (affinity_all.size() < 2) {
116 std::ifstream in(CPUINFO_FILE);
117 std::string line;
118 uint32_t pmax = 0;
119 while (std::getline(in, line)) {
120 size_t cp = line.find(' ');
121 std::string name = line.substr(0, cp);
122 if (name == "processor") {
123 cp = line.find(':');
124 std::istringstream iss(line.substr(cp+1));
125 uint32_t val;
126 iss >> val;
127 if (iss.fail()) {
128 return 0;
129 } else {
130 pmax = (val > pmax ? val : pmax);
131 }
132 }
133 }
134 if (pmax == 0) {
135 affinity_all = "0-1024";
136 } else {
137 std::ostringstream oss;
138 oss << "0-" << pmax;
139 affinity_all = oss.str();
140 }
141 }
142 return affinity_all;
143}
144
145
146std::vector< std::string> get_libs(std::string_view )
147{
148 pid_t pid = getpid();
149 std::ostringstream cmd;
150 cmd << "/usr/bin/grep r-xp /proc/" << pid << "/maps | /usr/bin/grep '\\.so' | /usr/bin/sed -e 's%.* /%/%g'";
151 pipe_data proclibs = out_pipe::run(cmd.str());
152 std::vector< std::string> libs;
153 if (proclibs.rc) {
154 return libs;
155 }
156 std::istringstream nss(proclibs.output);
157 std::string line;
158 while (std::getline(nss, line)) {
159 libs.push_back(line);
160 }
161 return libs;
162}
163
164boost::json::object get_numa_hardware()
165{
166 static bool shell_done;
167 static boost::json::object numa_json;
168 if (!shell_done) {
169 shell_done = 1;
170 pipe_data numactl = out_pipe::run("numactl -H");
171 size_t numa_node_count = 0;
172 if (! numactl.rc) {
173 std::vector<std::string> cpulist;
174 std::vector<int64_t> sizes;
175 shell_done = 1;
176 adc::pipe_data numactl = adc::out_pipe::run("numactl -H");
177 std::string line;
178 std::istringstream nss(numactl.output);
179 while (std::getline(nss, line)) {
180 size_t cp = line.find(':');
181 std::string name = line.substr(0, cp);
182 if (name == "available") {
183 std::istringstream iss(line.substr(cp+1));
184 iss >> numa_node_count;
185 continue;
186 }
187 if (name.substr(name.length()-4) == "cpus" ) {
188 cpulist.push_back(line.substr(cp+2));
189 continue;
190 }
191 if (name.substr(name.length()-4) == "size" ) {
192 std::istringstream iss(line.substr(cp+1));
193 int64_t mb;
194 iss >> mb;
195 sizes.push_back(mb);
196 continue;
197 }
198 if (name.substr(name.length()-4) == "nces" )
199 break; // stop on "node distances:"
200 }
201 if (! numa_node_count || sizes.size() != numa_node_count || cpulist.size() != numa_node_count)
202 return numa_json; // inconsistent data
203#if 0 // FLAT lists
204 numa_json["numa_node_count"] = numa_node_count;
205 numa_json["numa_cpu_list"] = boost::json::value_from(cpulist);
206 numa_json["numa_node_megabyte"] = boost::json::value_from(sizes);
207#endif
208 boost::json::array na;
209 for (size_t i = 0; i < numa_node_count; i++) {
210 boost::json::object o = {
211 {"node_number", i},
212 {"node_megabytes", sizes[i]},
213 {"cpu_list", cpulist[i]}
214 };
215 na.emplace_back(o);
216 }
217 numa_json["node_list"] = na;
218 }
219 }
220 return numa_json;
221}
222
223boost::json::object get_gpu_data(bool debug)
224{
225 static bool shell_done;
226 static boost::json::object gpu_json;
227 if (!shell_done) {
228#ifdef ADC_GPU_DEBUG
229 std::cerr << "add_host_section: doing gpu" <<std::endl;
230#endif
231 size_t gpu_count = 0;
232 shell_done = 1;
233 pipe_data lspci = out_pipe::run("lspci -vmm |grep -B1 -A 6 -i '3d controller'");
234 if (! lspci.rc) {
235#ifdef ADC_GPU_DEBUG
236 std::cerr << "add_host_section: parsing gpu" <<std::endl;
237#endif
238 std::vector<std::string> vendor;
239 std::vector<std::string> device;
240 std::vector<std::string> rev;
241 std::vector<int32_t> numa_node;
242 shell_done = 1;
243 std::string line;
244 std::istringstream nss(lspci.output);
245 while (std::getline(nss, line)) {
246 if (line.substr(0,1) == "-")
247 continue;
248 size_t cp = line.find(':');
249 std::string name = line.substr(0, cp);
250 if (name == "Class") {
251 std::string cname = boost::algorithm::trim_copy(line.substr(cp+1));
252 if (cname == "3D controller") {
253 if (debug) {
254 std::cerr << "add_host_section: found gpu " << gpu_count <<std::endl;
255 }
256 gpu_count++;
257 } else {
258 if (debug) {
259 std::cerr << "add_host_section: found non-gpu " << cname <<std::endl;
260 }
261 }
262 continue;
263 }
264 if (name == "Vendor" ) {
265 vendor.push_back(boost::algorithm::trim_copy(line.substr(cp+1)));
266 continue;
267 }
268 if (name == "Device" ) {
269 device.push_back(boost::algorithm::trim_copy(line.substr(cp+1)));
270 continue;
271 }
272 if (name == "Rev" ) {
273 rev.push_back(boost::algorithm::trim_copy(line.substr(cp+1)));
274 continue;
275 }
276 if (name == "NUMANode" ) {
277 std::istringstream iss(line.substr(cp+1));
278 int32_t nn;
279 iss >> nn;
280 numa_node.push_back(nn);
281 continue;
282 }
283 }
284 if (vendor.size() != gpu_count ||
285 device.size() != gpu_count ||
286 rev.size() != gpu_count ||
287 numa_node.size() != gpu_count) {
288 if (debug) {
289 std::cerr << "add_host_section: size mismatch " <<
290 gpu_count <<
291 vendor.size() <<
292 device.size() <<
293 rev.size() <<
294 numa_node.size() <<
295 std::endl;
296 }
297 return gpu_json; // inconsistent data
298 }
299 gpu_json["gpu_count"] = gpu_count;
300 boost::json::array ga;
301 for (size_t i = 0; i < gpu_count; i++) {
302 boost::json::object o = {
303 {"gpu_number", i},
304 {"numa_node", numa_node[i]},
305 {"vendor", vendor[i]},
306 {"device", device[i]},
307 {"rev", rev[i]}
308 };
309 ga.emplace_back(o);
310 }
311 gpu_json["gpulist"] = ga;
312 }
313#ifdef ADC_GPU_DEBUG
314 else {
315 std::cerr << "add_host_section: lspci fail " << lspci.rc <<std::endl;
316 }
317#endif
318 }
319 return gpu_json;
320}
321
322#define MEMINFO_FILE "/proc/meminfo"
323
324struct item {
325 std::string name;
326 uint64_t value;
327};
328
329// data read from MEMINFO_FILE or computed therewith
330// direct read fields are not documented.
331static std::map<std::string, uint64_t> midata = {
332 {"MemTotal", 0 },
333 {"MemUsed", 0 }, // total - free
334 {"MemFree", 0 },
335 {"Shmem", 0 },
336 {"SReclaimable", 0 },
337 {"Buffers", 0 },
338 {"Cached", 0 },
339 {"CachedAll", 0 }, // Cached + SReclaimable,
340 {"MemAvailable", 0 },
341 {"SwapTotal", 0 },
342 {"SwapUsed", 0 }, // SwapTotal - SwapFree
343 {"SwapFree", 0},
344 {"valid", 0} // is the full map populated
345};
346
347// the number of undocumented members of midata (those read directly)
348const size_t meminfo_raw = 9;
349
350// get MemTotal off top of /proc/meminfo
351uint64_t get_memtotal()
352{
353 std::ifstream in(MEMINFO_FILE);
354 std::string line;
355 while (std::getline(in, line)) {
356 size_t cp = line.find(':');
357 std::string name = line.substr(0, cp);
358 if (name == "MemTotal") {
359 std::istringstream iss(line.substr(cp+1));
360 uint64_t val;
361 iss >> val;
362 if (iss.fail()) {
363 return 0;
364 } else {
365 return val;
366 }
367 }
368 }
369 return 0;
370}
371
372// on return, midata["valid"] is 1 if successful or 0 if a problem reading all
373// expected values.
374// produce data equivalent to free -k -w
375void update_meminfo(bool debug)
376{
377 std::ifstream in(MEMINFO_FILE);
378
379 std::string line;
380 size_t count = 0;
381 while (std::getline(in, line)) {
382 size_t cp = line.find(':');
383 std::string name = line.substr(0, cp);
384 auto it = midata.find(name);
385 if (it != midata.end()) {
386 std::istringstream iss(line.substr(cp+1));
387 uint64_t val;
388 iss >> val;
389 if (iss.fail()) {
390 if (debug) {
391 std::cerr << "error reading value for " << it->first << std::endl;
392 }
393 continue;
394 }
395 it->second = val;
396 count++;
397#ifdef ADC_MEMINFO_DEBUG
398 std::cerr << "parsed meminfo." << name << std::endl;
399#endif
400 }
401 }
402 if (count == meminfo_raw) {
403 midata["valid"] = 1;
404 midata["MemUsed"] = midata["MemTotal"] - midata["MemFree"];
405 midata["SwapUsed"] = midata["SwapTotal"] - midata["SwapFree"];
406 midata["CachedAll"] = midata["Cached"] + midata["SReclaimable"];
407 if (midata["MemAvailable"] > midata["MemTotal"]) {
408 midata["MemAvailable"] = midata["MemFree"];
409 // work around container misreporting
410 // documented in procps utility 'free'
411 }
412 midata["valid"] = 1;
413 } else {
414 midata["valid"] = 0;
415#ifdef ADC_MEMINFO_DEBUG
416 std::cerr << "read meminfo failed" << std::endl;
417 std::cerr << "count = " << count << std::endl;
418 std::cerr << "mdsize = " << midata.size() << std::endl;
419#endif
420 }
421}
422
423builder::builder(void *mpi_communicator_p) : mpi_comm_p(mpi_communicator_p) {
424 const char *env = getenv("ADC_BUILDER_DEBUG");
425 if (env) {
426 debug = true;
427 }
428}
429
430// auto-populate the header section with application name
431void builder::add_header_section(std::string_view application_name)
432{
433 uid_t uid = geteuid();
434 struct passwd pw;
435 struct passwd *pwp = nullptr;
436 int bsize = sysconf(_SC_GETPW_R_SIZE_MAX);
437 if (bsize < 0)
438 bsize = 16384;
439 char buf[bsize];
440 getpwuid_r(uid, &pw, buf, bsize, &pwp);
441 const char *uname;
442 if (pwp) {
443 uname = pw.pw_name;
444 } else {
445 uname = "<unknown_user>";
446 }
447 struct timespec ts;
448 clock_gettime(CLOCK_REALTIME, &ts);
449 std::string ts_8601 = format_timespec_8601(ts);
450 std::string ts_ns = format_timespec_utc_ns(ts);
451
452 boost::json::object vv = {
453 { "version", adc::enum_version.name}
454 };
455 vv["tags"] = boost::json::value_from(adc::enum_version.tags);
456 uuid_t uuid;
457 uuid_generate_random(uuid);
458 char uuidbuf[40];
459 uuid_unparse_lower(uuid, uuidbuf);
460 boost::json::value jv = {
461 {"adc_api_version", vv },
462 {"timestamp", ts_ns },
463 {"datestamp", ts_8601 },
464 {"user", uname },
465 {"uid", std::to_string(uid) },
466 {"application", application_name},
467 {"uuid", uuidbuf }
468 };
469 d["header"] = jv;
470}
471
472
473std::vector<std::string> split_string(const std::string& s, char delimiter)
474{
475 std::vector<std::string> tokens;
476 std::stringstream ss(s);
477 std::string token;
478 while (std::getline(ss, token, delimiter)) {
479 if (token.length() > 0) {
480 tokens.push_back(token);
481 }
482 }
483 return tokens;
484}
485
486std::vector<std::string> builder::get_host_env_vars()
487{
488 const char *env = getenv("ADC_HOST_SECTION_ENV");
489 if (env) {
490 return split_string(std::string(env), ':');
491 }
492 std::vector<std::string> s;
493 return s;
494}
495
496#define ADC_HS_BAD ~(ADC_HS_ALL)
497// auto-populate the host section
498void builder::add_host_section(int32_t subsections)
499{
500
501 if (ADC_HS_BAD & subsections) {
502 if (debug) {
503 std::cerr << "bad arg to add_host_section: " << subsections <<std::endl;
504 }
505 return;
506 }
507 struct utsname ubuf;
508 int uerr = uname(&ubuf);
509 if (uerr < 0) {
510 if (debug) {
511 std::cerr << "uname failed in add_host_section" <<std::endl;
512 }
513 return;
514 }
515 boost::json::object jv = {
516 {"name", std::string(ubuf.nodename) }
517 };
518 if ( subsections & ADC_HS_OS ) {
519 jv["os_family"] = std::string(ubuf.sysname);
520 jv["os_version"] = std::string(ubuf.release);
521 jv["os_arch"] = std::string(ubuf.machine);
522 jv["os_build"] = std::string(ubuf.version);
523 }
524 if (subsections & ADC_HS_ENV) {
525 std::vector<std::string> env_vars = get_host_env_vars();
526 for (auto it = env_vars.begin();
527 it != env_vars.end(); it++) {
528 const char *s = getenv(it->c_str());
529 if (!s)
530 s = "";
531 jv[*it] = std::string(s);
532 }
533 }
534 if (subsections & ADC_HS_RAMSIZE) {
535 if (midata["valid"] == 1) {
536 jv["mem_total"] = midata["MemTotal"];
537 } else {
538 update_meminfo(debug);
539 if (midata["valid"] == 1)
540 jv["mem_total"] = midata["MemTotal"];
541 else
542 jv["mem_total"] = 0;
543 }
544 }
545 if (subsections & ADC_HS_CPU) {
546 boost::system::error_code ec;
547 std::string lscpu = get_lscpu();
548 auto cv = boost::json::parse(lscpu, ec);
549 if (ec) {
550 if (debug) {
551 std::cerr << "unable to parse ("<< ec <<") lscpu output: " <<
552 lscpu << std::endl;
553 }
554 }
555 jv["cpu"] = cv;
556 }
557 if (subsections & ADC_HS_GPU) {
558 jv["gpu"] = get_gpu_data(debug);
559 }
560 if (subsections & ADC_HS_NUMA) {
561 jv["numa_hardware"] = get_numa_hardware();
562 }
563
564 d["host"] = jv;
565}
566
567// populate application run-time data to app_data section.
568// any relationship to previous jobs/higher level workflows goes in app_data
569// somehow.
570void builder::add_app_data_section(std::shared_ptr< builder_api > app_data)
571{
572 auto app_data_derived = std::dynamic_pointer_cast<builder>(app_data);
573 boost::json::object no_details;
574 d["app_data"] = app_data_derived ? app_data_derived->flatten() : no_details;
575}
576
578 update_meminfo(debug);
579 if (midata["valid"] == 0) {
580 boost::json::object jv;
581 d["memory_usage"] = jv;
582 return;
583 }
584
585 boost::json::value jv = {
586 {"mem_total", midata["MemTotal"]},
587 {"mem_used", midata["MemUsed"]},
588 {"mem_free", midata["MemFree"]},
589 {"mem_shared", midata["Shmem"]},
590 {"mem_buffers", midata["Buffers"]},
591 {"mem_cache", midata["CachedAll"]},
592 {"mem_available", midata["MemAvailable"]},
593 {"swap_total", midata["SwapTotal"]},
594 {"swap_used", midata["SwapUsed"]},
595 {"swap_free", midata["SwapFree"]}
596 };
597 d["memory_usage"] = jv;
598}
599
600// populate application run-time physics (re)configuration/result to model_data section.
601// e.g. changes in mesh/particle decomp go here.
602void builder::add_model_data_section(std::shared_ptr< builder_api > model_data)
603{
604 auto model_data_derived = std::dynamic_pointer_cast<builder>(model_data);
605 boost::json::object no_details;
606 d["model_data"] = model_data_derived ? model_data_derived->flatten() : no_details;
607}
608
609// auto-populate code section with os-derived info at time of call,
610// tag, version, and code_details blob.
611void builder::add_code_section(std::string tag, std::shared_ptr< builder_api > version, std::shared_ptr< builder_api > code_details)
612{
613 std::string fullpath = get_exe(4096);
614 const char *basename = strrchr(fullpath.c_str(), '/');
615 if (!basename)
616 basename = fullpath.c_str();
617 else
618 basename++;
619 std::vector< std::string> libs = get_libs(fullpath);
620 auto code_details_derived = std::dynamic_pointer_cast<builder>(code_details);
621 auto version_derived = std::dynamic_pointer_cast<builder>(version);
622 boost::json::object no_details;
623 boost::json::value jv = {
624 {"name", tag },
625 {"program", basename},
626 {"path", fullpath},
627 {"version", version_derived ? version_derived->d : no_details},
628 {"libs", boost::json::value_from(libs) },
629 {"details", code_details_derived ?
630 code_details_derived->flatten() : no_details}
631 };
632 d["code"] = jv;
633}
634
635// populate build/install configuration information like options enabled
636void builder::add_code_configuration_section(std::shared_ptr< builder_api > build_details)
637{
638 auto build_details_derived = std::dynamic_pointer_cast<builder>(build_details);
639 if (build_details_derived) {
640 d["code_configuration"] = build_details_derived->flatten();
641 } else {
642 boost::json::object no_details;
643 d["code_configuration"] = no_details;
644 }
645}
646
647// populate exit_data section
648void builder::add_exit_data_section(int return_code, std::string status, std::shared_ptr< builder_api > status_details)
649{
650 auto status_details_derived = std::dynamic_pointer_cast<builder>(status_details);
651 if (status_details && status_details_derived) {
652 boost::json::value jv = {
653 { "return_code", std::to_string(return_code)},
654 { "status", status},
655 { "details", status_details_derived->flatten()}
656 };
657 d["exit_data"] = jv;
658 } else {
659 boost::json::object no_details;
660 boost::json::value jv = {
661 { "return_code", std::to_string(return_code)},
662 { "status", status},
663 { "details", no_details }
664 };
665 d["exit_data"] = jv;
666 }
667}
668
669
670void builder::add_section(std::string_view name, std::shared_ptr< builder_api > section)
671{
672 if (!section) {
673 return;
674 }
675 auto nk = kind(name);
676 if (nk == k_none) {
677 auto section_derived = std::dynamic_pointer_cast<builder>(section);
678 if (!section_derived) {
679 return;
680 }
681 sections[std::string(name)] = std::move(section_derived);
682 }
683}
684
685std::shared_ptr< builder_api > builder::get_section(std::string_view name)
686{
687 auto nk = kind(name);
688 if (nk == k_section)
689 return sections[std::string(name)];
690 // should we throw here instead? probably not, for optional sections
691 return std::shared_ptr< builder_api >(nullptr);
692}
693
694std::vector< std::string > builder::get_section_names()
695{
696 std::vector< std::string > result;
697 result.reserve(sections.size());
698 for (const auto& element : sections) {
699 result.push_back(element.first);
700 }
701 return result;
702}
703
704std::vector< std::string > builder::get_field_names()
705{
706// iterate d keys
707// can we return an interator here instead?
708 std::vector< std::string > result;
709 for (auto const& element : d) {
710 result.push_back(element.key());
711 }
712 return result;
713}
714
715static void get_scalar(field& f, scalar_type st, boost::json::value *v)
716{
717 boost::json::string *s;
718 switch (st) {
719 case cp_bool:
720 f.data = variant(*(v->if_bool()));
721 f.vp = &std::get< bool >(f.data);
722 f.count = 1;
723 return;
724 case cp_char:
725 f.data = variant(static_cast<char>(*(v->if_int64())));
726 f.vp = &std::get< char >(f.data);
727 f.count = 1;
728 return;
729 case cp_char16:
730 f.data = variant(static_cast<char16_t>(*(v->if_uint64())));
731 f.vp = &std::get< char16_t >(f.data);
732 f.count = 1;
733 return;
734 case cp_char32:
735 f.data = variant(static_cast<char32_t>(*(v->if_uint64())));
736 f.vp = &std::get< char32_t >(f.data);
737 f.count = 1;
738 return;
739 case cp_uint8:
740 f.data = variant(static_cast<uint8_t>(*(v->if_uint64())));
741 f.vp = &std::get< uint8_t >(f.data);
742 f.count = 1;
743 return;
744 case cp_uint16:
745 f.data = variant(static_cast<uint16_t>(*(v->if_uint64())));
746 f.vp = &std::get< uint16_t >(f.data);
747 f.count = 1;
748 return;
749 case cp_uint32:
750 f.data = variant(static_cast<uint32_t>(*(v->if_uint64())));
751 f.vp = &std::get< uint32_t >(f.data);
752 f.count = 1;
753 return;
754 case cp_uint64:
755 s = v->if_string();
756 if (s) {
757 uint64_t u64;
758 std::string ss(*s);
759 std::istringstream iss(ss);
760 iss >> u64;
761 f.data = variant(u64);
762 f.vp = &std::get< uint64_t >(f.data);
763 f.count = 1;
764 }
765 return;
766 case cp_int8:
767 f.data = variant(static_cast<int8_t>(*(v->if_int64())));
768 f.vp = &std::get< int8_t >(f.data);
769 f.count = 1;
770 return;
771 case cp_int16:
772 f.data = variant(static_cast<int16_t>(*(v->if_int64())));
773 f.vp = &std::get< int16_t >(f.data);
774 f.count = 1;
775 return;
776 case cp_int32:
777 f.data = variant(static_cast<int32_t>(*(v->if_int64())));
778 f.vp = &std::get< int32_t >(f.data);
779 f.count = 1;
780 return;
781 case cp_int64:
782 f.data = variant(*(v->if_int64()));
783 f.vp = &std::get< int64_t >(f.data);
784 f.count = 1;
785 return;
786 case cp_epoch:
787 f.data = variant(*(v->if_int64()));
788 f.vp = &std::get< int64_t >(f.data);
789 f.count = 1;
790 return;
791 // char *
792 // fallthrough block for all string variants
793 case cp_cstr:
794 case cp_json_str:
795 case cp_yaml_str:
796 case cp_xml_str:
797 case cp_json:
798 case cp_path:
799 case cp_number_str:
800 s = v->if_string();
801 if (s) {
802#ifdef ADC_GV_STR_DEBUG
803 std::cerr << "s c ptr " << (void*)(s->c_str()) << std::endl;
804#endif
805 std::string ss(*s);
806#ifdef ADC_GV_STR_DEBUG
807 std::cerr << "ss c ptr " << (void*)(ss.c_str()) << std::endl;
808#endif
809 //f.data = variant(ss);
810 f.data = ss;
811 //f.vp = (&std::get< std::string >(f.data))->data();
812#ifdef ADC_GV_STR_DEBUG
813 std::cerr << "f ptr " << (void*)&f.kt << std::endl;
814 std::cerr << "f.data " << (void*)&std::get< std::string >(f.data) << std::endl;
815 std::cerr << "f.data>data " << (void*)std::get< std::string >(f.data).data() << std::endl;
816#endif
817 f.vp = (&std::get< std::string >(f.data))->data();
818#ifdef ADC_GV_STR_DEBUG
819 std::cerr << "f.vp " << f.vp << std::endl;
820#endif
821 f.count = s->size();
822 }
823 return;
824 case cp_f32:
825 f.data = variant( static_cast<float>(*(v->if_double())));
826 f.vp = &std::get< float >(f.data);
827 f.count = 1;
828 return;
829 case cp_f64:
830 f.data = variant( *(v->if_double()));
831 f.vp = &std::get< double >(f.data);
832 f.count = 1;
833 return;
834#if ADC_SUPPORT_EXTENDED_FLOATS
835 case cp_f80:
836 // prec fixme cp_F80 get_value
837 return;
838#endif
839#if ADC_SUPPORT_QUAD_FLOATS
840 case cp_f128:
841 // prec fixme cp_F128 get_value
842 return;
843#endif
844#if ADC_SUPPORT_GPU_FLOATS
845 // prec fixme cp_Fx gpufloats get_value
846 case cp_f8_e4m3:
847 case cp_f8_e5m2:
848 case cp_f16_e5m10:
849 case cp_f16_e8m7:
850 return;
851#endif
852 case cp_c_f32: {
853 std::complex<float> cv(0,0);
854 if (v->is_array()) {
855 float re = 0, im = 0;
856 auto a = v->as_array();
857 if (a.size() == 2 &&
858 a[0].is_double() && a[1].is_double() ) {
859 re = static_cast<float>(a[0].as_double());
860 im = static_cast<float>(a[1].as_double());
861 cv = { re, im};
862 }
863 }
864 f.data = variant(cv) ;
865 f.vp = &std::get< std::complex<float> >(f.data);
866 f.count = 1;
867 }
868 return;
869 case cp_c_f64: {
870 std::complex<double> cv(0,0);
871 if (v->is_array()) {
872 double re = 0, im = 0;
873 auto a = v->as_array();
874 if (a.size() == 2 &&
875 a[0].is_double() &&
876 a[1].is_double() ) {
877 re = a[0].as_double();
878 im = a[1].as_double();
879 cv = { re, im};
880 }
881 }
882 f.data = variant(cv) ;
883 f.vp = &std::get< std::complex<double> >(f.data);
884 f.count = 1;
885 }
886 return;
887#if ADC_SUPPORT_EXTENDED_FLOATS
888 case cp_c_f80:
889 // prec fixme cp_c_F80 get_value
890#endif
891#if ADC_SUPPORT_QUAD_FLOATS
892 case cp_c_f128:
893 // prec fixme cp_c_F128 get_value
894#endif
895 case cp_timespec:
896 // fallthrough
897 case cp_timeval: {
898 std::array<int64_t, 2> av = {0,0};
899 if (v->is_array()) {
900 int64_t sec = 0, subsec = 0;
901 auto a = v->as_array();
902 if (a.size() == 2 &&
903 a[0].is_int64() &&
904 a[1].is_int64() ) {
905 sec = a[0].as_int64();
906 subsec = a[1].as_int64();
907 av = { sec, subsec};
908 }
909 }
910 f.data = variant(av);
911 f.vp = &std::get< std::array<int64_t,2> >(f.data);
912 f.count = 1;
913 }
914 return;
915 default:
916 return;
917 }
918}
919
920/* copy all elements of a matching type st into matching positions
921 * of a shared array. type mismatches are silently ignored, their
922 * values being converted to 0.
923 *
924 * As we are querying arrays we built, there should never be a mismatch.
925 */
926template<typename T>
927static void fill_array(field& f, scalar_type st, boost::json::array& a) {
928 auto a_len = a.size();
929 //c++20 std::shared_ptr<T[]> sa = std::make_shared<T[]>(a_len);
930 std::shared_ptr<T[]> sa(new T[a_len]);
931 size_t i;
932 auto json_type = scalar_type_representation(st);
933 for (i = 0; i < a_len; i++) {
934 if ( a[i].kind() == json_type) {
935 boost::system::error_code ec;
936 T x = a[i].to_number<T>(ec);
937 if (!ec.failed()) {
938 sa[i] = x;
939 } else {
940 sa[i] = 0;
941 }
942 } else {
943 sa[i] = 0;
944 }
945 }
946 f.data = variant( sa ) ;
947 f.vp = (std::get< std::shared_ptr<T[]> >(f.data)).get();
948 f.count = a_len;
949};
950
951static void fill_array_u64(field& f, boost::json::array& a) {
952 auto a_len = a.size();
953 //c++20 std::shared_ptr<uint64_t[]> sa = std::make_shared<uint64_t[]>(a_len);
954 std::shared_ptr<uint64_t[]> sa(new uint64_t[a_len]);
955 size_t i;
956 for (i = 0; i < a_len; i++) {
957 boost::json::string *s = a[i].if_string();
958 if ( s ) {
959 std::string ss (*s);
960 std::istringstream iss(ss);
961 uint64_t x;
962 iss >> x;
963 if (!iss.fail()) {
964 sa[i] = x;
965 } else {
966 sa[i] = 0;
967 }
968 } else {
969 sa[i] = 0;
970 }
971 }
972 f.data = variant( sa ) ;
973 f.vp = (std::get< std::shared_ptr<uint64_t[]> >(f.data)).get();
974 f.count = a_len;
975};
976
977/* copy pairs of a matching type st into matching positions
978 * of a shared array of complex. type mismatches are silently ignored, their
979 * values being converted to 0.
980 *
981 * As we are querying arrays we built, there should never be a mismatch.
982 */
983template<typename T>
984static void fill_array_complex(field& f, boost::json::array& a) {
985 auto a_len = a.size();
986 // c++20: std::shared_ptr<std::complex<T>[]> sa = std::make_shared<std::complex<T>[]>(a_len);
987 std::shared_ptr<std::complex<T>[]> sa(new std::complex<T>[a_len]);
988 size_t i;
989 for (i = 0; i < a_len; i++) {
990 if ( a[i].kind() == boost::json::kind::array) {
991 auto pair = a[i];
992 if (pair.as_array().size() != 2) {
993 sa[i] = { 0, 0 };
994 continue;
995 }
996 boost::system::error_code ecr;
997 boost::system::error_code eci;
998 T re = pair.as_array()[0].to_number<T>(ecr);
999 T im = pair.as_array()[1].to_number<T>(eci);
1000 if (!(ecr.failed() || eci.failed())) {
1001 sa[i] = { re, im };
1002 } else {
1003 sa[i] = { 0, 0 };
1004 }
1005 } else {
1006 sa[i] = { 0, 0 };
1007 }
1008 }
1009 f.data = variant( sa ) ;
1010 f.vp = (std::get< std::shared_ptr<std::complex<T>[]> >(f.data)).get();
1011 f.count = a_len;
1012};
1013
1014/* expects a json array of boolean values. any non-boolean value is
1015 * mapped to false in the output array.
1016 */
1017void fill_array_bool(field& f, boost::json::array& a) {
1018 auto a_len = a.size();
1019 //c++20 std::shared_ptr<T[]> sa = std::make_shared<T[]>(a_len);
1020 std::shared_ptr<bool[]> sa(new bool[a_len]);
1021 size_t i;
1022 for (i = 0; i < a_len; i++) {
1023 bool* bptr = a[i].if_bool();
1024 if (bptr) {
1025 sa[i] = *bptr;
1026 } else {
1027 sa[i] = false;
1028 }
1029 }
1030 f.data = variant( sa ) ;
1031 f.vp = (std::get< std::shared_ptr<bool[]> >(f.data)).get();
1032 f.count = a_len;
1033}
1034
1035void fill_array_string(field& f, boost::json::array& a) {
1036 auto a_len = a.size();
1037 //c++20 std::shared_ptr<T[]> sa = std::make_shared<T[]>(a_len);
1038 std::shared_ptr<std::string[]> sa(new std::string[a_len]);
1039 size_t i;
1040 for (i = 0; i < a_len; i++) {
1041 boost::json::string* sptr = a[i].if_string();
1042 if (sptr) {
1043 sa[i] = std::string(sptr->c_str());
1044 } else {
1045 sa[i] = std::string("");
1046 }
1047 }
1048 f.data = variant( sa ) ;
1049 f.vp = (std::get< std::shared_ptr<std::string[]> >(f.data)).get();
1050 f.count = a_len;
1051}
1052
1053static void get_array(field& f, scalar_type st, boost::json::value *v)
1054{
1055 if (!v->is_array()) {
1056 return;
1057 }
1058
1059 auto a = v->as_array();
1060 switch (st) {
1061 case cp_bool:
1062 fill_array_bool(f, a);
1063 return;
1064 case cp_char:
1065 fill_array<char>(f, st, a);
1066 return;
1067 case cp_char16:
1068 fill_array<char16_t>(f, st, a);
1069 return;
1070 case cp_char32:
1071 fill_array<char32_t>(f, st, a);
1072 return;
1073 case cp_uint8:
1074 fill_array<uint8_t>(f, st, a);
1075 return;
1076 case cp_uint16:
1077 fill_array<uint16_t>(f, st, a);
1078 return;
1079 case cp_uint32:
1080 fill_array<uint32_t>(f, st, a);
1081 return;
1082 case cp_uint64:
1083 fill_array_u64(f, a);
1084 return;
1085 case cp_int8:
1086 fill_array<int8_t>(f, st, a);
1087 return;
1088 case cp_int16:
1089 fill_array<int16_t>(f, st, a);
1090 return;
1091 case cp_int32:
1092 fill_array<int32_t>(f, st, a);
1093 return;
1094 case cp_int64:
1095 fill_array<int64_t>(f, st, a);
1096 return;
1097 // fallthrough block for all string variants
1098 case cp_cstr:
1099 case cp_json_str:
1100 case cp_yaml_str:
1101 case cp_xml_str:
1102 case cp_json:
1103 case cp_path:
1104 case cp_number_str:
1105 fill_array_string(f, a);
1106 return;
1107 case cp_f32:
1108 fill_array<float>(f, st, a);
1109 return;
1110 case cp_f64:
1111 fill_array<double>(f, st, a);
1112 return;
1113#if ADC_SUPPORT_EXTENDED_FLOATS
1114 case cp_f80:
1115 // prec fixme cp_F80 get_value
1116 return;
1117#endif
1118#if ADC_SUPPORT_QUAD_FLOATS
1119 case cp_f128:
1120 // prec fixme cp_F128 get_value
1121 return;
1122#endif
1123#if ADC_SUPPORT_GPU_FLOATS
1124 // prec fixme cp_Fx gpufloats get_value
1125 case cp_f8_e4m3:
1126 case cp_f8_e5m2:
1127 case cp_f16_e5m10:
1128 case cp_f16_e8m7:
1129 return;
1130#endif
1131 case cp_c_f32:
1132 fill_array_complex<float>(f, a);
1133 return;
1134 case cp_c_f64:
1135 fill_array_complex<double>(f, a);
1136 return;
1137#if ADC_SUPPORT_EXTENDED_FLOATS
1138 case cp_c_f80:
1139 // prec fixme cp_c_F80 get_value
1140#endif
1141#if ADC_SUPPORT_QUAD_FLOATS
1142 case cp_c_f128:
1143 // prec fixme cp_c_F128 get_value
1144#endif
1145 default:
1146 return;
1147 }
1148}
1149
1150const field builder::get_value(std::string_view path_full)
1151{
1152 // hunt up value by following the path, independent of
1153 // section vs json and typed-tuple vs not.
1154 field f = { k_none, cp_none, nullptr, 0, "", variant() };
1155 auto path = strip(path_full, '/');
1156 key_type kt;
1157 // recurse anything which is a leading section.
1158 auto pos = path.find("/");
1159 std::string_view name;
1160 if (pos == std::string::npos) {
1161 name = path;
1162 } else {
1163 name = std::string_view(path.begin(), pos);
1164 }
1165 kt = kind(name);
1166 const boost::json::value jv = d;
1167 const boost::json::value * jit;
1168 boost::system::error_code ec;
1169 auto child = path.substr(pos);
1170 switch (kt) {
1171 case k_none:
1172 return f;
1173 case k_section:
1174 return sections[std::string(name)]->get_value(child);
1175 case k_value:
1176 jit = d[name].find_pointer(child, ec);
1177 if (!jit) {
1178 if (debug) {
1179 std::cerr << "get_value json missing: " << child << ": " << ec.message() << std::endl;
1180 }
1181 return f;
1182 } else {
1183 if (debug) {
1184 std::cerr << "get_value found: " << child << ": " << *jit << std::endl;
1185 }
1186 }
1187 }
1188
1189 if (jit->kind() == boost::json::kind::object) {
1190 auto obj = jit->as_object();
1191 auto v = obj.if_contains("value");
1192 auto type_name_v = obj.if_contains("type");
1193 std::string type_name;
1194 if (type_name_v) {
1195 type_name = *(type_name_v->if_string());
1196 }
1197 if (v && type_name.size()) {
1198 auto c = obj.if_contains("container_type");
1199 auto st = scalar_type_from_name(type_name);
1200 if (!c) {
1201 f.st = st;
1202 get_scalar(f, st, v);
1203 return f;
1204 } else {
1205 f.st = st;
1206 f.container = *(c->if_string());
1207 get_array(f, st, v);
1208 return f;
1209 }
1210 }
1211 }
1212 if (jit->kind() == boost::json::kind::array) {
1213 // untyped json arrays in get_value make no sense
1214 return f;
1215 }
1216 if (jit->kind() == boost::json::kind::object) {
1217 // object in get_value make no sense
1218 return f;
1219 }
1220 if (jit->kind() == boost::json::kind::string) {
1221 // bare strings default to cp_cstr
1222 f.kt = kt;
1223 f.st = cp_cstr;
1224 f.vp = jit->if_string()->c_str();
1225 f.count = jit->if_string()->size();
1226 return f;
1227 }
1228 if (jit->kind() == boost::json::kind::bool_) {
1229 f.kt = kt;
1230 f.st = cp_bool;
1231 f.vp = jit->if_bool();
1232 f.count = 1;
1233 return f;
1234 }
1235 if (jit->kind() == boost::json::kind::int64) {
1236 f.kt = kt;
1237 f.st = cp_int64;
1238 f.vp = jit->if_int64();
1239 f.count = 1;
1240 return f;
1241 }
1242 if (jit->kind() == boost::json::kind::uint64) {
1243 f.kt = kt;
1244 f.st = cp_uint64;
1245 f.vp = jit->if_uint64();
1246 f.count = 1;
1247 return f;
1248 }
1249 if (jit->kind() == boost::json::kind::double_) {
1250 f.kt = kt;
1251 f.st = cp_f64;
1252 f.vp = jit->if_double();
1253 f.count = 1;
1254 return f;
1255 }
1256 return f;
1257}
1258
1259const char *builder::get_value_string(std::string_view path) {
1260 field f = get_value(path);
1261 if (f.kt != k_value || f.container.size() != 0)
1262 return nullptr;
1263 switch (f.st) {
1264 case cp_cstr:
1265 case cp_json_str:
1266 case cp_yaml_str:
1267 case cp_xml_str:
1268 case cp_json:
1269 case cp_path:
1270 case cp_number_str:
1271 return static_cast<const char *>(f.vp);
1272 default:
1273 return nullptr;
1274 }
1275}
1276
1277int64_t builder::get_value_int64(std::string_view path) {
1278 field f = get_value(path);
1279 if (f.kt != k_value || f.count != 1)
1280 return INT64_MAX;
1281 int64_t i = INT64_MAX;
1282 switch (f.st) {
1283 case cp_bool:
1284 i = *static_cast<const bool *>(f.vp);
1285 break;
1286 case cp_char16:
1287 [[fallthrough]];
1288 case cp_char32:
1289 i = *static_cast<const uint64_t *>(f.vp);
1290 break;
1291 case cp_char:
1292 [[fallthrough]];
1293 case cp_int8:
1294 [[fallthrough]];
1295 case cp_int16:
1296 [[fallthrough]];
1297 case cp_int32:
1298 [[fallthrough]];
1299 case cp_int64:
1300 i = *static_cast<const int64_t *>(f.vp);
1301 break;
1302 default:
1303 break;
1304 }
1305 return i;
1306}
1307
1308uint64_t builder::get_value_uint64(std::string_view path) {
1309 field f = get_value(path);
1310 if (f.kt != k_value || f.count != 1)
1311 return UINT64_MAX;
1312 uint64_t i = UINT64_MAX;
1313 switch (f.st) {
1314 case cp_bool:
1315 i = *static_cast<const bool *>(f.vp);
1316 break;
1317 case cp_char16:
1318 [[fallthrough]];
1319 case cp_char32:
1320 i = *static_cast<const uint64_t *>(f.vp);
1321 break;
1322 case cp_uint8:
1323 [[fallthrough]];
1324 case cp_uint16:
1325 [[fallthrough]];
1326 case cp_uint32:
1327 i = *static_cast<const int64_t *>(f.vp);
1328 break;
1329 case cp_uint64:
1330 i = std::stoull(std::string(static_cast<const char *>(f.vp)));
1331 break;
1332 default:
1333 break;
1334 }
1335 return i;
1336}
1337
1338void builder::add_mpi_section(std::string_view name, void *mpi_comm_p, adc_mpi_field_flags bitflags)
1339{
1340 if (!mpi_comm_p || bitflags == ADC_MPI_NONE)
1341 return;
1342 std::string commname = std::string("mpi_comm_") += name;
1343#ifdef ADC_HAVE_MPI
1344 if (*(MPI_Comm *)mpi_comm_p == MPI_COMM_NULL)
1345 return;
1346 MPI_Comm *comm = (MPI_Comm *)mpi_comm_p;
1347 boost::json::object jv;
1348 int err;
1349 int rank = -1;
1350 if ( bitflags & ADC_MPI_RANK) {
1351 err = MPI_Comm_rank(*comm, &rank);
1352 if (!err) {
1353 jv["mpi_rank"] = rank;
1354 }
1355 }
1356
1357 int size = -1;
1358 if ( bitflags & ADC_MPI_SIZE) {
1359 err = MPI_Comm_size(*comm, &size);
1360 if (!err) {
1361 jv["mpi_size"] = size;
1362 }
1363 }
1364
1365 if ( bitflags & ADC_MPI_NAME) {
1366 char name[MPI_MAX_OBJECT_NAME];
1367 int len = 0;
1368 err = MPI_Comm_get_name(*comm, name, &len);
1369 if (!err) {
1370 jv["mpi_name"] = name;
1371 }
1372 }
1373
1374 int major=0, minor=0;
1375 if ( bitflags & ADC_MPI_VER) {
1376 err = MPI_Get_version(&major, &minor);
1377 std::ostringstream mversion;
1378 mversion << MPI_VERSION << "." << MPI_SUBVERSION;
1379 if (!err) {
1380 jv["mpi_version"] = mversion.str();
1381 }
1382 }
1383
1384 err = 0;
1385 if ( bitflags & ADC_MPI_LIB_VER) {
1386 std::ostringstream lversion;
1387#ifdef OMPI_VERSION
1388#define USE_set_lib_version
1389 lversion << "OpenMPI " << OMPI_MAJOR_VERSION << "." <<
1390 OMPI_MINOR_VERSION << "." << OMPI_RELEASE_VERSION;
1391 goto set_lib_version;
1392#else
1393#ifdef MVAPICH2_VERSION
1394#define USE_set_lib_version
1395 lversion << MVAPICH2_VERSION;
1396 goto set_lib_version;
1397#else
1398#ifdef MPICH_VERSION
1399#define USE_set_lib_version
1400 lversion << MPICH_VERSION;
1401 goto set_lib_version;
1402#endif
1403#endif
1404#endif
1405 char lv[MPI_MAX_LIBRARY_VERSION_STRING];
1406 int sz = 0;
1407 err = MPI_Get_library_version(lv, &sz);
1408 lversion << lv;
1409#ifdef USE_set_lib_version
1410set_lib_version:
1411#endif
1412 if (!err) {
1413 jv["mpi_library_version"] = lversion.str();
1414 }
1415 }
1416
1417 if (bitflags & (ADC_MPI_HOSTLIST | ADC_MPI_RANK_HOST)) {
1418 if (rank < 0) {
1419 err = MPI_Comm_rank(*comm, &rank);
1420 if (err)
1421 goto mpi_out;
1422 }
1423 if (size < 0) {
1424 err = MPI_Comm_size(*comm, &size);
1425 if (err)
1426 goto mpi_out;
1427 }
1428 char *hostnames = (char *)calloc(size*MPI_MAX_PROCESSOR_NAME, 1);
1429 int nlen;
1430 MPI_Get_processor_name(hostnames + rank * MPI_MAX_PROCESSOR_NAME, &nlen);
1431 MPI_Allgather(hostnames + rank * MPI_MAX_PROCESSOR_NAME,
1432 MPI_MAX_PROCESSOR_NAME,
1433 MPI_CHAR,
1434 hostnames,
1435 MPI_MAX_PROCESSOR_NAME,
1436 MPI_CHAR,
1437 *comm);
1438
1439 std::vector<std::string> rh;
1440 for (auto i = 0; i < size; i++)
1441 rh.push_back(std::string(hostnames + rank * MPI_MAX_PROCESSOR_NAME));
1442
1443 if (bitflags & ADC_MPI_RANK_HOST) {
1444 boost::json::array av(rh.begin(), rh.end());
1445 jv["mpi_rank_host"] = av;
1446 }
1447
1448 if (bitflags & ADC_MPI_HOSTLIST ) {
1449 std::vector<std::string> hl;
1450 std::set<std::string> hm;
1451 for (auto i = 0; i < size; i++)
1452 hm.insert(std::string(hostnames + rank * MPI_MAX_PROCESSOR_NAME));
1453 for (auto i = 0; i < size; i++) {
1454 std::string stmp(hostnames + rank * MPI_MAX_PROCESSOR_NAME);
1455 if (hm.count(stmp)) {
1456 hl.push_back(stmp);
1457 hm.erase(stmp);
1458 }
1459 }
1460 boost::json::array hv(hl.begin(), hl.end());
1461 jv["mpi_hostlist"] = hv;
1462 }
1463
1464 free(hostnames);
1465 }
1466
1467mpi_out:
1468
1469#else
1470 // add fake rank 0, size 1, versions and names "none" per bitflags
1471 boost::json::object jv;
1472 int rank = 0;
1473 if ( bitflags & ADC_MPI_RANK) {
1474 jv["mpi_rank"] = rank;
1475 }
1476
1477 int size = 1;
1478 if ( bitflags & ADC_MPI_SIZE) {
1479 jv["mpi_size"] = size;
1480 }
1481
1482 if ( bitflags & ADC_MPI_NAME) {
1483 jv["mpi_name"] = "none";
1484 }
1485
1486 if ( bitflags & ADC_MPI_VER) {
1487 jv["mpi_version"] = "none";
1488 }
1489
1490 if ( bitflags & ADC_MPI_LIB_VER) {
1491 jv["mpi_library_version"] = "none";
1492 }
1493
1494 if (bitflags & (ADC_MPI_HOSTLIST | ADC_MPI_RANK_HOST)) {
1495 char myhost[HOST_NAME_MAX];
1496 int herr = gethostname(myhost, HOST_NAME_MAX);
1497 if (herr < 0)
1498 sprintf(myhost, "name_unavailable");
1499
1500 std::vector<std::string> rh;
1501 rh.push_back(myhost);
1502 boost::json::array av(rh.begin(), rh.end());
1503
1504 if (bitflags & ADC_MPI_RANK_HOST) {
1505 jv["mpi_rank_host"] = av;
1506 }
1507
1508 if (bitflags & ADC_MPI_HOSTLIST ) {
1509 jv["mpi_hostlist"] = av;
1510 }
1511 }
1512#endif // ADC_HAVE_MPI
1513 d[commname] = jv;
1514}
1515
1517{
1518 std::vector< std::string >slurmvars;
1519 add_slurm_section(slurmvars);
1520}
1521
1522void builder::add_slurm_section(const std::vector< std::string >& slurmvars)
1523{
1524 static std::vector< std::pair<const char *, const char *> > slurm_names =
1525 {
1526 { "cluster", "SLURM_CLUSTER_NAME"},
1527 { "job_id", "SLURM_JOB_ID"},
1528 { "num_nodes", "SLURM_JOB_NUM_NODES"},
1529 { "dependency", "SLURM_JOB_DEPENDENCY"},
1530 };
1531
1532 boost::json::object jv;
1533 for (const auto& i : slurm_names) {
1534 const char *val = getenv(i.second);
1535 if (val)
1536 jv[i.first] = val;
1537 else
1538 jv[i.first] = "";
1539 }
1540 for (const auto& i : slurmvars) {
1541 const char *val = getenv(i.c_str());
1542 if (val)
1543 jv[i] = val;
1544 else
1545 jv[i] = "";
1546 }
1547 d["slurm"] = jv;
1548}
1549
1551{
1552 static std::vector< std::pair<const char *, const char *> > names =
1553 {
1554 { "wfid", "ADC_WFID"},
1555 { "wfid_parent", "ADC_WFID_PARENT"},
1556 { "wfid_path", "ADC_WFID_PATH"}
1557 };
1558
1559 boost::json::object jv;
1560 for (const auto& i : names) {
1561 const char *val = getenv(i.second);
1562 if (val)
1563 jv[i.first] = val;
1564 else
1565 jv[i.first] = "";
1566 }
1567 boost::json::array av;
1568 const char *cenv = getenv("ADC_WFID_CHILDREN");
1569 if (cenv) {
1570 // split and append to wfid_children
1571 std::stringstream ss(cenv);
1572 string elt;
1573 char pathsep = ':';
1574 while (getline(ss, elt, pathsep)) {
1575 if (elt.size() > 0) {
1576#ifdef ADC_WORKFLOW_DEBUG
1577 std::cerr << __func__ << ": add child " << elt << std::endl;
1578#endif
1579 av.emplace_back(elt);
1580 }
1581 }
1582 }
1583 jv["wfid_children"] = av;
1584 d["adc_workflow"] = jv;
1585}
1586
1587bool array_contains_string( boost::json::array& av, string_view uuid)
1588{
1589 for (const auto& v : av) {
1590 if (v.is_string() && v.as_string() == uuid) {
1591 return true;
1592 }
1593 }
1594 return false;
1595}
1596
1597void builder::add_workflow_children(std::vector< std::string >& child_uuids)
1598{
1599 auto wsection = d.if_contains("adc_workflow");
1600 if (! wsection) {
1601 std::cerr << __func__ << ": called before add_workflow_section" << std::endl;
1602 return;
1603 }
1604 if (! wsection->is_object()) {
1605 std::cerr << __func__ << ": adc_workflow is not an object. (?!)" << std::endl;
1606 return;
1607 }
1608 auto haschildren = wsection->as_object().if_contains("wfid_children");
1609 if (!haschildren) {
1610 std::cerr << __func__ << ": called before successful add_workflow_section" << std::endl;
1611 return;
1612 }
1613 auto children = wsection->as_object()["wfid_children"];
1614 if (!children.is_array()) {
1615 std::cerr << __func__ << ": wfid_children is not an array. (?!)" << std::endl;
1616 return;
1617 }
1618 for (auto const& uuid : child_uuids) {
1619 if (array_contains_string(wsection->as_object()["wfid_children"].as_array(), uuid)) {
1620 continue;
1621 }
1622 wsection->as_object()["wfid_children"].as_array().emplace_back(uuid);
1623 }
1624}
1625
1626
1628{
1629 static const std::vector<std::string > gitlab_ci_names = {
1630 "CI_RUNNER_ID",
1631 "CI_RUNNER_VERSION",
1632 "CI_PROJECT_ID",
1633 "CI_PROJECT_NAME",
1634 "CI_SERVER_FQDN",
1635 "CI_SERVER_VERSION",
1636 "CI_JOB_ID",
1637 "CI_JOB_STARTED_AT",
1638 "CI_PIPELINE_ID",
1639 "CI_PIPELINE_SOURCE",
1640 "CI_COMMIT_SHA",
1641 "GITLAB_USER_LOGIN"
1642 };
1643 boost::json::object jv;
1644 for (const auto& i : gitlab_ci_names) {
1645 const char *val = getenv(i.c_str());
1646 if (val)
1647 jv[tolower_s(i)] = val;
1648 else
1649 jv[tolower_s(i)] = "";
1650 }
1651 d["gitlab_ci"] = jv;
1652}
1653
1654void builder::add(std::string_view name, bool value) {
1655 if (badkey(name)) return;
1656 boost::json::value jv = {
1657 {"type", adc::to_string(cp_bool)},
1658 {"value", value}
1659 };
1660 d[name] = jv;
1661}
1662
1663void builder::add(std::string_view name, char value) {
1664 if (badkey(name)) return;
1665 boost::json::value jv = {
1666 {"type", adc::to_string(cp_char)},
1667 {"value", value }
1668 };
1669 d[name] = jv;
1670}
1671
1672void builder::add(std::string_view name, char16_t value) {
1673 if (badkey(name)) return;
1674 uint64_t ivalue = value;
1675 boost::json::value jv = {
1676 {"type", adc::to_string(cp_char16)},
1677 {"value", ivalue}
1678 };
1679 d[name] = jv;
1680}
1681
1682void builder::add(std::string_view name, char32_t value) {
1683 if (badkey(name)) return;
1684 uint64_t ivalue = value;
1685 boost::json::value jv = {
1686 {"type", adc::to_string(cp_char32)},
1687 {"value", ivalue}
1688 };
1689 d[name] = jv;
1690}
1691
1692// builder::add null-terminated string
1693void builder::add(std::string_view name, char* value) {
1694 if (badkey(name)) return;
1695 boost::json::value jv = {
1696 {"type", adc::to_string(cp_cstr)},
1697 {"value", value}
1698 };
1699 d[name] = jv;
1700}
1701void builder::add(std::string_view name, const char* value) {
1702 if (badkey(name)) return;
1703 boost::json::value jv = {
1704 {"type", adc::to_string(cp_cstr)},
1705 {"value", value}
1706 };
1707 d[name] = jv;
1708}
1709void builder::add(std::string_view name, std::string_view value) {
1710 if (badkey(name)) return;
1711 boost::json::value jv = {
1712 {"type", adc::to_string(cp_cstr)},
1713 {"value", value}
1714 };
1715 d[name] = jv;
1716}
1717void builder::add(std::string_view name, std::string& value) {
1718 if (badkey(name)) return;
1719 boost::json::value jv = {
1720 {"type", adc::to_string(cp_cstr)},
1721 {"value", value}
1722 };
1723 d[name] = jv;
1724}
1725
1726// builder::add null-terminated string file path
1727void builder::add_path(std::string_view name, char* value) {
1728 if (badkey(name)) return;
1729 boost::json::value jv = {
1730 {"type", adc::to_string(cp_path)},
1731 {"value", value}
1732 };
1733 d[name] = jv;
1734}
1735void builder::add_path(std::string_view name, const char* value) {
1736 if (badkey(name)) return;
1737 boost::json::value jv = {
1738 {"type", adc::to_string(cp_path)},
1739 {"value", value}
1740 };
1741 d[name] = jv;
1742}
1743void builder::add_path(std::string_view name, std::string_view value) {
1744 if (badkey(name)) return;
1745 boost::json::value jv = {
1746 {"type", adc::to_string(cp_path)},
1747 {"value", value}
1748 };
1749 d[name] = jv;
1750}
1751void builder::add_path(std::string_view name, std::string& value) {
1752 if (badkey(name)) return;
1753 boost::json::value jv = {
1754 {"type", adc::to_string(cp_path)},
1755 {"value", value}
1756 };
1757 d[name] = jv;
1758}
1759
1760// builder::add string which is serialized json.
1761void builder::add_json_string(std::string_view name, std::string_view value) {
1762 if (badkey(name)) return;
1763 boost::json::value jv = {
1764 {"type", adc::to_string(cp_json_str)},
1765 {"value", value}
1766 };
1767 d[name] = jv;
1768}
1769
1770void builder::add_yaml_string(std::string_view name, std::string_view value) {
1771 if (badkey(name)) return;
1772 boost::json::value jv = {
1773 {"type", adc::to_string(cp_yaml_str)},
1774 {"value", value}
1775 };
1776 d[name] = jv;
1777}
1778
1779void builder::add_xml_string(std::string_view name, std::string_view value) {
1780 if (badkey(name)) return;
1781 boost::json::value jv = {
1782 {"type", adc::to_string(cp_xml_str)},
1783 {"value", value}
1784 };
1785 d[name] = jv;
1786}
1787
1788void builder::add_number_string(std::string_view name, std::string_view value) {
1789 if (badkey(name)) return;
1790 boost::json::value jv = {
1791 {"type", adc::to_string(cp_number_str)},
1792 {"value", value}
1793 };
1794 d[name] = jv;
1795}
1796
1797#if ADC_BOOST_JSON_PUBLIC
1798void builder::add(std::string_view name, boost::json::value value) {
1799 if (badkey(name)) return;
1800 boost::json::value jv = {
1801 {"type", adc::to_string(cp_json)},
1802 {"value", value}
1803 };
1804 d[name] = jv;
1805}
1806#endif
1807
1808
1809void builder::add(std::string_view name, uint8_t value) {
1810 if (badkey(name)) return;
1811 boost::json::value jv = {
1812 {"type", adc::to_string(cp_uint8)},
1813 {"value", value}
1814 };
1815 d[name] = jv;
1816}
1817void builder::add(std::string_view name, uint16_t value) {
1818 if (badkey(name)) return;
1819 boost::json::value jv = {
1820 {"type", adc::to_string(cp_uint16)},
1821 {"value", value}
1822 };
1823 d[name] = jv;
1824}
1825void builder::add(std::string_view name, uint32_t value) {
1826 if (badkey(name)) return;
1827 boost::json::value jv = {
1828 {"type", adc::to_string(cp_uint32)},
1829 {"value", value}
1830 };
1831 d[name] = jv;
1832}
1833void builder::add(std::string_view name, uint64_t value) {
1834 if (badkey(name)) return;
1835 boost::json::value jv = {
1836 {"type", adc::to_string(cp_uint64)},
1837 {"value", std::to_string(value)}
1838 };
1839 d[name] = jv;
1840}
1841void builder::add(std::string_view name, int8_t value) {
1842 if (badkey(name)) return;
1843 boost::json::value jv = {
1844 {"type", adc::to_string(cp_int8)},
1845 {"value", value}
1846 };
1847 d[name] = jv;
1848}
1849void builder::add(std::string_view name, int16_t value) {
1850 if (badkey(name)) return;
1851 boost::json::value jv = {
1852 {"type", adc::to_string(cp_int16)},
1853 {"value", value}
1854 };
1855 d[name] = jv;
1856}
1857void builder::add(std::string_view name, int32_t value) {
1858 if (badkey(name)) return;
1859 boost::json::value jv = {
1860 {"type", adc::to_string(cp_int32)},
1861 {"value", value }
1862 };
1863 d[name] = jv;
1864}
1865void builder::add(std::string_view name, int64_t value) {
1866 if (badkey(name)) return;
1867 boost::json::value jv = {
1868 {"type", adc::to_string(cp_int64)},
1869 {"value", value}
1870 };
1871 d[name] = jv;
1872}
1873void builder::add(std::string_view name, float value) {
1874 if (badkey(name)) return;
1875 boost::json::value jv = {
1876 {"type", adc::to_string(cp_f32)},
1877 {"value", value }
1878 };
1879 d[name] = jv;
1880}
1881void builder::add(std::string_view name, const std::complex<float>& value) {
1882 if (badkey(name)) return;
1883 boost::json::value jv = {
1884 {"type", adc::to_string(cp_c_f32)},
1885 {"value", { value.real(), value.imag() }}
1886 };
1887 d[name] = jv;
1888}
1889void builder::add(std::string_view name, double value) {
1890 if (badkey(name)) return;
1891 boost::json::value jv = {
1892 {"type", adc::to_string(cp_f64)},
1893 {"value", value }
1894 };
1895 d[name] = jv;
1896}
1897void builder::add(std::string_view name, const std::complex<double>& value) {
1898 if (badkey(name)) return;
1899 boost::json::value jv = {
1900 {"type" , adc::to_string(cp_c_f64)},
1901 {"value", { value.real(), value.imag() }}
1902 };
1903 d[name] = jv;
1904}
1905
1906
1907void builder::add(std::string_view name, const struct timeval& tv) {
1908 if (badkey(name)) return;
1909 boost::json::value jv = {
1910 {"type" , adc::to_string(cp_timeval)},
1911 {"value", { (int64_t)tv.tv_sec, (int64_t)tv.tv_usec }}
1912 };
1913 d[name] = jv;
1914}
1915
1916void builder::add(std::string_view name, const struct timespec& ts) {
1917 if (badkey(name)) return;
1918 boost::json::value jv = {
1919 {"type" , adc::to_string(cp_timespec)},
1920 {"value", { (int64_t)ts.tv_sec, (int64_t)ts.tv_nsec }}
1921 };
1922 d[name] = jv;
1923}
1924
1925void builder::add_epoch(std::string_view name, int64_t epoch) {
1926 if (badkey(name)) return;
1927 boost::json::value jv = {
1928 {"type", adc::to_string(cp_epoch)},
1929 {"value", epoch }
1930 };
1931 d[name] = jv;
1932}
1933
1934
1935void builder::add_from_pointer_type(std::string_view name, void* p, enum scalar_type t)
1936{
1937 if (badkey(name)) return;
1938 switch (t) {
1939 case cp_bool:
1940 {
1941 bool *v = (bool *)p;
1942 add(name, *v);
1943 }
1944 break;
1945 case cp_char:
1946 {
1947 char *v = (char *)p;
1948 add(name, *v);
1949 }
1950 break;
1951 case cp_char16:
1952 {
1953 char16_t *v = (char16_t *)p;
1954 add(name, *v);
1955 }
1956 break;
1957 case cp_char32:
1958 {
1959 char32_t *v = (char32_t *)p;
1960 add(name, *v);
1961 }
1962 break;
1963 case cp_cstr:
1964 {
1965 char *v = (char *)p;
1966 add(name, v);
1967 }
1968 break;
1969 case cp_json_str:
1970 {
1971 char *v = (char *)p;
1972 add_json_string(name, v);
1973 }
1974 break;
1975 case cp_yaml_str:
1976 {
1977 char *v = (char *)p;
1978 add_yaml_string(name, v);
1979 }
1980 break;
1981 case cp_xml_str:
1982 {
1983 char *v = (char *)p;
1984 add_xml_string(name, v);
1985 }
1986 break;
1987#if ADC_BOOST_JSON_PUBLIC
1988 case cp_json:
1989 return; // pointers to json objects are ignored. fixme? ADC_BOOST_JSON_PUBLIC in builder::add_from_pointer_type
1990#endif
1991 case cp_path:
1992 {
1993 char *v = (char *)p;
1994 add_path(name, v);
1995 }
1996 break;
1997 case cp_number_str:
1998 {
1999 char *v = (char *)p;
2000 add_number_string(name, v);
2001 }
2002 break;
2003 case cp_uint8:
2004 {
2005 uint8_t *v = (uint8_t *)p;
2006 add(name, *v);
2007 }
2008 break;
2009 case cp_uint16:
2010 {
2011 uint16_t *v = (uint16_t *)p;
2012 add(name, *v);
2013 }
2014 break;
2015 case cp_uint32:
2016 {
2017 uint32_t *v = (uint32_t *)p;
2018 add(name, *v);
2019 }
2020 break;
2021 case cp_uint64:
2022 {
2023 uint64_t *v = (uint64_t *)p;
2024 add(name, *v);
2025 }
2026 break;
2027 case cp_int8:
2028 {
2029 int8_t *v = (int8_t *)p;
2030 add(name, *v);
2031 }
2032 break;
2033 case cp_int16:
2034 {
2035 int16_t *v = (int16_t *)p;
2036 add(name, *v);
2037 }
2038 break;
2039 case cp_int32:
2040 {
2041 int32_t *v = (int32_t *)p;
2042 add(name, *v);
2043 }
2044 break;
2045 case cp_int64:
2046 {
2047 int64_t *v = (int64_t *)p;
2048 add(name, *v);
2049 }
2050 break;
2051 case cp_f32:
2052 {
2053 float *v = (float *)p;
2054 add(name, *v);
2055 }
2056 break;
2057 case cp_f64:
2058 {
2059 double *v = (double *)p;
2060 add(name, *v);
2061 }
2062 break;
2063#if ADC_SUPPORT_EXTENDED_FLOATS
2064 case cp_f80:
2065 {
2066 // prec fixme? verify cp_f80 in supporting compiler builder::add_from_pointer_type
2067 __float80 *v = (__float80 *)p;
2068 add(name, *v);
2069 }
2070 break;
2071#endif
2072#if ADC_SUPPORT_QUAD_FLOATS
2073 case cp_f128:
2074 {
2075 __float128 *v = (__float128 *)p;
2076 add(name, *v);
2077 }
2078 break;
2079#endif
2080#if ADC_SUPPORT_GPU_FLOATS
2081 case cp_f8_e4m3:
2082 {
2083 // prec fixme verify cp_f8_e4m3 in supporting compiler ; builder::add_from_pointer_type
2084 }
2085 break;
2086 case cp_f8_e5m2:
2087 {
2088 // prec fixme verify cp_f8_e5m2 in supporting compiler ; builder::add_from_pointer_type
2089 }
2090 break;
2091 case cp_f16_e5m10:
2092 {
2093 // prec fixme verify cp_f16_e5m10 in supporting compiler ; builder::add_from_pointer_type
2094 }
2095 break;
2096 case cp_f16_e8m7:
2097 {
2098 // prec fixme verify cp_f16_e8m7 in supporting compiler ; builder::add_from_pointer_type
2099 }
2100 break;
2101#endif
2102 case cp_c_f32:
2103 {
2104 float *v = (float *)p;
2105 std::complex<float> c(v[0], v[1]);
2106 add(name, c);
2107 }
2108 break;
2109 case cp_c_f64:
2110 {
2111 double *v = (double *)p;
2112 std::complex<double> c(v[0], v[1]);
2113 add(name, c);
2114 }
2115 break;
2116#if ADC_SUPPORT_QUAD_FLOATS
2117 case cp_c_f128:
2118 {
2119 __float128 *v = (__float128 *)p;
2120 std::complex<__float128> c(v[0], v[1]);
2121 add(name, c);
2122 }
2123 break;
2124#endif
2125#if ADC_SUPPORT_EXTENDED_FLOATS
2126 case cp_c_f80:
2127 {
2128 // prec fixme? cp_c_f80 ; builder::add_from_pointer_type
2129 __float80 *v = (__float80 *)p;
2130 std::complex<__float80> c(v[0], v[1]);
2131 add(name, c);
2132 }
2133 break;
2134#endif
2135 case cp_timespec:
2136 {
2137 struct timespec *v = (struct timespec *)p;
2138 add(name, *v);
2139 }
2140 break;
2141 case cp_timeval:
2142 {
2143 struct timeval *v = (struct timeval *)p;
2144 add(name, *v);
2145 }
2146 break;
2147 case cp_epoch:
2148 {
2149 int64_t *v = (int64_t *)p;
2150 add_epoch(name, *v);
2151 }
2152 break;
2153 default:
2154 break;
2155 }
2156}
2157
2158void builder::add_array(std::string_view name, bool value[], size_t len, std::string_view c) {
2159 if (badkey(name)) return;
2160 boost::json::array av(value, value+len);
2161 boost::json::value jv = {
2162 {"type", "array_" + adc::to_string(cp_bool)},
2163 {"container_type", c},
2164 {"value", av}
2165 };
2166 d[name] = jv;
2167}
2168
2169void builder::add_array(std::string_view name, const char *value, size_t len, std::string_view c) {
2170 if (badkey(name)) return;
2171 boost::json::array av(value, value+len);
2172 boost::json::value jv = {
2173 {"type", "array_" + adc::to_string(cp_char)},
2174 {"container_type", c},
2175 {"value", av}
2176 };
2177 d[name] = jv;
2178}
2179
2180void builder::add_array(std::string_view name, char16_t value[], size_t len, std::string_view c) {
2181 if (badkey(name)) return;
2182 boost::json::array av(value, value+len);
2183 boost::json::value jv = {
2184 {"type", "array_" + adc::to_string(cp_char16)},
2185 {"container_type", c},
2186 {"value", av}
2187 };
2188 d[name] = jv;
2189}
2190
2191void builder::add_array(std::string_view name, char32_t value[], size_t len, std::string_view c) {
2192 if (badkey(name)) return;
2193 boost::json::array av(value, value+len);
2194 boost::json::value jv = {
2195 {"type", "array_" + adc::to_string(cp_char32)},
2196 {"container_type", c},
2197 {"value", av}
2198 };
2199 d[name] = jv;
2200}
2201
2202void builder::add_array(std::string_view name, uint8_t value[], size_t len, std::string_view c) {
2203 if (badkey(name)) return;
2204 boost::json::array av(value, value+len);
2205 boost::json::value jv = {
2206 {"type", "array_" + adc::to_string(cp_uint8)},
2207 {"container_type", c},
2208 {"value", av}
2209 };
2210 d[name] = jv;
2211}
2212void builder::add_array(std::string_view name, uint16_t value[], size_t len, std::string_view c) {
2213 if (badkey(name)) return;
2214 boost::json::array av(value, value+len);
2215 boost::json::value jv = {
2216 {"type", "array_" + adc::to_string(cp_uint16)},
2217 {"container_type", c},
2218 {"value", av}
2219 };
2220 d[name] = jv;
2221}
2222void builder::add_array(std::string_view name, uint32_t value[], size_t len, std::string_view c) {
2223 if (badkey(name)) return;
2224 boost::json::array av(value, value+len);
2225 boost::json::value jv = {
2226 {"type", "array_" + adc::to_string(cp_uint32)},
2227 {"container_type", c},
2228 {"value", av}
2229 };
2230 d[name] = jv;
2231}
2232void builder::add_array(std::string_view name, uint64_t value[], size_t len, std::string_view c) {
2233 if (badkey(name)) return;
2234 std::vector<std::string> sv(len);
2235 for (size_t i = 0; i < len; i++)
2236 sv[i] = std::to_string(value[i]);
2237 boost::json::array av(sv.begin(), sv.end());
2238 boost::json::value jv = {
2239 {"type", "array_" + adc::to_string(cp_uint64)},
2240 {"container_type", c},
2241 {"value", av}
2242 };
2243 d[name] = jv;
2244}
2245void builder::add_array(std::string_view name, int8_t value[], size_t len, std::string_view c) {
2246 if (badkey(name)) return;
2247 boost::json::array av(value, value+len);
2248 boost::json::value jv = {
2249 {"type", "array_" + adc::to_string(cp_int8)},
2250 {"container_type", c},
2251 {"value", av}
2252 };
2253 d[name] = jv;
2254}
2255void builder::add_array(std::string_view name, int16_t value[], size_t len, std::string_view c) {
2256 if (badkey(name)) return;
2257 boost::json::array av(value, value+len);
2258 boost::json::value jv = {
2259 {"type", "array_" + adc::to_string(cp_int16)},
2260 {"container_type", c},
2261 {"value", av}
2262 };
2263 d[name] = jv;
2264}
2265void builder::add_array(std::string_view name, int32_t value[], size_t len, std::string_view c) {
2266 if (badkey(name)) return;
2267 boost::json::array av(value, value+len);
2268 boost::json::value jv = {
2269 {"type", "array_" + adc::to_string(cp_int32)},
2270 {"container_type", c},
2271 {"value", av}
2272 };
2273 d[name] = jv;
2274}
2275void builder::add_array(std::string_view name, int64_t value[], size_t len, std::string_view c) {
2276 if (badkey(name)) return;
2277 boost::json::array av(value, value+len);
2278 boost::json::value jv = {
2279 {"type", "array_" + adc::to_string(cp_int64)},
2280 {"container_type", c},
2281 {"value", av}
2282 };
2283 d[name] = jv;
2284}
2285void builder::add_array(std::string_view name, float value[], size_t len, std::string_view c) {
2286 if (badkey(name)) return;
2287 boost::json::array av(value, value+len);
2288 boost::json::value jv = {
2289 {"type", "array_" + adc::to_string(cp_f32)},
2290 {"container_type", c},
2291 {"value", av}
2292 };
2293 d[name] = jv;
2294}
2295#if 0
2296void builder::add_array(std::string_view name, const std::complex<float> value[], size_t len, std::string_view c) {
2297 if (badkey(name)) return;
2298 boost::json::array av(value, value+len);
2299 boost::json::value jv = {
2300 {"type", "array_" + adc::to_string(cp_c_f32)},
2301 {"container_type", c},
2302 {"value", { value.real(), value.imag() }}
2303 };
2304 d[name] = jv;
2305}
2306#endif
2307void builder::add_array(std::string_view name, double value[], size_t len, std::string_view c) {
2308 if (badkey(name)) return;
2309 boost::json::array av(value, value+len);
2310 boost::json::value jv = {
2311 {"type", "array_" + adc::to_string(cp_f64)},
2312 {"container_type", c},
2313 {"value", av}
2314 };
2315 d[name] = jv;
2316}
2317#if 0
2318void builder::add_array(std::string_view name, const std::complex<double> value[], size_t len, std::string_view c) {
2319 if (badkey(name)) return;
2320 boost::json::array av(value, value+len);
2321 boost::json::value jv = {
2322 {"type" , "array_" + adc::to_string(cp_c_f64)},
2323 {"container_type", c},
2324 {"value", { value.real(), value.imag() }}
2325 };
2326 d[name] = jv;
2327}
2328#endif
2329
2330void builder::add_array(std::string_view name, char* value[], size_t len, std::string_view c) {
2331 if (badkey(name)) return;
2332 boost::json::array av(value, value+len);
2333 boost::json::value jv = {
2334 {"type", "array_" + adc::to_string(cp_cstr)},
2335 {"container_type", c},
2336 {"value", av}
2337 };
2338 d[name] = jv;
2339}
2340void builder::add_array(std::string_view name, const char* value[], size_t len, std::string_view c) {
2341 if (badkey(name)) return;
2342 boost::json::array av(value, value+len);
2343 boost::json::value jv = {
2344 {"type", "array_" + adc::to_string(cp_cstr)},
2345 {"container_type", c},
2346 {"value", av}
2347 };
2348 d[name] = jv;
2349}
2350void builder::add_array(std::string_view name, std::string value[], size_t len, std::string_view c) {
2351 if (badkey(name)) return;
2352 boost::json::array av(value, value+len);
2353 boost::json::value jv = {
2354 {"type", "array_" + adc::to_string(cp_cstr)},
2355 {"container_type", c},
2356 {"value", av}
2357 };
2358 d[name] = jv;
2359}
2360void builder::add_array(std::string_view name, const std::string value[], size_t len, std::string_view c) {
2361 if (badkey(name)) return;
2362 boost::json::array av(value, value+len);
2363 boost::json::value jv = {
2364 {"type", "array_" + adc::to_string(cp_cstr)},
2365 {"container_type", c},
2366 {"value", av}
2367 };
2368 d[name] = jv;
2369}
2370void builder::add_array(std::string_view name, const std::vector<std::string> value, std::string_view c) {
2371 if (badkey(name)) return;
2372 boost::json::value jv = {
2373 {"type", "array_" + adc::to_string(cp_cstr)},
2374 {"container_type", c},
2375 {"value", boost::json::value_from(value)}
2376 };
2377 d[name] = jv;
2378}
2379void builder::add_array(std::string_view name, const std::set<std::string> value, std::string_view c) {
2380 if (badkey(name)) return;
2381 boost::json::value jv = {
2382 {"type", "array_" + adc::to_string(cp_cstr)},
2383 {"container_type", c},
2384 {"value", boost::json::value_from(value)}
2385 };
2386 d[name] = jv;
2387}
2388void builder::add_array(std::string_view name, const std::list<std::string> value, std::string_view c) {
2389 if (badkey(name)) return;
2390 boost::json::value jv = {
2391 {"type", "array_" + adc::to_string(cp_cstr)},
2392 {"container_type", c},
2393 {"value", boost::json::value_from(value)}
2394 };
2395 d[name] = jv;
2396}
2397// Array of strings which are serialized json.
2398void builder::add_array_json_string(std::string_view name, const std::string value[], size_t len, std::string_view c) {
2399 if (badkey(name)) return;
2400 boost::json::array av(value, value+len);
2401 boost::json::value jv = {
2402 {"type", "array_" + adc::to_string(cp_json_str)},
2403 {"container_type", c},
2404 {"value", av}
2405 };
2406 d[name] = jv;
2407}
2408
2409
2410/*
2411 * recursively merge sections into a single json tree
2412 */
2413boost::json::object builder::flatten()
2414{
2415 boost::json::object tot(d); // copy
2416 for (auto it = sections.begin(); it != sections.end(); it++) {
2417 tot[it->first] = it->second->flatten();
2418 }
2419 return tot;
2420}
2421
2422BOOST_SYMBOL_VISIBLE std::string builder::serialize() {
2423 boost::json::object total;
2424 total = flatten();
2425 return boost::json::serialize(total);
2426}
2427
2428key_type builder::kind(std::string_view name) {
2429 auto sit = sections.find(std::string(name));
2430 if (sit != sections.end())
2431 return k_section;
2432 auto jit = d.find(name);
2433 if (jit != d.end())
2434 return k_value;
2435 return k_none;
2436}
2437
2438} // end namespace adc
#define CPUINFO_FILE
Definition builder.ipp:111
#define ADC_HS_BAD
Definition builder.ipp:496
#define MEMINFO_FILE
Definition builder.ipp:322
std::shared_ptr< builder_api > get_section(std::string_view name)
get the existing named section
Definition builder.ipp:685
std::vector< std::string > get_section_names()
get the names of sections
Definition builder.ipp:694
builder(void *mpi_communicator_p=NULL)
Definition builder.ipp:423
void add_json_string(std::string_view name, std::string_view value)
add string which is serialized json.
Definition builder.ipp:1761
void add_mpi_section(std::string_view name, void *mpi_comm_p, adc_mpi_field_flags bitflags)
add data about a named mpi communicator. In most applications, "mpi_comm_world" is the recommended na...
Definition builder.ipp:1338
void add_code_configuration_section(std::shared_ptr< builder_api > build_details)
Populate build/install "configuration" information such as options enabled.
Definition builder.ipp:636
void add_model_data_section(std::shared_ptr< builder_api > model_data)
populate application run-time physics (re)configuration/result to "model_data" section....
Definition builder.ipp:602
void add_array_json_string(std::string_view name, const std::string value[], size_t len, std::string_view c)
Add Array of strings which are serialized json.
Definition builder.ipp:2398
void add_xml_string(std::string_view name, std::string_view value)
add string which is xml.
Definition builder.ipp:1779
void add_yaml_string(std::string_view name, std::string_view value)
add string which is yaml.
Definition builder.ipp:1770
void add(std::string_view name, bool value)
add a named boolean
Definition builder.ipp:1654
void add_host_section(int32_t subsections)
auto-populate the "host" section based on bitflags. There are many optional subsections covering cpus...
Definition builder.ipp:498
const char * get_value_string(std::string_view path)
get the existing named nested scalar string value.
Definition builder.ipp:1259
void add_workflow_section()
add data from adc_wfid_ environment variables. The section name is "adc_workflow".
Definition builder.ipp:1550
void add_array(std::string_view name, bool value[], size_t len, std::string_view c)
Fixed length arrays of scalar members.
Definition builder.ipp:2158
void add_from_pointer_type(std::string_view name, void *, enum scalar_type t)
add data from a c pointer
Definition builder.ipp:1935
void add_number_string(std::string_view name, std::string_view value)
add string which is an arbitrary precision decimal number
Definition builder.ipp:1788
void add_gitlab_ci_section()
add gitlab_ci environment variable dictionary. The section added is named "gitlab_ci".
Definition builder.ipp:1627
void add_section(std::string_view name, std::shared_ptr< builder_api > section)
copy populated generic section into the builder under specified name.
Definition builder.ipp:670
void add_memory_usage_section()
populate "memory_usage" section with current host /proc/meminfo data in the style of free(1).
Definition builder.ipp:577
void add_code_section(std::string tag, std::shared_ptr< builder_api > version, std::shared_ptr< builder_api > code_details)
Definition builder.ipp:611
std::vector< std::string > get_field_names()
get the names of non-section fields in the section
Definition builder.ipp:704
uint64_t get_value_uint64(std::string_view path)
get the existing named scalar value that can be correctly cast as int64_t
Definition builder.ipp:1308
const field get_value(std::string_view path)
get the existing named nested field in the builder.
Definition builder.ipp:1150
int64_t get_value_int64(std::string_view path)
get the existing named scalar value that can be correctly cast as int64_t
Definition builder.ipp:1277
void add_slurm_section()
add slurm output environment variable dictionary elements. The section added is named "slurm".
Definition builder.ipp:1516
void add_exit_data_section(int return_code, std::string status, std::shared_ptr< builder_api > status_details)
populate "exit_data" section with code and status stream and user provided details.
Definition builder.ipp:648
void add_epoch(std::string_view name, int64_t epoch)
add unix epoch seconds (gettimeofday)
Definition builder.ipp:1925
void add_app_data_section(std::shared_ptr< builder_api > app_data)
create the "app_data" section with data defined by the application writer.
Definition builder.ipp:570
void add_path(std::string_view name, char *value)
add null-terminated string filepath
Definition builder.ipp:1727
std::string serialize()
convert object to a json string reflecting the section hierarchy.
Definition builder.ipp:2422
void add_workflow_children(std::vector< std::string > &child_uuids)
add list of child uuids to "adc_workflow" section after add_workflow_section has been called....
Definition builder.ipp:1597
void add_header_section(std::string_view application_name)
auto-populate the "header" section with application name and required local data
Definition builder.ipp:431
static pipe_data run(const std::string &proc)
Definition outpipe.ipp:33
std::string get_default_affinity()
Definition builder.ipp:112
std::string format_timespec_utc_ns(struct timespec &ts)
Definition builder.ipp:96
key_type
when expanding scalar_type, always update enum.ipp to match.
Definition types.hpp:112
scalar_type scalar_type_from_name(const std::string &name)
get the enum representation of a scalar_type string
Definition enums.ipp:104
version enum_version("1.0.0", {"none"})
the version number of enum scalar_type and object_type
std::variant< bool, char, char16_t, char32_t, int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t, uint32_t, uint64_t, float, double, std::complex< float >, std::complex< double >, std::array< int64_t, 2 >, std::string, std::shared_ptr< bool[]>, std::shared_ptr< char[]>, std::shared_ptr< char16_t[]>, std::shared_ptr< char32_t[]>, std::shared_ptr< int8_t[]>, std::shared_ptr< int16_t[]>, std::shared_ptr< int32_t[]>, std::shared_ptr< int64_t[]>, std::shared_ptr< uint8_t[]>, std::shared_ptr< uint16_t[]>, std::shared_ptr< uint32_t[]>, std::shared_ptr< uint64_t[]>, std::shared_ptr< float[]>, std::shared_ptr< double[]>, std::shared_ptr< std::complex< float >[]>, std::shared_ptr< std::complex< double >[]>, std::shared_ptr< std::string[]> > variant
variant for querying builder data.
Definition types.hpp:159
const std::string to_string(float f)
get string of float using to_chars.
Definition enums.ipp:132
std::string format_timespec_8601(struct timespec &ts)
Definition builder.ipp:81
scalar_type
field types for scientific data encode/decode with json.
Definition types.hpp:62
@ k_value
Definition types.hpp:115
@ k_none
Definition types.hpp:113
@ k_section
Definition types.hpp:114
@ cp_xml_str
c null-terminated string that contains valid xml
Definition types.hpp:72
@ cp_int64
int64_t
Definition types.hpp:85
@ cp_epoch
time(NULL) seconds since the epoch (UNIX) as int64_t
Definition types.hpp:104
@ cp_f16_e8m7
16 bit bfloat (7 mantissa, 8 exponent); requires ADC_SUPPORT_GPU_FLOATS support
Definition types.hpp:95
@ cp_int32
int32_t
Definition types.hpp:84
@ cp_c_f32
complex<float>
Definition types.hpp:97
@ cp_char
char (8 bit)
Definition types.hpp:65
@ cp_path
c null-terminated string which names a file-system path
Definition types.hpp:74
@ cp_char16
char16_t
Definition types.hpp:66
@ cp_json_str
c null-terminated string that contains valid json
Definition types.hpp:70
@ cp_timespec
(second, nanosecond) as int64_t, int64_t pair from clock_gettime
Definition types.hpp:102
@ cp_timeval
gettimeofday struct timeval (second, microsecond) as int64_t pair
Definition types.hpp:103
@ cp_c_f64
complex<double>
Definition types.hpp:98
@ cp_bool
bool (true/false,1/0)
Definition types.hpp:64
@ cp_int8
int8_t
Definition types.hpp:82
@ cp_f128
128 bit float; requires ADC_SUPPORT_QUAD_FLOATS support
Definition types.hpp:90
@ cp_uint16
uint16_t
Definition types.hpp:78
@ cp_json
json value (object, list, etc)
Definition types.hpp:73
@ cp_c_f80
complex<extended>; requires ADC_SUPPORT_EXTENDED_FLOATS support
Definition types.hpp:99
@ cp_number_str
c null-terminated string containing an exact decimal representation of arbitrary precision
Definition types.hpp:75
@ cp_uint32
uint32_t
Definition types.hpp:79
@ cp_int16
int16_t
Definition types.hpp:83
@ cp_f64
64 bit float
Definition types.hpp:88
@ cp_f80
80 bit float; requires ADC_SUPPORT_EXTENDED_FLOATS support
Definition types.hpp:89
@ cp_none
Definition types.hpp:63
@ cp_f32
32 bit float
Definition types.hpp:87
@ cp_yaml_str
c null-terminated string that contains valid yaml
Definition types.hpp:71
@ cp_f16_e5m10
16 bit float (10 mantissa, 5 exponent); requires ADC_SUPPORT_GPU_FLOATS support
Definition types.hpp:94
@ cp_c_f128
complex<quad>; requires ADC_SUPPORT_QUAD_FLOATS support
Definition types.hpp:100
@ cp_cstr
c null-terminated string
Definition types.hpp:69
@ cp_char32
char32_t
Definition types.hpp:67
@ cp_uint64
uint64_t
Definition types.hpp:80
@ cp_uint8
uint8_t
Definition types.hpp:77
@ cp_f8_e5m2
8 bit float (2 mantissa, 5 exponent); requires ADC_SUPPORT_GPU_FLOATS support
Definition types.hpp:93
@ cp_f8_e4m3
8 bit float (3 mantissa, 4 exponent); requires ADC_SUPPORT_GPU_FLOATS support
Definition types.hpp:92
#define ADC_HS_GPU
ADC_HS_GPU collects gpu data available from lspci (requires lspci installed)
Definition builder.hpp:67
#define ADC_HS_RAMSIZE
ADC_HS_RAMSIZE collects MemTotal.
Definition builder.hpp:57
#define ADC_HS_OS
ADC_HS_OS collects other items from uname().
Definition builder.hpp:54
#define ADC_HS_CPU
ADC_HS_CPU collects details from lscpu -J (requires lscpu installed)
Definition builder.hpp:64
#define ADC_HS_NUMA
ADC_HS_NUMA collects numa node, cpu, and per node memory from numactl -H (requires numactl installed)
Definition builder.hpp:70
#define ADC_HS_ENV
ADC_HS_ENV collects env vars listed in env("ADC_HOST_SECTION_ENV") which is :-separated....
Definition builder.hpp:61
#define ADC_MPI_NAME
include "mpi_name" field from mpi_comm_name
Definition builder.hpp:99
#define ADC_MPI_RANK
include "mpi_rank" field from mpi_comm_rank
Definition builder.hpp:93
#define ADC_MPI_VER
include "mpi_version" field from MPI_VERSION.MPI_SUBVERSIUON
Definition builder.hpp:112
#define ADC_MPI_LIB_VER
include mpi_get_library_version result.
Definition builder.hpp:117
int32_t adc_mpi_field_flags
Definition builder.hpp:87
#define ADC_MPI_RANK_HOST
include "mpi_rank_host" subsection from the communicator
Definition builder.hpp:109
#define ADC_MPI_SIZE
include "mpi_size" field from mpi_comm_size
Definition builder.hpp:96
#define ADC_MPI_NONE
include no mpi fields
Definition builder.hpp:90
#define ADC_MPI_HOSTLIST
include "mpi_hostlist" subsection from the communicator
Definition builder.hpp:104
Definition adc.hpp:82
std::string get_lscpu()
Definition builder.ipp:103
std::string_view strip(std::string_view s, char c='/')
Definition builder.ipp:47
boost::json::kind scalar_type_representation(scalar_type st)
Definition enums.ipp:95
void fill_array_bool(field &f, boost::json::array &a)
Definition builder.ipp:1017
std::string get_exe(size_t bufsize)
Definition builder.ipp:66
std::string_view string_view
Definition curl.ipp:18
boost::json::object get_gpu_data(bool debug)
Definition builder.ipp:223
void fill_array_string(field &f, boost::json::array &a)
Definition builder.ipp:1035
std::vector< std::string > get_libs(std::string_view)
Definition builder.ipp:146
void update_meminfo(bool debug)
Definition builder.ipp:375
bool badkey(std::string_view)
Definition builder.ipp:39
boost::json::object get_numa_hardware()
Definition builder.ipp:164
std::vector< std::string > split_string(const std::string &s, char delimiter)
Definition builder.ipp:473
const size_t meminfo_raw
Definition builder.ipp:348
std::string tolower_s(std::string s)
Definition builder.ipp:58
bool array_contains_string(boost::json::array &av, string_view uuid)
Definition builder.ipp:1587
uint64_t get_memtotal()
Definition builder.ipp:351
std::string container
name of the container variety given to see builder::add_array
Definition types.hpp:168
scalar_type st
scalar type of the data as published,
Definition types.hpp:165
size_t count
number of elements in vp.
Definition types.hpp:167
variant data
Definition types.hpp:169
key_type kt
kind of data associated with the name queried
Definition types.hpp:164
const void * vp
address of data to be cast according to st for use with c/fortran
Definition types.hpp:166
uint64_t value
Definition builder.ipp:326
std::string name
Definition builder.ipp:325
std::string output
Definition outpipe.ipp:16
A version with tags list.
Definition types.hpp:28
const std::string name
Definition types.hpp:29