Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 31 additions & 14 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,34 @@
#
# Base image
#
FROM debian:jessie
FROM ubuntu:trusty

#
# Dependencies
#

# Install JDK 11 as sampling heap profiler depends on the new JVMTI APIs.
RUN apt-get update && apt-get install -y software-properties-common
RUN add-apt-repository -y ppa:openjdk-r/ppa
RUN apt-get update

# Installing openjdk-11-jdk-headless can be very slow due to repo download
# speed.

# Everything we can get through apt-get
RUN apt-get update && apt-get -y -q install \
apt-utils \
autoconf \
automake \
cmake \
curl \
g++ \
git \
libcurl4-openssl-dev \
libssl-dev \
libtool \
make \
openjdk-7-jdk \
openjdk-11-jdk-headless \
python \
unzip \
zlib1g-dev
Expand All @@ -47,13 +57,6 @@ RUN git clone --depth=1 -b curl-7_55_1 https://github.com/curl/curl.git /tmp/cur
make -j && make install && \
cd ~ && rm -rf /tmp/curl

# cmake
RUN git clone -b v3.6.2 https://github.com/Kitware/CMake.git /tmp/cmake && \
cd /tmp/cmake && \
./bootstrap && \
make -j && make install && \
cd ~ && rm -rf /tmp/cmake

# gflags
RUN git clone --depth=1 -b v2.1.2 https://github.com/gflags/gflags.git /tmp/gflags && \
cd /tmp/gflags && \
Expand Down Expand Up @@ -90,12 +93,26 @@ RUN mkdir /tmp/openssl && cd /tmp/openssl && \
# process of gRPC puts the OpenSSL static object files into the gRPC archive
# which causes link errors later when the agent is linked with the static
# OpenSSL library itself.
RUN git clone --depth=1 --recursive -b v1.15.0 https://github.com/grpc/grpc.git /tmp/grpc && \
RUN git clone --depth=1 --recursive -b v1.21.0 https://github.com/grpc/grpc.git /tmp/grpc && \
cd /tmp/grpc && \

# TODO: Remove patch when GKE Istio versions support HTTP 1.0 or
# gRPC http_cli supports HTTP 1.1
# This sed command is needed until GKE provides Istio 1.1+ exclusively which
# supports HTTP 1.0.
sed -i 's/1\.0/1.1/g' src/core/lib/http/format_request.cc && \

# TODO: Remove patch when GKE Istio versions support unambiguous
# FQDNs in rule sets.
# https://github.com/istio/istio/pull/14405 is merged but wait till GKE
# Istio versions includes this PR
sed -i 's/metadata\.google\.internal\./metadata.google.internal/g' src/core/lib/security/credentials/google_default/google_default_credentials.cc && \
sed -i 's/metadata\.google\.internal\./metadata.google.internal/g' src/core/lib/security/credentials/credentials.h && \
cd third_party/protobuf && \
./autogen.sh && ./configure --with-pic CXXFLAGS=-Os && make -j && make install && ldconfig && \
./autogen.sh && \
./configure --with-pic CXXFLAGS="$(pkg-config --cflags protobuf)" LIBS="$(pkg-config --libs protobuf)" LDFLAGS="-Wl,--no-as-needed" && \
make -j && make install && ldconfig && \
cd ../.. && \
CPPFLAGS="-I /usr/local/ssl/include" make -j CONFIG=opt EMBED_OPENSSL=false V=1 HAS_SYSTEM_OPENSSL_NPN=0 && \
CPPFLAGS="-I /usr/local/ssl/include" make CONFIG=opt EMBED_OPENSSL=false V=1 HAS_SYSTEM_OPENSSL_NPN=0 install && \
CPPFLAGS="-I /usr/local/ssl/include" LDFLAGS="-Wl,--no-as-needed" make -j CONFIG=opt EMBED_OPENSSL=false V=1 HAS_SYSTEM_OPENSSL_NPN=0 && \
CPPFLAGS="-I /usr/local/ssl/include" LDFLAGS="-Wl,--no-as-needed" make CONFIG=opt EMBED_OPENSSL=false V=1 HAS_SYSTEM_OPENSSL_NPN=0 install && \
rm -rf /tmp/grpc

15 changes: 13 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ CFLAGS = \
-g0 \
-DSTANDALONE_BUILD \
-D_GNU_SOURCE \
-DENABLE_HEAP_SAMPLING

SRC_ROOT_PATH=.

