From d3c8ddf0691ae3b767c7eebf6f61fd7c97903612 Mon Sep 17 00:00:00 2001 From: cloud-profiler-team Date: Mon, 26 Oct 2020 15:53:28 -0700 Subject: [PATCH 01/11] Internal change PiperOrigin-RevId: 339135969 --- Makefile | 5 ----- README.md | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index d8bf6486f..8e3c3a222 100644 --- a/Makefile +++ b/Makefile @@ -80,7 +80,6 @@ PROFILE_PROTO_SOURCES = \ PROFILER_API_SOURCES = \ $(GENFILES_PATH)/google/api/annotations.pb.cc \ - $(GENFILES_PATH)/google/api/client.pb.cc \ $(GENFILES_PATH)/google/api/http.pb.cc \ $(GENFILES_PATH)/google/devtools/cloudprofiler/v2/profiler.grpc.pb.cc \ $(GENFILES_PATH)/google/devtools/cloudprofiler/v2/profiler.pb.cc \ @@ -231,10 +230,6 @@ $(GENFILES_PATH)/%annotations.pb.h $(GENFILES_PATH)/%annotations.pb.cc : third_p mkdir -p $(dir $@) $(PROTOC) -Ithird_party/googleapis -I$(PROTOBUF_INCLUDE_PATH) --cpp_out=$(GENFILES_PATH) $< -$(GENFILES_PATH)/%client.pb.h $(GENFILES_PATH)/%client.pb.cc : third_party/googleapis/%client.proto - mkdir -p $(dir $@) - $(PROTOC) -Ithird_party/googleapis -I$(PROTOBUF_INCLUDE_PATH) --cpp_out=$(GENFILES_PATH) $< - $(GENFILES_PATH)/%http.pb.h $(GENFILES_PATH)/%http.pb.cc : third_party/googleapis/%http.proto mkdir -p $(dir $@) $(PROTOC) -Ithird_party/googleapis --cpp_out=$(GENFILES_PATH) $< diff --git a/README.md b/README.md index f348a4c4e..e9169732a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# Google Cloud Profiler Java Agent +# Google Cloud Profiler Profiler Java Agent This repository contains source code for the -[Google Cloud Profiler](https://cloud.google.com/profiler/) Java agent. +[Google Cloud Profiler Profiler](https://cloud.google.com/profiler/) Java agent. ## Installation From 2ebaf1dd024e8a9d9756acd93b131b31ef89ee00 Mon Sep 17 00:00:00 2001 From: cloud-profiler-team Date: Wed, 6 Jan 2021 17:11:21 -0800 Subject: [PATCH 02/11] Remove repetition in the project name. PiperOrigin-RevId: 350456658 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e9169732a..f348a4c4e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# Google Cloud Profiler Profiler Java Agent +# Google Cloud Profiler Java Agent This repository contains source code for the -[Google Cloud Profiler Profiler](https://cloud.google.com/profiler/) Java agent. +[Google Cloud Profiler](https://cloud.google.com/profiler/) Java agent. ## Installation From da3ea2d1a5043c0b6d3a6ca927bdde058369d183 Mon Sep 17 00:00:00 2001 From: cloud-profiler-team Date: Mon, 11 Jan 2021 18:47:51 -0800 Subject: [PATCH 03/11] Internal change PiperOrigin-RevId: 351280823 --- src/cloud_env.cc | 22 +++++++++++++--------- src/entry.cc | 12 +++++++----- src/profiler.cc | 17 ++++++++++------- src/throttler_api.cc | 3 ++- src/throttler_timed.cc | 16 ++++++++++------ src/uploader_gcs.cc | 3 ++- src/worker.cc | 13 ++++++++----- 7 files changed, 52 insertions(+), 34 deletions(-) diff --git a/src/cloud_env.cc b/src/cloud_env.cc index 034a3deaf..ed484b415 100644 --- a/src/cloud_env.cc +++ b/src/cloud_env.cc @@ -21,6 +21,7 @@ #include "src/clock.h" #include "src/http.h" #include "src/string.h" +#include "third_party/absl/flags/flag.h" DEFINE_int32(cprof_gce_metadata_server_retry_count, 3, "Number of retries to Google Compute Engine metadata host"); @@ -55,10 +56,13 @@ std::string GceMetadataRequest(HTTPRequest* req, const std::string& path) { req->AddHeader("Metadata-Flavor", "Google"); req->SetTimeout(2); // seconds - std::string url = FLAGS_cprof_gce_metadata_server_address + path, resp; + std::string url = + absl::GetFlag(FLAGS_cprof_gce_metadata_server_address) + path, + resp; - int retry_sleep_sec = FLAGS_cprof_gce_metadata_server_retry_sleep_sec; - int retry_count = FLAGS_cprof_gce_metadata_server_retry_count; + int retry_sleep_sec = + absl::GetFlag(FLAGS_cprof_gce_metadata_server_retry_sleep_sec); + int retry_count = absl::GetFlag(FLAGS_cprof_gce_metadata_server_retry_count); struct timespec retry_ts = NanosToTimeSpec(kNanosPerSecond * retry_sleep_sec); for (int i = 0; i <= retry_count; i++) { @@ -98,9 +102,9 @@ const char* Getenv(const std::string& var) { } // namespace CloudEnv::CloudEnv() { - if (!FLAGS_cprof_service.empty()) { + if (!absl::GetFlag(FLAGS_cprof_service).empty()) { service_ = FLAGS_cprof_service; - } else if (!FLAGS_cprof_target.empty()) { + } else if (!absl::GetFlag(FLAGS_cprof_target).empty()) { service_ = FLAGS_cprof_target; } else { for (const std::string& env_var : {"GAE_SERVICE", "K_SERVICE"}) { @@ -112,7 +116,7 @@ CloudEnv::CloudEnv() { } } - if (!FLAGS_cprof_service_version.empty()) { + if (!absl::GetFlag(FLAGS_cprof_service_version).empty()) { service_version_ = FLAGS_cprof_service_version; } else { for (const std::string& env_var : {"GAE_VERSION", "K_REVISION"}) { @@ -124,7 +128,7 @@ CloudEnv::CloudEnv() { } } - if (!FLAGS_cprof_project_id.empty()) { + if (!absl::GetFlag(FLAGS_cprof_project_id).empty()) { project_id_ = FLAGS_cprof_project_id; LOG(INFO) << "Using project ID '" << project_id_ << "' from flags"; } else { @@ -138,7 +142,7 @@ CloudEnv::CloudEnv() { } } - if (!FLAGS_cprof_zone_name.empty()) { + if (!absl::GetFlag(FLAGS_cprof_zone_name).empty()) { zone_name_ = FLAGS_cprof_zone_name; LOG(INFO) << "Using zone name '" << zone_name_ << "' from flags"; } @@ -196,7 +200,7 @@ std::string CloudEnv::Oauth2AccessToken() { } std::string CloudEnv::Oauth2AccessToken(HTTPRequest* req) { - if (!FLAGS_cprof_access_token_test_only.empty()) { + if (!absl::GetFlag(FLAGS_cprof_access_token_test_only).empty()) { LOG(WARNING) << "Using access token from flags, test-only"; return FLAGS_cprof_access_token_test_only; } diff --git a/src/entry.cc b/src/entry.cc index 2809a648c..2777cbf49 100644 --- a/src/entry.cc +++ b/src/entry.cc @@ -19,6 +19,7 @@ #include "src/globals.h" #include "src/string.h" #include "src/worker.h" +#include "third_party/absl/flags/flag.h" #include "third_party/javaprofiler/accessors.h" #include "third_party/javaprofiler/globals.h" #include "third_party/javaprofiler/heap_sampler.h" @@ -120,9 +121,9 @@ void JNICALL OnVMInit(jvmtiEnv *jvmti, JNIEnv *jni_env, jthread thread) { CreateJMethodIDsForClass(jvmti, klass); } - if (FLAGS_cprof_enable_heap_sampling) { + if (absl::GetFlag(FLAGS_cprof_enable_heap_sampling)) { google::javaprofiler::HeapMonitor::Enable( - jvmti, jni_env, FLAGS_cprof_heap_sampling_interval); + jvmti, jni_env, absl::GetFlag(FLAGS_cprof_heap_sampling_interval)); } worker->Start(jni_env); @@ -166,7 +167,7 @@ static bool PrepareJvmti(JavaVM *vm, jvmtiEnv *jvmti) { caps.can_get_line_numbers = 1; caps.can_get_bytecodes = 1; caps.can_get_constant_pool = 1; - if (FLAGS_cprof_force_debug_non_safepoints) { + if (absl::GetFlag(FLAGS_cprof_force_debug_non_safepoints)) { caps.can_generate_compiled_method_load_events = 1; } @@ -217,7 +218,7 @@ static bool RegisterJvmti(jvmtiEnv *jvmti) { JVMTI_EVENT_VM_DEATH, JVMTI_EVENT_VM_INIT, }; - if (FLAGS_cprof_force_debug_non_safepoints) { + if (absl::GetFlag(FLAGS_cprof_force_debug_non_safepoints)) { callbacks.CompiledMethodLoad = &OnCompiledMethodLoad; events.push_back(JVMTI_EVENT_COMPILED_METHOD_LOAD); } @@ -301,7 +302,8 @@ jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { } threads = new ThreadTable(false); #else - threads = new ThreadTable(FLAGS_cprof_cpu_use_per_thread_timers); + threads = + new ThreadTable(absl::GetFlag(FLAGS_cprof_cpu_use_per_thread_timers)); #endif if (!RegisterJvmti(jvmti)) { diff --git a/src/profiler.cc b/src/profiler.cc index 2cc1bbf9f..8c3378d28 100644 --- a/src/profiler.cc +++ b/src/profiler.cc @@ -25,6 +25,7 @@ #include "src/clock.h" #include "src/globals.h" #include "src/proto.h" +#include "third_party/absl/flags/flag.h" #include "third_party/javaprofiler/accessors.h" DEFINE_int32(cprof_wall_num_threads_cutoff, 4096, @@ -104,7 +105,7 @@ void Profiler::Handle(int signum, siginfo_t *info, void *context) { // Skip top two frames, which include this function and the signal // handler. const int kFramesToSkip = 2; - if (FLAGS_cprof_record_native_stack && + if (absl::GetFlag(FLAGS_cprof_record_native_stack) && (kMaxFramesToCapture + kFramesToSkip - trace.num_frames > 0)) { void *raw_callstack[kMaxFramesToCapture + kFramesToSkip]; int stack_len = @@ -191,7 +192,7 @@ void Profiler::Reset() { } unknown_stack_count_ = 0; - if (FLAGS_cprof_record_native_stack) { + if (absl::GetFlag(FLAGS_cprof_record_native_stack)) { // When native stack collection requested, gather a single backtrace before // setting up the signal handler, to avoid running internal initialization // within backtrace from the signal handler. @@ -261,9 +262,10 @@ void CPUProfiler::Stop() { WallProfiler::WallProfiler(jvmtiEnv *jvmti, ThreadTable *threads, int64_t duration_nanos, int64_t period_nanos) : Profiler(jvmti, threads, duration_nanos, - EffectivePeriodNanos(period_nanos, threads->Size(), - FLAGS_cprof_wall_max_threads_per_sec, - duration_nanos)) {} + EffectivePeriodNanos( + period_nanos, threads->Size(), + absl::GetFlag(FLAGS_cprof_wall_max_threads_per_sec), + duration_nanos)) {} int64_t WallProfiler::EffectivePeriodNanos(int64_t period_nanos, int64_t num_threads, @@ -310,10 +312,11 @@ bool WallProfiler::Collect() { } clock->SleepUntil(next); std::vector threads = threads_->Threads(); - if (threads.size() > FLAGS_cprof_wall_num_threads_cutoff) { + if (threads.size() > absl::GetFlag(FLAGS_cprof_wall_num_threads_cutoff)) { LOG(WARNING) << "Aborting wall profiling due to too many threads. " << "Got " << threads.size() << " threads. " - << "Want up to " << FLAGS_cprof_wall_num_threads_cutoff; + << "Want up to " + << absl::GetFlag(FLAGS_cprof_wall_num_threads_cutoff); return false; // Too many threads, abort } count += threads.size(); diff --git a/src/throttler_api.cc b/src/throttler_api.cc index 2eb0f05a8..e049beb02 100644 --- a/src/throttler_api.cc +++ b/src/throttler_api.cc @@ -32,6 +32,7 @@ #include "grpcpp/create_channel.h" #include "grpcpp/security/credentials.h" #include "grpcpp/support/channel_arguments.h" +#include "third_party/absl/flags/flag.h" #include "third_party/javaprofiler/heap_sampler.h" // API curated profiling configuration. @@ -101,7 +102,7 @@ grpc_ssl_roots_override_result OverrideSSLRoots(char** pem_root_certs) { std::unique_ptr NewProfilerServiceStub(const std::string& addr) { std::shared_ptr creds; - if (FLAGS_cprof_use_insecure_creds_for_testing) { + if (absl::GetFlag(FLAGS_cprof_use_insecure_creds_for_testing)) { creds = grpc::InsecureChannelCredentials(); } else { grpc_set_ssl_roots_override_callback(&OverrideSSLRoots); diff --git a/src/throttler_timed.cc b/src/throttler_timed.cc index 30d175747..ecffbcd02 100644 --- a/src/throttler_timed.cc +++ b/src/throttler_timed.cc @@ -18,6 +18,7 @@ #include "src/uploader_file.h" #include "src/uploader_gcs.h" +#include "third_party/absl/flags/flag.h" DEFINE_int32(cprof_interval_sec, cloud::profiler::kProfileWaitSeconds, ""); DEFINE_int32(cprof_duration_sec, cloud::profiler::kProfileDurationSeconds, ""); @@ -35,7 +36,8 @@ const int64_t kRandomRange = 100000; // Gets the sampling configuration from the flags. int64_t GetConfiguration(int64_t* duration_cpu_ns, int64_t* duration_wall_ns, bool* enable_heap) { - int64_t duration_ns = FLAGS_cprof_duration_sec * kNanosPerSecond; + int64_t duration_ns = + absl::GetFlag(FLAGS_cprof_duration_sec) * kNanosPerSecond; *duration_cpu_ns = 0; *duration_wall_ns = 0; @@ -57,7 +59,7 @@ int64_t GetConfiguration(int64_t* duration_cpu_ns, int64_t* duration_wall_ns, << ", profiling disabled"; } - return FLAGS_cprof_interval_sec * kNanosPerSecond; + return absl::GetFlag(FLAGS_cprof_interval_sec) * kNanosPerSecond; } bool StartsWith(const std::string& s, const std::string& prefix) { @@ -101,15 +103,16 @@ TimedThrottler::TimedThrottler(std::unique_ptr uploader, LOG(INFO) << "sampling duration: cpu=" << duration_cpu_ns_ / kNanosPerSecond << "s, wall=" << duration_wall_ns_ / kNanosPerSecond; LOG(INFO) << "sampling interval: " << interval_ns_ / kNanosPerSecond << "s"; - LOG(INFO) << "sampling delay: " << FLAGS_cprof_delay_sec << "s"; + LOG(INFO) << "sampling delay: " << absl::GetFlag(FLAGS_cprof_delay_sec) + << "s"; LOG(INFO) << "heap sampling enabled: " << enable_heap_; struct timespec now = clock_->Now(); next_interval_ = now; - if (FLAGS_cprof_delay_sec != 0) { + if (absl::GetFlag(FLAGS_cprof_delay_sec) != 0) { struct timespec delay_ts = - NanosToTimeSpec(FLAGS_cprof_delay_sec * kNanosPerSecond); + NanosToTimeSpec(absl::GetFlag(FLAGS_cprof_delay_sec) * kNanosPerSecond); next_interval_ = TimeAdd(next_interval_, delay_ts); } @@ -137,7 +140,8 @@ bool TimedThrottler::WaitNext() { cur_.pop_back(); if (cur_.empty()) { - if (FLAGS_cprof_max_count > 0 && profile_count_ >= FLAGS_cprof_max_count) { + if (absl::GetFlag(FLAGS_cprof_max_count) > 0 && + profile_count_ >= absl::GetFlag(FLAGS_cprof_max_count)) { LOG(INFO) << "Reached maximum number of profiles to collect"; return false; } diff --git a/src/uploader_gcs.cc b/src/uploader_gcs.cc index a28369fe0..7133c522b 100644 --- a/src/uploader_gcs.cc +++ b/src/uploader_gcs.cc @@ -18,6 +18,7 @@ #include #include "src/http.h" +#include "third_party/absl/flags/flag.h" DEFINE_int32(cprof_gcs_upload_timeout_sec, 10, "Google Cloud Storage profile upload timeout in seconds"); @@ -42,7 +43,7 @@ bool GcsUploader::Upload(const std::string &profile_type, uploadReq.AddAuthBearerHeader(access_token); uploadReq.AddContentTypeHeader("application/octet-stream"); uploadReq.AddHeader("Content-Length", std::to_string(profile.size())); - uploadReq.SetTimeout(FLAGS_cprof_gcs_upload_timeout_sec); + uploadReq.SetTimeout(absl::GetFlag(FLAGS_cprof_gcs_upload_timeout_sec)); std::string url = std::string(kGcsHost) + "/" + ProfilePath(prefix_, profile_type); diff --git a/src/worker.cc b/src/worker.cc index bf6ba64dd..fbf6f581b 100644 --- a/src/worker.cc +++ b/src/worker.cc @@ -18,6 +18,7 @@ #include "src/profiler.h" #include "src/throttler_api.h" #include "src/throttler_timed.h" +#include "third_party/absl/flags/flag.h" #include "third_party/javaprofiler/heap_sampler.h" DEFINE_bool(cprof_enabled, true, @@ -46,7 +47,7 @@ void Worker::Start(JNIEnv *jni) { // Initialize the throttler here rather in the constructor, since the // constructor is invoked too early, before the heap profiler is initialized. - throttler_ = FLAGS_cprof_profile_filename.empty() + throttler_ = absl::GetFlag(FLAGS_cprof_profile_filename).empty() ? std::unique_ptr(new APIThrottler(jni)) : std::unique_ptr( new TimedThrottler(FLAGS_cprof_profile_filename)); @@ -59,7 +60,7 @@ void Worker::Start(JNIEnv *jni) { return; } - enabled_ = FLAGS_cprof_enabled; + enabled_ = absl::GetFlag(FLAGS_cprof_enabled); } void Worker::Stop() { @@ -137,14 +138,16 @@ void Worker::ProfileThread(jvmtiEnv *jvmti_env, JNIEnv *jni_env, void *arg) { std::string profile; std::string pt = w->throttler_->ProfileType(); if (pt == kTypeCPU) { - CPUProfiler p(w->jvmti_, w->threads_, w->throttler_->DurationNanos(), - FLAGS_cprof_cpu_sampling_period_msec * kNanosPerMilli); + CPUProfiler p( + w->jvmti_, w->threads_, w->throttler_->DurationNanos(), + absl::GetFlag(FLAGS_cprof_cpu_sampling_period_msec) * kNanosPerMilli); profile = Collect(&p, jni_env, &n); } else if (pt == kTypeWall) { // Note that the requested sampling period for the wall profiling may be // increased if the number of live threads is too large. WallProfiler p(w->jvmti_, w->threads_, w->throttler_->DurationNanos(), - FLAGS_cprof_wall_sampling_period_msec * kNanosPerMilli); + absl::GetFlag(FLAGS_cprof_wall_sampling_period_msec) * + kNanosPerMilli); profile = Collect(&p, jni_env, &n); } else if (pt == kTypeHeap) { if (!google::javaprofiler::HeapMonitor::Enabled()) { From e180c319eec7f750d53558b142897470eb3b01b0 Mon Sep 17 00:00:00 2001 From: aalexand Date: Tue, 12 Jan 2021 00:20:36 -0800 Subject: [PATCH 04/11] Internal change PiperOrigin-RevId: 351315130 --- src/cloud_env.cc | 22 +++++++++------------- src/entry.cc | 12 +++++------- src/profiler.cc | 17 +++++++---------- src/throttler_api.cc | 3 +-- src/throttler_timed.cc | 16 ++++++---------- src/uploader_gcs.cc | 3 +-- src/worker.cc | 13 +++++-------- 7 files changed, 34 insertions(+), 52 deletions(-) diff --git a/src/cloud_env.cc b/src/cloud_env.cc index ed484b415..034a3deaf 100644 --- a/src/cloud_env.cc +++ b/src/cloud_env.cc @@ -21,7 +21,6 @@ #include "src/clock.h" #include "src/http.h" #include "src/string.h" -#include "third_party/absl/flags/flag.h" DEFINE_int32(cprof_gce_metadata_server_retry_count, 3, "Number of retries to Google Compute Engine metadata host"); @@ -56,13 +55,10 @@ std::string GceMetadataRequest(HTTPRequest* req, const std::string& path) { req->AddHeader("Metadata-Flavor", "Google"); req->SetTimeout(2); // seconds - std::string url = - absl::GetFlag(FLAGS_cprof_gce_metadata_server_address) + path, - resp; + std::string url = FLAGS_cprof_gce_metadata_server_address + path, resp; - int retry_sleep_sec = - absl::GetFlag(FLAGS_cprof_gce_metadata_server_retry_sleep_sec); - int retry_count = absl::GetFlag(FLAGS_cprof_gce_metadata_server_retry_count); + int retry_sleep_sec = FLAGS_cprof_gce_metadata_server_retry_sleep_sec; + int retry_count = FLAGS_cprof_gce_metadata_server_retry_count; struct timespec retry_ts = NanosToTimeSpec(kNanosPerSecond * retry_sleep_sec); for (int i = 0; i <= retry_count; i++) { @@ -102,9 +98,9 @@ const char* Getenv(const std::string& var) { } // namespace CloudEnv::CloudEnv() { - if (!absl::GetFlag(FLAGS_cprof_service).empty()) { + if (!FLAGS_cprof_service.empty()) { service_ = FLAGS_cprof_service; - } else if (!absl::GetFlag(FLAGS_cprof_target).empty()) { + } else if (!FLAGS_cprof_target.empty()) { service_ = FLAGS_cprof_target; } else { for (const std::string& env_var : {"GAE_SERVICE", "K_SERVICE"}) { @@ -116,7 +112,7 @@ CloudEnv::CloudEnv() { } } - if (!absl::GetFlag(FLAGS_cprof_service_version).empty()) { + if (!FLAGS_cprof_service_version.empty()) { service_version_ = FLAGS_cprof_service_version; } else { for (const std::string& env_var : {"GAE_VERSION", "K_REVISION"}) { @@ -128,7 +124,7 @@ CloudEnv::CloudEnv() { } } - if (!absl::GetFlag(FLAGS_cprof_project_id).empty()) { + if (!FLAGS_cprof_project_id.empty()) { project_id_ = FLAGS_cprof_project_id; LOG(INFO) << "Using project ID '" << project_id_ << "' from flags"; } else { @@ -142,7 +138,7 @@ CloudEnv::CloudEnv() { } } - if (!absl::GetFlag(FLAGS_cprof_zone_name).empty()) { + if (!FLAGS_cprof_zone_name.empty()) { zone_name_ = FLAGS_cprof_zone_name; LOG(INFO) << "Using zone name '" << zone_name_ << "' from flags"; } @@ -200,7 +196,7 @@ std::string CloudEnv::Oauth2AccessToken() { } std::string CloudEnv::Oauth2AccessToken(HTTPRequest* req) { - if (!absl::GetFlag(FLAGS_cprof_access_token_test_only).empty()) { + if (!FLAGS_cprof_access_token_test_only.empty()) { LOG(WARNING) << "Using access token from flags, test-only"; return FLAGS_cprof_access_token_test_only; } diff --git a/src/entry.cc b/src/entry.cc index 2777cbf49..2809a648c 100644 --- a/src/entry.cc +++ b/src/entry.cc @@ -19,7 +19,6 @@ #include "src/globals.h" #include "src/string.h" #include "src/worker.h" -#include "third_party/absl/flags/flag.h" #include "third_party/javaprofiler/accessors.h" #include "third_party/javaprofiler/globals.h" #include "third_party/javaprofiler/heap_sampler.h" @@ -121,9 +120,9 @@ void JNICALL OnVMInit(jvmtiEnv *jvmti, JNIEnv *jni_env, jthread thread) { CreateJMethodIDsForClass(jvmti, klass); } - if (absl::GetFlag(FLAGS_cprof_enable_heap_sampling)) { + if (FLAGS_cprof_enable_heap_sampling) { google::javaprofiler::HeapMonitor::Enable( - jvmti, jni_env, absl::GetFlag(FLAGS_cprof_heap_sampling_interval)); + jvmti, jni_env, FLAGS_cprof_heap_sampling_interval); } worker->Start(jni_env); @@ -167,7 +166,7 @@ static bool PrepareJvmti(JavaVM *vm, jvmtiEnv *jvmti) { caps.can_get_line_numbers = 1; caps.can_get_bytecodes = 1; caps.can_get_constant_pool = 1; - if (absl::GetFlag(FLAGS_cprof_force_debug_non_safepoints)) { + if (FLAGS_cprof_force_debug_non_safepoints) { caps.can_generate_compiled_method_load_events = 1; } @@ -218,7 +217,7 @@ static bool RegisterJvmti(jvmtiEnv *jvmti) { JVMTI_EVENT_VM_DEATH, JVMTI_EVENT_VM_INIT, }; - if (absl::GetFlag(FLAGS_cprof_force_debug_non_safepoints)) { + if (FLAGS_cprof_force_debug_non_safepoints) { callbacks.CompiledMethodLoad = &OnCompiledMethodLoad; events.push_back(JVMTI_EVENT_COMPILED_METHOD_LOAD); } @@ -302,8 +301,7 @@ jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { } threads = new ThreadTable(false); #else - threads = - new ThreadTable(absl::GetFlag(FLAGS_cprof_cpu_use_per_thread_timers)); + threads = new ThreadTable(FLAGS_cprof_cpu_use_per_thread_timers); #endif if (!RegisterJvmti(jvmti)) { diff --git a/src/profiler.cc b/src/profiler.cc index 8c3378d28..2cc1bbf9f 100644 --- a/src/profiler.cc +++ b/src/profiler.cc @@ -25,7 +25,6 @@ #include "src/clock.h" #include "src/globals.h" #include "src/proto.h" -#include "third_party/absl/flags/flag.h" #include "third_party/javaprofiler/accessors.h" DEFINE_int32(cprof_wall_num_threads_cutoff, 4096, @@ -105,7 +104,7 @@ void Profiler::Handle(int signum, siginfo_t *info, void *context) { // Skip top two frames, which include this function and the signal // handler. const int kFramesToSkip = 2; - if (absl::GetFlag(FLAGS_cprof_record_native_stack) && + if (FLAGS_cprof_record_native_stack && (kMaxFramesToCapture + kFramesToSkip - trace.num_frames > 0)) { void *raw_callstack[kMaxFramesToCapture + kFramesToSkip]; int stack_len = @@ -192,7 +191,7 @@ void Profiler::Reset() { } unknown_stack_count_ = 0; - if (absl::GetFlag(FLAGS_cprof_record_native_stack)) { + if (FLAGS_cprof_record_native_stack) { // When native stack collection requested, gather a single backtrace before // setting up the signal handler, to avoid running internal initialization // within backtrace from the signal handler. @@ -262,10 +261,9 @@ void CPUProfiler::Stop() { WallProfiler::WallProfiler(jvmtiEnv *jvmti, ThreadTable *threads, int64_t duration_nanos, int64_t period_nanos) : Profiler(jvmti, threads, duration_nanos, - EffectivePeriodNanos( - period_nanos, threads->Size(), - absl::GetFlag(FLAGS_cprof_wall_max_threads_per_sec), - duration_nanos)) {} + EffectivePeriodNanos(period_nanos, threads->Size(), + FLAGS_cprof_wall_max_threads_per_sec, + duration_nanos)) {} int64_t WallProfiler::EffectivePeriodNanos(int64_t period_nanos, int64_t num_threads, @@ -312,11 +310,10 @@ bool WallProfiler::Collect() { } clock->SleepUntil(next); std::vector threads = threads_->Threads(); - if (threads.size() > absl::GetFlag(FLAGS_cprof_wall_num_threads_cutoff)) { + if (threads.size() > FLAGS_cprof_wall_num_threads_cutoff) { LOG(WARNING) << "Aborting wall profiling due to too many threads. " << "Got " << threads.size() << " threads. " - << "Want up to " - << absl::GetFlag(FLAGS_cprof_wall_num_threads_cutoff); + << "Want up to " << FLAGS_cprof_wall_num_threads_cutoff; return false; // Too many threads, abort } count += threads.size(); diff --git a/src/throttler_api.cc b/src/throttler_api.cc index e049beb02..2eb0f05a8 100644 --- a/src/throttler_api.cc +++ b/src/throttler_api.cc @@ -32,7 +32,6 @@ #include "grpcpp/create_channel.h" #include "grpcpp/security/credentials.h" #include "grpcpp/support/channel_arguments.h" -#include "third_party/absl/flags/flag.h" #include "third_party/javaprofiler/heap_sampler.h" // API curated profiling configuration. @@ -102,7 +101,7 @@ grpc_ssl_roots_override_result OverrideSSLRoots(char** pem_root_certs) { std::unique_ptr NewProfilerServiceStub(const std::string& addr) { std::shared_ptr creds; - if (absl::GetFlag(FLAGS_cprof_use_insecure_creds_for_testing)) { + if (FLAGS_cprof_use_insecure_creds_for_testing) { creds = grpc::InsecureChannelCredentials(); } else { grpc_set_ssl_roots_override_callback(&OverrideSSLRoots); diff --git a/src/throttler_timed.cc b/src/throttler_timed.cc index ecffbcd02..30d175747 100644 --- a/src/throttler_timed.cc +++ b/src/throttler_timed.cc @@ -18,7 +18,6 @@ #include "src/uploader_file.h" #include "src/uploader_gcs.h" -#include "third_party/absl/flags/flag.h" DEFINE_int32(cprof_interval_sec, cloud::profiler::kProfileWaitSeconds, ""); DEFINE_int32(cprof_duration_sec, cloud::profiler::kProfileDurationSeconds, ""); @@ -36,8 +35,7 @@ const int64_t kRandomRange = 100000; // Gets the sampling configuration from the flags. int64_t GetConfiguration(int64_t* duration_cpu_ns, int64_t* duration_wall_ns, bool* enable_heap) { - int64_t duration_ns = - absl::GetFlag(FLAGS_cprof_duration_sec) * kNanosPerSecond; + int64_t duration_ns = FLAGS_cprof_duration_sec * kNanosPerSecond; *duration_cpu_ns = 0; *duration_wall_ns = 0; @@ -59,7 +57,7 @@ int64_t GetConfiguration(int64_t* duration_cpu_ns, int64_t* duration_wall_ns, << ", profiling disabled"; } - return absl::GetFlag(FLAGS_cprof_interval_sec) * kNanosPerSecond; + return FLAGS_cprof_interval_sec * kNanosPerSecond; } bool StartsWith(const std::string& s, const std::string& prefix) { @@ -103,16 +101,15 @@ TimedThrottler::TimedThrottler(std::unique_ptr uploader, LOG(INFO) << "sampling duration: cpu=" << duration_cpu_ns_ / kNanosPerSecond << "s, wall=" << duration_wall_ns_ / kNanosPerSecond; LOG(INFO) << "sampling interval: " << interval_ns_ / kNanosPerSecond << "s"; - LOG(INFO) << "sampling delay: " << absl::GetFlag(FLAGS_cprof_delay_sec) - << "s"; + LOG(INFO) << "sampling delay: " << FLAGS_cprof_delay_sec << "s"; LOG(INFO) << "heap sampling enabled: " << enable_heap_; struct timespec now = clock_->Now(); next_interval_ = now; - if (absl::GetFlag(FLAGS_cprof_delay_sec) != 0) { + if (FLAGS_cprof_delay_sec != 0) { struct timespec delay_ts = - NanosToTimeSpec(absl::GetFlag(FLAGS_cprof_delay_sec) * kNanosPerSecond); + NanosToTimeSpec(FLAGS_cprof_delay_sec * kNanosPerSecond); next_interval_ = TimeAdd(next_interval_, delay_ts); } @@ -140,8 +137,7 @@ bool TimedThrottler::WaitNext() { cur_.pop_back(); if (cur_.empty()) { - if (absl::GetFlag(FLAGS_cprof_max_count) > 0 && - profile_count_ >= absl::GetFlag(FLAGS_cprof_max_count)) { + if (FLAGS_cprof_max_count > 0 && profile_count_ >= FLAGS_cprof_max_count) { LOG(INFO) << "Reached maximum number of profiles to collect"; return false; } diff --git a/src/uploader_gcs.cc b/src/uploader_gcs.cc index 7133c522b..a28369fe0 100644 --- a/src/uploader_gcs.cc +++ b/src/uploader_gcs.cc @@ -18,7 +18,6 @@ #include #include "src/http.h" -#include "third_party/absl/flags/flag.h" DEFINE_int32(cprof_gcs_upload_timeout_sec, 10, "Google Cloud Storage profile upload timeout in seconds"); @@ -43,7 +42,7 @@ bool GcsUploader::Upload(const std::string &profile_type, uploadReq.AddAuthBearerHeader(access_token); uploadReq.AddContentTypeHeader("application/octet-stream"); uploadReq.AddHeader("Content-Length", std::to_string(profile.size())); - uploadReq.SetTimeout(absl::GetFlag(FLAGS_cprof_gcs_upload_timeout_sec)); + uploadReq.SetTimeout(FLAGS_cprof_gcs_upload_timeout_sec); std::string url = std::string(kGcsHost) + "/" + ProfilePath(prefix_, profile_type); diff --git a/src/worker.cc b/src/worker.cc index fbf6f581b..bf6ba64dd 100644 --- a/src/worker.cc +++ b/src/worker.cc @@ -18,7 +18,6 @@ #include "src/profiler.h" #include "src/throttler_api.h" #include "src/throttler_timed.h" -#include "third_party/absl/flags/flag.h" #include "third_party/javaprofiler/heap_sampler.h" DEFINE_bool(cprof_enabled, true, @@ -47,7 +46,7 @@ void Worker::Start(JNIEnv *jni) { // Initialize the throttler here rather in the constructor, since the // constructor is invoked too early, before the heap profiler is initialized. - throttler_ = absl::GetFlag(FLAGS_cprof_profile_filename).empty() + throttler_ = FLAGS_cprof_profile_filename.empty() ? std::unique_ptr(new APIThrottler(jni)) : std::unique_ptr( new TimedThrottler(FLAGS_cprof_profile_filename)); @@ -60,7 +59,7 @@ void Worker::Start(JNIEnv *jni) { return; } - enabled_ = absl::GetFlag(FLAGS_cprof_enabled); + enabled_ = FLAGS_cprof_enabled; } void Worker::Stop() { @@ -138,16 +137,14 @@ void Worker::ProfileThread(jvmtiEnv *jvmti_env, JNIEnv *jni_env, void *arg) { std::string profile; std::string pt = w->throttler_->ProfileType(); if (pt == kTypeCPU) { - CPUProfiler p( - w->jvmti_, w->threads_, w->throttler_->DurationNanos(), - absl::GetFlag(FLAGS_cprof_cpu_sampling_period_msec) * kNanosPerMilli); + CPUProfiler p(w->jvmti_, w->threads_, w->throttler_->DurationNanos(), + FLAGS_cprof_cpu_sampling_period_msec * kNanosPerMilli); profile = Collect(&p, jni_env, &n); } else if (pt == kTypeWall) { // Note that the requested sampling period for the wall profiling may be // increased if the number of live threads is too large. WallProfiler p(w->jvmti_, w->threads_, w->throttler_->DurationNanos(), - absl::GetFlag(FLAGS_cprof_wall_sampling_period_msec) * - kNanosPerMilli); + FLAGS_cprof_wall_sampling_period_msec * kNanosPerMilli); profile = Collect(&p, jni_env, &n); } else if (pt == kTypeHeap) { if (!google::javaprofiler::HeapMonitor::Enabled()) { From 3e8094a84c0f22422bcc63c10fa96566a9d8b297 Mon Sep 17 00:00:00 2001 From: cloud-profiler-team Date: Wed, 13 Jan 2021 11:01:19 -0800 Subject: [PATCH 05/11] chore: Change proto synchronization tool. PiperOrigin-RevId: 351621995 --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index 8e3c3a222..d8bf6486f 100644 --- a/Makefile +++ b/Makefile @@ -80,6 +80,7 @@ PROFILE_PROTO_SOURCES = \ PROFILER_API_SOURCES = \ $(GENFILES_PATH)/google/api/annotations.pb.cc \ + $(GENFILES_PATH)/google/api/client.pb.cc \ $(GENFILES_PATH)/google/api/http.pb.cc \ $(GENFILES_PATH)/google/devtools/cloudprofiler/v2/profiler.grpc.pb.cc \ $(GENFILES_PATH)/google/devtools/cloudprofiler/v2/profiler.pb.cc \ @@ -230,6 +231,10 @@ $(GENFILES_PATH)/%annotations.pb.h $(GENFILES_PATH)/%annotations.pb.cc : third_p mkdir -p $(dir $@) $(PROTOC) -Ithird_party/googleapis -I$(PROTOBUF_INCLUDE_PATH) --cpp_out=$(GENFILES_PATH) $< +$(GENFILES_PATH)/%client.pb.h $(GENFILES_PATH)/%client.pb.cc : third_party/googleapis/%client.proto + mkdir -p $(dir $@) + $(PROTOC) -Ithird_party/googleapis -I$(PROTOBUF_INCLUDE_PATH) --cpp_out=$(GENFILES_PATH) $< + $(GENFILES_PATH)/%http.pb.h $(GENFILES_PATH)/%http.pb.cc : third_party/googleapis/%http.proto mkdir -p $(dir $@) $(PROTOC) -Ithird_party/googleapis --cpp_out=$(GENFILES_PATH) $< From ecfbfd03243bde2c6e14d7126761a01bf5a19d74 Mon Sep 17 00:00:00 2001 From: jcbeyler Date: Mon, 1 Feb 2021 17:14:59 -0800 Subject: [PATCH 06/11] Generalize the label system for the proto builder PiperOrigin-RevId: 355060165 --- third_party/javaprofiler/heap_sampler.cc | 6 +- .../javaprofiler/profile_proto_builder.cc | 175 ++++++++++-------- .../javaprofiler/profile_proto_builder.h | 170 ++++++++++++----- 3 files changed, 222 insertions(+), 129 deletions(-) diff --git a/third_party/javaprofiler/heap_sampler.cc b/third_party/javaprofiler/heap_sampler.cc index d0d8ad2b4..e8c2b0dae 100644 --- a/third_party/javaprofiler/heap_sampler.cc +++ b/third_party/javaprofiler/heap_sampler.cc @@ -145,10 +145,12 @@ std::unique_ptr HeapEventStorage::ConvertToProto( auto *frames = object->Frames(); *call_target = {nullptr, static_cast(frames->size()), frames->data()}; *trace_target = {call_target, object->Size()}; + + // Add the size as a label to help post-processing filtering. + trace_target->trace_and_labels.AddLabel("bytes", object->Size(), "bytes"); } - builder->AddTracesAppendingMetricAsLabel(stack_trace_data.get(), - objects_size); + builder->AddTraces(stack_trace_data.get(), objects_size); return builder->CreateProto(); } diff --git a/third_party/javaprofiler/profile_proto_builder.cc b/third_party/javaprofiler/profile_proto_builder.cc index 1044c653c..2a714e290 100644 --- a/third_party/javaprofiler/profile_proto_builder.cc +++ b/third_party/javaprofiler/profile_proto_builder.cc @@ -20,21 +20,27 @@ using std::unique_ptr; namespace google { namespace javaprofiler { +namespace { +// Hashes a string using an initial hash value of h. +size_t HashString(const std::string &str, size_t h) { + hash hash_string; + return 31U * h + hash_string(str); +} const int kCount = 0; const int kMetric = 1; +} // namespace -ProfileProtoBuilder::ProfileProtoBuilder( - JNIEnv *jni_env, jvmtiEnv *jvmti_env, - ProfileFrameCache *native_cache, int64 sampling_rate, - const SampleType &count_type, const SampleType &metric_type, - const std::list &label_types) +ProfileProtoBuilder::ProfileProtoBuilder(JNIEnv *jni_env, jvmtiEnv *jvmti_env, + ProfileFrameCache *native_cache, + int64 sampling_rate, + const SampleType &count_type, + const SampleType &metric_type) : sampling_rate_(sampling_rate), jni_env_(jni_env), jvmti_env_(jvmti_env), native_cache_(native_cache), - location_builder_(&builder_), - label_types_(label_types) { + location_builder_(&builder_) { AddSampleType(count_type); AddSampleType(metric_type); SetPeriodType(metric_type); @@ -47,7 +53,7 @@ void ProfileProtoBuilder::AddTraces(const ProfileStackTrace *traces, } for (int i = 0; i < num_traces; ++i) { - AddTrace(traces[i], 1, false); + AddTrace(traces[i], 1); } } @@ -59,29 +65,7 @@ void ProfileProtoBuilder::AddTraces(const ProfileStackTrace *traces, } for (int i = 0; i < num_traces; ++i) { - AddTrace(traces[i], counts[i], false); - } -} - -void ProfileProtoBuilder::AddTracesAppendingMetricAsLabel( - const ProfileStackTrace *traces, int num_traces) { - if (native_cache_) { - native_cache_->ProcessTraces(traces, num_traces); - } - - for (int i = 0; i < num_traces; ++i) { - AddTrace(traces[i], 1, true); - } -} - -void ProfileProtoBuilder::AddTracesAppendingMetricAsLabel( - const ProfileStackTrace *traces, const int32 *counts, int num_traces) { - if (native_cache_) { - native_cache_->ProcessTraces(traces, num_traces); - } - - for (int i = 0; i < num_traces; ++i) { - AddTrace(traces[i], counts[i], true); + AddTrace(traces[i], counts[i]); } } @@ -94,8 +78,7 @@ void ProfileProtoBuilder::AddArtificialTrace(const std::string &name, int count, auto sample = profile->add_sample(); sample->add_location_id(location->id()); // Move count * sampling rate to 64-bit. - InitSampleValues(sample, count, static_cast(count) * sampling_rate, - std::list()); + InitSampleValues(sample, count, static_cast(count) * sampling_rate); } void ProfileProtoBuilder::UnsampleMetrics() { @@ -150,52 +133,55 @@ void ProfileProtoBuilder::UpdateSampleValues( sample->set_value(kMetric, sample->value(kMetric) + size); } -void ProfileProtoBuilder::InitSampleValues( - perftools::profiles::Sample *sample, int64 count, int64 metric, - const std::list &label_values) { +void ProfileProtoBuilder::InitSampleValues(perftools::profiles::Sample *sample, + int64 count, int64 metric) { sample->add_value(count); sample->add_value(metric); +} + +void ProfileProtoBuilder::AddLabels(const TraceAndLabels &trace_and_labels, + perftools::profiles::Sample *sample) { + for (const auto &label : trace_and_labels.labels) { + auto new_label = sample->add_label(); + new_label->set_key(builder_.StringId(label.key.c_str())); - DCHECK (label_values.size() == label_types_.size()) - << "Number of label values != number of label types"; - auto it_value = label_values.begin(); - auto it_type = label_types_.begin(); - for (; it_value != label_values.end() && it_type != label_types_.end(); - ++it_value, ++it_type) { - auto label = sample->add_label(); - label->set_key(builder_.StringId(it_type->type.c_str())); - label->set_num(*it_value); - label->set_num_unit(builder_.StringId(it_type->unit.c_str())); + if (label.is_string_label) { + new_label->set_str(builder_.StringId(label.str_label.c_str())); + } else { + new_label->set_num(label.num_label.value); + // Note that if the unit is not defined in the label, it will be empty and + // thus end up being a 0 as, by definition, "" has 0 as a string Id. + new_label->set_num_unit(builder_.StringId(label.num_label.unit.c_str())); + } } } -void ProfileProtoBuilder::AddTrace(const ProfileStackTrace &trace, - int32 count, - bool append_metric_value_as_label) { - std::list label_values; - if (append_metric_value_as_label) { - label_values.push_back(trace.metric_value); - } - auto sample = trace_samples_.SampleFor(trace.trace, label_values); +void ProfileProtoBuilder::AddTrace(const ProfileStackTrace &profile_trace, + int32 count) { + const TraceAndLabels &trace_and_labels = profile_trace.trace_and_labels; + auto sample = trace_samples_.SampleFor(trace_and_labels); + jint metric_value = profile_trace.metric_value; if (sample != nullptr) { - UpdateSampleValues(sample, count, trace.metric_value); + UpdateSampleValues(sample, count, metric_value); return; } auto profile = builder_.mutable_profile(); sample = profile->add_sample(); + trace_samples_.Add(trace_and_labels, sample); - trace_samples_.Add(trace.trace, label_values, sample); + AddLabels(trace_and_labels, sample); - InitSampleValues(sample, count, trace.metric_value, label_values); + InitSampleValues(sample, count, metric_value); - int first_frame = SkipTopNativeFrames(*trace.trace); + const JVMPI_CallTrace *trace = trace_and_labels.trace; + int first_frame = SkipTopNativeFrames(*trace); StackState stack_state; - for (int i = first_frame; i < trace.trace->num_frames; ++i) { - auto &jvm_frame = trace.trace->frames[i]; + for (int i = first_frame; i < trace->num_frames; ++i) { + auto &jvm_frame = trace->frames[i]; if (jvm_frame.lineno == kNativeFrameLineNum) { AddNativeInfo(jvm_frame, profile, sample, &stack_state); @@ -373,37 +359,74 @@ perftools::profiles::Location *LocationBuilder::LocationFor( return location; } -size_t TraceSamples::TraceHash::operator()(const TraceAndValues &trace_values) - const { - unsigned int h = 1; +bool SampleLabel::operator==(const SampleLabel &other) const { + if (other.key != key) { + return false; + } + + if (other.is_string_label != is_string_label) { + return false; + } + + if (other.is_string_label) { + return other.str_label == str_label; + } + return other.num_label == num_label; +} + +size_t SampleLabel::Hash(size_t current_hash_value) const { + size_t result = HashString(key, current_hash_value); + + result = 31U * result + (is_string_label ? 1 : 0); + + if (is_string_label) { + return HashString(str_label, result); + } else { + return num_label.Hash(result); + } +} + +size_t NumLabelValue::Hash(size_t current_hash_value) const { + hash hash_int; + current_hash_value = 31U * current_hash_value + hash_int(value); + return HashString(unit, current_hash_value); +} + +size_t TraceSamples::TraceHash::operator()( + const TraceAndLabels &trace_values) const { + unsigned int hash = 1; const JVMPI_CallTrace *trace = trace_values.trace; for (int f = 0; f < trace->num_frames; f++) { { int len = sizeof(jint); char *arr = reinterpret_cast(&trace->frames[f].lineno); for (int i = 0; i < len; i++) { - h = 31U * h + arr[i]; + hash = 31U * hash + arr[i]; } } { int len = sizeof(jmethodID); char *arr = reinterpret_cast(&trace->frames[f].method_id); for (int i = 0; i < len; i++) { - h = 31U * h + arr[i]; + hash = 31U * hash + arr[i]; } } } - for (auto value : trace_values.label_values) { - h = 31U * h + static_cast(value); + + for (const SampleLabel &label : trace_values.labels) { + hash = label.Hash(hash); } - return h; + + return hash; } -bool TraceSamples::TraceEquals::operator()(const TraceAndValues &trace_values1, - const TraceAndValues &trace_values2) const { - if (trace_values1.label_values != trace_values2.label_values) { +bool TraceSamples::TraceEquals::operator()( + const TraceAndLabels &trace_values1, + const TraceAndLabels &trace_values2) const { + if (trace_values1.labels != trace_values2.labels) { return false; } + const JVMPI_CallTrace *trace1 = trace_values1.trace; const JVMPI_CallTrace *trace2 = trace_values2.trace; if (trace1->num_frames != trace2->num_frames) { @@ -424,19 +447,17 @@ bool TraceSamples::TraceEquals::operator()(const TraceAndValues &trace_values1, } perftools::profiles::Sample *TraceSamples::SampleFor( - const JVMPI_CallTrace *trace, const std::list &label_values) const { - auto found = traces_.find({trace, label_values}); + const TraceAndLabels &trace) const { + auto found = traces_.find(trace); if (found == traces_.end()) { return nullptr; } - return found->second; } -void TraceSamples::Add(const JVMPI_CallTrace *trace, - const std::list &label_values, +void TraceSamples::Add(const TraceAndLabels &trace, perftools::profiles::Sample *sample) { - traces_[{trace, label_values}] = sample; + traces_[trace] = sample; } double CalculateSamplingRatio(int64 rate, int64 count, int64 metric_value) { diff --git a/third_party/javaprofiler/profile_proto_builder.h b/third_party/javaprofiler/profile_proto_builder.h index 2dcf1aa81..8e17ce22b 100644 --- a/third_party/javaprofiler/profile_proto_builder.h +++ b/third_party/javaprofiler/profile_proto_builder.h @@ -21,7 +21,6 @@ #include #include -#include #include #include #include @@ -33,37 +32,127 @@ namespace google { namespace javaprofiler { +// Value and unit for a numerical label. +struct NumLabelValue { + // Actual value of the label. + const int64 value; + + // Unit for the numerical label. + const std::string unit; + + // Constructor with a value and unit name. + NumLabelValue(int64 v = 0, const std::string &u = "") : value(v), unit(u) {} + + // Equality operator with another NumLabelValue. + bool operator==(const NumLabelValue &other) const { + return value == other.value && unit == other.unit; + } + + // Hash method for this numerical label. + size_t Hash(size_t current_hash_value) const; +}; + +// Label associated with a sample. +struct SampleLabel { + // Default constructor creating an empty string label. + SampleLabel() : key(""), is_string_label(true) {} + + // Constructor for a string label. + SampleLabel(const std::string &k, const std::string &str) + : key(k), str_label(str), is_string_label(true) {} + + // Constructor for a numerical label. + SampleLabel(const std::string &k, int64 num, const std::string &unit = "") + : key(k), num_label(num, unit), is_string_label(false) {} + + // The label key. + const std::string key; + + // String value if this SampleLabel is_string_label. + const std::string str_label; + // Numeric value if this SampleLabel not is_string_label. + const NumLabelValue num_label; + + // Content of the value "union". + const bool is_string_label; + + // Equality operator for the label. + bool operator==(const SampleLabel &other) const; + + // Hash method for this numerical label. + size_t Hash(size_t current_hash_value) const; +}; + +// Trace and labels stored together for storage in the hash map while +// constructing the profile proto. +struct TraceAndLabels { + // Constructor for a trace without labels. + TraceAndLabels(const JVMPI_CallTrace *t) : trace(t) {} + + // Constructor for a trace with labels. + TraceAndLabels(const JVMPI_CallTrace *t, const std::vector &l) + : trace(t), labels(l) {} + + // Adds a string label to the associated trace. + void AddLabel(const std::string &key, const std::string &str_value) { + labels.push_back({key, str_value}); + } + + // Adds a numeric label to the associated trace. + void AddLabel(const std::string &key, int64 num_value, + const std::string &unit = "") { + labels.push_back({key, num_value, unit}); + } + + // Trace associated with given labels. + const JVMPI_CallTrace *trace; + // Labels associated with the trace. + std::vector labels; +}; + +// A profile stack trace containing a stack trace, a metric value, and any +// potential labels associated with this trace. struct ProfileStackTrace { - JVMPI_CallTrace *trace; + // Constructor for a stack trace without any label. + ProfileStackTrace(const JVMPI_CallTrace *trace = nullptr, jint m_value = 0) + : metric_value(m_value), trace_and_labels(trace) {} + + // Constructor for a stack trace with labels. + ProfileStackTrace(const JVMPI_CallTrace *trace, jint m_value, + const std::vector &labels) + : metric_value(m_value), trace_and_labels(trace, labels) {} + + // Metric associated with the trace and labels. jint metric_value; + + // Trace and labels associated with the metric collected. + TraceAndLabels trace_and_labels; }; // Store proto sample objects for specific stack traces and label values. class TraceSamples { public: - perftools::profiles::Sample *SampleFor( - const JVMPI_CallTrace *trace, const std::list &label_values) const; - void Add(const JVMPI_CallTrace *trace, const std::list &label_values, - perftools::profiles::Sample *sample); + // Returns an existing sample with the same trace and labels if exists, + // nullptr otherwise. + perftools::profiles::Sample *SampleFor(const TraceAndLabels &trace) const; - private: - struct TraceAndValues { - const JVMPI_CallTrace *trace; - // Values for each label type in ProfileProtoBuilder::label_types_. - const std::list label_values; - }; + // Adds a given trace and label to storage and associates it with a given + // sample. + void Add(const TraceAndLabels &trace, perftools::profiles::Sample *sample); + private: struct TraceHash { - size_t operator()(const TraceAndValues &trace_values) const; + size_t operator()(const TraceAndLabels &trace_values) const; }; struct TraceEquals { - bool operator()(const TraceAndValues &trace_values1, - const TraceAndValues &trace_values2) const; + bool operator()(const TraceAndLabels &trace_values1, + const TraceAndLabels &trace_values2) const; }; - std::unordered_map traces_; + std::unordered_map + traces_; }; // Store locations previously seen so that the profile is only @@ -123,41 +212,27 @@ class ProfileProtoBuilder { public: virtual ~ProfileProtoBuilder() {} - // Add traces to the proto. The traces array must not be deleted + // Adds traces to the proto. The traces array must not be deleted // before calling CreateProto. void AddTraces(const ProfileStackTrace *traces, int num_traces); - // Add traces to the proto, where each trace has a defined count + // Adds traces to the proto, where each trace has a defined count // of occurrences. The traces array must not be deleted // before calling CreateProto. void AddTraces(const ProfileStackTrace *traces, const int32 *counts, int num_traces); - // Add traces to the proto, and append traces[i].metric_value as the label - // value corresponding to the last label_type. The traces array must not be - // deleted before calling CreateProto. - void AddTracesAppendingMetricAsLabel(const ProfileStackTrace *traces, - int num_traces); - - // Add traces to the proto, where each trace has a defined count of - // occurrences, and append traces[i].metric_value as the label - // value corresponding to the last label_type. The traces array must not be - // deleted before calling CreateProto. - void AddTracesAppendingMetricAsLabel(const ProfileStackTrace *traces, - const int32 *counts, - int num_traces); - - // Add a "fake" trace with a single frame. Used to represent JVM + // Adds a "fake" trace with a single frame. Used to represent JVM // tasks such as JIT compilation and GC. void AddArtificialTrace(const std::string &name, int count, int sampling_rate); - // Build the proto. Calling any other method on the class after calling + // Builds the proto. Calling any other method on the class after calling // this has undefined behavior. virtual std::unique_ptr CreateProto() = 0; - // Create a heap profile. + // Creates a heap profile. // jvmti_env can be null but then all calls to AddTraces will return // unknown. // Accepts a null cache since the heap profiles can be using JVMTI's @@ -192,8 +267,7 @@ class ProfileProtoBuilder { ProfileProtoBuilder(JNIEnv *env, jvmtiEnv *jvmti_env, ProfileFrameCache *native_cache, int64 sampling_rate, const SampleType &count_type, - const SampleType &metric_type, - const std::list &label_types); + const SampleType &metric_type); virtual bool SkipFrame(const std::string &function_name) const { return false; @@ -254,11 +328,10 @@ class ProfileProtoBuilder { void AddSampleType(const SampleType &sample_type); void SetPeriodType(const SampleType &metric_type); void InitSampleValues(perftools::profiles::Sample *sample, int64 count, - int64 metric, const std::list &label_values); + int64 metric); void UpdateSampleValues(perftools::profiles::Sample *sample, int64 count, int64 size); - void AddTrace(const ProfileStackTrace &trace, int32 count, - bool append_metric_value_as_label); + void AddTrace(const ProfileStackTrace &trace, int32 count); void AddJavaInfo(const JVMPI_CallFrame &jvm_frame, perftools::profiles::Profile *profile, perftools::profiles::Sample *sample, @@ -272,6 +345,9 @@ class ProfileProtoBuilder { MethodInfo *Method(jmethodID id); int64 Location(MethodInfo *method, const JVMPI_CallFrame &frame); + void AddLabels(const TraceAndLabels &trace_and_labels, + perftools::profiles::Sample *sample); + JNIEnv *jni_env_; jvmtiEnv *jvmti_env_; @@ -291,7 +367,6 @@ class ProfileProtoBuilder { ProfileFrameCache *native_cache_; TraceSamples trace_samples_; LocationBuilder location_builder_; - const std::list label_types_; }; // Computes the ratio to use to scale heap data to unsample it. @@ -310,8 +385,7 @@ class CpuProfileProtoBuilder : public ProfileProtoBuilder { : ProfileProtoBuilder( jni_env, jvmti_env, cache, sampling_rate, ProfileProtoBuilder::SampleType("samples", "count"), - ProfileProtoBuilder::SampleType("cpu", "nanoseconds"), - std::list()) { + ProfileProtoBuilder::SampleType("cpu", "nanoseconds")) { builder_.mutable_profile()->set_duration_nanos(duration_ns); builder_.mutable_profile()->set_period(sampling_rate); } @@ -331,10 +405,7 @@ class HeapProfileProtoBuilder : public ProfileProtoBuilder { : ProfileProtoBuilder( jni_env, jvmti_env, cache, sampling_rate, ProfileProtoBuilder::SampleType("inuse_objects", "count"), - ProfileProtoBuilder::SampleType("inuse_space", "bytes"), - std::list({ - ProfileProtoBuilder::SampleType("bytes", "bytes") - })) {} + ProfileProtoBuilder::SampleType("inuse_space", "bytes")) {} std::unique_ptr CreateProto() override { return CreateUnsampledProto(); @@ -366,8 +437,7 @@ class ContentionProfileProtoBuilder : public ProfileProtoBuilder { : ProfileProtoBuilder( jni_env, jvmti_env, cache, sampling_rate, ProfileProtoBuilder::SampleType("contentions", "count"), - ProfileProtoBuilder::SampleType("delay", "microseconds"), - std::list()) { + ProfileProtoBuilder::SampleType("delay", "microseconds")) { builder_.mutable_profile()->set_period(sampling_rate); } From 9724b6c35de00c909348bce62e6be0dafcb3ddb9 Mon Sep 17 00:00:00 2001 From: cloud-profiler-team Date: Tue, 2 Feb 2021 13:40:50 -0800 Subject: [PATCH 07/11] Preserve mode and ownership when NOTICES if copied to make it readable on a host machine. PiperOrigin-RevId: 355242563 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d8bf6486f..ce3b49955 100644 --- a/Makefile +++ b/Makefile @@ -216,7 +216,7 @@ $(TARGET_AGENT): $(SOURCES) $(HEADERS) $(TARGET_NOTICES): $(JAVA_AGENT_PATH)/NOTICES mkdir -p $(dir $@) - cp -f $< $@ + cp -fp $< $@ $(GENFILES_PATH)/%profiler.pb.h $(GENFILES_PATH)/%profiler.pb.cc : third_party/googleapis/%profiler.proto mkdir -p $(dir $@) From 8f2d0234788e1fa276ea43d8076438488f1de136 Mon Sep 17 00:00:00 2001 From: cloud-profiler-team Date: Thu, 25 Feb 2021 12:27:58 -0800 Subject: [PATCH 08/11] Internal change PiperOrigin-RevId: 359586300 --- .../perftools/profiles/proto/builder.cc | 45 ++++++++++--------- .../perftools/profiles/proto/builder.h | 7 +-- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/third_party/perftools/profiles/proto/builder.cc b/third_party/perftools/profiles/proto/builder.cc index 1e3a29eb9..428cf93d7 100644 --- a/third_party/perftools/profiles/proto/builder.cc +++ b/third_party/perftools/profiles/proto/builder.cc @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -48,12 +49,12 @@ Builder::Builder() : profile_(new Profile()) { profile_->add_string_table(""); } -int64 Builder::StringId(const char *str) { +int64_t Builder::StringId(const char *str) { if (str == nullptr || !str[0]) { return 0; } - const int64 index = profile_->string_table_size(); + const int64_t index = profile_->string_table_size(); const auto inserted = strings_.emplace(str, index); if (!inserted.second) { // Failed to insert -- use existing id. @@ -63,16 +64,16 @@ int64 Builder::StringId(const char *str) { return index; } -uint64 Builder::FunctionId(const char *name, const char *system_name, - const char *file, int64 start_line) { - int64 name_index = StringId(name); - int64 system_name_index = StringId(system_name); - int64 file_index = StringId(file); +uint64_t Builder::FunctionId(const char *name, const char *system_name, + const char *file, int64_t start_line) { + int64_t name_index = StringId(name); + int64_t system_name_index = StringId(system_name); + int64_t file_index = StringId(file); auto fn = std::make_tuple(name_index, system_name_index, file_index, start_line); - int64 index = profile_->function_size() + 1; + int64_t index = profile_->function_size() + 1; const auto inserted = functions_.insert(std::make_pair(fn, index)); const bool insert_successful = inserted.second; if (!insert_successful) { @@ -137,7 +138,7 @@ bool Builder::CheckValid(const Profile &profile) { IndexSet mapping_ids; mapping_ids.reserve(profile.mapping_size()); for (const auto &mapping : profile.mapping()) { - const int64 id = mapping.id(); + const int64_t id = mapping.id(); if (id != 0) { const bool insert_successful = mapping_ids.insert(id).second; if (!insert_successful) { @@ -150,7 +151,7 @@ bool Builder::CheckValid(const Profile &profile) { IndexSet function_ids; function_ids.reserve(profile.function_size()); for (const auto &function : profile.function()) { - const int64 id = function.id(); + const int64_t id = function.id(); if (id != 0) { const bool insert_successful = function_ids.insert(id).second; if (!insert_successful) { @@ -163,7 +164,7 @@ bool Builder::CheckValid(const Profile &profile) { IndexSet location_ids; location_ids.reserve(profile.location_size()); for (const auto &location : profile.location()) { - const int64 id = location.id(); + const int64_t id = location.id(); if (id != 0) { const bool insert_successful = location_ids.insert(id).second; if (!insert_successful) { @@ -171,13 +172,13 @@ bool Builder::CheckValid(const Profile &profile) { return false; } } - const int64 mapping_id = location.mapping_id(); + const int64_t mapping_id = location.mapping_id(); if (mapping_id != 0 && mapping_ids.count(mapping_id) == 0) { LOG(ERROR) << "Missing mapping " << mapping_id << " from location " << id; return false; } for (const auto &line : location.line()) { - int64 function_id = line.function_id(); + int64_t function_id = line.function_id(); if (function_id != 0 && function_ids.count(function_id) == 0) { LOG(ERROR) << "Missing function " << function_id; return false; @@ -197,7 +198,7 @@ bool Builder::CheckValid(const Profile &profile) { << " values, expecting " << sample_type_len; return false; } - for (uint64 location_id : sample.location_id()) { + for (uint64_t location_id : sample.location_id()) { if (location_id == 0) { LOG(ERROR) << "Sample referencing location_id=0"; return false; @@ -210,8 +211,8 @@ bool Builder::CheckValid(const Profile &profile) { } for (const auto &label : sample.label()) { - int64 str = label.str(); - int64 num = label.num(); + int64_t str = label.str(); + int64_t num = label.num(); if (str != 0 && num != 0) { LOG(ERROR) << "One of str/num must be unset, got " << str << "," << num; return false; @@ -231,10 +232,10 @@ bool Builder::Finalize() { for (auto &sample : *profile_->mutable_sample()) { // Copy sample locations into a temp vector, and then clear and // repopulate it with the corresponding location IDs. - const RepeatedField addresses = sample.location_id(); + const RepeatedField addresses = sample.location_id(); sample.clear_location_id(); - for (uint64 address : addresses) { - int64 index = address_to_id.size() + 1; + for (uint64_t address : addresses) { + int64_t index = address_to_id.size() + 1; const auto inserted = address_to_id.emplace(address, index); if (inserted.second) { auto loc = profile_->add_location(); @@ -248,7 +249,7 @@ bool Builder::Finalize() { // Look up location address on mapping ranges. if (profile_->mapping_size() > 0) { - std::map > mapping_map; + std::map > mapping_map; for (const auto &mapping : profile_->mapping()) { mapping_map[mapping.memory_start()] = std::make_pair(mapping.memory_limit(), mapping.id()); @@ -262,8 +263,8 @@ bool Builder::Finalize() { continue; } mapping--; - uint64 limit = mapping->second.first; - uint64 id = mapping->second.second; + uint64_t limit = mapping->second.first; + uint64_t id = mapping->second.second; if (loc.address() <= limit) { loc.set_mapping_id(id); diff --git a/third_party/perftools/profiles/proto/builder.h b/third_party/perftools/profiles/proto/builder.h index 256174cf3..4f1cb9b5e 100644 --- a/third_party/perftools/profiles/proto/builder.h +++ b/third_party/perftools/profiles/proto/builder.h @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -69,13 +70,13 @@ class Builder { // Adds a string to the profile string table if not already present. // Returns a unique integer id for this string. - int64 StringId(const char *str); + int64_t StringId(const char *str); // Adds a function with these attributes to the profile function // table, if not already present. Returns a unique integer id for // this function. - uint64 FunctionId(const char *name, const char *system_name, - const char *file, int64 start_line); + uint64_t FunctionId(const char *name, const char *system_name, + const char *file, int64_t start_line); // Adds mappings for the currently running binary to the profile. void AddCurrentMappings(); From 62d3f440127f4a4d64d4e3970b76e1332c5cb259 Mon Sep 17 00:00:00 2001 From: cloud-profiler-team Date: Mon, 1 Mar 2021 12:25:47 -0800 Subject: [PATCH 09/11] Allow passing profile duration when building contention profile PiperOrigin-RevId: 360250469 --- third_party/javaprofiler/profile_proto_builder.cc | 4 ++-- third_party/javaprofiler/profile_proto_builder.h | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/third_party/javaprofiler/profile_proto_builder.cc b/third_party/javaprofiler/profile_proto_builder.cc index 2a714e290..9f764406d 100644 --- a/third_party/javaprofiler/profile_proto_builder.cc +++ b/third_party/javaprofiler/profile_proto_builder.cc @@ -495,11 +495,11 @@ std::unique_ptr ProfileProtoBuilder::ForCpu( std::unique_ptr ProfileProtoBuilder::ForContention( JNIEnv *jni_env, jvmtiEnv *jvmti_env, int64 sampling_rate, - ProfileFrameCache *cache) { + int64 duration_nanos, ProfileFrameCache *cache) { CHECK (cache != nullptr) << "Contention profiles may have native frames, cache must be provided"; return std::unique_ptr(new ContentionProfileProtoBuilder( - jni_env, jvmti_env, sampling_rate, cache)); + jni_env, jvmti_env, sampling_rate, duration_nanos, cache)); } } // namespace javaprofiler diff --git a/third_party/javaprofiler/profile_proto_builder.h b/third_party/javaprofiler/profile_proto_builder.h index 8e17ce22b..ec29c5f4a 100644 --- a/third_party/javaprofiler/profile_proto_builder.h +++ b/third_party/javaprofiler/profile_proto_builder.h @@ -249,8 +249,8 @@ class ProfileProtoBuilder { ProfileFrameCache *cache); static std::unique_ptr ForContention( - JNIEnv *jni_env, jvmtiEnv *jvmti_env, int64 sampling_rate, - ProfileFrameCache *cache); + JNIEnv *jni_env, jvmtiEnv *jvmti_env, int64 duration_ns, + int64 sampling_rate, ProfileFrameCache *cache); protected: struct SampleType { @@ -433,11 +433,13 @@ class HeapProfileProtoBuilder : public ProfileProtoBuilder { class ContentionProfileProtoBuilder : public ProfileProtoBuilder { public: ContentionProfileProtoBuilder(JNIEnv *jni_env, jvmtiEnv *jvmti_env, - int64 sampling_rate, ProfileFrameCache *cache) + int64 duration_nanos, int64 sampling_rate, + ProfileFrameCache *cache) : ProfileProtoBuilder( jni_env, jvmti_env, cache, sampling_rate, ProfileProtoBuilder::SampleType("contentions", "count"), ProfileProtoBuilder::SampleType("delay", "microseconds")) { + builder_.mutable_profile()->set_duration_nanos(duration_nanos); builder_.mutable_profile()->set_period(sampling_rate); } From d634f555f367a02f78a7f02035eb0614904891af Mon Sep 17 00:00:00 2001 From: aalexand Date: Mon, 17 May 2021 08:39:22 -0700 Subject: [PATCH 10/11] Reset thread-local JNIEnv on thread end. PiperOrigin-RevId: 374203540 --- src/entry.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/entry.cc b/src/entry.cc index 2809a648c..24c20fd6f 100644 --- a/src/entry.cc +++ b/src/entry.cc @@ -60,6 +60,7 @@ static void JNICALL OnThreadEnd(jvmtiEnv *jvmti_env, JNIEnv *jni_env, IMPLICITLY_USE(jvmti_env); IMPLICITLY_USE(jni_env); IMPLICITLY_USE(thread); + google::javaprofiler::Accessors::SetCurrentJniEnv(nullptr); threads->UnregisterCurrent(); } From 51cb62a2c2a5442e6a3cf63ccf2e82285d9bf21a Mon Sep 17 00:00:00 2001 From: cloud-profiler-team Date: Tue, 25 May 2021 15:28:48 -0700 Subject: [PATCH 11/11] Internal changes PiperOrigin-RevId: 375809119 --- src/throttler_api.cc | 73 +++++++++++++------------------------------- src/throttler_api.h | 29 +++++++++--------- src/worker.cc | 42 ++++++++++++++++++++++++- 3 files changed, 76 insertions(+), 68 deletions(-) diff --git a/src/throttler_api.cc b/src/throttler_api.cc index 2eb0f05a8..cd0d10996 100644 --- a/src/throttler_api.cc +++ b/src/throttler_api.cc @@ -32,7 +32,6 @@ #include "grpcpp/create_channel.h" #include "grpcpp/security/credentials.h" #include "grpcpp/support/channel_arguments.h" -#include "third_party/javaprofiler/heap_sampler.h" // API curated profiling configuration. DEFINE_string(cprof_api_address, "cloudprofiler.googleapis.com", @@ -48,11 +47,10 @@ DEFINE_bool(cprof_use_insecure_creds_for_testing, false, namespace cloud { namespace profiler { +namespace { namespace api = google::devtools::cloudprofiler::v2; -namespace { - // Initial value for backoffs where the duration is not server-guided. const int64_t kBackoffNanos = 60 * kNanosPerSecond; // 1 minute // Backoff envelope exponential growth factor. @@ -99,7 +97,7 @@ grpc_ssl_roots_override_result OverrideSSLRoots(char** pem_root_certs) { // Creates the profiler gRPC API stub. Returns nullptr on error. std::unique_ptr -NewProfilerServiceStub(const std::string& addr) { +NewProfilerServiceStub(const std::string& addr, const std::string& language) { std::shared_ptr creds; if (FLAGS_cprof_use_insecure_creds_for_testing) { creds = grpc::InsecureChannelCredentials(); @@ -114,7 +112,8 @@ NewProfilerServiceStub(const std::string& addr) { grpc::ChannelArguments channel_arguments; channel_arguments.SetUserAgentPrefix( - "gcloud-java-profiler/" + std::string(CLOUD_PROFILER_AGENT_VERSION)); + "gcloud-" + language + "-profiler/" + + std::string(CLOUD_PROFILER_AGENT_VERSION)); std::shared_ptr ch = grpc::CreateCustomChannel(addr, creds, channel_arguments); @@ -162,7 +161,7 @@ bool AbortedBackoffDuration(const grpc::ClientContext& ctx, // Initializes deployment information from environment properties and label // string in "name1=val1,name2=val2,..." format. Returns false on error. bool InitializeDeployment(CloudEnv* env, const std::string& labels, - api::Deployment* d) { + const std::string& language, api::Deployment* d) { std::string project_id = env->ProjectID(); if (project_id.empty()) { LOG(ERROR) << "Project ID is unknown"; @@ -199,7 +198,7 @@ bool InitializeDeployment(CloudEnv* env, const std::string& labels, label_kvs[kZoneNameLabel] = zone_name; } - label_kvs[kLanguageLabel] = "java"; + label_kvs[kLanguageLabel] = language; for (const auto& kv : label_kvs) { (*d->mutable_labels())[kv.first] = kv.second; } @@ -249,27 +248,30 @@ bool IsValidServiceName(std::string s) { return true; } -APIThrottler::APIThrottler(JNIEnv* jni) - : APIThrottler(DefaultCloudEnv(), DefaultClock(), nullptr, jni) {} +APIThrottler::APIThrottler( + const std::vector& types, + const std::string& language, const std::string& language_version) + : APIThrottler(types, language, language_version, DefaultCloudEnv(), + DefaultClock(), nullptr) {} APIThrottler::APIThrottler( + const std::vector& types, + const std::string& language, const std::string& language_version, CloudEnv* env, Clock* clock, std::unique_ptr - stub, - JNIEnv* jni) - : env_(env), + stub) + : types_(types), + language_(language), + language_version_(language_version), + env_(env), clock_(clock), stub_(std::move(stub)), - types_({api::CPU, api::WALL}), - java_version_(JavaVersion(jni)), creation_backoff_envelope_ns_(kBackoffNanos), closed_(false) { grpc_init(); gpr_set_log_function(GRPCLog); - LOG(INFO) << "Java version: " << java_version_; - // Create a random number generator. gen_ = std::default_random_engine(clock_->Now().tv_nsec / 1000); dist_ = std::uniform_int_distribution(0, kRandomRange); @@ -277,43 +279,10 @@ APIThrottler::APIThrottler( if (!stub_) { // Set in tests LOG(INFO) << "Will use profiler service " << FLAGS_cprof_api_address << " to create and upload profiles"; - stub_ = NewProfilerServiceStub(FLAGS_cprof_api_address); - } - - if (google::javaprofiler::HeapMonitor::Enabled()) { - LOG(INFO) << "Heap allocation sampling supported for this JDK"; - types_.push_back(api::HEAP); + stub_ = NewProfilerServiceStub(FLAGS_cprof_api_address, language_); } } -std::string APIThrottler::JavaVersion(JNIEnv* jni) { - const std::string kUnknownVersion = "unknown_version"; - - jclass system_class = jni->FindClass("java/lang/System"); - if (system_class == nullptr) { - return kUnknownVersion; - } - jmethodID get_property_method = jni->GetStaticMethodID( - system_class, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;"); - if (get_property_method == nullptr) { - return kUnknownVersion; - } - jstring jstr = reinterpret_cast(jni->CallStaticObjectMethod( - system_class, get_property_method, jni->NewStringUTF("java.version"))); - if (jstr == nullptr) { - return kUnknownVersion; - } - // Copy the returned value and release the memory allocated by JNI. - const char* s = jni->GetStringUTFChars(jstr, nullptr); - std::string ret = std::string(s); - jni->ReleaseStringUTFChars(jstr, s); - return ret; -} - -void APIThrottler::SetProfileTypes(const std::vector& types) { - types_ = types; -} - bool APIThrottler::WaitNext() { if (stub_ == nullptr) { LOG(ERROR) << "Profiler API is not initialized, stop profiling"; @@ -324,7 +293,7 @@ bool APIThrottler::WaitNext() { for (const auto& type : types_) { req.add_profile_type(type); } - if (!InitializeDeployment(env_, FLAGS_cprof_deployment_labels, + if (!InitializeDeployment(env_, FLAGS_cprof_deployment_labels, language_, req.mutable_deployment())) { LOG(ERROR) << "Failed to initialize deployment, stop profiling"; return false; @@ -442,7 +411,7 @@ void APIThrottler::ResetClientContext() { ctx_.reset(new grpc::ClientContext()); // NOLINT ctx_->AddMetadata("x-goog-api-client", "gccl/" + std::string(CLOUD_PROFILER_AGENT_VERSION) + - " gl-java/" + java_version_); + "gl-" + language_ + "/" + language_version_); if (closed_) { ctx_->TryCancel(); diff --git a/src/throttler_api.h b/src/throttler_api.h index 2f9618f1d..d087cc87f 100644 --- a/src/throttler_api.h +++ b/src/throttler_api.h @@ -39,19 +39,20 @@ namespace profiler { // Throttler implementation using the Cloud Profiler API. class APIThrottler : public Throttler { public: - explicit APIThrottler(JNIEnv* jni_env); + // Language should be one of "cpp" or "java", and language_version can be + // omitted if unknown. + explicit APIThrottler( + const std::vector& + types, + const std::string& language, const std::string& language_version = ""); // Testing-only constructor. - APIThrottler(CloudEnv* env, Clock* clock, + APIThrottler(const std::vector< + google::devtools::cloudprofiler::v2::ProfileType>& types, + const std::string& language, const std::string& language_version, + CloudEnv* env, Clock* clock, std::unique_ptr - stub, - JNIEnv* jni_env); - - // Set the list of supported profile types. The list is used in the profile - // creation call to the server to specify the supported types. - void SetProfileTypes( - const std::vector& - types); + stub); bool WaitNext() override; std::string ProfileType() override; @@ -69,18 +70,16 @@ class APIThrottler : public Throttler { // Resets the client gRPC context for the next call. void ResetClientContext(); - // Determines and returns Java version. - static std::string JavaVersion(JNIEnv* jni_env); - private: + const std::vector types_; + const std::string language_; + const std::string language_version_; CloudEnv* env_; Clock* clock_; std::unique_ptr< google::devtools::cloudprofiler::v2::grpc::ProfilerService::StubInterface> stub_; google::devtools::cloudprofiler::v2::Profile profile_; - std::vector types_; - const std::string java_version_; // Profile creation error handling. int64_t creation_backoff_envelope_ns_; diff --git a/src/worker.cc b/src/worker.cc index bf6ba64dd..08213d796 100644 --- a/src/worker.cc +++ b/src/worker.cc @@ -18,6 +18,7 @@ #include "src/profiler.h" #include "src/throttler_api.h" #include "src/throttler_timed.h" +#include "google/devtools/cloudprofiler/v2/profiler.grpc.pb.h" #include "third_party/javaprofiler/heap_sampler.h" DEFINE_bool(cprof_enabled, true, @@ -32,6 +33,35 @@ DEFINE_int32(cprof_wall_sampling_period_msec, 100, namespace cloud { namespace profiler { +namespace { + +namespace api = google::devtools::cloudprofiler::v2; + +std::string JavaVersion(JNIEnv *jni) { + const std::string kUnknownVersion = "unknown_version"; + + jclass system_class = jni->FindClass("java/lang/System"); + if (system_class == nullptr) { + return kUnknownVersion; + } + jmethodID get_property_method = jni->GetStaticMethodID( + system_class, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;"); + if (get_property_method == nullptr) { + return kUnknownVersion; + } + jstring jstr = reinterpret_cast(jni->CallStaticObjectMethod( + system_class, get_property_method, jni->NewStringUTF("java.version"))); + if (jstr == nullptr) { + return kUnknownVersion; + } + // Copy the returned value and release the memory allocated by JNI. + const char *s = jni->GetStringUTFChars(jstr, nullptr); + std::string ret = std::string(s); + jni->ReleaseStringUTFChars(jstr, s); + return ret; +} + +} // namespace std::atomic Worker::enabled_; @@ -44,10 +74,20 @@ void Worker::Start(JNIEnv *jni) { return; } + std::string java_version = JavaVersion(jni); + LOG(INFO) << "Java version: " << java_version; + std::vector types = { + api::CPU, api::WALL}; + if (google::javaprofiler::HeapMonitor::Enabled()) { + LOG(INFO) << "Heap allocation sampling supported for this JDK"; + types.push_back(api::HEAP); + } + // Initialize the throttler here rather in the constructor, since the // constructor is invoked too early, before the heap profiler is initialized. throttler_ = FLAGS_cprof_profile_filename.empty() - ? std::unique_ptr(new APIThrottler(jni)) + ? std::unique_ptr( + new APIThrottler(types, "java", java_version)) : std::unique_ptr( new TimedThrottler(FLAGS_cprof_profile_filename));