aboutsummaryrefslogtreecommitdiff
path: root/src/zenmaster/zenmaster.cpp
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2025-08-26 13:45:54 +0200
committerStefan Boberg <[email protected]>2025-08-26 13:45:54 +0200
commitf4c029e6accbf8df3496e28ba9e07eed4cbde851 (patch)
treeee3329f981e3c0b9609c90cfef4b610ffd2c4223 /src/zenmaster/zenmaster.cpp
parentMerge pull request #139 from ue-foundation/de/zen-service-command (diff)
downloadzen-f4c029e6accbf8df3496e28ba9e07eed4cbde851.tar.xz
zen-f4c029e6accbf8df3496e28ba9e07eed4cbde851.zip
zenmaster + zenmaster-test skeletons
Diffstat (limited to 'src/zenmaster/zenmaster.cpp')
-rw-r--r--src/zenmaster/zenmaster.cpp321
1 files changed, 321 insertions, 0 deletions
diff --git a/src/zenmaster/zenmaster.cpp b/src/zenmaster/zenmaster.cpp
new file mode 100644
index 000000000..483ec6828
--- /dev/null
+++ b/src/zenmaster/zenmaster.cpp
@@ -0,0 +1,321 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+// Zen command line client utility
+//
+
+#include "zenmaster.h"
+
+#include <zencore/callstack.h>
+#include <zencore/filesystem.h>
+#include <zencore/fmtutils.h>
+#include <zencore/logging.h>
+#include <zencore/process.h>
+#include <zencore/scopeguard.h>
+#include <zencore/sentryintegration.h>
+#include <zencore/string.h>
+#include <zencore/trace.h>
+#include <zencore/windows.h>
+#include <zenhttp/httpcommon.h>
+#include <zenutil/environmentoptions.h>
+#include <zenutil/logging.h>
+#include <zenutil/workerpools.h>
+#include <zenutil/zenserverprocess.h>
+
+#include <zencore/memory/fmalloc.h>
+#include <zencore/memory/llm.h>
+#include <zencore/memory/memory.h>
+#include <zencore/memory/memorytrace.h>
+#include <zencore/memory/newdelete.h>
+
+#if ZEN_WITH_TESTS
+# define ZEN_TEST_WITH_RUNNER 1
+# include <zencore/testing.h>
+#endif
+
+ZEN_THIRD_PARTY_INCLUDES_START
+#include <cpr/cpr.h>
+#include <spdlog/sinks/ansicolor_sink.h>
+#include <spdlog/spdlog.h>
+#include <gsl/gsl-lite.hpp>
+ZEN_THIRD_PARTY_INCLUDES_END
+
+#include <zencore/memory/newdelete.h>
+
+#if ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC
+# include <sys/ioctl.h>
+# include <unistd.h>
+#endif
+
+//////////////////////////////////////////////////////////////////////////
+
+namespace zen {
+
+}
+
+int
+main(int argc, char** argv)
+{
+ zen::SetCurrentThreadName("main");
+
+ std::vector<std::string> Args;
+#if ZEN_PLATFORM_WINDOWS
+ LPWSTR RawCommandLine = GetCommandLine();
+ std::string CommandLine = zen::WideToUtf8(RawCommandLine);
+ Args = zen::ParseCommandLine(CommandLine);
+#else
+ Args.reserve(argc);
+ for (int I = 0; I < argc; I++)
+ {
+ std::string Arg(argv[I]);
+ if ((!Arg.empty()) && (Arg != " "))
+ {
+ Args.emplace_back(std::move(Arg));
+ }
+ }
+#endif
+ std::vector<char*> RawArgs = zen::StripCommandlineQuotes(Args);
+
+ argc = gsl::narrow<int>(RawArgs.size());
+ argv = RawArgs.data();
+
+ using namespace zen;
+ using namespace std::literals;
+
+#if ZEN_WITH_TRACE
+ TraceInit("zen");
+ TraceOptions TraceCommandlineOptions;
+ if (GetTraceOptionsFromCommandline(TraceCommandlineOptions))
+ {
+ TraceConfigure(TraceCommandlineOptions);
+ }
+#endif // ZEN_WITH_TRACE
+
+ // Split command line into options, commands and any pass-through arguments
+
+ std::string Passthrough;
+ std::string PassthroughArgs;
+ std::vector<std::string> PassthroughArgV;
+
+ for (int i = 1; i < argc; ++i)
+ {
+ if ("--"sv == argv[i])
+ {
+ bool IsFirst = true;
+ zen::ExtendableStringBuilder<256> Line;
+ zen::ExtendableStringBuilder<256> Arguments;
+
+ for (int j = i + 1; j < argc; ++j)
+ {
+ auto AppendAscii = [&](auto X) {
+ Line.Append(X);
+ if (!IsFirst)
+ {
+ Arguments.Append(X);
+ }
+ };
+
+ if (!IsFirst)
+ {
+ AppendAscii(" ");
+ }
+
+ std::string_view ThisArg(argv[j]);
+ PassthroughArgV.push_back(std::string(ThisArg));
+
+ const bool NeedsQuotes =
+ (ThisArg.find(' ') != std::string_view::npos) && !(ThisArg.starts_with("\"") && ThisArg.ends_with("\""));
+
+ if (NeedsQuotes)
+ {
+ AppendAscii("\"");
+ }
+
+ AppendAscii(ThisArg);
+
+ if (NeedsQuotes)
+ {
+ AppendAscii("\"");
+ }
+
+ IsFirst = false;
+ }
+
+ Passthrough = Line.c_str();
+ PassthroughArgs = Arguments.c_str();
+
+ // This will "truncate" the arg vector and terminate the loop
+ argc = i;
+ }
+ }
+
+ // Parse global CLI arguments
+
+ ZenMasterCliOptions GlobalOptions;
+
+ GlobalOptions.PassthroughCommandLine = Passthrough;
+ GlobalOptions.PassthroughArgs = PassthroughArgs;
+ GlobalOptions.PassthroughArgV = PassthroughArgV;
+
+ std::string MemoryOptions;
+
+ std::string SubCommand = "<None>";
+
+ cxxopts::Options Options("zenmaster", "Zen master orchestration tool");
+
+ Options.add_options()("d, debug", "Enable debugging", cxxopts::value<bool>(GlobalOptions.IsDebug));
+ Options.add_options()("v, verbose", "Enable verbose logging", cxxopts::value<bool>(GlobalOptions.IsVerbose));
+ Options.add_options()("malloc", "Configure memory allocator subsystem", cxxopts::value(MemoryOptions)->default_value("mimalloc"));
+ Options.add_options()("help", "Show command line help");
+
+#if ZEN_WITH_TRACE
+ // We only have this in options for command line help purposes - we parse these argument separately earlier using
+ // GetTraceOptionsFromCommandline()
+
+ Options.add_option("ue-trace",
+ "",
+ "trace",
+ "Specify which trace channels should be enabled",
+ cxxopts::value<std::string>(TraceCommandlineOptions.Channels)->default_value(""),
+ "");
+
+ Options.add_option("ue-trace",
+ "",
+ "tracehost",
+ "Hostname to send the trace to",
+ cxxopts::value<std::string>(TraceCommandlineOptions.Host)->default_value(""),
+ "");
+
+ Options.add_option("ue-trace",
+ "",
+ "tracefile",
+ "Path to write a trace to",
+ cxxopts::value<std::string>(TraceCommandlineOptions.File)->default_value(""),
+ "");
+#endif // ZEN_WITH_TRACE
+
+#if ZEN_USE_SENTRY
+ SentryIntegration::Config SentryConfig;
+
+ bool NoSentry = false;
+
+ Options
+ .add_option("sentry", "", "no-sentry", "Disable Sentry crash handler", cxxopts::value<bool>(NoSentry)->default_value("false"), "");
+ Options.add_option("sentry",
+ "",
+ "sentry-allow-personal-info",
+ "Allow personally identifiable information in sentry crash reports",
+ cxxopts::value<bool>(SentryConfig.AllowPII)->default_value("false"),
+ "");
+ Options.add_option("sentry", "", "sentry-dsn", "Sentry DSN to send events to", cxxopts::value<std::string>(SentryConfig.Dsn), "");
+ Options.add_option("sentry", "", "sentry-environment", "Sentry environment", cxxopts::value<std::string>(SentryConfig.Environment), "");
+ Options.add_options()("sentry-debug", "Enable debug mode for Sentry", cxxopts::value<bool>(SentryConfig.Debug)->default_value("false"));
+#endif
+
+ try
+ {
+ cxxopts::ParseResult ParseResult = Options.parse(argc, argv);
+
+ if (ParseResult.count("help"))
+ {
+ std::string Help = Options.help();
+
+ printf("%s\n", Help.c_str());
+
+ exit(0);
+ }
+
+#if ZEN_USE_SENTRY
+
+ {
+ EnvironmentOptions EnvOptions;
+
+ EnvOptions.AddOption("UE_ZEN_SENTRY_DSN"sv, SentryConfig.Dsn, "sentry-dsn"sv);
+ EnvOptions.AddOption("UE_ZEN_SENTRY_ALLOWPERSONALINFO"sv, SentryConfig.AllowPII, "sentry-allow-personal-info"sv);
+ EnvOptions.AddOption("UE_ZEN_SENTRY_ENVIRONMENT"sv, SentryConfig.Environment, "sentry-environment"sv);
+
+ bool EnvEnableSentry = !NoSentry;
+ EnvOptions.AddOption("UE_ZEN_SENTRY_ENABLED"sv, EnvEnableSentry, "no-sentry"sv);
+
+ EnvOptions.AddOption("UE_ZEN_SENTRY_DEBUG"sv, SentryConfig.Debug, "sentry-debug"sv);
+
+ EnvOptions.Parse(ParseResult);
+
+ if (EnvEnableSentry != !NoSentry)
+ {
+ NoSentry = !EnvEnableSentry;
+ }
+ }
+
+ SentryIntegration Sentry;
+
+ if (NoSentry == false)
+ {
+ std::string SentryDatabasePath = (std::filesystem::temp_directory_path() / ".zen-sentry-native").string();
+
+ ExtendableStringBuilder<512> SB;
+ for (int i = 0; i < argc; ++i)
+ {
+ if (i)
+ {
+ SB.Append(' ');
+ }
+
+ SB.Append(argv[i]);
+ }
+
+ SentryConfig.DatabasePath = SentryDatabasePath;
+
+ Sentry.Initialize(SentryConfig, SB.ToString());
+
+ SentryIntegration::ClearCaches();
+ }
+#endif
+
+ zen::LoggingOptions LogOptions;
+ LogOptions.IsDebug = GlobalOptions.IsDebug;
+ LogOptions.IsVerbose = GlobalOptions.IsVerbose;
+ LogOptions.AllowAsync = false;
+ zen::InitializeLogging(LogOptions);
+
+ std::set_terminate([]() {
+ void* Frames[8];
+ uint32_t FrameCount = GetCallstack(2, 8, Frames);
+ CallstackFrames* Callstack = CreateCallstack(FrameCount, Frames);
+ ZEN_CRITICAL("Program exited abnormally via std::terminate()\n{}", CallstackToString(Callstack, " "));
+ FreeCallstack(Callstack);
+ });
+
+ zen::MaximizeOpenFileCount();
+
+ //////////////////////////////////////////////////////////////////////////
+
+ auto _ = zen::MakeGuard([] {
+ ShutdownWorkerPools();
+ ShutdownLogging();
+ });
+
+ zen::Sleep(1000000);
+ }
+ catch (const OptionParseException& Ex)
+ {
+ std::string HelpMessage = Options.help();
+
+ printf("Error parsing program arguments: %s\n\n%s", Ex.what(), HelpMessage.c_str());
+
+ return 9;
+ }
+ catch (const std::system_error& Ex)
+ {
+ printf("System Error: %s\n", Ex.what());
+
+ return Ex.code() ? Ex.code().value() : 10;
+ }
+ catch (const std::exception& Ex)
+ {
+ printf("Error: %s\n", Ex.what());
+
+ return 11;
+ }
+
+ return 0;
+}