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
39 changes: 16 additions & 23 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,30 @@ RUN apt-get update && apt-get -y -q install \
curl \
g++ \
git \
libcurl4-openssl-dev \
libssl-dev \
libtool \
make \
openjdk-11-jdk-headless \
python \
unzip \
zlib1g-dev

# openssl
# This openssl (compiled with -fPIC) is used to statically link into the agent
# shared library.
ENV PKG_CONFIG_PATH=/usr/local/ssl/lib/pkgconfig
RUN mkdir /tmp/openssl && cd /tmp/openssl && \
curl -sL https://github.com/openssl/openssl/archive/OpenSSL_1_1_1f.tar.gz | \
tar xzv --strip=1 && \
./config no-shared -fPIC --openssldir=/usr/local/ssl --prefix=/usr/local/ssl && \
make && make install_sw && \
cd ~ && rm -rf /tmp/openssl

# curl
RUN git clone --depth=1 -b curl-7_55_1 https://github.com/curl/curl.git /tmp/curl && \
RUN git clone --depth=1 -b curl-7_66_0 https://github.com/curl/curl.git /tmp/curl && \
cd /tmp/curl && \
./buildconf && \
./configure --disable-ldap --disable-shared --without-libssh2 \
--without-librtmp --without-libidn --enable-static \
--with-pic --with-ssl && \
--with-pic --with-ssl=/usr/local/ssl/ && \
make -j && make install && \
cd ~ && rm -rf /tmp/curl

Expand All @@ -72,34 +80,19 @@ RUN mkdir /tmp/glog && cd /tmp/glog && \
make -j && make install && \
cd ~ && rm -rf /tmp/glog

# openssl
# This openssl (compiled with -fPIC) is used to statically link into the agent
# shared library. We still install libssl-dev above since getting libcurl to
# link with the custom version is tricky, see
# http://askubuntu.com/questions/475670/how-to-build-curl-with-the-latest-openssl.
# Also, intentionally not using -j as OpenSSL < 1.1.0 not quite supporting the
# parallel builds, see
# https://github.com/openssl/openssl/issues/5762#issuecomment-376622684.
RUN mkdir /tmp/openssl && cd /tmp/openssl && \
curl -sL https://www.openssl.org/source/openssl-1.0.2o.tar.gz | \
tar xzv --strip=1 && \
./config no-shared -fPIC --openssldir=/usr/local/ssl && \
make && make install_sw && \
cd ~ && rm -rf /tmp/openssl

# gRPC & protobuf
# Use the protobuf version from gRPC for everything to avoid conflicting
# versions to be linked in. Disable OpenSSL embedding: when it's on, the build
# 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.25.0 https://github.com/grpc/grpc.git /tmp/grpc && \
RUN git clone --depth=1 --recursive -b v1.28.1 https://github.com/grpc/grpc.git /tmp/grpc && \
cd /tmp/grpc && \
cd third_party/protobuf && \
./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" 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 && \
CPPFLAGS="-I /usr/local/ssl/include" LDFLAGS="-L /usr/local/ssl/lib/ -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="-L /usr/local/ssl/lib/ -Wl,--no-as-needed" make CONFIG=opt EMBED_OPENSSL=false V=1 HAS_SYSTEM_OPENSSL_NPN=0 install && \
rm -rf /tmp/grpc
96 changes: 96 additions & 0 deletions Dockerfile.alpine
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

#
# Base image
#
FROM alpine:3.11

#
# Dependencies
#

# Everything we can get through apt-get
RUN apk --no-cache add \
autoconf \
automake \
cmake \
curl \
g++ \
git \
libexecinfo-dev \
libexecinfo-static \
libtool \
linux-headers \
make \
nghttp2-static \
unzip \
zlib-dev

# Install JDK 11 as sampling heap profiler depends on the new JVMTI APIs.
RUN apk --no-cache add openjdk11-jdk

# openssl
# This openssl (compiled with -fPIC) is used to statically link into the agent
# shared library.
ENV PKG_CONFIG_PATH=/usr/local/ssl/lib/pkgconfig
ENV JAVA_PATH=/usr/lib/jvm/java-11-openjdk/
RUN mkdir /tmp/openssl && cd /tmp/openssl && \
curl -sL https://github.com/openssl/openssl/archive/OpenSSL_1_1_1f.tar.gz | \
tar xzv --strip=1 && \
./config no-shared -fPIC --openssldir=/usr/local/ssl --prefix=/usr/local/ssl && \
make && make install_sw && \
cd ~ && rm -rf /tmp/openssl

# curl
RUN git clone --depth=1 -b curl-7_66_0 https://github.com/curl/curl.git /tmp/curl && \
cd /tmp/curl && \
./buildconf && \
./configure --disable-ldap --disable-shared --without-libssh2 \
--without-librtmp --without-libidn --enable-static \
--with-pic --with-ssl=/usr/local/ssl/ && \
make -j && make install && \
cd ~ && rm -rf /tmp/curl

