Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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...
commit 0274e597f77f500c7ded64348e3760c42b59a8ea 1 parent 2cf62c4
Kaushik Srenevasan authored committed
2  .gitignore
@@ -2,3 +2,5 @@
2 2 *.jnilib
3 3 *.o
4 4 *.so
  5 +generated/*
  6 +*.log
12 Makefile
... ... @@ -1,5 +1,9 @@
1 1 CC=gcc
2 2 OS=$(shell uname -s | tr '[A-Z]' '[a-z]')
  3 +GENERATED=generated
  4 +
  5 +XXD=xxd
  6 +XXD_OPTIONS=-i
3 7
4 8 ifeq ("$(OS)", "darwin")
5 9 JAVA_HOME=$(shell /usr/libexec/java_home)
@@ -15,12 +19,13 @@ endif
15 19
16 20 CFLAGS=-Ijava_crw_demo -fno-strict-aliasing \
17 21 -fPIC -fno-omit-frame-pointer -W -Wall -Wno-unused -Wno-parentheses \
18   - -I$(JAVA_HEADERS)
  22 + -I$(JAVA_HEADERS) -I$(GENERATED)
  23 +
19 24 LDFLAGS=-fno-strict-aliasing -fPIC -fno-omit-frame-pointer \
20 25 -static-libgcc -shared
21 26 DEBUG=-g
22 27
23   -all: $(OBJ) Heapster.class
  28 +all: Heapster.class $(OBJ)
24 29
25 30 $(OBJ): heapster.o sampler.o util.o java_crw_demo/java_crw_demo.o
26 31 g++ $(DEBUG) $(LDFLAGS) -o $@ $^ -lc
@@ -30,8 +35,11 @@ $(OBJ): heapster.o sampler.o util.o java_crw_demo/java_crw_demo.o
30 35
31 36 %.class: %.java
32 37 javac $<
  38 + $(XXD) $(XXD_OPTIONS) $@ > $(GENERATED)/$*-inl.h
33 39
34 40 clean:
35 41 rm -f *.o
36 42 rm -f $(OBJ)
37 43 rm -f java_crw_demo/*.o
  44 + rm -f $(GENERATED)/*
  45 + rm -f *.class
8 README.md
Source Rendered
@@ -9,7 +9,7 @@ profiling in a production setting.
9 9 Currently it allows for profiling similar to the TCMalloc library,
10 10 e.g.:
11 11
12   - $ HEAPSTER_PROFILE=/tmp/OUT java -Xbootclasspath/a:. -agentlib:heapster Test
  12 + $ HEAPSTER_PROFILE=/tmp/OUT java -agentlib:heapster Test
13 13 $ pprof /tmp/OUT
14 14 Welcome to pprof! For help, type 'help'.
15 15 (pprof) top
@@ -26,10 +26,10 @@ This is still work in progress.
26 26
27 27 # Ostrich integration
28 28
29   -If you use [Ostrich](https://github.com/twitter/ostrich), and run your program with heapster, you can generate
30   -runtime heap profiles like so:
  29 +If you use [Ostrich](https://github.com/twitter/ostrich), and run your
  30 +program with heapster, you can generate runtime heap profiles like so:
31 31
32 32 $ curl 'localhost:9990/pprof/heap?pause=10&sample_period=1024' > /tmp/prof
33   -
  33 +
34 34 This will collect heap growth for 10 seconds, with a sampling period
35 35 of 1kB.
0  generated/.generated
No changes.
175 heapster.cc
@@ -22,6 +22,8 @@
22 22 #include "sampler.h"
23 23 #include "util.h"
24 24
  25 +#include <Heapster-inl.h>
  26 +
25 27 // NB: only works on little-endian machines
26 28
27 29 // TODO
@@ -56,7 +58,7 @@ size_t AtomicIO(ssize_t (*f)(int, const void*, size_t),
56 58 const char* s = reinterpret_cast<const char*>(_s);
57 59 size_t pos = 0;
58 60 ssize_t res;
59   -
  61 +
60 62 while (n > pos) {
61 63 res = (f) (fd, s + pos, n - pos);
62 64 switch (res) {
@@ -64,19 +66,19 @@ size_t AtomicIO(ssize_t (*f)(int, const void*, size_t),
64 66 if (errno == EINTR || errno == EAGAIN)
65 67 continue;
66 68 return 0;
67   -
68   - case 0:
  69 +
  70 + case 0:
69 71 errno = EPIPE;
70 72 return pos;
71   -
72   - default:
  73 +
  74 + default:
73 75 pos += (u_int)res;
74 76 }
75 77 }
76   -
  78 +
77 79 return pos;
78 80 }
79   -
  81 +
80 82 void AtomicWrite(int fd, const char* buf, int n) {
81 83 if (AtomicIO(write, fd, (const void*)buf, n) != (size_t)n) {
82 84 perror("AtomicWrite");
@@ -95,7 +97,7 @@ class Monitor {
95 97 errx(3, "jvmti error while creating monitor: %s\n", strerr);
96 98 }
97 99 }
98   -
  100 +
99 101 jvmtiEnv* jvmti() {
100 102 return jvmti_;
101 103 }
@@ -177,38 +179,15 @@ class Heapster {
177 179
178 180 static Heapster* instance;
179 181
180   - // * Static JNI hooks.
181   - static JNIEXPORT void JNICALL JNI_NewObject(
182   - JNIEnv* env, jclass klass,
183   - jthread thread, jobject o) {
184   - instance->NewObject(env, klass, thread, o);
185   - }
186   -
187   - static JNIEXPORT jbyteArray JNICALL JNI_DumpProfile(
188   - JNIEnv* env, jclass klass, jboolean force_gc) {
189   - const string profile = instance->DumpProfile(force_gc);
190   - jbyteArray buf = env->NewByteArray(profile.size());
191   - // TODO: check error here?
192   - env->SetByteArrayRegion(
193   - buf, 0, profile.size(), (jbyte*)profile.data());
194   -
195   - return buf;
196   - }
197   -
198   - static JNIEXPORT void JNICALL JNI_ClearProfile(JNIEnv* env, jclass klass) {
199   - instance->ClearProfile();
  182 + // Static JVMTI hooks.
  183 + static void JNICALL JVMTI_VMStart(jvmtiEnv* jvmti, JNIEnv* env) {
  184 + instance->VMStart(env);
200 185 }
201 186
202   - static JNIEXPORT void JNICALL JNI_SetSamplingPeriod(
203   - JNIEnv* env, jclass klass, int period) {
204   - instance->SetSamplingPeriod(period);
  187 + static void JNICALL JVMTI_VMInit(jvmtiEnv* jvmti, JNIEnv* env, jthread thread) {
  188 + instance->VMInit(env);
205 189 }
206 190
207   - // * Static JVMTI hooks
208   - static void JNICALL JVMTI_VMStart(jvmtiEnv* jvmti, JNIEnv* env) {
209   - instance->VMStart(env);
210   - }
211   -
212 191 static void JNICALL JVMTI_VMDeath(jvmtiEnv* jvmti, JNIEnv* env) {
213 192 instance->VMDeath(env);
214 193 }
@@ -223,15 +202,23 @@ class Heapster {
223 202 const char* name, jobject protection_domain,
224 203 jint class_data_len, const unsigned char* class_data,
225 204 jint* new_class_data_len, unsigned char** new_class_data) {
  205 + //
226 206 // Currently, we always instrument classes (but profiling is
227 207 // optional, and won't leave bytecode unecessarily), but in the
228 208 // future we may consider dynamic BCI, as per:
229 209 //
230 210 // http://download.oracle.com/javase/6/docs/platform/jvmti/jvmti.html#bci
231   - instance->ClassFileLoadHook(
232   - jvmti, env, class_being_redefined, loader, name,
233   - protection_domain, class_data_len, class_data,
234   - new_class_data_len, new_class_data);
  211 + //
  212 + instance->ClassFileLoadHook(jvmti,
  213 + env,
  214 + class_being_redefined,
  215 + loader,
  216 + name,
  217 + protection_domain,
  218 + class_data_len,
  219 + class_data,
  220 + new_class_data_len,
  221 + new_class_data);
235 222 }
236 223
237 224 // * Instance methods.
@@ -251,31 +238,25 @@ class Heapster {
251 238
252 239 void VMStart(JNIEnv* env) {
253 240 jclass klass;
254   - static JNINativeMethod registry[] = {
255   - { (char*)"_newObject",
256   - (char*)"(Ljava/lang/Object;Ljava/lang/Object;)V",
257   - (void*)&Heapster::JNI_NewObject },
258   - { (char*)"_dumpProfile",
259   - (char*)"(Z)[B",
260   - (void*)&Heapster::JNI_DumpProfile },
261   - { (char*)"_clearProfile",
262   - (char*)"()V",
263   - (void*)&Heapster::JNI_ClearProfile },
264   - { (char*)"_setSamplingPeriod",
265   - (char*)"(I)V",
266   - (void*)&Heapster::JNI_SetSamplingPeriod }
267   - };
268 241
269   - if ((klass = env->FindClass(HELPER_CLASS)) == NULL)
  242 + klass = env->DefineClass(HELPER_CLASS,
  243 + NULL,
  244 + (jbyte const *)Heapster_class,
  245 + Heapster_class_len);
  246 +
  247 + if (klass == NULL)
270 248 errx(3, "Failed to find the heapster helper class (%s)\n", HELPER_CLASS);
271 249
272 250 { // Register natives.
273 251 Lock l(monitor_);
274 252 vm_started_ = true;
  253 + }
  254 + }
275 255
276   - // TODO: Does this need to be inside of the lock?
277   - if (env->RegisterNatives(klass, registry, arraysize(registry)) != 0)
278   - errx(3, "Failed to register natives for %s", HELPER_CLASS);
  256 + void VMInit(JNIEnv* env) {
  257 + jclass klass = env->FindClass(HELPER_CLASS);
  258 + if (!klass) {
  259 + errx(3, "Failed to get handle to helper class - %s.\n", HELPER_CLASS);
279 260 }
280 261
281 262 // Set the static field to hint the helper.
@@ -287,8 +268,9 @@ class Heapster {
287 268 // If we ask for a static profile, make sure we turn profiling on
288 269 // from the beginning.
289 270 if (getenv("HEAPSTER_PROFILE") != NULL) {
290   - jfieldID is_profiling_field = env->GetStaticFieldID(
291   - klass, HELPER_FIELD_ISPROFILING, "Z");
  271 + jfieldID is_profiling_field = env->GetStaticFieldID(klass,
  272 + HELPER_FIELD_ISPROFILING,
  273 + "Z");
292 274 if (is_profiling_field == NULL)
293 275 errx(3, "Failed to get %s field\n", HELPER_FIELD_ISPROFILING);
294 276
@@ -449,7 +431,7 @@ class Heapster {
449 431 break;
450 432 }
451 433 }
452   -
  434 +
453 435 if (i == nframes)
454 436 break;
455 437 }
@@ -544,7 +526,7 @@ class Heapster {
544 526 buf[4] = 0;
545 527
546 528 prof.append(reinterpret_cast<char*>(buf), sizeof(buf[0]) * 5);
547   -
  529 +
548 530 for (uint32_t i = 0; i < kHashTableSize; ++i) {
549 531 for (Site* s = sites_[i]; s != NULL; s = s->next) {
550 532 buf[0] = s->num_bytes; // nsamples
@@ -620,16 +602,17 @@ class Heapster {
620 602 if (sample_period_env != NULL)
621 603 sample_period = strtoll(sample_period_env, NULL, 10);
622 604
623   - jvmtiCapabilities c;
  605 + jvmtiCapabilities c;
624 606 memset(&c, 0, sizeof(c));
625 607 c.can_generate_all_class_hook_events = 1;
626 608 c.can_tag_objects = 1;
627 609 c.can_generate_object_free_events = 1;
628 610 Assert(jvmti_->AddCapabilities(&c), "failed to add capabilities");
629 611
630   - jvmtiEventCallbacks cb;
  612 + jvmtiEventCallbacks cb;
631 613 memset(&cb, 0, sizeof(cb));
632 614 cb.VMStart = &Heapster::JVMTI_VMStart;
  615 + cb.VMInit = &Heapster::JVMTI_VMInit;
633 616 cb.VMDeath = &Heapster::JVMTI_VMDeath;
634 617 cb.ObjectFree = &Heapster::JVMTI_ObjectFree;
635 618 cb.ClassFileLoadHook = &Heapster::JVMTI_ClassFileLoadHook;
@@ -637,7 +620,9 @@ class Heapster {
637 620 "failed to set callbacks");
638 621
639 622 jvmtiEvent events[] = {
640   - JVMTI_EVENT_VM_START, JVMTI_EVENT_VM_DEATH,
  623 + JVMTI_EVENT_VM_START,
  624 + JVMTI_EVENT_VM_INIT,
  625 + JVMTI_EVENT_VM_DEATH,
641 626 JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
642 627 JVMTI_EVENT_OBJECT_FREE
643 628 };
@@ -666,6 +651,66 @@ class Heapster {
666 651 bool vm_started_;
667 652 };
668 653
  654 +
  655 +#define FUNC_IMPL(name) Java_Heapster__1##name
  656 +extern "C" {
  657 +
  658 +/*
  659 + * Class: Heapster
  660 + * Method: _dumpProfile
  661 + * Signature: (Z)[B
  662 + */
  663 +JNIEXPORT jbyteArray JNICALL FUNC_IMPL(dumpProfile)(JNIEnv *env,
  664 + jclass klass,
  665 + jboolean force_gc)
  666 +{
  667 + const string profile = Heapster::instance->DumpProfile(force_gc);
  668 +
  669 + jbyteArray buf = env->NewByteArray(profile.size());
  670 + // TODO: check error here?
  671 + env->SetByteArrayRegion(buf, 0, profile.size(), (jbyte*)profile.data());
  672 +
  673 + return buf;
  674 +}
  675 +
  676 +/*
  677 + * Class: Heapster
  678 + * Method: _newObject
  679 + * Signature: (Ljava/lang/Object;Ljava/lang/Object;)V
  680 + */
  681 +JNIEXPORT void JNICALL FUNC_IMPL(newObject)(JNIEnv *env,
  682 + jclass klass,
  683 + jobject thread,
  684 + jobject object)
  685 +{
  686 + Heapster::instance->NewObject(env, klass, thread, object);
  687 +}
  688 +
  689 +/*
  690 + * Class: Heapster
  691 + * Method: _clearProfile
  692 + * Signature: ()V
  693 + */
  694 +JNIEXPORT void JNICALL FUNC_IMPL(clearProfile)(JNIEnv *env, jclass klass)
  695 +{
  696 + Heapster::instance->ClearProfile();
  697 +}
  698 +
  699 +/*
  700 + * Class: Heapster
  701 + * Method: _setSamplingPeriod
  702 + * Signature: (I)V
  703 + */
  704 +JNIEXPORT void JNICALL FUNC_IMPL(setSamplingPeriod)(JNIEnv *env,
  705 + jclass klass,
  706 + jint period)
  707 +{
  708 + Heapster::instance->SetSamplingPeriod(period);
  709 +}
  710 +
  711 +}
  712 +#undef FUNC_IMPL
  713 +
669 714 // Same hash table size as TCMalloc.
670 715 const uint32_t Heapster::kHashTableSize = 179999;
671 716 const uint32_t Heapster::kMaxStackFrames = 100;

0 comments on commit 0274e59

Please sign in to comment.
Something went wrong with that request. Please try again.