#include "job_profiler.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace NYT { using namespace NYTProf; //////////////////////////////////////////////////////////////////////////////// static void RunSubprocess(const std::vector& cmd) { auto command = cmd[0]; auto args = TList(cmd.begin() + 1, cmd.end()); TShellCommand(command, args) .Run() .Wait(); } //////////////////////////////////////////////////////////////////////////////// class TJobProfiler : public IJobProfiler { public: TJobProfiler() { try { InitializeProfiler(); } catch (const std::exception& ex) { YT_LOG_ERROR("Failed to initialize job profiler: %v", ex.what()); } } void Start() override { if (CpuProfiler_) { CpuProfiler_->Start(); } } void Stop() override { if (CpuProfiler_) { CpuProfiler_->Stop(); auto profile = CpuProfiler_->ReadProfile(); SymbolizeAndWriteProfile(&profile); } if (MemoryProfilingToken_) { auto profile = ConvertAllocationProfile(std::move(*MemoryProfilingToken_).Stop()); SymbolizeAndWriteProfile(&profile); } if (ProfilePeakMemoryUsage_) { auto profile = ReadHeapProfile(tcmalloc::ProfileType::kPeakHeap); SymbolizeAndWriteProfile(&profile); } } private: std::unique_ptr CpuProfiler_; std::optional MemoryProfilingToken_; bool ProfilePeakMemoryUsage_ = false; bool RunExternalSymbolizer_ = false; void InitializeProfiler() { auto profilerSpecYson = GetEnv("YT_JOB_PROFILER_SPEC"); if (!profilerSpecYson) { return; } auto profilerSpec = NodeFromYsonString(profilerSpecYson); if (profilerSpec["type"] == "cpu") { auto samplingFrequency = profilerSpec["sampling_frequency"].AsInt64(); CpuProfiler_ = std::make_unique(TCpuProfilerOptions{ .SamplingFrequency = static_cast(samplingFrequency), }); } else if (profilerSpec["type"] == "memory") { MemoryProfilingToken_ = tcmalloc::MallocExtension::StartAllocationProfiling(); } else if (profilerSpec["type"] == "peak_memory") { ProfilePeakMemoryUsage_ = true; } if (profilerSpec["run_external_symbolizer"] == true) { RunExternalSymbolizer_ = true; } } void SymbolizeAndWriteProfile(NYTProf::NProto::Profile* profile) { Symbolize(profile, /*filesOnly*/ true); AddBuildInfo(profile, TBuildInfo::GetDefault()); if (RunExternalSymbolizer_) { SymbolizeByExternalPProf(profile, TSymbolizationOptions{ .RunTool = RunSubprocess, }); } auto serializedProfile = SerializeProfile(*profile); constexpr int ProfileFileDescriptor = 8; TFile profileFile(ProfileFileDescriptor); profileFile.Write(serializedProfile.data(), serializedProfile.size()); profileFile.FlushData(); } }; //////////////////////////////////////////////////////////////////////////////// std::unique_ptr CreateJobProfiler() { return std::make_unique(); } //////////////////////////////////////////////////////////////////////////////// } // namespace NYT