# gflags
RUN git clone --depth=1 -b v2.1.2 https://github.com/gflags/gflags.git /tmp/gflags && \
cd /tmp/gflags && \
mkdir build && cd build && \
cmake -DCMAKE_CXX_FLAGS=-fpic -DGFLAGS_NAMESPACE=google .. && \
make -j && make install && \
cd ~ && rm -rf /tmp/gflags

# google-glog
RUN mkdir /tmp/glog && cd /tmp/glog && \
curl -sL https://github.com/google/glog/archive/v0.3.5.tar.gz | \
tar xzv --strip=1 && LDFLAGS="-lexecinfo" ./configure --with-pic --enable-static && \
make -j && make install && \
cd ~ && rm -rf /tmp/glog

# gRPC & protobuf
# Use the protobuf version from gRPC for everything to avoid conflicting
# versions to be linked in. Disable OpenSSL embedding: when it's on, the build
# 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.28.1 https://github.com/grpc/grpc.git /tmp/grpc && \
cd /tmp/grpc && \
cd third_party/protobuf && \
./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" LDFLAGS="-L /usr/local/ssl/lib/ -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="-L /usr/local/ssl/lib/ -Wl,--no-as-needed" make CONFIG=opt EMBED_OPENSSL=false V=1 HAS_SYSTEM_OPENSSL_NPN=0 install && \
rm -rf /tmp/grpc
15 changes: 15 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,21 @@ GRPC_LIBS= \
$(LIB_ROOT_PATH)/lib/libgrpc.a \
$(LIB_ROOT_PATH)/lib/libgpr.a \

# Detect if running on Alpine and modify various flags
ifeq ("$(wildcard /etc/alpine-release)","/etc/alpine-release")
# musl only supports global dynamic tls model, and is documented as
# async-signal-safe. See
# https://wiki.musl-libc.org/design-concepts.html#Thread-local-storage
# This selects global-dynamic TLS model in
# third_party/javaprofiler/accessors.h
CFLAGS += -DALPINE

LIBS1 += /usr/lib/libexecinfo.a

# libgcc is not installed by default on Alpine.
LDFLAGS += -static-libgcc
endif

all: \
$(TARGET_AGENT) \
$(TARGET_NOTICES) \
Expand Down
24 changes: 18 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Stackdriver Profiler Java Agent
# Google Cloud Profiler Profiler Java Agent

This repository contains source code for the [Stackdriver
Profiler](https://cloud.google.com/profiler/) Java agent.
This repository contains source code for the
[Google Cloud Profiler Profiler](https://cloud.google.com/profiler/) Java agent.

## Installation

Expand All @@ -12,9 +12,9 @@ wget -q -O- https://storage.googleapis.com/cloud-profiler/java/latest/profiler_j
| sudo tar xzv -C /opt/cprof
```

See the [Stackdriver Profiler Java profiling
doc](https://cloud.google.com/profiler/docs/profiling-java) for detailed and
most up-to-date guide on installing and using the agent.
See the
[Google Cloud Profiler Profiler Java profiling doc](https://cloud.google.com/profiler/docs/profiling-java)
for detailed and most up-to-date guide on installing and using the agent.

## Build from Source

Expand All @@ -27,3 +27,15 @@ commands below.
$ cd cloud-profiler-java
$ ./build.sh
```

## To build for Alpine.

**Per thread timers are not available on Alpine.** Since SIGEV_THREAD_ID is not
supported by `timer_create` on Alpine, per thread timers are not implemented and
the flag `-cprof_cpu_use_per_thread_timers` is ignored on this platform.

```shell
$ git clone https://github.com/GoogleCloudPlatform/cloud-profiler-java.git
$ cd cloud-profiler-java
$ ./build.sh -a
```
22 changes: 17 additions & 5 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,14 @@ set -o nounset
# Command line arguments: [-d]
# -d: specify the temporary directory for the build.

while getopts ":d:" opt; do
ALPINE_BUILD="0"
DOCKERFILE="Dockerfile"
FILE_SUFFIX=""
while getopts ":ad:" opt; do
case $opt in
a)
ALPINE_BUILD="1"
;;
d)
BUILD_TEMP_DIR=$OPTARG
;;
Expand Down Expand Up @@ -57,8 +63,14 @@ trap "{ echo 'FAILED: see ${LOG_FILE} for details' ; exit 1; }" ERR

mkdir -p "${BUILD_TEMP_DIR}"

PrintMessage "Building the builder Docker container..."
docker build -t cprof-agent-builder . >> "${LOG_FILE}" 2>&1
if [[ "${ALPINE_BUILD}" = "1" ]]; then
PrintMessage "Building the builder Alpine Docker container..."
DOCKERFILE="Dockerfile.alpine"
FILE_SUFFIX="_alpine"
else
PrintMessage "Building the builder Docker container..."
fi
docker build -f "${DOCKERFILE}" -t cprof-agent-builder . >> "${LOG_FILE}" 2>&1

