diff src/share/vm/prims/jvmtiEnvThreadState.cpp @ 0:a61af66fc99e jdk7-b24

Initial load
author duke
date Sat, 01 Dec 2007 00:00:00 +0000
parents
children c18cbe5936b8
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/prims/jvmtiEnvThreadState.cpp	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,313 @@
+/*
+ * Copyright 2003-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ */
+
+# include "incls/_precompiled.incl"
+# include "incls/_jvmtiEnvThreadState.cpp.incl"
+
+
+///////////////////////////////////////////////////////////////
+//
+// class JvmtiFramePop
+//
+
+#ifndef PRODUCT
+void JvmtiFramePop::print() {
+  tty->print_cr("_frame_number=%d", _frame_number);
+}
+#endif
+
+
+///////////////////////////////////////////////////////////////
+//
+// class JvmtiFramePops - private methods
+//
+
+void
+JvmtiFramePops::set(JvmtiFramePop& fp) {
+  if (_pops->find(fp.frame_number()) < 0) {
+    _pops->append(fp.frame_number());
+  }
+}
+
+
+void
+JvmtiFramePops::clear(JvmtiFramePop& fp) {
+  assert(_pops->length() > 0, "No more frame pops");
+
+  _pops->remove(fp.frame_number());
+}
+
+
+int
+JvmtiFramePops::clear_to(JvmtiFramePop& fp) {
+  int cleared = 0;
+  int index = 0;
+  while (index < _pops->length()) {
+    JvmtiFramePop pop = JvmtiFramePop(_pops->at(index));
+    if (pop.above_on_stack(fp)) {
+      _pops->remove_at(index);
+      ++cleared;
+    } else {
+      ++index;
+    }
+  }
+  return cleared;
+}
+
+
+///////////////////////////////////////////////////////////////
+//
+// class JvmtiFramePops - public methods
+//
+
+JvmtiFramePops::JvmtiFramePops() {
+  _pops = new (ResourceObj::C_HEAP) GrowableArray<int> (2, true);
+}
+
+JvmtiFramePops::~JvmtiFramePops() {
+  // return memory to c_heap.
+  delete _pops;
+}
+
+
+#ifndef PRODUCT
+void JvmtiFramePops::print() {
+  ResourceMark rm;
+
+  int n = _pops->length();
+  for (int i=0; i<n; i++) {
+    JvmtiFramePop fp = JvmtiFramePop(_pops->at(i));
+    tty->print("%d: ", i);
+    fp.print();
+    tty->print_cr("");
+  }
+}
+#endif
+
+///////////////////////////////////////////////////////////////
+//
+// class JvmtiEnvThreadState
+//
+// Instances of JvmtiEnvThreadState hang off of each JvmtiThreadState,
+// one per JvmtiEnv.
+//
+
+JvmtiEnvThreadState::JvmtiEnvThreadState(JavaThread *thread, JvmtiEnvBase *env) :
+  _event_enable() {
+  _thread                 = thread;
+  _env                    = (JvmtiEnv*)env;
+  _next                   = NULL;
+  _frame_pops             = NULL;
+  _current_bci            = 0;
+  _current_method_id      = NULL;
+  _breakpoint_posted      = false;
+  _single_stepping_posted = false;
+  _agent_thread_local_storage_data = NULL;
+}
+
+JvmtiEnvThreadState::~JvmtiEnvThreadState()   {
+  delete _frame_pops;
+  _frame_pops = NULL;
+}
+
+// Given that a new (potential) event has come in,
+// maintain the current JVMTI location on a per-thread per-env basis
+// and use it to filter out duplicate events:
+// - instruction rewrites
+// - breakpoint followed by single step
+// - single step at a breakpoint
+void JvmtiEnvThreadState::compare_and_set_current_location(methodOop new_method,
+                                                           address new_location, jvmtiEvent event) {
+
+  int new_bci = new_location - new_method->code_base();
+
+  // The method is identified and stored as a jmethodID which is safe in this
+  // case because the class cannot be unloaded while a method is executing.
+  jmethodID new_method_id = new_method->jmethod_id();
+
+  // the last breakpoint or single step was at this same location
+  if (_current_bci == new_bci && _current_method_id == new_method_id) {
+    switch (event) {
+    case JVMTI_EVENT_BREAKPOINT:
+      // Repeat breakpoint is complicated. If we previously posted a breakpoint
+      // event at this location and if we also single stepped at this location
+      // then we skip the duplicate breakpoint.
+      _breakpoint_posted = _breakpoint_posted && _single_stepping_posted;
+      break;
+    case JVMTI_EVENT_SINGLE_STEP:
+      // Repeat single step is easy: just don't post it again.
+      // If step is pending for popframe then it may not be
+      // a repeat step. The new_bci and method_id is same as current_bci
+      // and current method_id after pop and step for recursive calls.
+      // This has been handled by clearing the location
+      _single_stepping_posted = true;
+      break;
+    default:
+      assert(false, "invalid event value passed");
+      break;
+    }
+    return;
+  }
+
+  set_current_location(new_method_id, new_bci);
+  _breakpoint_posted = false;
+  _single_stepping_posted = false;
+}
+
+
+JvmtiFramePops* JvmtiEnvThreadState::get_frame_pops() {
+#ifdef ASSERT
+  uint32_t debug_bits = 0;
+#endif
+  assert(get_thread() == Thread::current() || JvmtiEnv::is_thread_fully_suspended(get_thread(), false, &debug_bits),
+         "frame pop data only accessible from same thread or while suspended");
+
+  if (_frame_pops == NULL) {
+    _frame_pops = new JvmtiFramePops();
+    assert(_frame_pops != NULL, "_frame_pops != NULL");
+  }
+  return _frame_pops;
+}
+
+
+bool JvmtiEnvThreadState::has_frame_pops() {
+  return _frame_pops == NULL? false : (_frame_pops->length() > 0);
+}
+
+void JvmtiEnvThreadState::set_frame_pop(int frame_number) {
+#ifdef ASSERT
+  uint32_t debug_bits = 0;
+#endif
+  assert(get_thread() == Thread::current() || JvmtiEnv::is_thread_fully_suspended(get_thread(), false, &debug_bits),
+         "frame pop data only accessible from same thread or while suspended");
+  JvmtiFramePop fpop(frame_number);
+  JvmtiEventController::set_frame_pop(this, fpop);
+}
+
+
+void JvmtiEnvThreadState::clear_frame_pop(int frame_number) {
+#ifdef ASSERT
+  uint32_t debug_bits = 0;
+#endif
+  assert(get_thread() == Thread::current() || JvmtiEnv::is_thread_fully_suspended(get_thread(), false, &debug_bits),
+         "frame pop data only accessible from same thread or while suspended");
+  JvmtiFramePop fpop(frame_number);
+  JvmtiEventController::clear_frame_pop(this, fpop);
+}
+
+
+void JvmtiEnvThreadState::clear_to_frame_pop(int frame_number)  {
+#ifdef ASSERT
+  uint32_t debug_bits = 0;
+#endif
+  assert(get_thread() == Thread::current() || JvmtiEnv::is_thread_fully_suspended(get_thread(), false, &debug_bits),
+         "frame pop data only accessible from same thread or while suspended");
+  JvmtiFramePop fpop(frame_number);
+  JvmtiEventController::clear_to_frame_pop(this, fpop);
+}
+
+
+bool JvmtiEnvThreadState::is_frame_pop(int cur_frame_number) {
+#ifdef ASSERT
+  uint32_t debug_bits = 0;
+#endif
+  assert(get_thread() == Thread::current() || JvmtiEnv::is_thread_fully_suspended(get_thread(), false, &debug_bits),
+         "frame pop data only accessible from same thread or while suspended");
+  if (!get_thread()->is_interp_only_mode() || _frame_pops == NULL) {
+    return false;
+  }
+  JvmtiFramePop fp(cur_frame_number);
+  return get_frame_pops()->contains(fp);
+}
+
+
+class VM_GetCurrentLocation : public VM_Operation {
+ private:
+   JavaThread *_thread;
+   jmethodID _method_id;
+   int _bci;
+
+ public:
+  VM_GetCurrentLocation(JavaThread *thread) {
+     _thread = thread;
+   }
+  VMOp_Type type() const { return VMOp_GetCurrentLocation; }
+  void doit() {
+    ResourceMark rmark; // _thread != Thread::current()
+    RegisterMap rm(_thread, false);
+    javaVFrame* vf = _thread->last_java_vframe(&rm);
+    assert(vf != NULL, "must have last java frame");
+    methodOop method = vf->method();
+    _method_id = method->jmethod_id();
+    _bci = vf->bci();
+  }
+  void get_current_location(jmethodID *method_id, int *bci) {
+    *method_id = _method_id;
+    *bci = _bci;
+  }
+};
+
+void JvmtiEnvThreadState::reset_current_location(jvmtiEvent event_type, bool enabled) {
+  assert(event_type == JVMTI_EVENT_SINGLE_STEP || event_type == JVMTI_EVENT_BREAKPOINT,
+         "must be single-step or breakpoint event");
+
+  // Current location is used to detect the following:
+  // 1) a breakpoint event followed by single-stepping to the same bci
+  // 2) single-step to a bytecode that will be transformed to a fast version
+  // We skip to avoid posting the duplicate single-stepping event.
+
+  // If single-stepping is disabled, clear current location so that
+  // single-stepping to the same method and bcp at a later time will be
+  // detected if single-stepping is enabled at that time (see 4388912).
+
+  // If single-stepping is enabled, set the current location to the
+  // current method and bcp. This covers the following type of case,
+  // e.g., the debugger stepi command:
+  // - bytecode single stepped
+  // - SINGLE_STEP event posted and SINGLE_STEP event disabled
+  // - SINGLE_STEP event reenabled
+  // - bytecode rewritten to fast version
+
+  // If breakpoint event is disabled, clear current location only if
+  // single-stepping is not enabled.  Otherwise, keep the thread location
+  // to detect any duplicate events.
+
+  if (enabled) {
+    // If enabling breakpoint, no need to reset.
+    // Can't do anything if empty stack.
+    if (event_type == JVMTI_EVENT_SINGLE_STEP && _thread->has_last_Java_frame()) {
+      jmethodID method_id;
+      int bci;
+      // The java thread stack may not be walkable for a running thread
+      // so get current location at safepoint.
+      VM_GetCurrentLocation op(_thread);
+      VMThread::execute(&op);
+      op.get_current_location(&method_id, &bci);
+      set_current_location(method_id, bci);
+    }
+  } else if (event_type == JVMTI_EVENT_SINGLE_STEP || !is_enabled(JVMTI_EVENT_SINGLE_STEP)) {
+    // If this is to disable breakpoint, also check if single-step is not enabled
+    clear_current_location();
+  }
+}