1028 std::string
const& defaultDriverClient,
1031 fair::Logger::SetConsoleColor(
false);
1032 fair::Logger::OnFatal([]() {
throw runtime_error(
"Fatal error"); });
1034 LOG(info) <<
"Spawning new device " << spec.id <<
" in process with pid " << getpid();
1036 fair::mq::DeviceRunner runner{argc, argv};
1040 runner.AddHook<fair::mq::hooks::SetCustomCmdLineOptions>([&spec, driverConfig, defaultDriverClient](fair::mq::DeviceRunner&
r) {
1041 std::string defaultExitTransitionTimeout =
"0";
1042 std::string defaultDataProcessingTimeout =
"0";
1043 std::string defaultInfologgerMode =
"";
1046 defaultExitTransitionTimeout =
"40";
1047 defaultDataProcessingTimeout =
"20";
1048 defaultInfologgerMode =
"infoLoggerD";
1050 defaultExitTransitionTimeout =
"40";
1051 defaultDataProcessingTimeout =
"20";
1053 boost::program_options::options_description optsDesc;
1055 char const* defaultSignposts = getenv(
"DPL_SIGNPOSTS");
1056 optsDesc.add_options()(
"monitoring-backend", bpo::value<std::string>()->default_value(
"default"),
"monitoring backend info")
1057 (
"dpl-stats-min-online-publishing-interval", bpo::value<std::string>()->default_value(
"0"),
"minimum flushing interval for online metrics (in s)")
1058 (
"driver-client-backend", bpo::value<std::string>()->default_value(defaultDriverClient),
"backend for device -> driver communicataon: stdout://: use stdout, ws://: use websockets")
1059 (
"infologger-severity", bpo::value<std::string>()->default_value(
""),
"minimum FairLogger severity to send to InfoLogger")
1060 (
"dpl-tracing-flags", bpo::value<std::string>()->default_value(
""),
"pipe `|` separate list of events to be traced")
1061 (
"signposts", bpo::value<std::string>()->default_value(defaultSignposts ? defaultSignposts :
""),
"comma separated list of signposts to enable")
1062 (
"expected-region-callbacks", bpo::value<std::string>()->default_value(
"0"),
"how many region callbacks we are expecting")
1063 (
"exit-transition-timeout", bpo::value<std::string>()->default_value(defaultExitTransitionTimeout),
"how many second to wait before switching from RUN to READY")
1064 (
"error-on-exit-transition-timeout", bpo::value<bool>()->zero_tokens()->default_value(
false),
"print error instead of warning when exit transition timer expires")
1065 (
"data-processing-timeout", bpo::value<std::string>()->default_value(defaultDataProcessingTimeout),
"how many second to wait before stopping data processing and allowing data calibration")
1066 (
"timeframes-rate-limit", bpo::value<std::string>()->default_value(
"0"),
"how many timeframe can be in flight at the same moment (0 disables)")
1067 (
"configuration,cfg", bpo::value<std::string>()->default_value(
"command-line"),
"configuration backend")
1068 (
"infologger-mode", bpo::value<std::string>()->default_value(defaultInfologgerMode),
"O2_INFOLOGGER_MODE override")
1069 (
"log-timestamp-us", bpo::value<bool>()->zero_tokens()->default_value(
false),
"enable microsecond timestamps in log messages");
1070 r.fConfig.AddToCmdLineOptions(optsDesc,
true);
1075 std::unique_ptr<SimpleRawDeviceService> simpleRawDeviceService;
1076 std::unique_ptr<DeviceState> deviceState;
1077 std::unique_ptr<ComputingQuotaEvaluator> quotaEvaluator;
1078 std::unique_ptr<FairMQDeviceProxy> deviceProxy;
1079 std::unique_ptr<DeviceContext> deviceContext;
1081 auto afterConfigParsingCallback = [&simpleRawDeviceService,
1087 &danglingEdgesContext,
1090 &processingPolicies,
1093 &loop](fair::mq::DeviceRunner&
r) {
1095 simpleRawDeviceService = std::make_unique<SimpleRawDeviceService>(
nullptr, spec);
1096 serviceRef.
registerService(ServiceRegistryHelpers::handleForService<RawDeviceService>(simpleRawDeviceService.get()));
1098 deviceState = std::make_unique<DeviceState>();
1099 deviceState->loop = loop;
1101 serviceRef.
registerService(ServiceRegistryHelpers::handleForService<DeviceState>(deviceState.get()));
1103 quotaEvaluator = std::make_unique<ComputingQuotaEvaluator>(serviceRef);
1104 serviceRef.
registerService(ServiceRegistryHelpers::handleForService<ComputingQuotaEvaluator>(quotaEvaluator.get()));
1107 serviceRef.
registerService(ServiceRegistryHelpers::handleForService<DeviceSpec const>(&spec));
1108 serviceRef.
registerService(ServiceRegistryHelpers::handleForService<RunningWorkflowInfo const>(&runningWorkflow));
1109 serviceRef.
registerService(ServiceRegistryHelpers::handleForService<DeviceContext>(deviceContext.get()));
1110 serviceRef.
registerService(ServiceRegistryHelpers::handleForService<DriverConfig const>(&driverConfig));
1111 serviceRef.
registerService(ServiceRegistryHelpers::handleForService<DanglingEdgesContext>(&danglingEdgesContext));
1113 auto device = std::make_unique<DataProcessingDevice>(
ref, serviceRegistry);
1116 r.fDevice = std::move(device);
1117 fair::Logger::SetConsoleColor(
false);
1118 if (
r.fConfig.GetProperty<
bool>(
"log-timestamp-us")) {
1119 fair::Logger::DefineVerbosity(fair::Verbosity::user1,
1120 fair::VerbositySpec::Make(fair::VerbositySpec::Info::timestamp_us,
1121 fair::VerbositySpec::Info::severity));
1122 fair::Logger::SetVerbosity(fair::Verbosity::user1);
1126 for (
auto& service : spec.services) {
1127 LOG(
debug) <<
"Declaring service " << service.name;
1128 serviceRegistry.
declareService(service, *deviceState.get(),
r.fConfig);
1131 serviceRef.
get<
Monitoring>().enableProcessMonitoring(spec.resourceMonitoringInterval, {PmMeasurement::Cpu, PmMeasurement::Mem, PmMeasurement::Smaps});
1135 runner.AddHook<fair::mq::hooks::InstantiateDevice>(afterConfigParsingCallback);
1137 auto result = runner.Run();
1381 DriverInfo& driverInfo,
1383 std::vector<DeviceMetricsInfo>& metricsInfos,
1384 std::vector<ConfigParamSpec>
const& detectedParams,
1385 boost::program_options::variables_map& varmap,
1386 std::vector<ServiceSpec>& driverServices,
1387 std::string frameworkId)
1391 .shmSegmentId = (int16_t)atoi(varmap[
"shm-segment-id"].as<std::string>().c_str())};
1395 auto* devicesManager =
new DevicesManager{.
controls = controls, .infos = infos, .specs = runningWorkflow.devices, .messages = {}};
1399 std::vector<uv_poll_t*> pollHandles;
1400 std::vector<DeviceStdioContext> childFds;
1402 std::vector<ComputingResource> resources;
1404 if (driverInfo.resources !=
"") {
1410 auto resourceManager = std::make_unique<SimpleResourceManager>(resources);
1413 void* window =
nullptr;
1414 decltype(debugGUI->
getGUIDebugger(infos, runningWorkflow.devices, allStates, dataProcessorInfos, metricsInfos, driverInfo, controls, driverControl)) debugGUICallback;
1417 auto initDebugGUI = []() ->
DebugGUI* {
1418 uv_lib_t supportLib;
1421 result = uv_dlopen(
"libO2FrameworkGUISupport.dylib", &supportLib);
1423 result = uv_dlopen(
"libO2FrameworkGUISupport.so", &supportLib);
1426 LOG(error) << uv_dlerror(&supportLib);
1431 result = uv_dlsym(&supportLib,
"dpl_plugin_callback", (
void**)&dpl_plugin_callback);
1433 LOG(error) << uv_dlerror(&supportLib);
1437 return PluginManager::getByName<DebugGUI>(pluginInstance,
"ImGUIDebugGUI");
1445 if ((driverConfig.
batch ==
false || getenv(
"DPL_DRIVER_REMOTE_GUI") !=
nullptr) && frameworkId.empty()) {
1446 debugGUI = initDebugGUI();
1448 if (driverConfig.
batch ==
false) {
1449 window = debugGUI->
initGUI(
"O2 Framework debug GUI", serviceRegistry);
1451 window = debugGUI->
initGUI(
nullptr, serviceRegistry);
1454 }
else if (getenv(
"DPL_DEVICE_REMOTE_GUI") && !frameworkId.empty()) {
1455 debugGUI = initDebugGUI();
1463 window = debugGUI->
initGUI(
nullptr, serviceRegistry);
1466 if (driverConfig.
batch ==
false && window ==
nullptr && frameworkId.empty()) {
1467 LOG(warn) <<
"Could not create GUI. Switching to batch mode. Do you have GLFW on your system?";
1468 driverConfig.
batch =
true;
1469 if (varmap[
"error-policy"].defaulted()) {
1470 driverInfo.processingPolicies.error = TerminationPolicy::QUIT;
1473 bool guiQuitRequested =
false;
1474 bool hasError =
false;
1478 DriverState current;
1479 DriverState previous;
1485 if (!driverConfig.
batch) {
1487 uv_timer_init(loop, gui_timer);
1490 std::vector<ServiceMetricHandling> metricProcessingCallbacks;
1491 std::vector<ServiceSummaryHandling> summaryCallbacks;
1492 std::vector<ServicePreSchedule> preScheduleCallbacks;
1493 std::vector<ServicePostSchedule> postScheduleCallbacks;
1494 std::vector<ServiceDriverInit> driverInitCallbacks;
1495 for (
auto& service : driverServices) {
1496 if (service.driverStartup ==
nullptr) {
1499 service.driverStartup(serviceRegistry,
DeviceConfig{varmap});
1503 ref.registerService(ServiceRegistryHelpers::handleForService<DevicesManager>(devicesManager));
1505 bool guiTimerExpired =
false;
1507 guiContext.
plugin = debugGUI;
1510 guiContext.
frameCost = &driverInfo.frameCost;
1519 .controls = &controls,
1521 .states = &allStates,
1522 .specs = &runningWorkflow.devices,
1523 .metrics = &metricsInfos,
1524 .metricProcessingCallbacks = &metricProcessingCallbacks,
1525 .summaryCallbacks = &summaryCallbacks,
1526 .driver = &driverInfo,
1528 .isDriver = frameworkId.empty(),
1531 serverContext.serverHandle.data = &serverContext;
1534 uv_timer_init(loop, &force_step_timer);
1536 uv_timer_init(loop, &force_exit_timer);
1538 bool guiDeployedOnce =
false;
1542 metricDumpTimer.data = &serverContext;
1543 bool allChildrenGone =
false;
1550 serverContext.asyncLogProcessing->data = &serverContext;
1551 uv_async_init(loop, serverContext.asyncLogProcessing, [](
uv_async_t* handle) {
1552 auto* context = (DriverServerContext*)handle->data;
1553 processChildrenOutput(context->loop, *context->driver, *context->infos, *context->specs, *context->controls);
1554 for (auto* statusHandler : context->statusHandlers) {
1555 for (size_t di = 0; di < context->infos->size(); ++di) {
1556 statusHandler->sendNewLogs(di);
1563 if (driverControl.forcedTransitions.empty() ==
false) {
1564 for (
auto transition : driverControl.forcedTransitions) {
1565 driverInfo.states.push_back(transition);
1567 driverControl.forcedTransitions.resize(0);
1572 auto currentTime = uv_hrtime();
1573 uint64_t diff = (currentTime - driverInfo.startTime) / 1000000000LL;
1574 if ((
graceful_exit ==
false) && (driverInfo.timeout > 0) && (diff > driverInfo.timeout)) {
1575 LOG(info) <<
"Timout ellapsed. Requesting to quit.";
1580 if (
graceful_exit ==
true && driverInfo.sigintRequested ==
false) {
1581 driverInfo.sigintRequested =
true;
1582 driverInfo.states.resize(0);
1583 driverInfo.states.push_back(DriverState::QUIT_REQUESTED);
1588 driverInfo.sigchldRequested =
true;
1589 driverInfo.states.push_back(DriverState::HANDLE_CHILDREN);
1591 if (driverInfo.states.empty() ==
false) {
1593 current = driverInfo.states.back();
1595 current = DriverState::UNKNOWN;
1597 driverInfo.states.pop_back();
1599 case DriverState::BIND_GUI_PORT:
1600 bindGUIPort(driverInfo, serverContext, frameworkId);
1602 case DriverState::INIT:
1603 LOGP(info,
"Initialising O2 Data Processing Layer. Driver PID: {}.", getpid());
1604 LOGP(info,
"Driver listening on port: {}", driverInfo.port);
1607 driverInfo.sa_handle_child.sa_handler = &handle_sigchld;
1608 sigemptyset(&driverInfo.sa_handle_child.sa_mask);
1609 driverInfo.sa_handle_child.sa_flags = SA_RESTART | SA_NOCLDSTOP;
1610 if (sigaction(SIGCHLD, &driverInfo.sa_handle_child,
nullptr) == -1) {
1617 if (driverInfo.noSHMCleanup) {
1618 LOGP(warning,
"Not cleaning up shared memory.");
1626 for (
auto& callback : driverInitCallbacks) {
1627 callback(serviceRegistry, {varmap});
1629 driverInfo.states.push_back(DriverState::RUNNING);
1631 LOG(info) <<
"O2 Data Processing Layer initialised. We brake for nobody.";
1633 LOGF(info,
"Optimised build. O2DEBUG / LOG(debug) / LOGF(debug) / assert statement will not be shown.");
1636 case DriverState::IMPORT_CURRENT_WORKFLOW:
1639 dataProcessorInfos = previousDataProcessorInfos;
1640 for (
auto const& device : runningWorkflow.devices) {
1641 auto exists = std::find_if(dataProcessorInfos.begin(),
1642 dataProcessorInfos.end(),
1643 [
id = device.id](
DataProcessorInfo const& info) ->
bool { return info.name == id; });
1644 if (exists != dataProcessorInfos.end()) {
1648 for (
auto channel : device.inputChannels) {
1651 for (
auto channel : device.outputChannels) {
1654 dataProcessorInfos.push_back(
1659 workflowInfo.options,
1663 case DriverState::MATERIALISE_WORKFLOW:
1666 if (driverConfig.batch ==
true && varmap[
"dds"].as<std::string>().empty() && !varmap[
"dump-workflow"].as<
bool>() && workflowState == WorkflowParsingState::Empty) {
1667 LOGP(error,
"Empty workflow provided while running in batch mode.");
1673 auto altered_workflow = workflow;
1675 auto confNameFromParam = [](std::string
const& paramName) {
1676 std::regex name_regex(R
"(^control:([\w-]+)\/(\w+))");
1677 auto match = std::sregex_token_iterator(paramName.begin(), paramName.end(), name_regex, 0);
1678 if (
match == std::sregex_token_iterator()) {
1679 throw runtime_error_f(
"Malformed process control spec: %s", paramName.c_str());
1681 std::string task = std::sregex_token_iterator(paramName.begin(), paramName.end(), name_regex, 1)->str();
1682 std::string conf = std::sregex_token_iterator(paramName.begin(), paramName.end(), name_regex, 2)->str();
1683 return std::pair{task, conf};
1685 bool altered =
false;
1686 for (
auto& device : altered_workflow) {
1688 if (device.name.find(
"internal") != std::string::npos) {
1692 if (device.inputs.empty() ==
true) {
1696 auto hasMetadata = std::ranges::any_of(device.inputs, [](
InputSpec const& spec) {
1697 return spec.metadata.empty() == false;
1703 auto hasControls = std::ranges::any_of(device.inputs, [](
InputSpec const& spec) {
1704 return std::ranges::any_of(spec.metadata, [](ConfigParamSpec const& param) {
1705 return param.type == VariantType::Bool && param.name.find(
"control:") != std::string::npos;
1712 LOGP(
debug,
"Adjusting device {}", device.name.c_str());
1715 if (configStore !=
nullptr) {
1716 auto reg = std::make_unique<ConfigParamRegistry>(std::move(configStore));
1717 for (
auto& input : device.inputs) {
1718 for (
auto&
param : input.metadata) {
1719 if (
param.type == VariantType::Bool &&
param.name.find(
"control:") != std::string::npos) {
1720 if (
param.name !=
"control:default" &&
param.name !=
"control:spawn" &&
param.name !=
"control:build" &&
param.name !=
"control:define") {
1721 auto confName = confNameFromParam(
param.name).second;
1722 param.defaultValue = reg->get<
bool>(confName.c_str());
1729 LOGP(
debug,
"Original inputs: ");
1730 for (
auto& input : device.inputs) {
1731 LOGP(
debug,
"-> {}", input.binding);
1733 auto end = device.inputs.end();
1734 auto new_end = std::remove_if(device.inputs.begin(), device.inputs.end(), [](
InputSpec& input) {
1735 auto requested = false;
1736 auto hasControls = false;
1737 for (auto& param : input.metadata) {
1738 if (param.type != VariantType::Bool) {
1741 if (param.name.find(
"control:") != std::string::npos) {
1743 if (param.defaultValue.get<bool>() == true) {
1754 device.inputs.erase(new_end,
end);
1755 LOGP(
debug,
"Adjusted inputs: ");
1756 for (
auto& input : device.inputs) {
1764 for (
auto& service : driverServices) {
1765 if (service.adjustTopology ==
nullptr) {
1768 service.adjustTopology(
node, *driverInfo.configContext);
1776 driverInfo.channelPolicies,
1777 driverInfo.completionPolicies,
1778 driverInfo.dispatchPolicies,
1779 driverInfo.resourcePolicies,
1780 driverInfo.callbacksPolicies,
1781 driverInfo.sendingPolicies,
1782 driverInfo.forwardingPolicies,
1783 runningWorkflow.devices,
1785 driverInfo.uniqueWorkflowId,
1786 *driverInfo.configContext,
1787 !varmap[
"no-IPC"].as<
bool>(),
1788 driverInfo.resourcesMonitoringInterval,
1789 varmap[
"channel-prefix"].as<std::string>(),
1791 metricProcessingCallbacks.clear();
1792 std::vector<std::string> matchingServices;
1795 matchingServices.clear();
1796 for (
auto& device : runningWorkflow.devices) {
1797 for (
auto& service : device.services) {
1799 if (std::find(matchingServices.begin(), matchingServices.end(), service.name) != matchingServices.end()) {
1802 if (service.metricHandling) {
1803 metricProcessingCallbacks.push_back(service.metricHandling);
1804 matchingServices.push_back(service.name);
1810 matchingServices.clear();
1811 for (
auto& device : runningWorkflow.devices) {
1812 for (
auto& service : device.services) {
1814 if (std::find(matchingServices.begin(), matchingServices.end(), service.name) != matchingServices.end()) {
1817 if (service.summaryHandling) {
1818 summaryCallbacks.push_back(service.summaryHandling);
1819 matchingServices.push_back(service.name);
1824 preScheduleCallbacks.clear();
1825 matchingServices.clear();
1826 for (
auto& device : runningWorkflow.devices) {
1827 for (
auto& service : device.services) {
1829 if (std::find(matchingServices.begin(), matchingServices.end(), service.name) != matchingServices.end()) {
1832 if (service.preSchedule) {
1833 preScheduleCallbacks.push_back(service.preSchedule);
1837 postScheduleCallbacks.clear();
1838 matchingServices.clear();
1839 for (
auto& device : runningWorkflow.devices) {
1840 for (
auto& service : device.services) {
1842 if (std::find(matchingServices.begin(), matchingServices.end(), service.name) != matchingServices.end()) {
1845 if (service.postSchedule) {
1846 postScheduleCallbacks.push_back(service.postSchedule);
1850 driverInitCallbacks.clear();
1851 matchingServices.clear();
1852 for (
auto& device : runningWorkflow.devices) {
1853 for (
auto& service : device.services) {
1855 if (std::find(matchingServices.begin(), matchingServices.end(), service.name) != matchingServices.end()) {
1858 if (service.driverInit) {
1859 driverInitCallbacks.push_back(service.driverInit);
1867 for (
auto& device : runningWorkflow.devices) {
1869 if (device.name.find(
"internal") != std::string::npos) {
1873 if (configStore !=
nullptr) {
1874 auto reg = std::make_unique<ConfigParamRegistry>(std::move(configStore));
1875 for (
auto& option : device.options) {
1876 const char*
name = option.name.c_str();
1877 switch (option.type) {
1878 case VariantType::Int:
1879 option.defaultValue = reg->get<int32_t>(
name);
1881 case VariantType::Int8:
1882 option.defaultValue = reg->get<int8_t>(
name);
1884 case VariantType::Int16:
1885 option.defaultValue = reg->get<int16_t>(
name);
1887 case VariantType::UInt8:
1890 case VariantType::UInt16:
1891 option.defaultValue = reg->get<uint16_t>(
name);
1893 case VariantType::UInt32:
1894 option.defaultValue = reg->get<uint32_t>(
name);
1896 case VariantType::UInt64:
1897 option.defaultValue = reg->get<uint64_t>(
name);
1899 case VariantType::Int64:
1902 case VariantType::Float:
1903 option.defaultValue = reg->get<
float>(
name);
1905 case VariantType::Double:
1906 option.defaultValue = reg->get<
double>(
name);
1908 case VariantType::String:
1909 option.defaultValue = reg->get<std::string>(
name);
1911 case VariantType::Bool:
1912 option.defaultValue = reg->get<
bool>(
name);
1914 case VariantType::ArrayInt:
1915 option.defaultValue = reg->get<std::vector<int>>(
name);
1917 case VariantType::ArrayFloat:
1918 option.defaultValue = reg->get<std::vector<float>>(
name);
1920 case VariantType::ArrayDouble:
1921 option.defaultValue = reg->get<std::vector<double>>(
name);
1923 case VariantType::ArrayString:
1924 option.defaultValue = reg->get<std::vector<std::string>>(
name);
1926 case VariantType::Array2DInt:
1929 case VariantType::Array2DFloat:
1932 case VariantType::Array2DDouble:
1935 case VariantType::LabeledArrayInt:
1938 case VariantType::LabeledArrayFloat:
1941 case VariantType::LabeledArrayDouble:
1944 case VariantType::LabeledArrayString:
1953 }
catch (std::runtime_error& e) {
1954 LOGP(error,
"invalid workflow in {}: {}", driverInfo.argv[0], e.what());
1958#ifdef DPL_ENABLE_BACKTRACE
1961 LOGP(error,
"invalid workflow in {}: {}", driverInfo.argv[0], err.what);
1964 LOGP(error,
"invalid workflow in {}: Unknown error while materialising workflow", driverInfo.argv[0]);
1968 case DriverState::DO_CHILD:
1970 if (driverControl.defaultStopped) {
1971 kill(getpid(), SIGSTOP);
1973 for (
size_t di = 0;
di < runningWorkflow.devices.size();
di++) {
1975 if (runningWorkflow.devices[
di].id == frameworkId) {
1976 return doChild(driverInfo.argc, driverInfo.argv,
1979 runningWorkflow,
ref,
1981 driverInfo.processingPolicies,
1982 driverInfo.defaultDriverClient,
1987 std::ostringstream ss;
1988 for (
auto& processor : workflow) {
1989 ss <<
" - " << processor.name <<
"\n";
1991 for (
auto& spec : runningWorkflow.devices) {
1992 ss <<
" - " << spec.name <<
"(" << spec.id <<
")"
1995 driverInfo.lastError = fmt::format(
1996 "Unable to find component with id {}."
1997 " Available options:\n{}",
1998 frameworkId, ss.str());
1999 driverInfo.states.push_back(DriverState::QUIT_REQUESTED);
2002 case DriverState::REDEPLOY_GUI:
2012 if (!driverConfig.batch || getenv(
"DPL_DRIVER_REMOTE_GUI")) {
2014 uv_timer_stop(gui_timer);
2017 auto callback = debugGUI->getGUIDebugger(infos, runningWorkflow.devices, allStates, dataProcessorInfos, metricsInfos, driverInfo, controls, driverControl);
2018 guiContext.callback = [&serviceRegistry, &driverServices, &debugGUI, &infos, &runningWorkflow, &dataProcessorInfos, &metricsInfos, &driverInfo, &controls, &driverControl, callback]() {
2020 for (
auto& service : driverServices) {
2021 if (service.postRenderGUI) {
2022 service.postRenderGUI(serviceRegistry);
2026 guiContext.window = window;
2029 gui_timer->data = &guiContext;
2032 guiDeployedOnce =
true;
2035 case DriverState::MERGE_CONFIGS: {
2037 controls.resize(runningWorkflow.devices.size());
2040 if (varmap.count(
"dpl-tracing-flags")) {
2041 for (
auto& control : controls) {
2043 control.tracingFlags = tracingFlags;
2046 deviceExecutions.resize(runningWorkflow.devices.size());
2050 const auto uniformOptions = {
2052 "--aod-memory-rate-limit",
2053 "--aod-writer-json",
2054 "--aod-writer-ntfmerge",
2055 "--aod-writer-resdir",
2056 "--aod-writer-resfile",
2057 "--aod-writer-resmode",
2058 "--aod-writer-maxfilesize",
2059 "--aod-writer-keep",
2060 "--aod-max-io-rate",
2061 "--aod-parent-access-level",
2062 "--aod-parent-base-path-replacement",
2063 "--aod-origin-level-mapping",
2064 "--driver-client-backend",
2065 "--fairmq-ipc-prefix",
2068 "--resources-monitoring",
2069 "--resources-monitoring-file",
2070 "--resources-monitoring-dump-interval",
2074 for (
auto& option : uniformOptions) {
2080 driverControl.defaultStopped,
2081 driverInfo.processingPolicies.termination == TerminationPolicy::WAIT,
2085 runningWorkflow.devices,
2089 driverInfo.uniqueWorkflowId);
2092 LOGP(error,
"unable to merge configurations in {}: {}", driverInfo.argv[0], err.what);
2093#ifdef DPL_ENABLE_BACKTRACE
2094 std::cerr <<
"\nStacktrace follows:\n\n";
2100 case DriverState::SCHEDULE: {
2105 LOG(info) <<
"Redeployment of configuration asked.";
2106 std::ostringstream forwardedStdin;
2108 infos.reserve(runningWorkflow.devices.size());
2111 unsigned parentCPU = -1;
2112 unsigned parentNode = -1;
2113#if defined(__linux__) && __has_include(<sched.h>)
2114 parentCPU = sched_getcpu();
2115#elif __has_include(<linux/getcpu.h>)
2116 getcpu(&parentCPU, &parentNode,
nullptr);
2117#elif __has_include(<cpuid.h>) && (__x86_64__ || __i386__)
2122 for (
auto& callback : preScheduleCallbacks) {
2123 callback(serviceRegistry, {varmap});
2125 childFds.resize(runningWorkflow.devices.size());
2126 for (
int di = 0;
di < (
int)runningWorkflow.devices.size(); ++
di) {
2127 auto& context = childFds[
di];
2130 if (driverInfo.mode == DriverMode::EMBEDDED || runningWorkflow.devices[
di].resource.hostname != driverInfo.deployHostname) {
2132 runningWorkflow.devices[
di], controls[
di], deviceExecutions[
di], infos, allStates);
2137 runningWorkflow.devices, driverInfo,
2138 controls, deviceExecutions, infos,
2140 serviceRegistry, varmap,
2141 childFds, parentCPU, parentNode);
2146 for (
auto& callback : postScheduleCallbacks) {
2147 callback(serviceRegistry, {varmap});
2149 assert(infos.empty() ==
false);
2154 uv_timer_init(loop, &metricDumpTimer);
2156 driverInfo.resourcesMonitoringDumpInterval * 1000,
2157 driverInfo.resourcesMonitoringDumpInterval * 1000);
2160 for (
const auto& processorInfo : dataProcessorInfos) {
2161 const auto& cmdLineArgs = processorInfo.cmdLineArgs;
2162 if (std::find(cmdLineArgs.begin(), cmdLineArgs.end(),
"--severity") != cmdLineArgs.end()) {
2163 for (
size_t counter = 0;
const auto& spec : runningWorkflow.devices) {
2164 if (spec.name.compare(processorInfo.name) == 0) {
2166 const auto logLevelIt = std::find(cmdLineArgs.begin(), cmdLineArgs.end(),
"--severity") + 1;
2167 if ((*logLevelIt).compare(
"debug") == 0) {
2168 info.logLevel = LogParsingHelpers::LogLevel::Debug;
2169 }
else if ((*logLevelIt).compare(
"detail") == 0) {
2170 info.logLevel = LogParsingHelpers::LogLevel::Debug;
2171 }
else if ((*logLevelIt).compare(
"info") == 0) {
2172 info.logLevel = LogParsingHelpers::LogLevel::Info;
2173 }
else if ((*logLevelIt).compare(
"warning") == 0) {
2174 info.logLevel = LogParsingHelpers::LogLevel::Warning;
2175 }
else if ((*logLevelIt).compare(
"error") == 0) {
2176 info.logLevel = LogParsingHelpers::LogLevel::Error;
2177 }
else if ((*logLevelIt).compare(
"important") == 0) {
2178 info.logLevel = LogParsingHelpers::LogLevel::Info;
2179 }
else if ((*logLevelIt).compare(
"alarm") == 0) {
2180 info.logLevel = LogParsingHelpers::LogLevel::Alarm;
2181 }
else if ((*logLevelIt).compare(
"critical") == 0) {
2182 info.logLevel = LogParsingHelpers::LogLevel::Critical;
2183 }
else if ((*logLevelIt).compare(
"fatal") == 0) {
2184 info.logLevel = LogParsingHelpers::LogLevel::Fatal;
2192 LOG(info) <<
"Redeployment of configuration done.";
2194 case DriverState::RUNNING:
2198 devicesManager->flush();
2202 if (guiTimerExpired ==
false) {
2203 O2_SIGNPOST_EVENT_EMIT(driver, sid,
"mainloop",
"Entering event loop with %{public}s", once ?
"UV_RUN_ONCE" :
"UV_RUN_NOWAIT");
2205 uv_run(loop, once ? UV_RUN_ONCE : UV_RUN_NOWAIT);
2209 if (guiQuitRequested ||
2210 (driverInfo.processingPolicies.termination == TerminationPolicy::QUIT && (
checkIfCanExit(infos) ==
true))) {
2215 LOG(info) <<
"Quitting";
2216 driverInfo.states.push_back(DriverState::QUIT_REQUESTED);
2217 }
else if (infos.size() != runningWorkflow.devices.size()) {
2222 driverInfo.states.push_back(DriverState::RUNNING);
2223 driverInfo.states.push_back(DriverState::REDEPLOY_GUI);
2224 driverInfo.states.push_back(DriverState::SCHEDULE);
2225 driverInfo.states.push_back(DriverState::MERGE_CONFIGS);
2226 }
else if (runningWorkflow.devices.empty() && driverConfig.batch ==
true) {
2227 LOG(info) <<
"No device resulting from the workflow. Quitting.";
2229 driverInfo.states.push_back(DriverState::EXIT);
2230 }
else if (runningWorkflow.devices.empty() && driverConfig.batch ==
false && !guiDeployedOnce) {
2232 driverInfo.states.push_back(DriverState::RUNNING);
2233 driverInfo.states.push_back(DriverState::REDEPLOY_GUI);
2235 driverInfo.states.push_back(DriverState::RUNNING);
2238 case DriverState::QUIT_REQUESTED: {
2239 std::time_t
result = std::time(
nullptr);
2243 guiQuitRequested =
true;
2250 force_step_timer.data = &infos;
2252 driverInfo.states.push_back(DriverState::HANDLE_CHILDREN);
2255 case DriverState::HANDLE_CHILDREN: {
2259 uv_run(loop, once ? UV_RUN_ONCE : UV_RUN_NOWAIT);
2264 static bool forcefulExitMessage =
true;
2265 if (forcefulExitMessage) {
2266 LOG(info) <<
"Forceful exit requested.";
2267 forcefulExitMessage =
false;
2273 driverInfo.sigchldRequested =
false;
2278 bool supposedToQuit = (guiQuitRequested || canExit ||
graceful_exit);
2280 if (allChildrenGone && (supposedToQuit || driverInfo.processingPolicies.termination == TerminationPolicy::QUIT)) {
2282 driverInfo.states.resize(0);
2283 driverInfo.states.push_back(DriverState::EXIT);
2284 }
else if (hasError && driverInfo.processingPolicies.error == TerminationPolicy::QUIT && !supposedToQuit) {
2286 force_exit_timer.data = &infos;
2287 static bool forceful_timer_started =
false;
2288 if (forceful_timer_started ==
false) {
2289 forceful_timer_started =
true;
2292 driverInfo.states.push_back(DriverState::QUIT_REQUESTED);
2293 }
else if (allChildrenGone ==
false && supposedToQuit) {
2294 driverInfo.states.push_back(DriverState::HANDLE_CHILDREN);
2298 case DriverState::EXIT: {
2300 if (driverInfo.resourcesMonitoringDumpInterval) {
2301 uv_timer_stop(&metricDumpTimer);
2303 LOGP(info,
"Dumping performance metrics to {}.json file", driverInfo.resourcesMonitoringFilename);
2306 dumpRunSummary(serverContext, driverInfo, infos, runningWorkflow.devices);
2313 if (infos.empty()) {
2316 boost::property_tree::ptree finalConfig;
2317 assert(infos.size() == runningWorkflow.devices.size());
2318 for (
size_t di = 0;
di < infos.size(); ++
di) {
2320 auto& spec = runningWorkflow.devices[
di];
2321 finalConfig.put_child(spec.name,
info.currentConfig);
2323 LOG(info) <<
"Dumping used configuration in dpl-config.json";
2325 std::ofstream outDPLConfigFile(
"dpl-config.json", std::ios::out);
2326 if (outDPLConfigFile.is_open()) {
2327 boost::property_tree::write_json(outDPLConfigFile, finalConfig);
2329 LOGP(warning,
"Could not write out final configuration file. Read only run folder?");
2331 if (driverInfo.noSHMCleanup) {
2332 LOGP(warning,
"Not cleaning up shared memory.");
2336 return calculateExitCode(driverInfo, runningWorkflow.devices, infos);
2338 case DriverState::PERFORM_CALLBACKS:
2339 for (
auto& callback : driverControl.callbacks) {
2340 callback(workflow, runningWorkflow.devices, deviceExecutions, dataProcessorInfos, commandInfo);
2342 driverControl.callbacks.clear();
2345 LOG(error) <<
"Driver transitioned in an unknown state("
2346 <<
"current: " << (
int)current
2347 <<
", previous: " << (
int)previous
2348 <<
"). Shutting down.";
2349 driverInfo.states.push_back(DriverState::QUIT_REQUESTED);
2887 std::vector<ChannelConfigurationPolicy>
const& channelPolicies,
2888 std::vector<CompletionPolicy>
const& completionPolicies,
2889 std::vector<DispatchPolicy>
const& dispatchPolicies,
2890 std::vector<ResourcePolicy>
const& resourcePolicies,
2891 std::vector<CallbacksPolicy>
const& callbacksPolicies,
2892 std::vector<SendingPolicy>
const& sendingPolicies,
2893 std::vector<ConfigParamSpec>
const& currentWorkflowOptions,
2894 std::vector<ConfigParamSpec>
const& detectedParams,
2899 if (getenv(
"DPL_DRIVER_SIGNPOSTS")) {
2903 std::vector<std::string> currentArgs;
2904 std::vector<PluginInfo> plugins;
2907 for (
int ai = 1; ai < argc; ++ai) {
2908 currentArgs.emplace_back(argv[ai]);
2914 currentWorkflowOptions};
2918 bpo::options_description executorOptions(
"Executor options");
2919 const char* helpDescription =
"print help: short, full, executor, or processor name";
2921 executorOptions.add_options()
2922 (
"help,h", bpo::value<std::string>()->implicit_value(
"short"), helpDescription)
2923 (
"quiet,q", bpo::value<bool>()->zero_tokens()->default_value(
false),
"quiet operation")
2924 (
"stop,s", bpo::value<bool>()->zero_tokens()->default_value(
false),
"stop before device start")
2925 (
"single-step", bpo::value<bool>()->zero_tokens()->default_value(
false),
"start in single step mode")
2926 (
"batch,b", bpo::value<std::vector<std::string>>()->zero_tokens()->composing(),
"batch processing mode")
2927 (
"no-batch", bpo::value<bool>()->zero_tokens(),
"force gui processing mode")
2928 (
"no-cleanup", bpo::value<bool>()->zero_tokens()->default_value(
false),
"do not cleanup the shm segment")
2929 (
"hostname", bpo::value<std::string>()->default_value(
"localhost"),
"hostname to deploy")
2930 (
"resources", bpo::value<std::string>()->default_value(
""),
"resources allocated for the workflow")
2931 (
"start-port,p", bpo::value<unsigned short>()->default_value(22000),
"start port to allocate")
2932 (
"port-range,pr", bpo::value<unsigned short>()->default_value(1000),
"ports in range")
2933 (
"completion-policy,c", bpo::value<TerminationPolicy>(&processingPolicies.
termination)->default_value(TerminationPolicy::QUIT),
2934 "what to do when processing is finished: quit, wait")
2935 (
"error-policy", bpo::value<TerminationPolicy>(&processingPolicies.
error)->default_value(TerminationPolicy::QUIT),
2936 "what to do when a device has an error: quit, wait")
2937 (
"min-failure-level", bpo::value<LogParsingHelpers::LogLevel>(&minFailureLevel)->default_value(LogParsingHelpers::LogLevel::Fatal),
2938 "minimum message level which will be considered as fatal and exit with 1")
2939 (
"graphviz,g", bpo::value<bool>()->zero_tokens()->default_value(
false),
"produce graphviz output")
2940 (
"mermaid", bpo::value<std::string>()->default_value(
""),
"produce graph output in mermaid format in file under specified name or on stdout if argument is \"-\"")
2941 (
"timeout,t", bpo::value<uint64_t>()->default_value(0),
"forced exit timeout (in seconds)")
2942 (
"dds,D", bpo::value<std::string>()->default_value(
""),
"create DDS configuration")
2943 (
"dds-workflow-suffix,D", bpo::value<std::string>()->default_value(
""),
"suffix for DDS names")
2944 (
"dump-workflow,dump", bpo::value<bool>()->zero_tokens()->default_value(
false),
"dump workflow as JSON")
2945 (
"dump-workflow-file", bpo::value<std::string>()->default_value(
"-"),
"file to which do the dump")
2946 (
"driver-mode", bpo::value<DriverMode>(&driverMode)->default_value(DriverMode::STANDALONE), R
"(how to run the driver. default: "standalone". Valid: "embedded")")
2947 (
"run", bpo::value<bool>()->zero_tokens()->default_value(
false),
"run workflow merged so far. It implies --batch. Use --no-batch to see the GUI")
2948 (
"no-IPC", bpo::value<bool>()->zero_tokens()->default_value(
false),
"disable IPC topology optimization")
2949 (
"o2-control,o2", bpo::value<std::string>()->default_value(
""),
"dump O2 Control workflow configuration under the specified name")
2950 (
"resources-monitoring", bpo::value<unsigned short>()->default_value(0),
"enable cpu/memory monitoring for provided interval in seconds")
2951 (
"resources-monitoring-file", bpo::value<std::string>()->default_value(
"performanceMetrics.json"),
"file where to dump the metrics")
2952 (
"resources-monitoring-dump-interval", bpo::value<unsigned short>()->default_value(0),
"dump monitoring information to disk every provided seconds");
2957 (
"id,i", bpo::value<std::string>(),
"device id for child spawning")
2958 (
"channel-config", bpo::value<std::vector<std::string>>(),
"channel configuration")
2959 (
"control",
"control plugin")
2960 (
"log-color",
"logging color scheme")(
"color",
"logging color scheme");
2962 bpo::options_description visibleOptions;
2963 visibleOptions.add(executorOptions);
2965 auto physicalWorkflow = workflow;
2966 std::map<std::string, size_t> rankIndex;
2973 size_t workflowHashA = 0;
2974 std::hash<std::string> hash_fn;
2976 for (
auto& dp : workflow) {
2977 workflowHashA += hash_fn(dp.name);
2980 for (
auto& dp : workflow) {
2981 rankIndex.insert(std::make_pair(dp.name, workflowHashA));
2984 std::vector<DataProcessorInfo> dataProcessorInfos;
2988 std::vector<DataProcessorSpec> importedWorkflow;
2990 if (previousWorked ==
false) {
2994 size_t workflowHashB = 0;
2995 for (
auto& dp : importedWorkflow) {
2996 workflowHashB += hash_fn(dp.name);
3003 for (
auto& dp : importedWorkflow) {
3004 auto found = std::find_if(physicalWorkflow.begin(), physicalWorkflow.end(),
3006 if (found == physicalWorkflow.end()) {
3007 physicalWorkflow.push_back(dp);
3008 rankIndex.insert(std::make_pair(dp.name, workflowHashB));
3017 for (
auto& dp : physicalWorkflow) {
3019 if (std::find_if(dp.labels.begin(), dp.labels.end(), isExpendable) != dp.labels.end()) {
3020 for (
auto&
output : dp.outputs) {
3021 if (
output.lifetime == Lifetime::Timeframe) {
3022 output.lifetime = Lifetime::Sporadic;
3034 if (!(dec.requestedAODs.empty() && dec.requestedDYNs.empty() && dec.requestedIDXs.empty() && dec.requestedTIMs.empty())) {
3037 for (
auto& service : driverServices) {
3038 if (service.injectTopology ==
nullptr) {
3042 service.injectTopology(
node, configContext);
3044 for (
auto& dp : physicalWorkflow) {
3045 if (dp.name.rfind(
"internal-", 0) == 0) {
3046 rankIndex.insert(std::make_pair(dp.name, hash_fn(
"internal")));
3053 return a.name < b.name;
3056 for (
auto& dp : physicalWorkflow) {
3057 std::stable_sort(dp.inputs.begin(), dp.inputs.end(),
3058 [](
InputSpec const&
a,
InputSpec const&
b) { return DataSpecUtils::describe(a) < DataSpecUtils::describe(b); });
3059 std::stable_sort(dp.outputs.begin(), dp.outputs.end(),
3060 [](
OutputSpec const&
a,
OutputSpec const&
b) { return DataSpecUtils::describe(a) < DataSpecUtils::describe(b); });
3065 std::vector<std::pair<int, int>>
edges;
3067 if (physicalWorkflow.size() > 1) {
3071 if (topoInfos.size() != physicalWorkflow.size()) {
3074 throw std::runtime_error(
"Unable to do topological sort of the resulting workflow. Do you have loops?\n" +
debugTopoInfo(physicalWorkflow, topoInfos,
edges));
3078 auto aRank = std::make_tuple(a.layer, -workflow.at(a.index).outputs.size(), workflow.at(a.index).name);
3079 auto bRank = std::make_tuple(b.layer, -workflow.at(b.index).outputs.size(), workflow.at(b.index).name);
3080 return aRank < bRank;
3083 std::vector<int> dataProcessorOrder;
3084 dataProcessorOrder.resize(topoInfos.size());
3085 for (
size_t i = 0;
i < topoInfos.size(); ++
i) {
3086 dataProcessorOrder[topoInfos[
i].index] =
i;
3088 std::vector<int> newLocations;
3089 newLocations.resize(dataProcessorOrder.size());
3090 for (
size_t i = 0;
i < dataProcessorOrder.size(); ++
i) {
3091 newLocations[dataProcessorOrder[
i]] =
i;
3101 bpo::options_description od;
3102 od.add(visibleOptions);
3107 using namespace bpo::command_line_style;
3108 auto style = (allow_short | short_allow_adjacent | short_allow_next | allow_long | long_allow_adjacent | long_allow_next | allow_sticky | allow_dash_for_short);
3109 bpo::variables_map varmap;
3112 bpo::command_line_parser(argc, argv)
3117 }
catch (std::exception
const& e) {
3118 LOGP(error,
"error parsing options of {}: {}", argv[0], e.what());
3134 if (varmap.count(
"help")) {
3135 printHelp(varmap, executorOptions, physicalWorkflow, currentWorkflowOptions);
3141 if (varmap.count(
"severity")) {
3142 auto logLevel = varmap[
"severity"].as<std::string>();
3143 if (logLevel ==
"debug") {
3144 fair::Logger::SetConsoleSeverity(fair::Severity::debug);
3145 }
else if (logLevel ==
"detail") {
3146 fair::Logger::SetConsoleSeverity(fair::Severity::detail);
3147 }
else if (logLevel ==
"info") {
3148 fair::Logger::SetConsoleSeverity(fair::Severity::info);
3149 }
else if (logLevel ==
"warning") {
3150 fair::Logger::SetConsoleSeverity(fair::Severity::warning);
3151 }
else if (logLevel ==
"error") {
3152 fair::Logger::SetConsoleSeverity(fair::Severity::error);
3153 }
else if (logLevel ==
"important") {
3154 fair::Logger::SetConsoleSeverity(fair::Severity::important);
3155 }
else if (logLevel ==
"alarm") {
3156 fair::Logger::SetConsoleSeverity(fair::Severity::alarm);
3157 }
else if (logLevel ==
"critical") {
3158 fair::Logger::SetConsoleSeverity(fair::Severity::critical);
3159 }
else if (logLevel ==
"fatal") {
3160 fair::Logger::SetConsoleSeverity(fair::Severity::fatal);
3162 LOGP(error,
"Invalid log level '{}'", logLevel);
3167 if (varmap[
"log-timestamp-us"].as<bool>()) {
3168 fair::Logger::DefineVerbosity(fair::Verbosity::user1,
3169 fair::VerbositySpec::Make(fair::VerbositySpec::Info::timestamp_us,
3170 fair::VerbositySpec::Info::severity));
3171 fair::Logger::SetVerbosity(fair::Verbosity::user1);
3176 auto evaluateBatchOption = [&varmap]() ->
bool {
3177 if (varmap.count(
"no-batch") > 0) {
3180 if (varmap.count(
"batch") == 0) {
3182 return isatty(fileno(stdout)) == 0;
3191 DriverInfo driverInfo{
3192 .sendingPolicies = sendingPolicies,
3193 .forwardingPolicies = forwardingPolicies,
3194 .callbacksPolicies = callbacksPolicies};
3195 driverInfo.states.reserve(10);
3196 driverInfo.sigintRequested =
false;
3197 driverInfo.sigchldRequested =
false;
3198 driverInfo.channelPolicies = channelPolicies;
3199 driverInfo.completionPolicies = completionPolicies;
3200 driverInfo.dispatchPolicies = dispatchPolicies;
3201 driverInfo.resourcePolicies = resourcePolicies;
3202 driverInfo.argc = argc;
3203 driverInfo.argv = argv;
3204 driverInfo.noSHMCleanup = varmap[
"no-cleanup"].as<
bool>();
3205 driverInfo.processingPolicies.termination = varmap[
"completion-policy"].as<
TerminationPolicy>();
3206 driverInfo.processingPolicies.earlyForward = varmap[
"early-forward-policy"].as<
EarlyForwardPolicy>();
3207 driverInfo.mode = varmap[
"driver-mode"].as<
DriverMode>();
3209 auto batch = evaluateBatchOption();
3212 .driverHasGUI = (batch ==
false) || getenv(
"DPL_DRIVER_REMOTE_GUI") !=
nullptr,
3215 if (varmap[
"error-policy"].defaulted() && driverConfig.batch ==
false) {
3216 driverInfo.processingPolicies.error = TerminationPolicy::WAIT;
3218 driverInfo.processingPolicies.error = varmap[
"error-policy"].as<
TerminationPolicy>();
3221 driverInfo.startTime = uv_hrtime();
3222 driverInfo.startTimeMsFromEpoch = std::chrono::duration_cast<std::chrono::milliseconds>(
3223 std::chrono::system_clock::now().time_since_epoch())
3225 driverInfo.timeout = varmap[
"timeout"].as<uint64_t>();
3226 driverInfo.deployHostname = varmap[
"hostname"].as<std::string>();
3227 driverInfo.resources = varmap[
"resources"].as<std::string>();
3228 driverInfo.resourcesMonitoringInterval = varmap[
"resources-monitoring"].as<
unsigned short>();
3229 driverInfo.resourcesMonitoringFilename = varmap[
"resources-monitoring-file"].as<std::string>();
3230 driverInfo.resourcesMonitoringDumpInterval = varmap[
"resources-monitoring-dump-interval"].as<
unsigned short>();
3233 driverInfo.processorInfo = dataProcessorInfos;
3234 driverInfo.configContext = &configContext;
3241 std::string frameworkId;
3244 if (varmap.count(
"id")) {
3248 frameworkId = std::regex_replace(varmap[
"id"].as<std::string>(), std::regex{
"_dds.*"},
"");
3249 driverInfo.uniqueWorkflowId = fmt::format(
"{}", getppid());
3250 driverInfo.defaultDriverClient =
"stdout://";
3252 driverInfo.uniqueWorkflowId = fmt::format(
"{}", getpid());
3253 driverInfo.defaultDriverClient =
"ws://";