PrintMessage "Packaging the agent code..."
mkdir -p "${BUILD_TEMP_DIR}"/build
Expand All @@ -72,12 +84,12 @@ docker run -ti -v "${BUILD_TEMP_DIR}/build":/root/build \
>> "${LOG_FILE}" 2>&1

PrintMessage "Packaging the agent binaries..."
tar zcf "${BUILD_TEMP_DIR}"/profiler_java_agent.tar.gz \
tar zcf "${BUILD_TEMP_DIR}"/profiler_java_agent${FILE_SUFFIX}.tar.gz \
-C "${BUILD_TEMP_DIR}"/build/.out \
NOTICES profiler_java_agent.so \
>> "${LOG_FILE}" 2>&1

PrintMessage "Agent built and stored locally in: ${BUILD_TEMP_DIR}/profiler_java_agent.tar.gz"
PrintMessage "Agent built and stored locally in: ${BUILD_TEMP_DIR}/profiler_java_agent${FILE_SUFFIX}.tar.gz"

trap - EXIT
PrintMessage "SUCCESS"
3 changes: 1 addition & 2 deletions src/NOTICES
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Google Stackdriver Profiler
Google Cloud Profiler
Copyright Google Inc.
Use of this software is governed by the Google Cloud Platform Terms of Service
found at https://cloud.google.com/terms/
Expand Down Expand Up @@ -470,4 +470,3 @@ OpenSSL License
* copied and put under another distribution licence
* [including the GNU Public Licence.]
*/

2 changes: 1 addition & 1 deletion src/cloud_env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ std::string GceMetadataRequest(HTTPRequest* req, const std::string& path) {
}

const char* Getenv(const std::string& var) {
#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17)
#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17) || ALPINE == 1
return secure_getenv(var.c_str());
#else
return __secure_getenv(var.c_str());
Expand Down
24 changes: 16 additions & 8 deletions src/entry.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@

DEFINE_bool(cprof_cpu_use_per_thread_timers, false,
"when true, use per-thread CLOCK_THREAD_CPUTIME_ID timers; "
"only profiles Java threads, non-Java threads will be missed");
"only profiles Java threads, non-Java threads will be missed. "
"This flag is ignored on Alpine.");
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,"
Expand Down Expand Up @@ -267,7 +268,7 @@ jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {

ParseArguments(options); // Initializes logger -- do not log before this call

LOG(INFO) << "Stackdriver Profiler Java agent version: "
LOG(INFO) << "Google Cloud Profiler Java agent version: "
<< CLOUD_PROFILER_AGENT_VERSION;
LOG(INFO) << "Profiler agent loaded";
google::javaprofiler::AttributeTable::Init();
Expand All @@ -292,7 +293,16 @@ jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {
// The process exit will free the memory. See comments to the variable on why.
// Initialize before registering the JVMTI callbacks to avoid the unlikely
// race of getting thread events before the thread table is born.
#ifdef ALPINE
// musl does not support SIGEV_THREAD_ID. Disable per thread timers.
if (FLAGS_cprof_cpu_use_per_thread_timers) {
LOG(WARNING) << "Per thread timers not available in Alpine. "
<< "Ignoring '-cprof_cpu_use_per_thread_timers' flag.";
}
threads = new ThreadTable(false);
#else
threads = new ThreadTable(FLAGS_cprof_cpu_use_per_thread_timers);
#endif

if (!RegisterJvmti(jvmti)) {
LOG(ERROR) << "Failed to enable JVMTI events. Continuing...";
Expand All @@ -304,21 +314,19 @@ jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {

google::javaprofiler::Asgct::SetAsgct(
google::javaprofiler::Accessors::GetJvmFunction<
google::javaprofiler::ASGCTType>("AsyncGetCallTrace"));
google::javaprofiler::ASGCTType>("AsyncGetCallTrace"));

worker = new Worker(jvmti, threads);
return 0;
}

void JNICALL Agent_OnUnload(JavaVM *vm) {
IMPLICITLY_USE(vm);
}
void JNICALL Agent_OnUnload(JavaVM *vm) { IMPLICITLY_USE(vm); }

} // namespace profiler
} // namespace cloud

AGENTEXPORT jint JNICALL
Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {
AGENTEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options,
void *reserved) {
return cloud::profiler::Agent_OnLoad(vm, options, reserved);
}

Expand Down
5 changes: 5 additions & 0 deletions src/threads.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ namespace {
const timer_t kInvalidTimer = reinterpret_cast<timer_t>(-1LL);

timer_t CreateTimer(pid_t tid) {
#ifdef ALPINE
// Per thread timers are not available on Alpine.
return kInvalidTimer;
#else
struct sigevent sevp = {};
sevp.sigev_notify = SIGEV_THREAD_ID;
sevp._sigev_un._tid = tid;
Expand All @@ -38,6 +42,7 @@ timer_t CreateTimer(pid_t tid) {
return kInvalidTimer;
}
return timer;
#endif
}

bool SetTimer(timer_t timer, int64_t period_usec) {
Expand Down
Loading