Line data Source code
1 : #include "source/extensions/watchdog/profile_action/profile_action.h" 2 : 3 : #include <chrono> 4 : 5 : #include "envoy/thread/thread.h" 6 : 7 : #include "source/common/profiler/profiler.h" 8 : #include "source/common/protobuf/utility.h" 9 : #include "source/common/stats/symbol_table.h" 10 : 11 : #include "absl/strings/str_format.h" 12 : 13 : namespace Envoy { 14 : namespace Extensions { 15 : namespace Watchdog { 16 : namespace ProfileAction { 17 : namespace { 18 : static constexpr uint64_t DefaultMaxProfiles = 10; 19 : 20 0 : std::string generateProfileFilePath(const std::string& directory, TimeSource& time_source) { 21 0 : const uint64_t timestamp = DateUtil::nowToSeconds(time_source); 22 0 : if (absl::EndsWith(directory, "/")) { 23 0 : return absl::StrFormat("%s%s.%d", directory, "ProfileAction", timestamp); 24 0 : } 25 0 : return absl::StrFormat("%s/%s.%d", directory, "ProfileAction", timestamp); 26 0 : } 27 : } // namespace 28 : 29 : ProfileAction::ProfileAction( 30 : envoy::extensions::watchdog::profile_action::v3::ProfileActionConfig& config, 31 : Server::Configuration::GuardDogActionFactoryContext& context) 32 : : path_(config.profile_path()), 33 : duration_( 34 : std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT(config, profile_duration, 5000))), 35 : max_profiles_(config.max_profiles() == 0 ? DefaultMaxProfiles : config.max_profiles()), 36 : profiles_attempted_(context.stats_.counterFromStatName( 37 : Stats::StatNameManagedStorage( 38 : absl::StrCat(context.guarddog_name_, ".profile_action.attempted"), 39 : context.stats_.symbolTable()) 40 : .statName())), 41 : profiles_successfully_captured_(context.stats_.counterFromStatName( 42 : Stats::StatNameManagedStorage( 43 : absl::StrCat(context.guarddog_name_, ".profile_action.successfully_captured"), 44 : context.stats_.symbolTable()) 45 : .statName())), 46 0 : context_(context), timer_cb_(context_.dispatcher_.createTimer([this] { 47 0 : if (Profiler::Cpu::profilerEnabled()) { 48 0 : Profiler::Cpu::stopProfiler(); 49 0 : running_profile_ = false; 50 0 : } else { 51 0 : ENVOY_LOG_MISC(error, 52 0 : "Profile Action's stop() was scheduled, but profiler isn't running!"); 53 0 : return; 54 0 : } 55 : 56 0 : if (!context_.api_.fileSystem().fileExists(profile_filename_)) { 57 0 : ENVOY_LOG_MISC(error, "Profile file {} wasn't created!", profile_filename_); 58 0 : } else { 59 0 : profiles_successfully_captured_.inc(); 60 0 : } 61 0 : })) {} 62 : 63 : void ProfileAction::run( 64 : envoy::config::bootstrap::v3::Watchdog::WatchdogAction::WatchdogEvent /*event*/, 65 : const std::vector<std::pair<Thread::ThreadId, MonotonicTime>>& thread_last_checkin_pairs, 66 0 : MonotonicTime /*now*/) { 67 0 : if (running_profile_) { 68 0 : return; 69 0 : } 70 0 : profiles_attempted_.inc(); 71 : 72 : // Check if there's a tid that justifies profiling 73 0 : if (thread_last_checkin_pairs.empty()) { 74 0 : ENVOY_LOG_MISC(warn, "Profile Action: No tids were provided."); 75 0 : return; 76 0 : } 77 : 78 0 : if (profiles_started_ >= max_profiles_) { 79 0 : ENVOY_LOG_MISC(warn, 80 0 : "Profile Action: Unable to profile: enabled but already wrote {} profiles.", 81 0 : profiles_started_); 82 0 : return; 83 0 : } 84 : 85 0 : auto& fs = context_.api_.fileSystem(); 86 0 : if (!fs.directoryExists(path_)) { 87 0 : ENVOY_LOG_MISC(error, "Profile Action: Directory path {} doesn't exist.", path_); 88 0 : return; 89 0 : } 90 : 91 : // Generate file path for output and try to profile 92 0 : profile_filename_ = generateProfileFilePath(path_, context_.api_.timeSource()); 93 : 94 0 : if (!Profiler::Cpu::profilerEnabled()) { 95 0 : if (Profiler::Cpu::startProfiler(profile_filename_)) { 96 : // Update state 97 0 : running_profile_ = true; 98 0 : ++profiles_started_; 99 : 100 : // Schedule callback to stop 101 0 : timer_cb_->enableTimer(duration_); 102 0 : } else { 103 0 : ENVOY_LOG_MISC(error, "Profile Action failed to start the profiler."); 104 0 : } 105 0 : } else { 106 0 : ENVOY_LOG_MISC(error, "Profile Action unable to start the profiler as it is in use elsewhere."); 107 0 : } 108 0 : } 109 : 110 : } // namespace ProfileAction 111 : } // namespace Watchdog 112 : } // namespace Extensions 113 : } // namespace Envoy