Tue, 14 Oct 2008 15:16:38 -0700
6306922: Dump dump created by +HeapDumpOnOutOfMemoryError should include stack traces for stack roots
Summary: Include stack traces of all threads in the heap dump
Reviewed-by: alanb
1.1 --- a/src/share/vm/includeDB_features Thu Oct 09 12:06:22 2008 -0400 1.2 +++ b/src/share/vm/includeDB_features Tue Oct 14 15:16:38 2008 -0700 1.3 @@ -99,6 +99,7 @@ 1.4 heapDumper.cpp reflectionUtils.hpp 1.5 heapDumper.cpp symbolTable.hpp 1.6 heapDumper.cpp systemDictionary.hpp 1.7 +heapDumper.cpp threadService.hpp 1.8 heapDumper.cpp universe.hpp 1.9 heapDumper.cpp vframe.hpp 1.10 heapDumper.cpp vmGCOperations.hpp
2.1 --- a/src/share/vm/services/heapDumper.cpp Thu Oct 09 12:06:22 2008 -0400 2.2 +++ b/src/share/vm/services/heapDumper.cpp Tue Oct 14 15:16:38 2008 -0700 2.3 @@ -343,7 +343,8 @@ 2.4 2.5 // Default stack trace ID (used for dummy HPROF_TRACE record) 2.6 enum { 2.7 - STACK_TRACE_ID = 1 2.8 + STACK_TRACE_ID = 1, 2.9 + INITIAL_CLASS_COUNT = 200 2.10 }; 2.11 2.12 2.13 @@ -408,6 +409,7 @@ 2.14 void write_u8(u8 x); 2.15 void write_objectID(oop o); 2.16 void write_classID(Klass* k); 2.17 + void write_id(u4 x); 2.18 }; 2.19 2.20 DumpWriter::DumpWriter(const char* path) { 2.21 @@ -548,6 +550,14 @@ 2.22 #endif 2.23 } 2.24 2.25 +void DumpWriter::write_id(u4 x) { 2.26 +#ifdef _LP64 2.27 + write_u8((u8) x); 2.28 +#else 2.29 + write_u4(x); 2.30 +#endif 2.31 +} 2.32 + 2.33 // We use java mirror as the class ID 2.34 void DumpWriter::write_classID(Klass* k) { 2.35 write_objectID(k->java_mirror()); 2.36 @@ -596,6 +606,8 @@ 2.37 static void dump_object_array(DumpWriter* writer, objArrayOop array); 2.38 // creates HPROF_GC_PRIM_ARRAY_DUMP record for the given type array 2.39 static void dump_prim_array(DumpWriter* writer, typeArrayOop array); 2.40 + // create HPROF_FRAME record for the given method and bci 2.41 + static void dump_stack_frame(DumpWriter* writer, int frame_serial_num, int class_serial_num, methodOop m, int bci); 2.42 }; 2.43 2.44 // write a header of the given type 2.45 @@ -1070,6 +1082,29 @@ 2.46 } 2.47 } 2.48 2.49 +// create a HPROF_FRAME record of the given methodOop and bci 2.50 +void DumperSupport::dump_stack_frame(DumpWriter* writer, 2.51 + int frame_serial_num, 2.52 + int class_serial_num, 2.53 + methodOop m, 2.54 + int bci) { 2.55 + int line_number; 2.56 + if (m->is_native()) { 2.57 + line_number = -3; // native frame 2.58 + } else { 2.59 + line_number = m->line_number_from_bci(bci); 2.60 + } 2.61 + 2.62 + write_header(writer, HPROF_FRAME, 4*oopSize + 2*sizeof(u4)); 2.63 + writer->write_id(frame_serial_num); // frame serial number 2.64 + writer->write_objectID(m->name()); // method's name 2.65 + writer->write_objectID(m->signature()); // method's signature 2.66 + 2.67 + assert(Klass::cast(m->method_holder())->oop_is_instance(), "not instanceKlass"); 2.68 + writer->write_objectID(instanceKlass::cast(m->method_holder())->source_file_name()); // source file name 2.69 + writer->write_u4(class_serial_num); // class serial number 2.70 + writer->write_u4((u4) line_number); // line number 2.71 +} 2.72 2.73 // Support class used to generate HPROF_UTF8 records from the entries in the 2.74 // SymbolTable. 2.75 @@ -1104,12 +1139,15 @@ 2.76 private: 2.77 DumpWriter* _writer; 2.78 u4 _thread_serial_num; 2.79 + int _frame_num; 2.80 DumpWriter* writer() const { return _writer; } 2.81 public: 2.82 JNILocalsDumper(DumpWriter* writer, u4 thread_serial_num) { 2.83 _writer = writer; 2.84 _thread_serial_num = thread_serial_num; 2.85 + _frame_num = -1; // default - empty stack 2.86 } 2.87 + void set_frame_number(int n) { _frame_num = n; } 2.88 void do_oop(oop* obj_p); 2.89 void do_oop(narrowOop* obj_p) { ShouldNotReachHere(); } 2.90 }; 2.91 @@ -1122,7 +1160,7 @@ 2.92 writer()->write_u1(HPROF_GC_ROOT_JNI_LOCAL); 2.93 writer()->write_objectID(o); 2.94 writer()->write_u4(_thread_serial_num); 2.95 - writer()->write_u4((u4)-1); // empty 2.96 + writer()->write_u4((u4)_frame_num); 2.97 } 2.98 } 2.99 2.100 @@ -1269,6 +1307,9 @@ 2.101 bool _gc_before_heap_dump; 2.102 bool _is_segmented_dump; 2.103 jlong _dump_start; 2.104 + GrowableArray<Klass*>* _klass_map; 2.105 + ThreadStackTrace** _stack_traces; 2.106 + int _num_threads; 2.107 2.108 // accessors 2.109 DumpWriter* writer() const { return _writer; } 2.110 @@ -1291,9 +1332,16 @@ 2.111 static void do_basic_type_array_class_dump(klassOop k); 2.112 2.113 // HPROF_GC_ROOT_THREAD_OBJ records 2.114 - void do_thread(JavaThread* thread, u4 thread_serial_num); 2.115 + int do_thread(JavaThread* thread, u4 thread_serial_num); 2.116 void do_threads(); 2.117 2.118 + void add_class_serial_number(Klass* k, int serial_num) { 2.119 + _klass_map->at_put_grow(serial_num, k); 2.120 + } 2.121 + 2.122 + // HPROF_TRACE and HPROF_FRAME records 2.123 + void dump_stack_traces(); 2.124 + 2.125 // writes a HPROF_HEAP_DUMP or HPROF_HEAP_DUMP_SEGMENT record 2.126 void write_dump_header(); 2.127 2.128 @@ -1313,6 +1361,18 @@ 2.129 _gc_before_heap_dump = gc_before_heap_dump; 2.130 _is_segmented_dump = false; 2.131 _dump_start = (jlong)-1; 2.132 + _klass_map = new (ResourceObj::C_HEAP) GrowableArray<Klass*>(INITIAL_CLASS_COUNT, true); 2.133 + _stack_traces = NULL; 2.134 + _num_threads = 0; 2.135 + } 2.136 + ~VM_HeapDumper() { 2.137 + if (_stack_traces != NULL) { 2.138 + for (int i=0; i < _num_threads; i++) { 2.139 + delete _stack_traces[i]; 2.140 + } 2.141 + FREE_C_HEAP_ARRAY(ThreadStackTrace*, _stack_traces); 2.142 + } 2.143 + delete _klass_map; 2.144 } 2.145 2.146 VMOp_Type type() const { return VMOp_HeapDumper; } 2.147 @@ -1436,6 +1496,9 @@ 2.148 Klass* klass = Klass::cast(k); 2.149 writer->write_classID(klass); 2.150 2.151 + // add the klassOop and class serial number pair 2.152 + dumper->add_class_serial_number(klass, class_serial_num); 2.153 + 2.154 writer->write_u4(STACK_TRACE_ID); 2.155 2.156 // class name ID 2.157 @@ -1465,15 +1528,15 @@ 2.158 // Walk the stack of the given thread. 2.159 // Dumps a HPROF_GC_ROOT_JAVA_FRAME record for each local 2.160 // Dumps a HPROF_GC_ROOT_JNI_LOCAL record for each JNI local 2.161 -void VM_HeapDumper::do_thread(JavaThread* java_thread, u4 thread_serial_num) { 2.162 +// 2.163 +// It returns the number of Java frames in this thread stack 2.164 +int VM_HeapDumper::do_thread(JavaThread* java_thread, u4 thread_serial_num) { 2.165 JNILocalsDumper blk(writer(), thread_serial_num); 2.166 2.167 oop threadObj = java_thread->threadObj(); 2.168 assert(threadObj != NULL, "sanity check"); 2.169 2.170 - // JNI locals for the top frame 2.171 - java_thread->active_handles()->oops_do(&blk); 2.172 - 2.173 + int stack_depth = 0; 2.174 if (java_thread->has_last_Java_frame()) { 2.175 2.176 // vframes are resource allocated 2.177 @@ -1484,13 +1547,14 @@ 2.178 RegisterMap reg_map(java_thread); 2.179 frame f = java_thread->last_frame(); 2.180 vframe* vf = vframe::new_vframe(&f, ®_map, java_thread); 2.181 + frame* last_entry_frame = NULL; 2.182 2.183 while (vf != NULL) { 2.184 + blk.set_frame_number(stack_depth); 2.185 if (vf->is_java_frame()) { 2.186 2.187 // java frame (interpreted, compiled, ...) 2.188 javaVFrame *jvf = javaVFrame::cast(vf); 2.189 - 2.190 if (!(jvf->method()->is_native())) { 2.191 StackValueCollection* locals = jvf->locals(); 2.192 for (int slot=0; slot<locals->size(); slot++) { 2.193 @@ -1501,44 +1565,61 @@ 2.194 writer()->write_u1(HPROF_GC_ROOT_JAVA_FRAME); 2.195 writer()->write_objectID(o); 2.196 writer()->write_u4(thread_serial_num); 2.197 - writer()->write_u4((u4)-1); // empty 2.198 + writer()->write_u4((u4) stack_depth); 2.199 } 2.200 } 2.201 } 2.202 + } else { 2.203 + // native frame 2.204 + if (stack_depth == 0) { 2.205 + // JNI locals for the top frame. 2.206 + java_thread->active_handles()->oops_do(&blk); 2.207 + } else { 2.208 + if (last_entry_frame != NULL) { 2.209 + // JNI locals for the entry frame 2.210 + assert(last_entry_frame->is_entry_frame(), "checking"); 2.211 + last_entry_frame->entry_frame_call_wrapper()->handles()->oops_do(&blk); 2.212 + } 2.213 + } 2.214 } 2.215 + // increment only for Java frames 2.216 + stack_depth++; 2.217 + last_entry_frame = NULL; 2.218 + 2.219 } else { 2.220 - 2.221 // externalVFrame - if it's an entry frame then report any JNI locals 2.222 - // as roots 2.223 + // as roots when we find the corresponding native javaVFrame 2.224 frame* fr = vf->frame_pointer(); 2.225 assert(fr != NULL, "sanity check"); 2.226 if (fr->is_entry_frame()) { 2.227 - fr->entry_frame_call_wrapper()->handles()->oops_do(&blk); 2.228 + last_entry_frame = fr; 2.229 } 2.230 } 2.231 - 2.232 vf = vf->sender(); 2.233 } 2.234 + } else { 2.235 + // no last java frame but there may be JNI locals 2.236 + java_thread->active_handles()->oops_do(&blk); 2.237 } 2.238 + return stack_depth; 2.239 } 2.240 2.241 2.242 // write a HPROF_GC_ROOT_THREAD_OBJ record for each java thread. Then walk 2.243 // the stack so that locals and JNI locals are dumped. 2.244 void VM_HeapDumper::do_threads() { 2.245 - u4 thread_serial_num = 0; 2.246 - for (JavaThread* thread = Threads::first(); thread != NULL ; thread = thread->next()) { 2.247 + for (int i=0; i < _num_threads; i++) { 2.248 + JavaThread* thread = _stack_traces[i]->thread(); 2.249 oop threadObj = thread->threadObj(); 2.250 - if (threadObj != NULL && !thread->is_exiting() && !thread->is_hidden_from_external_view()) { 2.251 - ++thread_serial_num; 2.252 - 2.253 - writer()->write_u1(HPROF_GC_ROOT_THREAD_OBJ); 2.254 - writer()->write_objectID(threadObj); 2.255 - writer()->write_u4(thread_serial_num); 2.256 - writer()->write_u4(STACK_TRACE_ID); 2.257 - 2.258 - do_thread(thread, thread_serial_num); 2.259 - } 2.260 + u4 thread_serial_num = i+1; 2.261 + u4 stack_serial_num = thread_serial_num + STACK_TRACE_ID; 2.262 + writer()->write_u1(HPROF_GC_ROOT_THREAD_OBJ); 2.263 + writer()->write_objectID(threadObj); 2.264 + writer()->write_u4(thread_serial_num); // thread number 2.265 + writer()->write_u4(stack_serial_num); // stack trace serial number 2.266 + int num_frames = do_thread(thread, thread_serial_num); 2.267 + assert(num_frames == _stack_traces[i]->get_stack_depth(), 2.268 + "total number of Java frames not matched"); 2.269 } 2.270 } 2.271 2.272 @@ -1547,16 +1628,16 @@ 2.273 // records: 2.274 // 2.275 // HPROF_HEADER 2.276 -// HPROF_TRACE 2.277 // [HPROF_UTF8]* 2.278 // [HPROF_LOAD_CLASS]* 2.279 +// [[HPROF_FRAME]*|HPROF_TRACE]* 2.280 // [HPROF_GC_CLASS_DUMP]* 2.281 // HPROF_HEAP_DUMP 2.282 // 2.283 -// The HPROF_TRACE record after the header is "dummy trace" record which does 2.284 -// not include any frames. Other records which require a stack trace ID will 2.285 -// specify the trace ID of this record (1). It also means we can run HAT without 2.286 -// needing the -stack false option. 2.287 +// The HPROF_TRACE records represent the stack traces where the heap dump 2.288 +// is generated and a "dummy trace" record which does not include 2.289 +// any frames. The dummy trace record is used to be referenced as the 2.290 +// unknown object alloc site. 2.291 // 2.292 // The HPROF_HEAP_DUMP record has a length following by sub-records. To allow 2.293 // the heap dump be generated in a single pass we remember the position of 2.294 @@ -1592,12 +1673,6 @@ 2.295 writer()->write_u4(oopSize); 2.296 writer()->write_u8(os::javaTimeMillis()); 2.297 2.298 - // HPROF_TRACE record without any frames 2.299 - DumperSupport::write_header(writer(), HPROF_TRACE, 3*sizeof(u4)); 2.300 - writer()->write_u4(STACK_TRACE_ID); 2.301 - writer()->write_u4(0); // thread number 2.302 - writer()->write_u4(0); // frame count 2.303 - 2.304 // HPROF_UTF8 records 2.305 SymbolTableDumper sym_dumper(writer()); 2.306 SymbolTable::oops_do(&sym_dumper); 2.307 @@ -1606,6 +1681,10 @@ 2.308 SystemDictionary::classes_do(&do_load_class); 2.309 Universe::basic_type_classes_do(&do_load_class); 2.310 2.311 + // write HPROF_FRAME and HPROF_TRACE records 2.312 + // this must be called after _klass_map is built when iterating the classes above. 2.313 + dump_stack_traces(); 2.314 + 2.315 // write HPROF_HEAP_DUMP or HPROF_HEAP_DUMP_SEGMENT 2.316 write_dump_header(); 2.317 2.318 @@ -1646,6 +1725,47 @@ 2.319 end_of_dump(); 2.320 } 2.321 2.322 +void VM_HeapDumper::dump_stack_traces() { 2.323 + // write a HPROF_TRACE record without any frames to be referenced as object alloc sites 2.324 + DumperSupport::write_header(writer(), HPROF_TRACE, 3*sizeof(u4)); 2.325 + writer()->write_u4((u4) STACK_TRACE_ID); 2.326 + writer()->write_u4(0); // thread number 2.327 + writer()->write_u4(0); // frame count 2.328 + 2.329 + _stack_traces = NEW_C_HEAP_ARRAY(ThreadStackTrace*, Threads::number_of_threads()); 2.330 + int frame_serial_num = 0; 2.331 + for (JavaThread* thread = Threads::first(); thread != NULL ; thread = thread->next()) { 2.332 + oop threadObj = thread->threadObj(); 2.333 + if (threadObj != NULL && !thread->is_exiting() && !thread->is_hidden_from_external_view()) { 2.334 + // dump thread stack trace 2.335 + ThreadStackTrace* stack_trace = new ThreadStackTrace(thread, false); 2.336 + stack_trace->dump_stack_at_safepoint(-1); 2.337 + _stack_traces[_num_threads++] = stack_trace; 2.338 + 2.339 + // write HPROF_FRAME records for this thread's stack trace 2.340 + int depth = stack_trace->get_stack_depth(); 2.341 + int thread_frame_start = frame_serial_num; 2.342 + for (int j=0; j < depth; j++) { 2.343 + StackFrameInfo* frame = stack_trace->stack_frame_at(j); 2.344 + methodOop m = frame->method(); 2.345 + int class_serial_num = _klass_map->find(Klass::cast(m->method_holder())); 2.346 + // the class serial number starts from 1 2.347 + assert(class_serial_num > 0, "class not found"); 2.348 + DumperSupport::dump_stack_frame(writer(), ++frame_serial_num, class_serial_num, m, frame->bci()); 2.349 + } 2.350 + 2.351 + // write HPROF_TRACE record for one thread 2.352 + DumperSupport::write_header(writer(), HPROF_TRACE, 3*sizeof(u4) + depth*oopSize); 2.353 + int stack_serial_num = _num_threads + STACK_TRACE_ID; 2.354 + writer()->write_u4(stack_serial_num); // stack trace serial number 2.355 + writer()->write_u4((u4) _num_threads); // thread serial number 2.356 + writer()->write_u4(depth); // frame count 2.357 + for (int j=1; j <= depth; j++) { 2.358 + writer()->write_id(thread_frame_start + j); 2.359 + } 2.360 + } 2.361 + } 2.362 +} 2.363 2.364 // dump the heap to given path. 2.365 int HeapDumper::dump(const char* path) {
3.1 --- a/src/share/vm/services/threadService.hpp Thu Oct 09 12:06:22 2008 -0400 3.2 +++ b/src/share/vm/services/threadService.hpp Tue Oct 14 15:16:38 2008 -0700 3.3 @@ -242,6 +242,7 @@ 3.4 ThreadStackTrace(JavaThread* thread, bool with_locked_monitors); 3.5 ~ThreadStackTrace(); 3.6 3.7 + JavaThread* thread() { return _thread; } 3.8 StackFrameInfo* stack_frame_at(int i) { return _frames->at(i); } 3.9 int get_stack_depth() { return _depth; } 3.10