JAVA_PATH ?= /usr/lib/jvm/java-7-openjdk-amd64
JAVA_PATH ?= /usr/lib/jvm/java-11-openjdk-amd64
PROTOC ?= /usr/local/bin/protoc
PROTOC_GEN_GRPC ?= /usr/local/bin/grpc_cpp_plugin

Expand Down Expand Up @@ -70,23 +71,33 @@ PROFILER_API_SOURCES = \
$(GENFILES_PATH)/google/rpc/error_details.pb.cc \

JAVAPROFILER_LIB_SOURCES = \
$(JAVAPROFILER_LIB_PATH)/accessors.cc \
$(JAVAPROFILER_LIB_PATH)/async_ref_counted_string.cc \
$(JAVAPROFILER_LIB_PATH)/clock.cc \
$(JAVAPROFILER_LIB_PATH)/display.cc \
$(JAVAPROFILER_LIB_PATH)/heap_sampler.cc \
$(JAVAPROFILER_LIB_PATH)/native.cc \
$(JAVAPROFILER_LIB_PATH)/profile_proto_builder.cc \
$(JAVAPROFILER_LIB_PATH)/stacktrace_fixer.cc \
$(JAVAPROFILER_LIB_PATH)/stacktraces.cc \
$(JAVAPROFILER_LIB_PATH)/tags.cc \

# Add any header not already as a .cc in JAVAPROFILER_LIB_SOURCES.
JAVAPROFILER_LIB_HEADERS = \
$(JAVAPROFILER_LIB_PATH)/accessors.h \
$(JAVAPROFILER_LIB_PATH)/async_ref_counted_string.h \
$(JAVAPROFILER_LIB_PATH)/heap_sampler.h \
$(JAVAPROFILER_LIB_PATH)/jvmti_error.h \
$(JAVAPROFILER_LIB_PATH)/profile_proto_builder.h \
$(JAVAPROFILER_LIB_PATH)/stacktrace_decls.h \
$(JAVAPROFILER_LIB_PATH)/stacktraces.h \
$(JAVAPROFILER_LIB_PATH)/tags.h \

SOURCES = \
$(JAVA_AGENT_PATH)/cloud_env.cc \
$(JAVA_AGENT_PATH)/config_dataflow_jni.cc \
$(JAVA_AGENT_PATH)/entry.cc \
$(JAVA_AGENT_PATH)/http.cc \
$(JAVA_AGENT_PATH)/jni.cc \
$(JAVA_AGENT_PATH)/pem_roots.cc \
$(JAVA_AGENT_PATH)/profiler.cc \
$(JAVA_AGENT_PATH)/proto.cc \
Expand Down
65 changes: 48 additions & 17 deletions src/cloud_env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,19 @@
#include <sstream>
#include <string>

#include "src/clock.h"
#include "src/http.h"
#include "src/string.h"

DEFINE_int32(cprof_gce_metadata_server_retry_count, 3,
"Number of retries to Google Compute Engine metadata host");
DEFINE_int32(
cprof_gce_metadata_server_retry_sleep_sec, 1,
"Seconds to sleep between retries to Google Compute Engine metadata host");
DEFINE_string(cprof_gce_metadata_server_address, "169.254.169.254:80",
"Google Compute Engine metadata host to use");

DEFINE_string(cprof_access_token_test_only, "",
"override OAuth2 access token for testing");

