6306922: Dump dump created by +HeapDumpOnOutOfMemoryError should include stack traces for stack roots

Tue, 14 Oct 2008 15:16:38 -0700

author
mchung
date
Tue, 14 Oct 2008 15:16:38 -0700
changeset 831
4d05b7cb7842
parent 827
05366dad12cf
child 832
1bf7a2ce4895

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

src/share/vm/includeDB_features file | annotate | diff | comparison | revisions
src/share/vm/services/heapDumper.cpp file | annotate | diff | comparison | revisions
src/share/vm/services/threadService.hpp file | annotate | diff | comparison | revisions
     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, &reg_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  

mercurial