Permalink
Browse files

Remove agent's dependency on Heapster.class

Add support to build Heapster to not depend on an external helper
class. This simplifies redistribution. In such a build specifying a
boot class path (-Xbootclasspath/a) is not necessary.

Signed-off-by: marius a. eriksen <marius@twitter.com>
  • Loading branch information...
1 parent 2cf62c4 commit 0274e597f77f500c7ded64348e3760c42b59a8ea Kaushik Srenevasan committed with Sep 17, 2012
Showing with 126 additions and 71 deletions.
  1. +2 −0 .gitignore
  2. +10 −2 Makefile
  3. +4 −4 README.md
  4. 0 generated/.generated
  5. +110 −65 heapster.cc
View
@@ -2,3 +2,5 @@
*.jnilib
*.o
*.so
+generated/*
+*.log
View
@@ -1,5 +1,9 @@
CC=gcc
OS=$(shell uname -s | tr '[A-Z]' '[a-z]')
+GENERATED=generated
+
+XXD=xxd
+XXD_OPTIONS=-i
ifeq ("$(OS)", "darwin")
JAVA_HOME=$(shell /usr/libexec/java_home)
@@ -15,12 +19,13 @@ endif
CFLAGS=-Ijava_crw_demo -fno-strict-aliasing \
-fPIC -fno-omit-frame-pointer -W -Wall -Wno-unused -Wno-parentheses \
- -I$(JAVA_HEADERS)
+ -I$(JAVA_HEADERS) -I$(GENERATED)
+
LDFLAGS=-fno-strict-aliasing -fPIC -fno-omit-frame-pointer \
-static-libgcc -shared
DEBUG=-g
-all: $(OBJ) Heapster.class
+all: Heapster.class $(OBJ)
$(OBJ): heapster.o sampler.o util.o java_crw_demo/java_crw_demo.o
g++ $(DEBUG) $(LDFLAGS) -o $@ $^ -lc
@@ -30,8 +35,11 @@ $(OBJ): heapster.o sampler.o util.o java_crw_demo/java_crw_demo.o
%.class: %.java
javac $<
+ $(XXD) $(XXD_OPTIONS) $@ > $(GENERATED)/$*-inl.h
clean:
rm -f *.o
rm -f $(OBJ)
rm -f java_crw_demo/*.o
+ rm -f $(GENERATED)/*
+ rm -f *.class
View
@@ -9,7 +9,7 @@ profiling in a production setting.
Currently it allows for profiling similar to the TCMalloc library,
e.g.:
- $ HEAPSTER_PROFILE=/tmp/OUT java -Xbootclasspath/a:. -agentlib:heapster Test
+ $ HEAPSTER_PROFILE=/tmp/OUT java -agentlib:heapster Test
$ pprof /tmp/OUT
Welcome to pprof! For help, type 'help'.
(pprof) top
@@ -26,10 +26,10 @@ This is still work in progress.
# Ostrich integration
-If you use [Ostrich](https://github.com/twitter/ostrich), and run your program with heapster, you can generate
-runtime heap profiles like so:
+If you use [Ostrich](https://github.com/twitter/ostrich), and run your
+program with heapster, you can generate runtime heap profiles like so:
$ curl 'localhost:9990/pprof/heap?pause=10&sample_period=1024' > /tmp/prof
-
+
This will collect heap growth for 10 seconds, with a sampling period
of 1kB.
View
No changes.
View
@@ -22,6 +22,8 @@
#include "sampler.h"
#include "util.h"
+#include <Heapster-inl.h>
+
// NB: only works on little-endian machines
// TODO
@@ -56,27 +58,27 @@ size_t AtomicIO(ssize_t (*f)(int, const void*, size_t),
const char* s = reinterpret_cast<const char*>(_s);
size_t pos = 0;
ssize_t res;
-
+
while (n > pos) {
res = (f) (fd, s + pos, n - pos);
switch (res) {
case -1:
if (errno == EINTR || errno == EAGAIN)
continue;
return 0;
-
- case 0:
+
+ case 0:
errno = EPIPE;
return pos;
-
- default:
+
+ default:
pos += (u_int)res;
}
}
-
+
return pos;
}
-
+
void AtomicWrite(int fd, const char* buf, int n) {
if (AtomicIO(write, fd, (const void*)buf, n) != (size_t)n) {
perror("AtomicWrite");
@@ -95,7 +97,7 @@ class Monitor {
errx(3, "jvmti error while creating monitor: %s\n", strerr);
}
}
-
+
jvmtiEnv* jvmti() {
return jvmti_;
}
@@ -177,38 +179,15 @@ class Heapster {
static Heapster* instance;
- // * Static JNI hooks.
- static JNIEXPORT void JNICALL JNI_NewObject(
- JNIEnv* env, jclass klass,
- jthread thread, jobject o) {
- instance->NewObject(env, klass, thread, o);
- }
-
- static JNIEXPORT jbyteArray JNICALL JNI_DumpProfile(
- JNIEnv* env, jclass klass, jboolean force_gc) {
- const string profile = instance->DumpProfile(force_gc);
- jbyteArray buf = env->NewByteArray(profile.size());
- // TODO: check error here?
- env->SetByteArrayRegion(
- buf, 0, profile.size(), (jbyte*)profile.data());
-
- return buf;
- }
-
- static JNIEXPORT void JNICALL JNI_ClearProfile(JNIEnv* env, jclass klass) {
- instance->ClearProfile();
+ // Static JVMTI hooks.
+ static void JNICALL JVMTI_VMStart(jvmtiEnv* jvmti, JNIEnv* env) {
+ instance->VMStart(env);
}
- static JNIEXPORT void JNICALL JNI_SetSamplingPeriod(
- JNIEnv* env, jclass klass, int period) {
- instance->SetSamplingPeriod(period);
+ static void JNICALL JVMTI_VMInit(jvmtiEnv* jvmti, JNIEnv* env, jthread thread) {
+ instance->VMInit(env);
}
- // * Static JVMTI hooks
- static void JNICALL JVMTI_VMStart(jvmtiEnv* jvmti, JNIEnv* env) {
- instance->VMStart(env);
- }
-
static void JNICALL JVMTI_VMDeath(jvmtiEnv* jvmti, JNIEnv* env) {
instance->VMDeath(env);
}
@@ -223,15 +202,23 @@ class Heapster {
const char* name, jobject protection_domain,
jint class_data_len, const unsigned char* class_data,
jint* new_class_data_len, unsigned char** new_class_data) {
+ //
// Currently, we always instrument classes (but profiling is
// optional, and won't leave bytecode unecessarily), but in the
// future we may consider dynamic BCI, as per:
//
// http://download.oracle.com/javase/6/docs/platform/jvmti/jvmti.html#bci
- instance->ClassFileLoadHook(
- jvmti, env, class_being_redefined, loader, name,
- protection_domain, class_data_len, class_data,
- new_class_data_len, new_class_data);
+ //
+ instance->ClassFileLoadHook(jvmti,
+ env,
+ class_being_redefined,
+ loader,
+ name,
+ protection_domain,
+ class_data_len,
+ class_data,
+ new_class_data_len,
+ new_class_data);
}
// * Instance methods.
@@ -251,31 +238,25 @@ class Heapster {
void VMStart(JNIEnv* env) {
jclass klass;
- static JNINativeMethod registry[] = {
- { (char*)"_newObject",
- (char*)"(Ljava/lang/Object;Ljava/lang/Object;)V",
- (void*)&Heapster::JNI_NewObject },
- { (char*)"_dumpProfile",
- (char*)"(Z)[B",
- (void*)&Heapster::JNI_DumpProfile },
- { (char*)"_clearProfile",
- (char*)"()V",
- (void*)&Heapster::JNI_ClearProfile },
- { (char*)"_setSamplingPeriod",
- (char*)"(I)V",
- (void*)&Heapster::JNI_SetSamplingPeriod }
- };
- if ((klass = env->FindClass(HELPER_CLASS)) == NULL)
+ klass = env->DefineClass(HELPER_CLASS,
+ NULL,
+ (jbyte const *)Heapster_class,
+ Heapster_class_len);
+
+ if (klass == NULL)
errx(3, "Failed to find the heapster helper class (%s)\n", HELPER_CLASS);
{ // Register natives.
Lock l(monitor_);
vm_started_ = true;
+ }
+ }
- // TODO: Does this need to be inside of the lock?
- if (env->RegisterNatives(klass, registry, arraysize(registry)) != 0)
- errx(3, "Failed to register natives for %s", HELPER_CLASS);
+ void VMInit(JNIEnv* env) {
+ jclass klass = env->FindClass(HELPER_CLASS);
+ if (!klass) {
+ errx(3, "Failed to get handle to helper class - %s.\n", HELPER_CLASS);
}
// Set the static field to hint the helper.
@@ -287,8 +268,9 @@ class Heapster {
// If we ask for a static profile, make sure we turn profiling on
// from the beginning.
if (getenv("HEAPSTER_PROFILE") != NULL) {
- jfieldID is_profiling_field = env->GetStaticFieldID(
- klass, HELPER_FIELD_ISPROFILING, "Z");
+ jfieldID is_profiling_field = env->GetStaticFieldID(klass,
+ HELPER_FIELD_ISPROFILING,
+ "Z");
if (is_profiling_field == NULL)
errx(3, "Failed to get %s field\n", HELPER_FIELD_ISPROFILING);
@@ -449,7 +431,7 @@ class Heapster {
break;
}
}
-
+
if (i == nframes)
break;
}
@@ -544,7 +526,7 @@ class Heapster {
buf[4] = 0;
prof.append(reinterpret_cast<char*>(buf), sizeof(buf[0]) * 5);
-
+
for (uint32_t i = 0; i < kHashTableSize; ++i) {
for (Site* s = sites_[i]; s != NULL; s = s->next) {
buf[0] = s->num_bytes; // nsamples
@@ -620,24 +602,27 @@ class Heapster {
if (sample_period_env != NULL)
sample_period = strtoll(sample_period_env, NULL, 10);
- jvmtiCapabilities c;
+ jvmtiCapabilities c;
memset(&c, 0, sizeof(c));
c.can_generate_all_class_hook_events = 1;
c.can_tag_objects = 1;
c.can_generate_object_free_events = 1;
Assert(jvmti_->AddCapabilities(&c), "failed to add capabilities");
- jvmtiEventCallbacks cb;
+ jvmtiEventCallbacks cb;
memset(&cb, 0, sizeof(cb));
cb.VMStart = &Heapster::JVMTI_VMStart;
+ cb.VMInit = &Heapster::JVMTI_VMInit;
cb.VMDeath = &Heapster::JVMTI_VMDeath;
cb.ObjectFree = &Heapster::JVMTI_ObjectFree;
cb.ClassFileLoadHook = &Heapster::JVMTI_ClassFileLoadHook;
Assert(jvmti_->SetEventCallbacks(&cb, (jint)sizeof(cb)),
"failed to set callbacks");
jvmtiEvent events[] = {
- JVMTI_EVENT_VM_START, JVMTI_EVENT_VM_DEATH,
+ JVMTI_EVENT_VM_START,
+ JVMTI_EVENT_VM_INIT,
+ JVMTI_EVENT_VM_DEATH,
JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
JVMTI_EVENT_OBJECT_FREE
};
@@ -666,6 +651,66 @@ class Heapster {
bool vm_started_;
};
+
+#define FUNC_IMPL(name) Java_Heapster__1##name
+extern "C" {
+
+/*
+ * Class: Heapster
+ * Method: _dumpProfile
+ * Signature: (Z)[B
+ */
+JNIEXPORT jbyteArray JNICALL FUNC_IMPL(dumpProfile)(JNIEnv *env,
+ jclass klass,
+ jboolean force_gc)
+{
+ const string profile = Heapster::instance->DumpProfile(force_gc);
+
+ jbyteArray buf = env->NewByteArray(profile.size());
+ // TODO: check error here?
+ env->SetByteArrayRegion(buf, 0, profile.size(), (jbyte*)profile.data());
+
+ return buf;
+}
+
+/*
+ * Class: Heapster
+ * Method: _newObject
+ * Signature: (Ljava/lang/Object;Ljava/lang/Object;)V
+ */
+JNIEXPORT void JNICALL FUNC_IMPL(newObject)(JNIEnv *env,
+ jclass klass,
+ jobject thread,
+ jobject object)
+{
+ Heapster::instance->NewObject(env, klass, thread, object);
+}
+
+/*
+ * Class: Heapster
+ * Method: _clearProfile
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL FUNC_IMPL(clearProfile)(JNIEnv *env, jclass klass)
+{
+ Heapster::instance->ClearProfile();
+}
+
+/*
+ * Class: Heapster
+ * Method: _setSamplingPeriod
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL FUNC_IMPL(setSamplingPeriod)(JNIEnv *env,
+ jclass klass,
+ jint period)
+{
+ Heapster::instance->SetSamplingPeriod(period);
+}
+
+}
+#undef FUNC_IMPL
+
// Same hash table size as TCMalloc.
const uint32_t Heapster::kHashTableSize = 179999;
const uint32_t Heapster::kMaxStackFrames = 100;

0 comments on commit 0274e59

Please sign in to comment.