DEFINE_string(cprof_project_id, "", "cloud project ID");
DEFINE_string(cprof_zone_name, "", "zone name");
DEFINE_string(cprof_service, "", "deployment service name");
Expand All @@ -47,23 +51,40 @@ const char kNoData[] = "";
namespace {

string GceMetadataRequest(HTTPRequest* req, const string& path) {
Clock* clock = DefaultClock();
req->AddHeader("Metadata-Flavor", "Google");
req->SetTimeout(2); // seconds

string url = FLAGS_cprof_gce_metadata_server_address + path, resp;
if (!req->DoGet(url, &resp)) {
LOG(ERROR) << "Error making HTTP request to the GCE metadata server";
return kNoData;
}

int resp_code = req->GetResponseCode();
if (resp_code != kHTTPStatusOK) {
LOG(ERROR) << "Request to the GCE metadata server failed, status code: "
<< resp_code;
return kNoData;
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++) {
if (!req->DoGet(url, &resp)) {
if (i < retry_count) {
LOG(ERROR) << "Error making HTTP request for " << url
<< " to the GCE metadata server. "
<< "Will retry in " << retry_ts.tv_sec << "s";
clock->SleepFor(retry_ts);
} else {
LOG(ERROR) << "Error making HTTP request for " << url
<< " to the GCE metadata server.";
}
continue;
}
int resp_code = req->GetResponseCode();
if (resp_code != kHTTPStatusOK) {
LOG(ERROR) << "Request to the GCE metadata server for " << url
<< " failed, status code: " << resp_code;
return kNoData;
}
return resp;
}

return resp;
LOG(ERROR) << "Unable to contact GCE metadata server for " << url << " after "
<< retry_count << " retries.";
return kNoData;
}

const char* Getenv(const string& var) {
Expand All @@ -82,15 +103,25 @@ CloudEnv::CloudEnv() {
} else if (!FLAGS_cprof_target.empty()) {
service_ = FLAGS_cprof_target;
} else {
const char* val = Getenv("GAE_SERVICE");
service_ = val == nullptr ? "" : val;
for (const string& env_var : {"GAE_SERVICE", "K_SERVICE"}) {
const char* val = Getenv(env_var);
if (val != nullptr) {
service_ = val;
break;
}
}
}

if (!FLAGS_cprof_service_version.empty()) {
service_version_ = FLAGS_cprof_service_version;
} else {
const char* val = Getenv("GAE_VERSION");
service_version_ = val == nullptr ? "" : val;
for (const string& env_var : {"GAE_VERSION", "K_REVISION"}) {
const char* val = Getenv(env_var);
if (val != nullptr) {
service_version_ = val;
break;
}
}
}

if (!FLAGS_cprof_project_id.empty()) {
Expand Down
10 changes: 5 additions & 5 deletions src/cloud_profiler_java_agent.lds
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ VERS_1.0 {
global:
Agent_OnLoad;
Agent_OnUnload;
Java_com_google_cloud_dataflow_worker_profiler_Profiler_disable;
Java_com_google_cloud_dataflow_worker_profiler_Profiler_enable;
Java_com_google_cloud_dataflow_worker_profiler_Profiler_getAttribute;
Java_com_google_cloud_dataflow_worker_profiler_Profiler_registerAttribute;
Java_com_google_cloud_dataflow_worker_profiler_Profiler_setAttribute;
Java_org_apache_beam_runners_dataflow_worker_profiler_Profiler_disable;
Java_org_apache_beam_runners_dataflow_worker_profiler_Profiler_enable;
Java_org_apache_beam_runners_dataflow_worker_profiler_Profiler_getAttribute;
Java_org_apache_beam_runners_dataflow_worker_profiler_Profiler_registerAttribute;
Java_org_apache_beam_runners_dataflow_worker_profiler_Profiler_setAttribute;
local:
*;
};
55 changes: 39 additions & 16 deletions src/entry.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@

#include "src/string.h"
#include "src/worker.h"
#include "third_party/javaprofiler/accessors.h"
#include "third_party/javaprofiler/globals.h"
#include "third_party/javaprofiler/heap_sampler.h"
#include "third_party/javaprofiler/stacktraces.h"

DEFINE_bool(cprof_cpu_use_per_thread_timers, false,
Expand All @@ -28,6 +30,10 @@ DEFINE_bool(cprof_force_debug_non_safepoints, true,
"when true, force DebugNonSafepoints flag by subscribing to the"
"code generation events. This improves the accuracy of profiles,"
"but may incur a bit of overhead.");
DEFINE_bool(cprof_enable_heap_sampling, false,
"when unset, heap allocation sampling is disabled");
DEFINE_int32(cprof_heap_sampling_interval, 512 * 1024,
"sampling interval for heap allocation sampling, 512k by default");

namespace cloud {
namespace profiler {
Expand Down Expand Up @@ -111,6 +117,12 @@ void JNICALL OnVMInit(jvmtiEnv *jvmti, JNIEnv *jni_env, jthread thread) {
jclass klass = class_list[i];
CreateJMethodIDsForClass(jvmti, klass);
}

if (FLAGS_cprof_enable_heap_sampling) {
google::javaprofiler::HeapMonitor::Enable(
jvmti, jni_env, FLAGS_cprof_heap_sampling_interval);
}

worker->Start(jni_env);
}

Expand All @@ -132,9 +144,13 @@ void JNICALL OnVMDeath(jvmtiEnv *jvmti_env, JNIEnv *jni_env) {
worker->Stop();
delete worker;
worker = NULL;

if (google::javaprofiler::HeapMonitor::Enabled()) {
google::javaprofiler::HeapMonitor::Disable();
}
}

static bool PrepareJvmti(jvmtiEnv *jvmti) {
static bool PrepareJvmti(JavaVM *vm, jvmtiEnv *jvmti) {
LOG(INFO) << "Prepare JVMTI";

// Set the list of permissions to do the various internal VM things
Expand Down Expand Up @@ -175,20 +191,23 @@ static bool PrepareJvmti(jvmtiEnv *jvmti) {
return false;
}
}

return true;
}

static bool RegisterJvmti(jvmtiEnv *jvmti) {
// Create the list of callbacks to be called on given events.
jvmtiEventCallbacks *callbacks = new jvmtiEventCallbacks();
memset(callbacks, 0, sizeof(jvmtiEventCallbacks));
jvmtiEventCallbacks callbacks;
memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));

callbacks->ThreadStart = &OnThreadStart;
callbacks->ThreadEnd = &OnThreadEnd;
callbacks->VMInit = &OnVMInit;
callbacks->VMDeath = &OnVMDeath;
callbacks->ClassLoad = &OnClassLoad;
callbacks->ClassPrepare = &OnClassPrepare;
callbacks.ThreadStart = &OnThreadStart;
callbacks.ThreadEnd = &OnThreadEnd;
callbacks.VMInit = &OnVMInit;
callbacks.VMDeath = &OnVMDeath;
callbacks.ClassLoad = &OnClassLoad;
callbacks.ClassPrepare = &OnClassPrepare;

google::javaprofiler::HeapMonitor::AddCallback(&callbacks);

std::vector<jvmtiEvent> events = {
JVMTI_EVENT_CLASS_LOAD, JVMTI_EVENT_CLASS_PREPARE,
Expand All @@ -197,12 +216,12 @@ static bool RegisterJvmti(jvmtiEnv *jvmti) {
};

if (FLAGS_cprof_force_debug_non_safepoints) {
callbacks->CompiledMethodLoad = &OnCompiledMethodLoad;
callbacks.CompiledMethodLoad = &OnCompiledMethodLoad;
events.push_back(JVMTI_EVENT_COMPILED_METHOD_LOAD);
}

JVMTI_ERROR_1(
(jvmti->SetEventCallbacks(callbacks, sizeof(jvmtiEventCallbacks))),
(jvmti->SetEventCallbacks(&callbacks, sizeof(jvmtiEventCallbacks))),
false);

// Enable the callbacks to be triggered when the events occur.
Expand Down Expand Up @@ -248,16 +267,21 @@ jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {
ParseArguments(options); // Initializes logger -- do not log before this call
LOG(INFO) << "Profiler agent loaded";

google::javaprofiler::Accessors::Init();
google::javaprofiler::AttributeTable::Init();

if ((err = (vm->GetEnv(reinterpret_cast<void **>(&jvmti), JVMTI_VERSION))) !=
JNI_OK) {
// Try to get the latest JVMTI_VERSION the agent was built with.
err = vm->GetEnv(reinterpret_cast<void **>(&jvmti), JVMTI_VERSION);
if (err == JNI_EVERSION) {
// The above call can fail if the VM is actually from an older VM, therefore
// try to get an older JVMTI (compatible with JDK8).
err = (vm->GetEnv(reinterpret_cast<void **>(&jvmti), JVMTI_VERSION_1_2));
}
if (err != JNI_OK) {
LOG(ERROR) << "JNI Error " << err;
return 1;
}

if (!PrepareJvmti(jvmti)) {
if (!PrepareJvmti(vm, jvmti)) {
LOG(ERROR) << "Failed to initialize JVMTI. Continuing...";
return 0;
}
Expand Down Expand Up @@ -285,7 +309,6 @@ jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {

void JNICALL Agent_OnUnload(JavaVM *vm) {
IMPLICITLY_USE(vm);
google::javaprofiler::Accessors::Destroy();
}

} // namespace profiler
Expand Down
1 change: 1 addition & 0 deletions src/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

#include "third_party/javaprofiler/jvmti_error.h"
#include "third_party/javaprofiler/stacktraces.h"

#include <glog/logging.h>

#include <string>
Expand Down
1 change: 1 addition & 0 deletions src/http.cc
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ void HTTPRequest::SetTimeout(int timeout_sec) {

bool HTTPRequest::DoGet(const string& url, string *data) {
data->clear();
curl_easy_setopt(curl_, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_easy_setopt(curl_, CURLOPT_WRITEDATA, data);
curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, ResponseCallback);
return DoRequest(url);
Expand Down
Loading