489 std::vector<DeviceSpec>& devices,
490 std::vector<DeviceId>& deviceIndex,
491 std::vector<DeviceConnectionId>& connections,
493 const std::vector<size_t>& outEdgeIndex,
494 const std::vector<DeviceConnectionEdge>& logicalEdges,
495 const std::vector<EdgeAction>& actions,
const WorkflowSpec& workflow,
496 const std::vector<OutputSpec>& outputsMatchers,
497 const std::vector<ChannelConfigurationPolicy>& channelPolicies,
498 const std::vector<SendingPolicy>& sendingPolicies,
499 const std::vector<ForwardingPolicy>& forwardingPolicies,
500 std::string
const& channelPrefix,
507 assert(!workflow.empty());
511 auto deviceForEdge = [&actions, &workflow, &devices,
512 &logicalEdges, &resourceManager,
513 &defaultOffer, &channelPrefix, overrideServices](
size_t ei,
ComputingOffer& acceptedOffer) {
514 auto& edge = logicalEdges[ei];
515 auto& action = actions[ei];
517 if (action.requiresNewDevice ==
false) {
518 assert(devices.empty() ==
false);
519 return devices.size() - 1;
521 if (acceptedOffer.hostname !=
"") {
525 auto& processor = workflow[edge.producer];
527 acceptedOffer.cpu = defaultOffer.
cpu;
528 acceptedOffer.memory = defaultOffer.
memory;
530 if (offer.cpu < acceptedOffer.cpu) {
533 if (offer.memory < acceptedOffer.memory) {
536 acceptedOffer.hostname = offer.hostname;
537 acceptedOffer.startPort = offer.startPort;
538 acceptedOffer.rangeSize = 0;
543 .
name = processor.name,
544 .id = processor.maxInputTimeslices == 1 ? processor.name : processor.name +
"_t" +
std::to_string(edge.producerTimeIndex),
545 .channelPrefix = channelPrefix,
547 .options = processor.options,
549 .algorithm = processor.algorithm,
550 .rank = processor.rank,
551 .nSlots = processor.nSlots,
552 .inputTimesliceId = edge.producerTimeIndex,
553 .maxInputTimeslices = processor.maxInputTimeslices,
554 .resource = {acceptedOffer},
555 .labels = processor.labels,
556 .metadata = processor.metadata});
585 for (
auto&
output : processor.outputs) {
586 if (
output.lifetime != Lifetime::OutOfBand) {
590 .
name =
"downstream",
593 .hostname =
"localhost",
596 for (
auto& meta :
output.metadata) {
597 if (meta.name ==
"channel-name") {
598 extraOutputChannelSpec.name = meta.defaultValue.get<std::string>();
600 if (meta.name ==
"port") {
601 extraOutputChannelSpec.port = meta.defaultValue.get<int32_t>();
603 if (meta.name ==
"address") {
604 extraOutputChannelSpec.hostname = meta.defaultValue.get<std::string>();
607 devices.back().outputChannels.push_back(extraOutputChannelSpec);
609 return devices.size() - 1;
612 auto channelFromDeviceEdgeAndPort = [&connections, &workflow, &channelPolicies](
const DeviceSpec& device,
617 auto& consumer = workflow[edge.consumer];
618 std::string consumerDeviceId = consumer.
name;
619 if (consumer.maxInputTimeslices != 1) {
622 channel.
name = device.channelPrefix +
"from_" + device.id +
"_to_" + consumerDeviceId;
623 channel.
port = acceptedOffer.startPort + acceptedOffer.rangeSize;
624 channel.
hostname = acceptedOffer.hostname;
625 deviceResource.usedPorts += 1;
626 acceptedOffer.rangeSize += 1;
628 for (
auto& policy : channelPolicies) {
629 if (policy.match(device.id, consumerDeviceId)) {
630 policy.modifyOutput(channel);
635 connections.push_back(
id);
637 auto&
source = workflow[edge.producer];
640 O2_SIGNPOST_START(device_spec_helpers, sid,
"new channels",
"Channel %{public}s has been created.", channel.
name.c_str());
642 O2_SIGNPOST_START(device_spec_helpers, iid,
"producer outputs",
"Producer %{public}s has the following outputs:",
source.name.c_str());
647 O2_SIGNPOST_START(device_spec_helpers, iid,
"producer forwards",
"Producer %{public}s has the following forwards:",
source.name.c_str());
648 for (
auto& forwards : device.forwards) {
652 O2_SIGNPOST_START(device_spec_helpers, iid,
"consumer inputs",
"Consumer %{public}s has the following inputs:", consumer.name.c_str());
653 for (
auto& input : consumer.inputs) {
661 auto isDifferentDestinationDeviceReferredBy = [&actions](
size_t ei) {
return actions[ei].requiresNewChannel; };
668 auto createChannelForDeviceEdge = [&devices, &logicalEdges, &channelFromDeviceEdgeAndPort,
670 auto& device = devices[
di];
671 auto& edge = logicalEdges[ei];
673 deviceIndex.emplace_back(
DeviceId{edge.producer, edge.producerTimeIndex,
di});
675 OutputChannelSpec channel = channelFromDeviceEdgeAndPort(device, device.resource, offer, edge);
677 device.outputChannels.push_back(channel);
678 return device.outputChannels.size() - 1;
685 auto appendOutputRouteToSourceDeviceChannel = [&outputsMatchers, &workflow, &devices, &logicalEdges, &sendingPolicies, &forwardingPolicies, &configContext](
686 size_t ei,
size_t di,
size_t ci) {
687 assert(ei < logicalEdges.size());
688 assert(
di < devices.size());
689 assert(ci < devices[
di].outputChannels.size());
690 auto& edge = logicalEdges[ei];
691 auto& device = devices[
di];
692 assert(edge.consumer < workflow.size());
693 auto& consumer = workflow[edge.consumer];
694 auto& producer = workflow[edge.producer];
695 auto& channel = devices[
di].outputChannels[ci];
696 assert(edge.outputGlobalIndex < outputsMatchers.size());
700 for (
auto& policy : sendingPolicies) {
701 if (policy.matcher(producer, consumer, configContext)) {
706 assert(forwardingPolicies.empty() ==
false);
707 for (
auto& policy : forwardingPolicies) {
708 if (policy.matcher(producer, consumer, configContext)) {
709 forwardPolicyPtr = &policy;
713 assert(policyPtr !=
nullptr);
714 assert(forwardPolicyPtr !=
nullptr);
716 if (edge.isForward ==
false) {
719 .maxTimeslices = consumer.maxInputTimeslices,
720 .matcher = outputsMatchers[edge.outputGlobalIndex],
721 .channel = channel.name,
724 device.outputs.emplace_back(route);
728 .maxTimeslices = consumer.maxInputTimeslices,
729 .matcher = workflow[edge.consumer].inputs[edge.consumerInputIndex],
730 .channel = channel.name,
731 .policy = forwardPolicyPtr,
736 if (route.matcher.lifetime == Lifetime::Timer) {
737 route.
matcher.lifetime = Lifetime::Timeframe;
739 device.forwards.emplace_back(route);
743 auto sortDeviceIndex = [&deviceIndex]() { std::sort(deviceIndex.begin(), deviceIndex.end()); };
745 auto lastChannelFor = [&devices](
size_t di) {
746 assert(
di < devices.size());
747 assert(devices[
di].outputChannels.empty() ==
false);
748 return devices[
di].outputChannels.size() - 1;
762 for (
auto edge : outEdgeIndex) {
763 auto device = deviceForEdge(edge, acceptedOffer);
765 if (isDifferentDestinationDeviceReferredBy(edge)) {
766 channel = createChannelForDeviceEdge(device, edge, acceptedOffer);
768 channel = lastChannelFor(device);
770 appendOutputRouteToSourceDeviceChannel(edge, device, channel);
772 if (std::string(acceptedOffer.hostname) !=
"") {
779 std::vector<DeviceId>& deviceIndex,
780 const std::vector<DeviceConnectionId>& connections,
782 const std::vector<size_t>& inEdgeIndex,
783 const std::vector<DeviceConnectionEdge>& logicalEdges,
784 const std::vector<EdgeAction>& actions,
const WorkflowSpec& workflow,
785 std::vector<LogicalForwardInfo>
const& availableForwardsInfo,
786 std::vector<ChannelConfigurationPolicy>
const& channelPolicies,
787 std::string
const& channelPrefix,
791 auto const& constDeviceIndex = deviceIndex;
792 if (!std::is_sorted(constDeviceIndex.cbegin(), constDeviceIndex.cend())) {
796 auto findProducerForEdge = [&logicalEdges, &constDeviceIndex](
size_t ei) {
797 auto& edge = logicalEdges[ei];
799 DeviceId pid{edge.producer, edge.producerTimeIndex, 0};
800 auto deviceIt = std::lower_bound(constDeviceIndex.cbegin(), constDeviceIndex.cend(),
pid);
802 assert(deviceIt != constDeviceIndex.end());
803 assert(deviceIt->processorIndex ==
pid.processorIndex && deviceIt->timeslice ==
pid.timeslice);
804 return deviceIt->deviceIndex;
807 auto findConsumerForEdge = [&logicalEdges, &constDeviceIndex](
size_t ei) {
808 auto& edge = logicalEdges[ei];
811 auto deviceIt = std::lower_bound(constDeviceIndex.cbegin(), constDeviceIndex.cend(),
pid);
813 assert(deviceIt != constDeviceIndex.end());
814 assert(deviceIt->processorIndex ==
pid.processorIndex && deviceIt->timeslice ==
pid.timeslice);
815 return deviceIt->deviceIndex;
824 decltype(deviceIndex.begin()) lastConsumerSearch;
825 size_t lastConsumerSearchEdge;
826 auto hasConsumerForEdge = [&lastConsumerSearch, &lastConsumerSearchEdge, &deviceIndex,
827 &logicalEdges](
size_t ei) ->
int {
828 auto& edge = logicalEdges[ei];
829 DeviceId cid{edge.consumer, edge.timeIndex, 0};
830 lastConsumerSearchEdge = ei;
831 lastConsumerSearch = std::lower_bound(deviceIndex.begin(), deviceIndex.end(), cid);
832 return lastConsumerSearch != deviceIndex.end() && cid.processorIndex == lastConsumerSearch->processorIndex &&
833 cid.timeslice == lastConsumerSearch->timeslice;
838 auto getConsumerForEdge = [&lastConsumerSearch, &lastConsumerSearchEdge](
size_t ei) {
839 assert(ei == lastConsumerSearchEdge);
840 return lastConsumerSearch->deviceIndex;
843 auto createNewDeviceForEdge = [&workflow, &logicalEdges, &devices,
844 &deviceIndex, &resourceManager, &defaultOffer,
845 &channelPrefix, &overrideServices](
size_t ei,
ComputingOffer& acceptedOffer) {
846 auto& edge = logicalEdges[ei];
848 if (acceptedOffer.hostname !=
"") {
852 auto& processor = workflow[edge.consumer];
854 acceptedOffer.cpu = defaultOffer.
cpu;
855 acceptedOffer.memory = defaultOffer.
memory;
857 if (offer.cpu < acceptedOffer.cpu) {
860 if (offer.memory < acceptedOffer.memory) {
863 acceptedOffer.hostname = offer.hostname;
864 acceptedOffer.startPort = offer.startPort;
865 acceptedOffer.rangeSize = 0;
870 .
name = processor.name,
871 .id = processor.name,
872 .channelPrefix = channelPrefix,
873 .options = processor.options,
875 .algorithm = processor.algorithm,
876 .rank = processor.rank,
877 .nSlots = processor.nSlots,
878 .inputTimesliceId = edge.timeIndex,
879 .maxInputTimeslices = processor.maxInputTimeslices,
880 .resource = {acceptedOffer},
881 .labels = processor.labels,
882 .metadata = processor.metadata};
884 if (processor.maxInputTimeslices != 1) {
890 auto id =
DeviceId{edge.consumer, edge.timeIndex, devices.size()};
891 devices.emplace_back(std::move(device));
892 deviceIndex.push_back(
id);
893 std::sort(deviceIndex.begin(), deviceIndex.end());
894 return devices.size() - 1;
901 auto findMatchingOutgoingPortForEdge = [&logicalEdges, &connections](
size_t ei) {
902 auto const& edge = logicalEdges[ei];
905 auto it = std::lower_bound(connections.begin(), connections.end(), connectionId);
907 assert(it != connections.end());
908 assert(it->producer == connectionId.producer);
909 assert(it->consumer == connectionId.consumer);
910 assert(it->timeIndex == connectionId.timeIndex);
911 assert(it->producerTimeIndex == connectionId.producerTimeIndex);
915 auto checkNoDuplicatesFor = [](std::vector<InputChannelSpec>
const&
channels,
const std::string&
name) {
916 for (
auto const& channel :
channels) {
917 if (channel.name ==
name) {
923 auto appendInputChannelForConsumerDevice = [&devices, &checkNoDuplicatesFor, &channelPolicies](
924 size_t pi,
size_t ci,
unsigned short port) {
925 auto const& producerDevice = devices[pi];
926 auto& consumerDevice = devices[ci];
928 channel.
name = producerDevice.channelPrefix +
"from_" + producerDevice.id +
"_to_" + consumerDevice.id;
929 channel.
hostname = producerDevice.resource.hostname;
931 for (
auto& policy : channelPolicies) {
932 if (policy.match(producerDevice.id, consumerDevice.id)) {
933 policy.modifyInput(channel);
937 assert(checkNoDuplicatesFor(consumerDevice.inputChannels, channel.
name));
938 consumerDevice.inputChannels.push_back(channel);
939 return consumerDevice.inputChannels.size() - 1;
945 auto getChannelForEdge = [&devices](
size_t pi,
size_t ci) {
946 auto& consumerDevice = devices[ci];
947 return consumerDevice.inputChannels.size() - 1;
954 auto appendInputRouteToDestDeviceChannel = [&devices, &logicalEdges, &workflow](
size_t ei,
size_t di,
size_t ci) {
955 auto const& edge = logicalEdges[ei];
956 auto const& consumer = workflow[edge.consumer];
957 auto const& producer = workflow[edge.producer];
958 auto& consumerDevice = devices[
di];
960 auto const& inputSpec = consumer.inputs[edge.consumerInputIndex];
961 auto const& sourceChannel = consumerDevice.inputChannels[ci].name;
965 edge.consumerInputIndex,
967 edge.producerTimeIndex,
974 for (
size_t iri = 0; iri < consumerDevice.inputs.size(); ++iri) {
975 auto& existingRoute = consumerDevice.inputs[iri];
976 if (existingRoute.timeslice != edge.producerTimeIndex) {
979 if (existingRoute.inputSpecIndex == edge.consumerInputIndex) {
987 if (edge.isForward && route.matcher.lifetime == Lifetime::Timer) {
989 "Warning: Forwarding timer {} from {} to a {} as both requested it."
990 " If this is undesired, please make sure to use two different data matchers for their InputSpec.",
992 producer.name.c_str(),
993 consumer.name.c_str());
994 route.matcher.lifetime = Lifetime::Timeframe;
997 consumerDevice.inputs.push_back(route);
1005 for (
size_t edge : inEdgeIndex) {
1006 auto& action = actions[edge];
1008 size_t consumerDevice = -1;
1010 if (action.requiresNewDevice) {
1011 if (hasConsumerForEdge(edge)) {
1012 consumerDevice = getConsumerForEdge(edge);
1014 consumerDevice = createNewDeviceForEdge(edge, acceptedOffer);
1017 consumerDevice = findConsumerForEdge(edge);
1019 size_t producerDevice = findProducerForEdge(edge);
1021 size_t channel = -1;
1022 if (action.requiresNewChannel) {
1023 int16_t port = findMatchingOutgoingPortForEdge(edge);
1024 channel = appendInputChannelForConsumerDevice(producerDevice, consumerDevice, port);
1026 channel = getChannelForEdge(producerDevice, consumerDevice);
1028 appendInputRouteToDestDeviceChannel(edge, consumerDevice, channel);
1032 for (
auto& device : devices) {
1033 for (
auto& route : device.inputs) {
1034 switch (route.matcher.lifetime) {
1035 case Lifetime::OutOfBand:
1036 route.configurator = {
1049 route.configurator = {
1055 case Lifetime::Timer:
1056 route.configurator = {
1062 case Lifetime::Enumeration:
1063 route.configurator = {
1064 .name =
"enumeration",
1069 case Lifetime::Signal:
1070 route.configurator = {
1076 case Lifetime::Transient:
1077 route.configurator = {
1078 .name =
"transient",
1083 case Lifetime::Optional:
1084 route.configurator = {
1096 if (acceptedOffer.
hostname !=
"") {
1105 std::vector<ChannelConfigurationPolicy>
const& channelPolicies,
1106 std::vector<CompletionPolicy>
const& completionPolicies,
1107 std::vector<DispatchPolicy>
const& dispatchPolicies,
1108 std::vector<ResourcePolicy>
const& resourcePolicies,
1109 std::vector<CallbacksPolicy>
const& callbacksPolicies,
1110 std::vector<SendingPolicy>
const& sendingPolicies,
1111 std::vector<ForwardingPolicy>
const& forwardingPolicies,
1112 std::vector<DeviceSpec>& devices,
1114 std::string
const& uniqueWorkflowId,
1116 bool optimizeTopology,
1117 unsigned short resourcesMonitoringInterval,
1118 std::string
const& channelPrefix,
1124 if (workflow.empty()) {
1127 std::vector<LogicalForwardInfo> availableForwardsInfo;
1128 std::vector<DeviceConnectionEdge> logicalEdges;
1129 std::vector<DeviceConnectionId> connections;
1130 std::vector<DeviceId> deviceIndex;
1135 std::vector<OutputSpec> outputs;
1149 std::vector<size_t> inEdgeIndex;
1150 std::vector<size_t> outEdgeIndex;
1157 size_t deviceCount = 0;
1158 for (
auto& action : outActions) {
1159 deviceCount += action.requiresNewDevice ? 1 : 0;
1161 for (
auto& action : inActions) {
1162 deviceCount += action.requiresNewDevice ? 1 : 0;
1167 defaultOffer.
cpu += offer.cpu;
1168 defaultOffer.
memory += offer.memory;
1172 defaultOffer.
cpu /= deviceCount + 1;
1173 defaultOffer.
memory /= deviceCount + 1;
1175 processOutEdgeActions(configContext, devices, deviceIndex, connections, resourceManager, outEdgeIndex, logicalEdges,
1176 outActions, workflow, outputs, channelPolicies, sendingPolicies, forwardingPolicies, channelPrefix, defaultOffer, overrideServices);
1179 std::sort(connections.begin(), connections.end());
1181 processInEdgeActions(devices, deviceIndex, connections, resourceManager, inEdgeIndex, logicalEdges,
1182 inActions, workflow, availableForwardsInfo, channelPolicies, channelPrefix, defaultOffer, overrideServices);
1185 std::map<std::string, DataProcessorPoliciesInfo> policies;
1187 bool hasPolicy =
false;
1188 policies[device.name].completionPolicyName =
"unknown";
1189 for (
auto& policy : completionPolicies) {
1190 if (policy.matcher(device) ==
true) {
1191 policies[policy.name].completionPolicyName = policy.name;
1192 device.completionPolicy = policy;
1197 if (hasPolicy ==
false) {
1198 throw runtime_error_f(
"Unable to find a completion policy for %s", device.id.c_str());
1200 for (
auto& policy : dispatchPolicies) {
1201 if (policy.deviceMatcher(device) ==
true) {
1202 device.dispatchPolicy = policy;
1206 for (
auto& policy : callbacksPolicies) {
1207 if (policy.matcher(device, configContext) ==
true) {
1208 device.callbacksPolicy = policy;
1213 for (
auto& policy : resourcePolicies) {
1214 if (policy.matcher(device) ==
true) {
1215 device.resourcePolicy = policy;
1220 if (hasPolicy ==
false) {
1221 throw runtime_error_f(
"Unable to find a resource policy for %s", device.id.c_str());
1225 std::vector<DataProcessorPoliciesInfo> policiesVector;
1226 for (
size_t wi = 0; wi < workflow.size(); ++wi) {
1227 auto& processor = workflow[wi];
1228 auto& info = policies[processor.name];
1229 policiesVector.push_back(info);
1234 for (
auto& device : devices) {
1235 device.resourceMonitoringInterval = resourcesMonitoringInterval;
1238 auto findDeviceIndex = [&deviceIndex](
size_t processorIndex,
size_t timeslice) {
1239 for (
auto& deviceEdge : deviceIndex) {
1240 if (deviceEdge.processorIndex != processorIndex) {
1243 if (deviceEdge.timeslice != timeslice) {
1246 return deviceEdge.deviceIndex;
1253 if (optimizeTopology) {
1254 for (
auto& connection : connections) {
1255 auto& device1 = devices[findDeviceIndex(connection.consumer, connection.timeIndex)];
1256 auto& device2 = devices[findDeviceIndex(connection.producer, connection.producerTimeIndex)];
1258 if (device1.resource.hostname != device2.resource.hostname) {
1261 for (
auto& input : device1.inputChannels) {
1262 for (
auto&
output : device2.outputChannels) {
1263 if (input.hostname ==
output.hostname && input.port ==
output.port) {
1266 input.hostname += uniqueWorkflowId;
1267 output.hostname += uniqueWorkflowId;
1385 unsigned short driverPort,
1387 std::vector<DataProcessorInfo>
const& processorInfos,
1388 std::vector<DeviceSpec>
const& deviceSpecs,
1389 std::vector<DeviceExecution>& deviceExecutions,
1390 std::vector<DeviceControl>& deviceControls,
1391 std::vector<ConfigParamSpec>
const& detectedOptions,
1392 std::string
const& uniqueWorkflowId)
1394 assert(deviceSpecs.size() == deviceExecutions.size());
1395 assert(deviceControls.size() == deviceExecutions.size());
1396 for (
size_t si = 0; si < deviceSpecs.size(); ++si) {
1397 auto& spec = deviceSpecs[si];
1399 O2_SIGNPOST_START(device_spec_helpers, poid,
"prepareArguments",
"Preparing options for %{public}s", spec.id.c_str());
1400 auto& control = deviceControls[si];
1401 auto& execution = deviceExecutions[si];
1403 control.quiet = defaultQuiet;
1404 control.stopped = defaultStopped;
1412 std::vector<ConfigParamSpec> workflowOptions = detectedOptions;
1413 for (
auto& opt : detectedOptions) {
1414 O2_SIGNPOST_EVENT_EMIT(device_spec_helpers, poid,
"prepareArguments",
"Processor option %{public}s passed as previously detected", opt.name.c_str());
1419 auto pi = std::find_if(processorInfos.begin(), processorInfos.end(), [&](
auto const&
x) { return x.name == spec.id; });
1420 argc = pi->cmdLineArgs.size() + 1;
1421 argv = (
char**)malloc(
sizeof(
char**) * (argc + 1));
1422 argv[0] = strdup(pi->executable.data());
1423 for (
size_t ai = 0; ai < pi->cmdLineArgs.size(); ++ai) {
1424 auto const& arg = pi->cmdLineArgs[ai];
1425 argv[ai + 1] = strdup(arg.data());
1427 argv[argc] =
nullptr;
1428 for (
auto& opt : pi->workflowOptions) {
1429 O2_SIGNPOST_EVENT_EMIT(device_spec_helpers, poid,
"prepareArguments",
"Processor option %{public}s found in process description", opt.name.c_str());
1430 workflowOptions.push_back(opt);
1433 auto last = std::unique(workflowOptions.begin(), workflowOptions.end());
1434 workflowOptions.erase(last, workflowOptions.end());
1436 for (
auto& opt : workflowOptions) {
1437 O2_SIGNPOST_EVENT_EMIT(device_spec_helpers, poid,
"prepareArguments",
"Final unique option %{public}s added to list of workflowOptions", opt.name.c_str());
1455 std::vector<std::string> tmpArgs = {argv[0],
1456 "--id", spec.id.c_str(),
1457 "--control", interactive ?
"gui" :
"static",
1458 "--shm-monitor",
"false",
1459 "--log-color",
"false",
1460 driverConfig.
batch ?
"--batch" :
"--no-batch",
1461 "--color",
"false"};
1466 std::vector<std::string> deviceOptionsSequence;
1467 std::unordered_map<std::string, std::string> uniqueDeviceArgs;
1468 auto updateDeviceArguments = [&deviceOptionsSequence, &uniqueDeviceArgs](
auto key,
auto value) {
1469 if (uniqueDeviceArgs.find(
key) == uniqueDeviceArgs.end()) {
1471 deviceOptionsSequence.emplace_back(
key);
1475 std::vector<std::string> tmpEnv;
1476 if (defaultStopped) {
1477 tmpArgs.emplace_back(
"-s");
1484 const char*
name = spec.name.c_str();
1485 bpo::options_description od;
1486 bpo::options_description foDesc;
1488 od.add_options()(
name, bpo::value<std::string>());
1492 foDesc.add(forwardedOptions);
1495 bool haveSessionArg =
false;
1496 using FilterFunctionT = std::function<
void(
decltype(argc),
decltype(argv),
decltype(od))>;
1497 bool useDefaultWS =
true;
1502 FilterFunctionT filterArgsFct = [&](
int largc,
char** largv,
const bpo::options_description& odesc) {
1504 using namespace bpo::command_line_style;
1505 auto style = (allow_short | short_allow_adjacent | short_allow_next | allow_long | long_allow_adjacent | long_allow_next | allow_sticky | allow_dash_for_short);
1507 bpo::command_line_parser parser{largc, largv};
1508 parser.options(odesc).allow_unregistered();
1509 parser.style(style);
1510 bpo::parsed_options parsed_options = parser.run();
1512 bpo::variables_map varmap;
1513 bpo::store(parsed_options, varmap);
1514 if (varmap.count(
"environment")) {
1515 auto environment = varmap[
"environment"].as<std::string>();
1516 split(environment, tmpEnv);
1520 if (varmap.count(
"stacktrace-on-signal") && varmap[
"stacktrace-on-signal"].as<std::string>() !=
"none" && varmap[
"stacktrace-on-signal"].as<std::string>() !=
"simple") {
1521 char const* preload = getenv(
"LD_PRELOAD");
1522 if (preload ==
nullptr || strcmp(preload,
"libSegFault.so") == 0) {
1523 tmpEnv.emplace_back(
"LD_PRELOAD=libSegFault.so");
1525 tmpEnv.push_back(fmt::format(
"LD_PRELOAD={}:libSegFault.so", preload));
1527 tmpEnv.push_back(fmt::format(
"SEGFAULT_SIGNALS={}", varmap[
"stacktrace-on-signal"].as<std::string>()));
1534 if (varmap.count(
name) > 0) {
1538 auto arguments =
"--unused " + varmap[
name].as<std::string>();
1539 wordexp_t expansions;
1540 wordexp(
arguments.c_str(), &expansions, 0);
1541 bpo::options_description realOdesc = odesc;
1542 realOdesc.add_options()(
"severity", bpo::value<std::string>());
1543 realOdesc.add_options()(
"child-driver", bpo::value<std::string>());
1544 realOdesc.add_options()(
"rate", bpo::value<std::string>());
1545 realOdesc.add_options()(
"exit-transition-timeout", bpo::value<std::string>());
1546 realOdesc.add_options()(
"error-on-exit-transition-timeout", bpo::value<bool>()->zero_tokens());
1547 realOdesc.add_options()(
"data-processing-timeout", bpo::value<std::string>());
1548 realOdesc.add_options()(
"expected-region-callbacks", bpo::value<std::string>());
1549 realOdesc.add_options()(
"timeframes-rate-limit", bpo::value<std::string>());
1550 realOdesc.add_options()(
"environment", bpo::value<std::string>());
1551 realOdesc.add_options()(
"stacktrace-on-signal", bpo::value<std::string>());
1552 realOdesc.add_options()(
"post-fork-command", bpo::value<std::string>());
1553 realOdesc.add_options()(
"bad-alloc-max-attempts", bpo::value<std::string>());
1554 realOdesc.add_options()(
"bad-alloc-attempt-interval", bpo::value<std::string>());
1555 realOdesc.add_options()(
"io-threads", bpo::value<std::string>());
1556 realOdesc.add_options()(
"shm-segment-size", bpo::value<std::string>());
1557 realOdesc.add_options()(
"shm-mlock-segment", bpo::value<std::string>());
1558 realOdesc.add_options()(
"shm-mlock-segment-on-creation", bpo::value<std::string>());
1559 realOdesc.add_options()(
"shm-zero-segment", bpo::value<std::string>());
1560 realOdesc.add_options()(
"shm-throw-bad-alloc", bpo::value<std::string>());
1561 realOdesc.add_options()(
"shm-segment-id", bpo::value<std::string>());
1562 realOdesc.add_options()(
"shm-allocation", bpo::value<std::string>());
1563 realOdesc.add_options()(
"shm-no-cleanup", bpo::value<std::string>());
1564 realOdesc.add_options()(
"shmid", bpo::value<std::string>());
1565 realOdesc.add_options()(
"shm-metadata-msg-size", bpo::value<std::string>()->default_value(
"0"));
1566 realOdesc.add_options()(
"shm-monitor", bpo::value<std::string>());
1567 realOdesc.add_options()(
"channel-prefix", bpo::value<std::string>());
1568 realOdesc.add_options()(
"network-interface", bpo::value<std::string>());
1569 realOdesc.add_options()(
"early-forward-policy", bpo::value<std::string>());
1570 realOdesc.add_options()(
"session", bpo::value<std::string>());
1571 realOdesc.add_options()(
"signposts", bpo::value<std::string>());
1572 filterArgsFct(expansions.we_wordc, expansions.we_wordv, realOdesc);
1573 wordfree(&expansions);
1577 const char* child_driver_key =
"child-driver";
1578 if (varmap.count(child_driver_key) > 0) {
1579 auto arguments = varmap[child_driver_key].as<std::string>();
1580 wordexp_t expansions;
1581 wordexp(
arguments.c_str(), &expansions, 0);
1582 tmpArgs.insert(tmpArgs.begin(), expansions.we_wordv, expansions.we_wordv + expansions.we_wordc);
1585 haveSessionArg = haveSessionArg || varmap.count(
"session") != 0;
1586 useDefaultWS = useDefaultWS && ((varmap.count(
"driver-client-backend") == 0) || varmap[
"driver-client-backend"].as<std::string>() ==
"ws://");
1588 auto processRawChannelConfig = [&tmpArgs, &spec](
const std::string& conf) {
1591 while (std::getline(ss, token,
';')) {
1592 token.erase(token.begin(), std::find_if(token.begin(), token.end(), [](
int ch) { return !std::isspace(ch); }));
1593 token.erase(std::find_if(token.rbegin(), token.rend(), [](
int ch) { return !std::isspace(ch); }).base(), token.end());
1594 if (!token.empty()) {
1595 tmpArgs.emplace_back(
"--channel-config");
1596 tmpArgs.emplace_back(token);
1606 std::unordered_map<std::string_view, const bpo::option_description*> odescByName;
1607 odescByName.reserve(odesc.options().size());
1608 for (
auto const& optDesc : odesc.options()) {
1609 auto [names,
count] = optDesc->long_names();
1610 for (
size_t ni = 0; ni <
count; ++ni) {
1611 auto [it, inserted] = odescByName.try_emplace(names[ni], optDesc.get());
1613 it->second =
nullptr;
1617 for (
const auto& varit : varmap) {
1620 auto descIt = odescByName.find(varit.first);
1621 const auto*
description = (descIt != odescByName.end() && descIt->second !=
nullptr)
1623 : odesc.find_nothrow(varit.first,
false);
1630 const char* optarg =
"";
1632 control.options.insert(std::make_pair(varit.first, optarg));
1636 if (semantic->min_tokens() == 0 && varit.second.as<
bool>()) {
1637 updateDeviceArguments(fmt::format(
"--{}", varit.first),
"");
1638 control.options.insert(std::make_pair(varit.first, optarg));
1645 assert(semantic->min_tokens() <= 1);
1647 if (semantic->min_tokens() == 0) {
1648 control.options.insert(std::make_pair(varit.first, optarg));
1652 if (semantic->min_tokens() > 0) {
1653 std::string stringRep;
1654 if (
auto v = boost::any_cast<std::string>(&varit.second.value())) {
1656 }
else if (
auto v = boost::any_cast<EarlyForwardPolicy>(&varit.second.value())) {
1657 std::stringstream tmp;
1659 stringRep = fmt::format(
"{}", tmp.str());
1661 if (varit.first ==
"channel-config") {
1666 processRawChannelConfig(stringRep);
1667 optarg = tmpArgs.back().c_str();
1669 std::string
key(fmt::format(
"--{}", varit.first));
1670 if (stringRep.length() == 0) {
1676 updateDeviceArguments(
key, stringRep);
1677 optarg = uniqueDeviceArgs[
key].c_str();
1680 control.options.insert(std::make_pair(varit.first, optarg));
1685 filterArgsFct(argc, argv, foDesc);
1687 filterArgsFct(argc, argv, od);
1690 for (
auto& channel : spec.outputChannels) {
1691 tmpArgs.emplace_back(
"--channel-config");
1694 for (
auto& channel : spec.inputChannels) {
1695 tmpArgs.emplace_back(
"--channel-config");
1700 if (!haveSessionArg) {
1701 updateDeviceArguments(std::string(
"--session"),
"dpl_" + uniqueWorkflowId);
1706 updateDeviceArguments(std::string(
"--driver-client-backend"),
"ws://0.0.0.0:" +
std::to_string(driverPort));
1709 if (spec.resourceMonitoringInterval > 0) {
1710 updateDeviceArguments(std::string(
"--resources-monitoring"),
std::to_string(spec.resourceMonitoringInterval));
1715 for (
auto& arg : tmpArgs) {
1716 execution.args.emplace_back(strdup(arg.c_str()));
1718 for (
auto&
key : deviceOptionsSequence) {
1719 execution.args.emplace_back(strdup(
key.c_str()));
1720 std::string
const&
value = uniqueDeviceArgs[
key];
1721 if (
value.empty()) {
1724 }
else if (
value ==
" ") {
1727 execution.args.emplace_back(strdup(
""));
1729 execution.args.emplace_back(strdup(
value.c_str()));
1733 execution.args.push_back(
nullptr);
1735 for (
auto& env : tmpEnv) {
1736 execution.environ.emplace_back(strdup(env.c_str()));
1740 std::ostringstream
str;
1741 for (
size_t ai = 0; ai < execution.args.size() - 1; ai++) {
1742 if (execution.args[ai] ==
nullptr) {
1743 LOG(error) <<
"Bad argument for " << execution.args[ai - 1];
1745 assert(execution.args[ai]);
1746 str <<
" " << execution.args[ai];
1748 O2_SIGNPOST_END(device_spec_helpers, poid,
"prepareArguments",
"The following options are being forwarded to %{public}s: %{public}s",
1749 spec.id.c_str(),
str.str().c_str());
1758 bpo::options_description forwardedDeviceOptions;
1759 char const* defaultSignposts = getenv(
"DPL_SIGNPOSTS") ? getenv(
"DPL_SIGNPOSTS") :
"";
1760 forwardedDeviceOptions.add_options()
1761 (
"severity", bpo::value<std::string>()->default_value(
"info"),
"severity level of the log")
1762 (
"plugin,P", bpo::value<std::string>(),
"FairMQ plugin list")
1763 (
"plugin-search-path,S", bpo::value<std::string>(),
"FairMQ plugins search path")
1764 (
"control-port", bpo::value<std::string>(),
"Utility port to be used by O2 Control")
1765 (
"rate", bpo::value<std::string>(),
"rate for a data source device (Hz)")
1766 (
"exit-transition-timeout", bpo::value<std::string>(),
"timeout before switching to READY state")
1767 (
"error-on-exit-transition-timeout", bpo::value<bool>()->zero_tokens(),
"print error instead of warning when exit transition timer expires")
1768 (
"data-processing-timeout", bpo::value<std::string>(),
"timeout after which only calibration can happen")
1769 (
"expected-region-callbacks", bpo::value<std::string>(),
"region callbacks to expect before starting")
1770 (
"timeframes-rate-limit", bpo::value<std::string>()->default_value(
"0"),
"how many timeframes can be in flight")
1771 (
"shm-monitor", bpo::value<std::string>(),
"whether to use the shared memory monitor")
1772 (
"channel-prefix", bpo::value<std::string>()->default_value(
""),
"prefix to use for multiplexing multiple workflows in the same session")
1773 (
"bad-alloc-max-attempts", bpo::value<std::string>()->default_value(
"1"),
"throw after n attempts to alloc shm")
1774 (
"bad-alloc-attempt-interval", bpo::value<std::string>()->default_value(
"50"),
"interval between shm alloc attempts in ms")
1775 (
"io-threads", bpo::value<std::string>()->default_value(
"1"),
"number of FMQ io threads")
1776 (
"shm-segment-size", bpo::value<std::string>(),
"size of the shared memory segment in bytes")
1777 (
"shm-mlock-segment", bpo::value<std::string>()->default_value(
"false"),
"mlock shared memory segment")
1778 (
"shm-mlock-segment-on-creation", bpo::value<std::string>()->default_value(
"false"),
"mlock shared memory segment once on creation")
1779 (
"shm-zero-segment", bpo::value<std::string>()->default_value(
"false"),
"zero shared memory segment")
1780 (
"shm-throw-bad-alloc", bpo::value<std::string>()->default_value(
"true"),
"throw if insufficient shm memory")
1781 (
"shm-segment-id", bpo::value<std::string>()->default_value(
"0"),
"shm segment id")
1782 (
"shm-allocation", bpo::value<std::string>()->default_value(
"rbtree_best_fit"),
"shm allocation method")
1783 (
"shm-no-cleanup", bpo::value<std::string>()->default_value(
"false"),
"no shm cleanup")
1784 (
"shmid", bpo::value<std::string>(),
"shmid")
1785 (
"shm-metadata-msg-size", bpo::value<std::string>()->default_value(
"0"),
"numeric value in B used for padding FairMQ header, see FairMQ v.1.6.0")
1786 (
"environment", bpo::value<std::string>(),
"comma separated list of environment variables to set for the device")
1787 (
"stacktrace-on-signal", bpo::value<std::string>()->default_value(
"simple"),
1788 "dump stacktrace on specified signal(s) (any of `all`, `segv`, `bus`, `ill`, `abrt`, `fpe`, `sys`.)"
1789 "Use `simple` to dump only the main thread in a reliable way")
1790 (
"post-fork-command", bpo::value<std::string>(),
"post fork command to execute (e.g. numactl {pid}")
1791 (
"session", bpo::value<std::string>(),
"unique label for the shared memory session")
1792 (
"network-interface", bpo::value<std::string>(),
"network interface to which to bind tpc fmq ports without specified address")
1793 (
"early-forward-policy", bpo::value<EarlyForwardPolicy>()->default_value(
EarlyForwardPolicy::NEVER),
"when to forward early the messages: never, noraw, always")
1794 (
"configuration,cfg", bpo::value<std::string>(),
"configuration connection string")
1795 (
"driver-client-backend", bpo::value<std::string>(),
"driver connection string")
1796 (
"monitoring-backend", bpo::value<std::string>(),
"monitoring connection string")
1797 (
"dpl-stats-min-online-publishing-interval", bpo::value<std::string>(),
"minimum flushing interval for online metrics (in s)")
1798 (
"infologger-mode", bpo::value<std::string>(),
"O2_INFOLOGGER_MODE override")
1799 (
"infologger-severity", bpo::value<std::string>(),
"minimun FairLogger severity which goes to info logger")
1800 (
"dpl-tracing-flags", bpo::value<std::string>(),
"pipe separated list of events to trace")
1801 (
"signposts", bpo::value<std::string>()->default_value(defaultSignposts),
1802 "comma separated list of signposts to enable (any of `completion`, `data_processor_context`, `stream_context`, `device`, `monitoring_service`)")
1803 (
"log-timestamp-us", bpo::value<bool>()->zero_tokens()->default_value(
false),
"enable microsecond timestamps in log messages")
1804 (
"child-driver", bpo::value<std::string>(),
"external driver to start childs with (e.g. valgrind)");
1806 return forwardedDeviceOptions;