1 #
   2 # Copyright 2015 Google, Inc.  All Rights Reserved.
   3 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4 #
   5 # This code is free software; you can redistribute it and/or modify it
   6 # under the terms of the GNU General Public License version 2 only, as
   7 # published by the Free Software Foundation.  This particular file is
   8 # subject to the "Classpath" exception as provided in the LICENSE file
   9 # that accompanied this code.
  10 #
  11 # This code is distributed in the hope that it will be useful, but WITHOUT
  12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14 # version 2 for more details (a copy is included in the LICENSE file that
  15 # accompanied this code).
  16 #
  17 # You should have received a copy of the GNU General Public License version
  18 # 2 along with this work; if not, write to the Free Software Foundation,
  19 # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20 #
  21 # Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22 # or visit www.oracle.com if you need additional information or have any
  23 # questions.
  24 #
  25 
  26 """Frame unwinder and frame filter to display Java frames in GDB backtraces.
  27 
  28 How to use it
  29 * Copy this file into the directory containing libjvm.so under the name
  30   libjvm.so-gdb.py (it should be jre/lib/amd64/{client,server}/ in the JDK
  31   installation directory.
  32 * Use GDB 7.9 (as of 26-Feb-2015, not released yet).
  33 * Enable autoloading in your GDB setup.
  34 
  35 This is prototype code, DO NOT COMMIT.
  36 TODO:
  37 * Move platform-dependent part into per-platform module and arrange this
  38   script to import the corresponding platform module dynamically.
  39 * Provide build machinery.
  40 * Add tests that load this module into GDB and verify it traverses various
  41   HotSpot structures correctly.
  42 """
  43 
  44 import itertools
  45 import re
  46 import traceback
  47 import gdb
  48 
  49 from gdb.FrameDecorator import FrameDecorator
  50 from gdb.sniffer import Sniffer
  51 
  52 
  53 def _init_jvm():
  54   """Initializes JVM if necessary and returns it."""
  55   inferior = gdb.selected_inferior().pid
  56   if inferior in jvms:
  57     return jvms[inferior]
  58 
  59   try:
  60     for objfile in gdb.objfiles():
  61       if not objfile.filename.endswith("libjvm.so"):
  62         continue
  63       # NOTE: gdb.parse_and_eval() cannot be called here: it fiddles
  64       # with frame chain, so if _init_jvm is called from the sniffer,
  65       # we can get into trouble. Fortunately, gdb.lookup_global_symbol
  66       # does not fiddle with the frame chain.
  67       init_symbol = gdb.lookup_global_symbol("Universe::_fully_initialized",
  68                                              gdb.SYMBOL_VAR_DOMAIN)
  69       if (init_symbol is not None and init_symbol.is_valid()
  70           and init_symbol.value()):
  71         # if False:
  72         jvms[inferior] = Jvm()
  73         FrameFilter()
  74         return jvms[inferior]
  75       else:
  76         return None
  77   except (AttributeError, NameError, RuntimeError):
  78     print(traceback.format_exc())
  79 
  80   return None
  81 
  82 
  83 class Jdk8Amd64Sniffer(Sniffer):
  84   """Inferior-specific sniffer class."""
  85 
  86   amd64_rbp_num = 6
  87   amd64_rsp_num = 7
  88   amd64_rip_num = 16
  89   CALL_STUB_START = None
  90   CALL_STUB_END = None
  91 
  92   def __init__(self):
  93     super(Jdk8Amd64Sniffer, self).__init__("jdk8-amd64-sniffer")
  94     self.initializing_jvm = False
  95 
  96   def __call__(self, sniffer_info):
  97     """GDB Sniffer for the frames created by the JVM."""
  98     # print "->Sniffer.sniff\n"
  99     if self.initializing_jvm:
 100       return None
 101 
 102     self.initializing_jvm = True
 103     result = self._sniff(sniffer_info)
 104     self.initializing_jvm = False
 105     return result
 106 
 107   def _sniff(self, sniffer_info):
 108     """TODO."""
 109     jvm = _init_jvm()
 110     if jvm is None:
 111       return None
 112 
 113     if Jdk8Amd64Sniffer.CALL_STUB_START is None:
 114       Jdk8Amd64Sniffer.CALL_STUB_START = gdb.parse_and_eval(
 115           "StubRoutines::_call_stub_entry")
 116       # NOTE(asmundak): assume that StubRoutines::_catch_exception_entry
 117       # is initialized immediately after _call_stub_entry
 118       Jdk8Amd64Sniffer.CALL_STUB_END = gdb.parse_and_eval(
 119           "StubRoutines::_catch_exception_entry")
 120     r_pc = sniffer_info.read_register(Jdk8Amd64Sniffer.amd64_rip_num)
 121     if jvm.in_interpreter(r_pc):
 122       return self.unwind_interpreted_frame(jvm, sniffer_info)
 123     elif jvm.code_cache.contains(r_pc):
 124       return self.unwind_compiled_frame(jvm, sniffer_info)
 125     return None
 126 
 127   def unwind_interpreted_frame(self, jvm, sniffer_info):
 128     """Unwind interpreted frame."""
 129     jvm_frame = self.create_jvm_frame(jvm, sniffer_info)
 130     if jvm_frame is None:
 131       return None
 132     jvm_java_frame = jvm.locate_java_vframe(jvm_frame)
 133     if jvm_java_frame is None:
 134       print("Error: cannot find VFrame for the interpreted code at %s."
 135             " FP=%s is probably incorrect\n" %
 136             (hexstr(jvm_frame.machine_frame.pc),
 137              hexstr(jvm_frame.machine_frame.fp)))
 138       return None
 139     if not isinstance(jvm_java_frame, JvmInterpretedFrame):
 140       print("Error: expected vframe for the interpreter at %s\n" %
 141             hexstr(jvm_frame.machine_frame.pc))
 142       return None
 143 
 144     next_frame = jvm_java_frame.machine_frame.sender()
 145     if _debug > 0:
 146       machine_frame = jvm_frame.machine_frame
 147       self.print_unwind_result(
 148           "INTR", machine_frame, next_frame.fp, next_frame.unextended_sp,
 149           next_frame.pc)
 150 
 151     return ((( Jdk8Amd64Sniffer.amd64_rbp_num, next_frame.fp),
 152              ( Jdk8Amd64Sniffer.amd64_rip_num, next_frame.pc),
 153              ( Jdk8Amd64Sniffer.amd64_rsp_num, next_frame.unextended_sp)),
 154             ( Jdk8Amd64Sniffer.amd64_rsp_num,  Jdk8Amd64Sniffer.amd64_rip_num))
 155 
 156   def unwind_compiled_frame(self, jvm, sniffer_info):
 157     """Unwinds compiled frame."""
 158     jvm_frame = self.create_jvm_frame(jvm, sniffer_info)
 159     machine_frame = jvm_frame.machine_frame
 160     p_code_blob = jvm.code_cache.find_blob_unsafe(machine_frame.pc)
 161     if p_code_blob is None:
 162       return None
 163     if jvm.is_instance_of(p_code_blob, "nmethod"):
 164       jvm_java_frame = jvm.locate_java_vframe(jvm_frame)
 165       if jvm_java_frame is not None:
 166         next_machine_frame = jvm_java_frame.machine_frame.sender()
 167         next_fp = next_machine_frame.fp
 168         next_sp = next_machine_frame.unextended_sp
 169         next_pc = next_machine_frame.pc
 170       else:
 171         next_sp = machine_frame.sp + CodeBlob(jvm, p_code_blob).frame_size
 172         next_fp = jvm.char_ptr_at(next_sp - 2 * jvm.pointer_size)
 173         next_pc = jvm.char_ptr_at(next_sp - jvm.pointer_size)
 174     else:
 175       next_pc = machine_frame.sender_pc
 176       next_fp = machine_frame.link
 177       next_sp = machine_frame.sender_sp
 178 
 179     if _debug > 0:
 180       self.print_unwind_result("COMP", machine_frame, next_fp, next_sp, next_pc)
 181 
 182     return ((( Jdk8Amd64Sniffer.amd64_rbp_num, next_fp),
 183              ( Jdk8Amd64Sniffer.amd64_rip_num, next_pc),
 184              ( Jdk8Amd64Sniffer.amd64_rsp_num, next_sp)),
 185             ( Jdk8Amd64Sniffer.amd64_rsp_num,  Jdk8Amd64Sniffer.amd64_rip_num))
 186 
 187   def create_jvm_frame(self, jvm, sniffer_info):
 188     """Creates JVM frame from the sniffer info."""
 189     lwp_id = gdb.selected_thread().ptid[1]
 190     java_thread = jvm.find_thread(lwp_id)
 191     r_fp = sniffer_info.read_register(Jdk8Amd64Sniffer.amd64_rbp_num)
 192     r_pc = sniffer_info.read_register(Jdk8Amd64Sniffer.amd64_rip_num)
 193     r_sp = sniffer_info.read_register(Jdk8Amd64Sniffer.amd64_rsp_num)
 194     if java_thread is None:
 195       print("Frame(PC=%s, FP=%s, SP=%s): cannot locate Java thread for "
 196             "thread with LWP_ID=%d" %
 197             (hexstr(r_pc), hexstr(r_fp), hexstr(r_sp), lwp_id))
 198       return None
 199     return jvm.make_vframe(java_thread, r_fp, r_sp, r_pc)
 200 
 201   def print_unwind_result(self, title, mframe, next_fp, next_sp, next_pc):
 202     print("\n%s FP   %s  SP   %s  PC   %s\n"
 203           "     FP   %s  SP   %s  PC   %s" %
 204           (title,
 205            hexstr(mframe.fp), hexstr(mframe.sp), hexstr(mframe.pc),
 206            hexstr(next_fp), hexstr(next_sp), hexstr(next_pc)))
 207 
 208 
 209 class FrameFilter(object):
 210   """Frame Filter for Java."""
 211 
 212   def __init__(self):
 213     self.name = "Java"
 214     self.priority = 100
 215     self.enabled = True
 216     gdb.frame_filters[self.name] = self
 217 
 218   def filter(self, frame_iter):
 219     frame_iter = itertools.imap(JavaFrameDecorator, frame_iter)
 220     return frame_iter
 221 
 222 
 223 class JavaFrameDecorator(FrameDecorator):
 224   """FrameDecorator that can handle the frames generated by the OpenJDK JVM.
 225 
 226   """
 227   BLOBS = (
 228       ("AdapterBlob", "<AdapterBlob>"),
 229       ("BufferBlob", "<StubRoutines>"),
 230       ("DeoptimizationBlob", "<DeoptimizationStub>"),
 231       ("ExceptionBlob", "<ExceptionStub>"),
 232       ("MethodHandlesAdapterBlob", "<MethodHandles adapters>"),
 233       ("RuntimeStub", "<RuntimeStub>"),
 234       ("SafepointBlob", "<SafepointStub>"),
 235       ("SingletonBlob", "<SingletonStub"),
 236       ("UncommonTrapBlob", "<UncommonTrapStub>")
 237   )
 238 
 239   def __init__(self, fobj):
 240     super(JavaFrameDecorator, self).__init__(fobj)
 241     self.fobj = fobj
 242     self.is_java_frame = None
 243 
 244   def function(self):
 245     return (self.java_method if self.check_java_frame()
 246             else super(JavaFrameDecorator, self).function())
 247 
 248   def filename(self):
 249     return (self.java_filename if self.check_java_frame()
 250             else super(JavaFrameDecorator, self).filename())
 251 
 252   def line(self):
 253     return (self.java_line if self.check_java_frame()
 254             else super(JavaFrameDecorator, self).line())
 255 
 256   def frame_args(self):
 257     return (self.args if self.check_java_frame()
 258             else super(JavaFrameDecorator, self).frame_args())
 259 
 260   def check_java_frame(self):
 261     if self.is_java_frame is not None:
 262       return self.is_java_frame
 263 
 264     self.is_java_frame = False
 265     jvm = _init_jvm()
 266     if jvm is None:
 267       return self.is_java_frame
 268 
 269     frame = self.fobj.inferior_frame()
 270     r_pc = frame.read_register("pc")
 271     if jvm.in_interpreter(r_pc):
 272       self.process_interpreted_frame(jvm, frame)
 273     elif jvm.code_cache.contains(r_pc):
 274       self.process_compiled_frame(jvm, frame)
 275     return self.is_java_frame
 276 
 277   def process_interpreted_frame(self, jvm, frame):
 278     jvm_frame = self.make_jvm_frame(jvm, frame)
 279     if jvm_frame is None:
 280       return
 281     machine_frame = jvm_frame.machine_frame
 282     jvm_java_frame = jvm.locate_java_vframe(jvm_frame)
 283     if jvm_java_frame is None:
 284       print("Error: cannot find VFrame for the interpreted code at %s."
 285             " FP=%s is probably incorrect\n" %
 286             (hexstr(machine_frame.pc), hexstr(machine_frame.fp)))
 287       return
 288     if not isinstance(jvm_java_frame, JvmInterpretedFrame):
 289       print("Error: expected vframe for the interpreter at %s\n" %
 290             hexstr(machine_frame.pc))
 291       return
 292     # TODO(asmundak): check SP and FP alignment
 293     method = jvm_java_frame.method
 294     tag = "N" if method.is_native else "I"
 295     self.java_method = "%s.%s#%s" % (
 296         method.klass_fqname, method.name.string(), tag)
 297     source_file_name = method.method_holder.source_file_name
 298     if source_file_name is not None:
 299       self.java_filename = source_file_name.string()
 300     self.java_line = method.line_number_from_bci(jvm_java_frame.bci)
 301     self.is_java_frame = True
 302     self.build_java_frame_args(jvm_java_frame, method)
 303     return
 304 
 305   def process_compiled_frame(self, jvm, frame):
 306     p_code_blob = jvm.code_cache.find_blob_unsafe(frame.read_register("pc"))
 307     if p_code_blob is None:
 308       return
 309     self.java_filename = None
 310     self.java_line = None
 311     if jvm.is_instance_of(p_code_blob, "nmethod"):
 312       jvm_frame = self.make_jvm_frame(jvm, frame)
 313       if jvm_frame is None:
 314         return
 315       method = Method(jvm, p_code_blob.cast(jvm.t_nmethod_ptr)["_method"])
 316       self.java_method = "%s.%s#C" % (
 317           method.klass_fqname, method.name.string())
 318       source_file_name = method.method_holder.source_file_name
 319       if source_file_name is not None:
 320         self.java_filename = source_file_name.string()
 321       self.java_line = method.line_number_from_bci(jvm_frame.bci)
 322       self.build_java_frame_args(jvm_frame, method)
 323     else:
 324       self.java_method = "<Unknown code blob>"
 325       for (klass, stub_name) in JavaFrameDecorator.BLOBS:
 326         if jvm.is_instance_of(p_code_blob, klass):
 327           self.java_method = stub_name
 328           break
 329       self.args = []
 330     self.is_java_frame = True
 331     return
 332 
 333   def make_jvm_frame(self, jvm, frame):
 334     r_pc = frame.read_register("pc")
 335     r_fp = frame.read_register("rbp")
 336     r_sp = frame.read_register("sp")
 337     lwp_id = gdb.selected_thread().ptid[1]
 338     java_thread = jvm.find_thread(lwp_id)
 339     if java_thread is None:
 340       print("Frame(PC=%s, FP=%s, SP=%s): cannot locate Java thread for "
 341             "thread with LWP_ID=%d" %
 342             (hexstr(r_pc), hexstr(r_fp), hexstr(r_sp), lwp_id))
 343       return None
 344     return jvm.make_vframe(java_thread, r_fp, r_sp, r_pc)
 345 
 346   def build_java_frame_args(self, jvm_java_frame, method):
 347     self.args = ArgumentsAndLocals(jvm_java_frame, method).arguments
 348 
 349 
 350 class SymbolValueWrapper(object):
 351   """An item of a collection returned by FrameDecorator.frame_args."""
 352 
 353   def __init__(self, symbol, value=None):
 354     self._symbol = symbol
 355     self._value = value
 356 
 357   def symbol(self):
 358     return self._symbol
 359 
 360   def value(self):
 361     return self._value
 362 
 363 
 364 class Jvm(object):
 365   """OpenJDK JVM state.
 366 
 367   """
 368 
 369   # Type sizes, in stack units
 370   BOOLEAN_SIZE = 1
 371   CHAR_SIZE = 1
 372   FLOAT_SIZE = 1
 373   DOUBLE_SIZE = 2
 374   BYTE_SIZE = 1
 375   SHORT_SIZE = 1
 376   INT_SIZE = 1
 377   LONG_SIZE = 2
 378   OBJECT_SIZE = 1
 379   ARRAY_SIZE = 1
 380 
 381   def __init__(self):
 382     self.hs_version_string = gdb.parse_and_eval(
 383         "Abstract_VM_Version::_s_vm_release").string()
 384     match = re.match("^(\\d+)", self.hs_version_string)
 385     if match:
 386       self.hs_version = int(match.group(1))
 387     else:
 388       self.hs_version = None
 389     self.stub_queue = gdb.parse_and_eval("AbstractInterpreter::_code")
 390     self.code_cache = CodeCache(self)
 391     self.min_obj_alignment_in_bytes = gdb.parse_and_eval(
 392         "ObjectAlignmentInBytes")
 393     self.is_compressed_oops_enabled = gdb.parse_and_eval("UseCompressedOops")
 394     self.invocation_entry_bci = gdb.parse_and_eval("InvocationEntryBci")
 395 
 396     self.t_array_oop = gdb.lookup_type("arrayOopDesc").pointer()
 397     self.t_bool = gdb.lookup_type("bool")
 398     self.t_char_ptr = gdb.lookup_type("unsigned char").pointer()
 399     self.t_char_ptr_ptr = self.t_char_ptr.pointer()
 400     self.t_codeblob_ptr = gdb.lookup_type("CodeBlob").pointer()
 401     self.t_double_ptr = gdb.lookup_type("double").pointer()
 402     self.t_float_ptr = gdb.lookup_type("float").pointer()
 403     self.t_int = gdb.lookup_type("int")
 404     self.t_int_ptr = self.t_int.pointer()
 405     self.t_int_ptr_ptr = self.t_int_ptr.pointer()
 406     self.t_long_ptr = gdb.lookup_type("long").pointer()
 407     self.t_short_ptr = gdb.lookup_type("short").pointer()
 408     self.t_u2ptr = gdb.lookup_type("unsigned short").pointer()
 409     self.t_ulong = gdb.lookup_type("unsigned long")
 410     self.t_void_ptr = gdb.lookup_type("void").pointer()
 411     self.t_local_variable_table_element_ptr = gdb.lookup_type(
 412         "LocalVariableTableElement").pointer()
 413     self.t_metadata_ptr_ptr = gdb.lookup_type("Metadata").pointer().pointer()
 414     self.t_method_ptr = gdb.lookup_type("Method").pointer()
 415     self.t_narrow_oop = gdb.lookup_type("narrowOop")
 416     self.t_narrow_oop_ptr = self.t_narrow_oop.pointer()
 417     self.t_nmethod_ptr = gdb.lookup_type("nmethod").pointer()
 418     self.t_obj_array_oop = gdb.lookup_type("objArrayOopDesc").pointer()
 419     self.t_obj_array_klass_ptr = gdb.lookup_type("ObjArrayKlass").pointer()
 420     self.t_oop = gdb.lookup_type("oop")
 421     self.t_oop_ptr = self.t_oop.pointer()
 422     self.t_instance_klass_ptr = gdb.lookup_type("InstanceKlass").pointer()
 423     self.t_klass_ptr = gdb.lookup_type("Klass").pointer()
 424     self.t_pcdesc_ptr = gdb.lookup_type("PcDesc").pointer()
 425     self.t_symbol_ptr = gdb.lookup_type("Symbol").pointer()
 426     self.t_symbol_ptr_ptr = self.t_symbol_ptr.pointer()
 427     self.t_type_array_oop = gdb.lookup_type("typeArrayOopDesc").pointer()
 428 
 429     self.oop_size = gdb.parse_and_eval("oopSize")
 430     self.const_method_oop_desc_size = gdb.lookup_type("ConstMethod").sizeof
 431     self.heap_word_size = gdb.parse_and_eval("HeapWordSize")
 432     self.pointer_size = self.t_char_ptr.sizeof
 433     self.narrow_oop_base = gdb.parse_and_eval("Universe::_narrow_oop._base")
 434     self.narrow_oop_shift = gdb.parse_and_eval("Universe::_narrow_oop._shift")
 435     self.narrow_klass_base = gdb.parse_and_eval("Universe::_narrow_klass._base")
 436     self.narrow_klass_shift = gdb.parse_and_eval(
 437         "Universe::_narrow_klass._shift")
 438 
 439     self.fields_straddle = gdb.parse_and_eval("FieldInfo::field_slots")
 440     self.fields_name_offset = gdb.parse_and_eval("FieldInfo::name_index_offset")
 441 
 442     self.vm_symbols = gdb.parse_and_eval("vmSymbols::_symbols")
 443     self.vm_symbols_first_sid = gdb.parse_and_eval("vmSymbols::FIRST_SID")
 444     self.vm_symbols_sid_limit = gdb.parse_and_eval("vmSymbols::SID_LIMIT")
 445 
 446     self.symbol_table = SymbolTable(self)
 447 
 448     self.cpp_class_vtable_cache = {}
 449     self.known_klasses = {}
 450     for (klass_name, klass_class) in (
 451         ("InstanceKlass", InstanceKlass),
 452         ("ArrayKlass", ArrayKlass),
 453         ("TypeArrayKlass", TypeArrayKlass),
 454         ("ObjArrayKlass", ObjArrayKlass),
 455         ("InstanceMirrorKlass", InstanceMirrorKlass),
 456         ("InstanceRefKlass", InstanceRefKlass),
 457         ("InstanceClassLoaderKlass", InstanceClassLoaderKlass)):
 458       vtable_address = self.vtable_address(klass_name)
 459       if vtable_address is None:
 460         raise RuntimeError("Initialization failed")
 461       self.known_klasses[long(vtable_address)] = klass_class
 462 
 463     self.object_heap = VmObjectHeap(self)
 464 
 465   @property
 466   def is_debugging(self):
 467     return False
 468 
 469   @property
 470   def is_core(self):
 471     # TODO(asmundak): Should be true if neither C1 nor C2 are present.
 472     return False
 473 
 474   def find_thread(self, lwpid):
 475     """Returns an address of a thread with given LwpID."""
 476     p_thread = gdb.parse_and_eval("Threads::_thread_list")
 477     while p_thread != 0:
 478       if lwpid == p_thread["_osthread"]["_thread_id"]:
 479         return p_thread
 480       p_thread = p_thread["_next"]
 481     return None
 482 
 483   def make_vframe(self, java_thread, r_fp, r_sp, r_pc):
 484     machine_frame = MachineFrame(self, fp=r_fp, sp=r_sp, pc=r_pc)
 485     if machine_frame.find_cb() is None:
 486       raise RuntimeError("Cannot find code blob for the frame with FP=%s, "
 487                          "SP=%s, PC=%s" %
 488                          (hexstr(r_fp), hexstr(r_sp), hexstr(r_pc)))
 489     return machine_frame.vframe(java_thread, True, True)
 490 
 491   def locate_java_vframe(self, vframe):
 492     """TODO."""
 493     if vframe is None:
 494       return None
 495     if vframe.is_java_vframe:
 496       return vframe
 497     imprecise = False
 498     if self.is_debugging:
 499       imprecise = vframe.maybe_imprecise_dbg
 500     sender_vframe = vframe.sender(imprecise)
 501     while sender_vframe is not None:
 502       if sender_vframe.is_java_vframe:
 503         break
 504       next_vframe = sender_vframe.sender(imprecise)
 505       if (sender_vframe.machine_frame.unextended_sp >=
 506           next_vframe.machine_frame.unextended_sp):
 507         return None
 508       sender_vframe = next_vframe
 509     return sender_vframe
 510 
 511   def align_object_size(self, size):
 512     return ((size + self.min_obj_alignment_in_bytes -1) &
 513             -self.min_obj_alignment_in_bytes)
 514 
 515   def is_instance_of(self, ptr, cpp_class_name):
 516     return (ptr.cast(self.t_char_ptr_ptr).dereference() ==
 517             self.cpp_vtable(cpp_class_name))
 518 
 519   def vtable_address(self, cpp_class_name):
 520     vtable_expr = "&_ZTV%d%s" % (len(cpp_class_name), cpp_class_name)
 521     try:
 522       return (gdb.parse_and_eval(vtable_expr).cast(self.t_char_ptr) +
 523               2 * self.t_char_ptr.sizeof)
 524     except gdb.error:
 525       print("%s: cannot find vtable" % cpp_class_name)
 526       return None
 527 
 528   def cpp_vtable(self, cpp_class_name):
 529     if cpp_class_name not in self.cpp_class_vtable_cache:
 530       self.cpp_class_vtable_cache[cpp_class_name] = (
 531           self.vtable_address(cpp_class_name))
 532     return self.cpp_class_vtable_cache[cpp_class_name]
 533 
 534   def in_interpreter(self, pc):
 535     stub_start = self.stub_queue["_stub_buffer"]
 536     return (pc >= stub_start and
 537             pc < stub_start + self.stub_queue["_buffer_limit"])
 538 
 539   def char_ptr_at(self, ptr):
 540     return ptr.cast(self.t_char_ptr_ptr).dereference()
 541 
 542   def decode_narrow_klass(self, narrow_klass):
 543     """Returns the address of the Klass instance."""
 544     return self.narrow_klass_base.cast(self.t_ulong) + (
 545         narrow_klass.cast(self.t_ulong) << self.narrow_klass_shift).cast(
 546             self.t_klass_ptr)
 547 
 548   def decode_narrow_oop(self, narrow_oop):
 549     """Returns oop."""
 550     return self.narrow_oop_base.cast(self.t_ulong) + (
 551         narrow_oop.cast(self.t_ulong) << self.narrow_oop_shift).cast(
 552             self.t_oop)
 553 
 554   def new_klass(self, klass_address):
 555     """Klass factory.
 556 
 557     Figures out which sublclass of JVM Klass resides at a given address and
 558     return the instance of the corresponding Python's subclass of Klass.
 559 
 560     Args:
 561       klass_address: points to JVM's Klass instance
 562     Returns:
 563       an instance of Klass subclass.
 564     Raises:
 565       RuntimeError: if cannot handle.
 566     """
 567     if klass_address is None:
 568       return None
 569     vtable = long(klass_address.cast(self.t_char_ptr_ptr).dereference())
 570     if vtable in self.known_klasses:
 571       cls = self.known_klasses[vtable]
 572       return cls(self, klass_address)
 573     raise RuntimeError("%s does not point to known Klass subclass" %
 574                        hexstr(klass_address))
 575 
 576   def new_oop(self, object_pointer):
 577     """Oop factory.
 578 
 579     Figures out what kind of oop resides at a given address and returns an
 580     instance of the corresponding Python's Oop subclass.
 581 
 582     Args:
 583       object_pointer: the pointer to oopDesc.
 584     Returns:
 585       an instance of Oop subclass.
 586     Raises:
 587       RuntimeError.
 588     """
 589     if self.is_compressed_oops_enabled:
 590       klass_pointer = self.decode_narrow_klass(
 591           object_pointer["_metadata"]["_compressed_klass"])
 592     else:
 593       klass_pointer = object_pointer["_metadata"]["_klass"]
 594     return self.new_klass(klass_pointer).new_oop(object_pointer)
 595 
 596   def symbol_at(self, index):
 597     """Returns 'global' symbol at a given index."""
 598     if index < self.vm_symbols_first_sid or index >= self.vm_symbols_sid_limit:
 599       raise IndexError("%d out of range [%d..%d)" %
 600                        (index, self.vm_symbols_first_sid,
 601                         self.vm_symbols_sid_limit))
 602     return Symbol(self, self.vm_symbols[index])
 603 
 604   def oop_value(self, oop):
 605     """Oop's printable value."""
 606     klass = oop.klass
 607     if klass.name.string() == "java/lang/String":
 608       value_field = oop.klass.find_field("value", "[C")
 609       if value_field is not None:
 610         chars = oop.obj_field(value_field.offset)
 611         return (chars.char_base.cast(self.t_char_ptr)
 612                 .string("utf-16", "replace", 2 * chars.length))
 613     elif isinstance(oop, ObjArrayOop):
 614       element_klass = klass.element_klass
 615       if element_klass.name.string() == "java/lang/String":
 616         return ("string1", "string2")
 617     return oop.address.cast(self.t_void_ptr)
 618 
 619 
 620 class SymbolTable(object):
 621   """Mirrors JVM's SymbolTable.
 622 
 623   See src/share/vm/classfile/symbolTable.{hpp, cpp}
 624   """
 625 
 626   def __init__(self, jvm):
 627     self.jvm = jvm
 628     self.symbol_table = gdb.parse_and_eval("SymbolTable::_the_table")
 629     self.t_entry = gdb.lookup_type("IntptrHashtableEntry").pointer()
 630 
 631   def verify(self):
 632     """Debugging: verifies symbol table is correct."""
 633     buckets = self.symbol_table["_buckets"]
 634     bucket_count = self.symbol_table["_table_size"]
 635     for i in range(bucket_count):
 636       entry = buckets[i]["_entry"].cast(self.t_entry)
 637       while entry != 0:
 638         sym = Symbol(self.jvm, entry["_literal"].cast(self.jvm.t_symbol_ptr))
 639         sym_string = sym.string()
 640         h1 = entry["_hash"]
 641         h2 = self._hash_symbol_name(sym_string)
 642         if h1 != h2:
 643           print("Warning: symbol's '%s' table entry has value %08x,"
 644                 " should be %08x. Symbol is unreachable" % (sym_string, h1, h2))
 645         elif i != (h1 % bucket_count):
 646           print("Warning symbol '%s' is in bucket #%d, should be in #%d" %
 647                 (sym_string, i, (h1 % bucket_count)))
 648         entry = entry["_next"].cast(self.t_entry)
 649 
 650   def probe_symbol(self, symbol_name):
 651     """Finds symbol with given name."""
 652     hash_value = self._hash_symbol_name(symbol_name)
 653     bucket_index = hash_value % self.symbol_table["_table_size"]
 654     entry = self.symbol_table["_buckets"][bucket_index]["_entry"].cast(
 655         self.t_entry)
 656     while entry != 0:
 657       if entry["_hash"] == hash_value:
 658         sym = Symbol(self.jvm, entry["_literal"].cast(self.jvm.t_symbol_ptr))
 659         if symbol_name == sym.string():
 660           return sym
 661       entry = entry["_next"].cast(self.t_entry)
 662     return None
 663 
 664   def _hash_symbol_name(self, symbol_name):
 665     h = 0L
 666     for c in symbol_name:
 667       h = 31 * h + ord(c)
 668     return h & 0xFFFFFFFFL
 669 
 670 
 671 class VmObject(object):
 672   """TODO."""
 673 
 674   def __init__(self, jvm, address):
 675     self.jvm = jvm
 676     self.address = address
 677 
 678 
 679 class Oop(VmObject):
 680   """Corresponds to JVM's oopDesc class.
 681 
 682   See src/share/vm/oops/oop.{hpp,cpp}.
 683   """
 684 
 685   def __init__(self, jvm, address):
 686     super(Oop, self).__init__(jvm, address)
 687 
 688   def __repr__(self):
 689     return "%s, klass=%s" % (self.address, self.klass)
 690 
 691   @property
 692   def klass(self):
 693     meta = self.address["_metadata"]
 694     klass_address = (self.jvm.decode_narrow_klass(meta["_compressed_klass"])
 695                      if self.jvm.is_compressed_oops_enabled
 696                      else meta["_klass"])
 697     return self.jvm.new_klass(klass_address)
 698 
 699   def obj_field(self, offset):
 700     """Fetches a field at a given offset. A field is an oop."""
 701     address = self.address.cast(self.jvm.t_char_ptr) + offset
 702     if self.jvm.is_compressed_oops_enabled:
 703       oop_address = self.jvm.decode_narrow_oop(
 704           address.cast(self.jvm.t_narrow_oop_ptr).dereference())
 705     else:
 706       oop_address = address.cast(self.jvm.t_oop_ptr).dereference()
 707     return self.jvm.new_oop(oop_address)
 708 
 709 
 710 class ArrayOop(Oop):
 711   """Abstract baseclass for all arrays.
 712 
 713   The layout of ArrayOops is:
 714   markOop
 715   Klass*    32 bits if compressed but declared 64 in LP64.
 716   length    shares klass memory or allocated after declared fields.
 717 
 718   See See src/share/vm/oops/arrayOop.{hpp,cpp}.
 719   """
 720 
 721   def __init__(self, jvm, address):
 722     super(ArrayOop, self).__init__(jvm, address)
 723 
 724   @property
 725   def length_address(self):
 726     if self.jvm.is_compressed_oops_enabled:
 727       return ((self.address["_metadata"]["_compressed_klass"].address+1)
 728               .cast(self.jvm.t_int_ptr))
 729     else:
 730       return ((self.address.cast(self.jvm.t_oop) + 1)
 731               .cast(self.jvm.t_int_ptr))
 732 
 733   @property
 734   def length(self):
 735     return self.length_address.dereference()
 736 
 737 
 738 class ObjArrayOop(ArrayOop):
 739   """An array containing oops.
 740 
 741   See src/share/vm/oops/objArrayOop.{hpp,cpp}.
 742   """
 743 
 744   def __init__(self, jvm, address):
 745     super(ObjArrayOop, self).__init__(jvm, address)
 746 
 747 
 748 class TypeArrayOop(ArrayOop):
 749   """An array containing basic types (non oop elements).
 750 
 751   It is used for arrays of {characters, singles, doubles, bytes, shorts,
 752   integers, longs}.
 753   See src/share/vm/oops/typeArrayOop.{hpp,cpp}
 754   """
 755 
 756   def __init(self, jvm, address):
 757     super(TypeArrayOop, self).__init__(jvm, address)
 758 
 759   @property
 760   def basic_type(self):
 761     return None
 762 
 763   @property
 764   def char_base(self):
 765     return (self.length_address + 1).cast(self.jvm.t_u2ptr)
 766 
 767 
 768 def hexstr(addr):
 769   if addr is None:
 770     return "NULL"
 771   return "0x%x" % addr.cast(gdb.lookup_type("long"))
 772 
 773 
 774 class CodeBlob(VmObject):
 775   """Superclass for all entries in the CodeCache.
 776 
 777   See src/share/vm/code/codeBlob.{hpp, cpp}.
 778   """
 779 
 780   def __init__(self, jvm, address):
 781     super(CodeBlob, self).__init__(jvm, address)
 782 
 783   def __repr__(self):
 784     return "%s" % self.address.dereference()
 785 
 786   @property
 787   def frame_size(self):
 788     return self.jvm.pointer_size * self.address["_frame_size"]
 789 
 790   @property
 791   def header_begin(self):
 792     return self.address.cast(self.jvm.t_char_ptr)
 793 
 794   @property
 795   def code_begin(self):
 796     return self.header_begin + self.address["_code_offset"]
 797 
 798 
 799 class NMethod(CodeBlob):
 800   """Compiled code versions of Java methods.
 801 
 802   See src/share/vm/code/nmethod.{hpp,cpp}.
 803   """
 804 
 805   def __init__(self, jvm, address):
 806     super(NMethod, self).__init__(jvm, address)
 807 
 808   @property
 809   def method(self):
 810     return Method(self.jvm, self.address["_method"])
 811 
 812   def is_deopt_pc(self, pc):
 813     return self.is_deopt_entry(pc) or self.is_deopt_mh_entry(pc)
 814 
 815   def is_deopt_mh_entry(self, pc):
 816     return pc == self.deopt_mh_handler_begin
 817 
 818   def is_deopt_entry(self, pc):
 819     return pc == self.deopt_handler_begin
 820 
 821   def is_method_handle_return(self, pc):
 822     pcdesc = self.pcdesc_at(pc)
 823     if pcdesc is not None:
 824       return pcdesc.is_method_handle_invoke
 825     return False
 826 
 827   @property
 828   def deopt_handler_begin(self):
 829     return self.header_begin + self.address["_deoptimize_offset"]
 830 
 831   @property
 832   def deopt_mh_handler_begin(self):
 833     return self.header_begin + self.address["_deoptimize_mh_offset"]
 834 
 835   @property
 836   def scopes_data_begin(self):
 837     return self.header_begin + self.address["_scopes_data_offset"]
 838 
 839   @property
 840   def oops_begin(self):
 841     return (self.header_begin +
 842             self.address["_oops_offset"]).cast(self.jvm.t_oop_ptr)
 843 
 844   def oops_size(self):
 845     return self.address["_metadata_offset"] - self.address["_oops_offset"]
 846 
 847   @property
 848   def metadata_begin(self):
 849     return (self.header_begin +
 850             self.address["_metadata_offset"]).cast(self.jvm.t_metadata_ptr_ptr)
 851 
 852   @property
 853   def metadata_size(self):
 854     return (self.address["_scopes_data_offset"] -
 855             self.address["_metadata_offset"])
 856 
 857   def scope_desc_near_dbg(self, pc):
 858     pc_desc = self.pcdesc_near_dbg(pc)
 859     if pc_desc is None:
 860       return None
 861     return ScopeDesc(self, pc_desc.scope_decode_offset,
 862                      pc_desc.obj_decode_offset, pc_desc.reexecute)
 863 
 864   def pcdesc_near_dbg(self, pc_needle):
 865     """TODO."""
 866     best_guess_pc_desc = None
 867     best_distance = 0
 868     pc_needle_offset = pc_needle - self.code_begin
 869     for p_pcdesc in self.scopes_pcs():
 870       distance = pc_needle_offset - p_pcdesc["_pc_offset"]
 871       if best_guess_pc_desc is None or (distance >= 0 and
 872                                         distance < best_distance):
 873         best_guess_pc_desc = p_pcdesc
 874         best_distance = distance
 875     if best_guess_pc_desc is not None:
 876       return PcDesc(self.jvm, best_guess_pc_desc)
 877     return None
 878 
 879   def pcdesc_at(self, pc):
 880     """TODO."""
 881     pc_offset = pc - self.code_begin
 882     for p_pcdesc in self.scopes_pcs():
 883       if pc_offset == p_pcdesc["_pc_offset"]:
 884         return PcDesc(self.jvm, p_pcdesc)
 885     return None
 886 
 887   def scopes_pcs(self):
 888     p_pcdesc = (
 889         self.header_begin + self.address["_scopes_pcs_offset"]).cast(
 890             self.jvm.t_pcdesc_ptr)
 891     p_pcdesc_end = (
 892         self.header_begin + self.address["_dependencies_offset"]).cast(
 893             self.jvm.t_pcdesc_ptr)
 894     while p_pcdesc < p_pcdesc_end:
 895       yield p_pcdesc
 896       p_pcdesc += 1
 897 
 898   def oop_at(self, i):
 899     if i == 0:
 900       return None
 901     if i < 0 or i >= self.oops_size:
 902       raise RuntimeError("%s: index %d out of bounds" % (self, i))
 903     return (self.oops_begin + i - 1).dereference
 904 
 905   def method_at(self, i):
 906     return Method(self.jvm, self.metadata_at(i).cast(self.jvm.t_char_ptr))
 907 
 908   def metadata_at(self, i):
 909     if i == 0:
 910       return None
 911     if i < 0 or i >= self.metadata_size:
 912       raise RuntimeError("%s: index %d out of bounds" % (self, i))
 913     return (self.metadata_begin + i - 1).dereference()
 914 
 915   @property
 916   def orig_pc_offset(self):
 917     return self.address["_orig_pc_offset"]
 918 
 919 
 920 class CodeCache(object):
 921   """Various pieces of generated code.
 922 
 923   (e.g., compiled java methods, runtime stubs, transition frames, etc.)
 924   The entries in the CodeCache are all CodeBlob's.
 925   See src/share/vm/code/codeCache.{hpp,cpp}
 926   """
 927 
 928   def __init__(self, jvm):
 929     self.jvm = jvm
 930     self.heap = CodeHeap(jvm, gdb.parse_and_eval("CodeCache::_heap"))
 931 
 932   def contains(self, pc):
 933     return self.heap.contains(pc)
 934 
 935   def find_blob_unsafe(self, pc):
 936     """Returns CodeBlob containing given PC."""
 937     return self.heap.find_start(pc)
 938 
 939   def find_blob(self, pc):
 940     code_blob = self.find_blob_unsafe(pc)
 941     if code_blob is None:
 942       return None
 943     if self.jvm.is_debugging:
 944       return code_blob
 945     # Assert.that(!(code_blob.isZombie() || code_blob.isLockedByVM())
 946     return code_blob
 947 
 948 
 949 class CodeHeap(object):
 950   """Memory for the CodeCache.
 951 
 952   See src/share/vm/memory/heap.{hpp,cpp}.
 953   """
 954 
 955   def __init__(self, jvm, address):
 956     self.jvm = jvm
 957     self.p_code_heap = address
 958 
 959   def contains(self, pc):
 960     mem = self.p_code_heap["_memory"]
 961     return mem["_low"] <= pc and pc < mem["_high"]
 962 
 963   def find_start(self, pc):
 964     if not self.contains(pc):
 965       return None
 966     p_heap_block = self.block_start(pc)
 967     if p_heap_block is None:
 968       return None
 969     if p_heap_block["_header"]["_used"]:
 970       return (p_heap_block+1).cast(self.jvm.t_char_ptr)
 971     return None
 972 
 973   def block_start(self, pc):
 974     """Locates HeapBlock containing given address."""
 975     memory_low = self.p_code_heap["_memory"]["_low"].cast(self.jvm.t_char_ptr)
 976     shift_value = self.p_code_heap["_log2_segment_size"]
 977     seg_nr = (pc - memory_low) >> shift_value
 978     seg_nr = (pc - memory_low) >> shift_value
 979     b = self.p_code_heap["_segmap"]["_low"].cast(self.jvm.t_char_ptr)
 980     count = (b + seg_nr).dereference()
 981     if count == 255:
 982       return None
 983     while count > 0:
 984       seg_nr -= count
 985       count = (b + seg_nr).dereference()
 986     ptr = memory_low + (seg_nr << shift_value)
 987     return ptr.cast(gdb.lookup_type("HeapBlock").pointer())
 988 
 989 
 990 class VmObjectHeap(object):
 991   """TODO."""
 992 
 993   def __init__(self, jvm):
 994     self.jvm = jvm
 995     self.narrow_oop_base = gdb.parse_and_eval("Universe::_narrow_oop._base")
 996     self.narrow_oop_shift = gdb.parse_and_eval("Universe::_narrow_oop._shift")
 997     return
 998 
 999   def decode_narrow_oop(self, narrow_oop):
1000     return self.narrow_oop_base + (narrow_oop << self.narrow_oop_shift)
1001 
1002   def metadata_for(self, ptr):
1003     if ptr is None or ptr == 0:
1004       return None
1005     hs_type = self.jvm.types.dynamic_type_for_address(ptr, self.metadata_t)
1006     if hs_type is None:
1007       raise RuntimeError("%s does does not point to an instance of %s" %
1008                          (ptr, self.metadata_t.name))
1009     return self.metadata_subclasses[hs_type.name](self.jvm, ptr)
1010 
1011 
1012 class FieldInfo(object):
1013   """Field information.
1014 
1015   Obtained from the contents in an element of the _fields array of
1016   an InstanceKlass.
1017   """
1018 
1019   FIELDINFO_TAG_MASK = 0x0003
1020   FIELDINFO_TAG_OFFSET = 0x0001
1021 
1022   def __init__(self, klass, index):
1023     self.klass = klass
1024     self.jvm = klass.jvm
1025     self.index = index
1026     fields = klass.address["_fields"]["_data"]
1027     base = index * 6
1028     self.access_flags = fields[base]
1029     self.name_index = fields[base+1]
1030     self.signature_index = fields[base+2]
1031     self.initval_index = fields[base+3]
1032     self.low_packed_offset = fields[base+4]
1033     self.high_packed_offset = fields[base+5]
1034 
1035   @property
1036   def name(self):
1037     if self.access_flags & ClassConstants.JVM_ACC_FIELD_INTERNAL == 0:
1038       return self.klass.constants.symbol_at(self.name_index)
1039     else:
1040       return self.jvm.symbol_at(self.name_index)
1041 
1042   @property
1043   def signature(self):
1044     if self.access_flags & ClassConstants.JVM_ACC_FIELD_INTERNAL == 0:
1045       return self.klass.constants.symbol_at(self.signature_index)
1046     else:
1047       return self.jvm.symbol_at(self.signature_index)
1048 
1049   @property
1050   def offset(self):
1051     if (self.low_packed_offset & FieldInfo.FIELDINFO_TAG_MASK ==
1052         FieldInfo.FIELDINFO_TAG_OFFSET):
1053       return (self.low_packed_offset + (self.high_packed_offset<<16)) >> 2
1054     else:
1055       raise RuntimeError("Bad field offset for %s", self)
1056 
1057 
1058 class Metadata(VmObject):
1059   """Reflects share/vm/oops/metadata.hpp."""
1060 
1061   def __init__(self, jvm, address):
1062     super(Metadata, self).__init__(jvm, address)
1063 
1064 
1065 class Klass(Metadata):
1066   """Mirrors JVM's Klass.
1067 
1068   See src/share/vm/oops/klass.{hpp,cpp}.
1069   """
1070 
1071   def __init__(self, jvm, address):
1072     super(Klass, self).__init__(jvm, address)
1073 
1074   @property
1075   def name(self):
1076     return Symbol(self.jvm, self.address["_name"])
1077 
1078   @property
1079   def superclass(self):
1080     return self.jvm.new_klass(self.address["_super"])
1081 
1082 
1083 class ArrayKlass(Klass):
1084   """Abstract base class of all array classes."""
1085 
1086   def __init__(self, jvm, address):
1087     super(ArrayKlass, self).__init__(jvm, address)
1088 
1089 
1090 class TypeArrayKlass(ArrayKlass):
1091   """Klass for base types array."""
1092 
1093   def __init__(self, jvm, address):
1094     super(TypeArrayKlass, self).__init__(jvm, address)
1095 
1096   def new_oop(self, address):
1097     return TypeArrayOop(self.jvm,
1098                         address.cast(self.jvm.t_type_array_oop))
1099 
1100 
1101 class ObjArrayKlass(ArrayKlass):
1102   """Klass for object arrays."""
1103 
1104   def __init__(self, jvm, address):
1105     super(ObjArrayKlass, self).__init__(
1106         jvm, address.cast(jvm.t_obj_array_klass_ptr))
1107 
1108   def new_oop(self, address):
1109     return ObjArrayOop(self.jvm,
1110                        address.cast(self.jvm.t_obj_array_oop))
1111 
1112   @property
1113   def element_klass(self):
1114     return self.jvm.new_klass(self.address["_element_klass"])
1115 
1116 
1117 class InstanceKlass(Klass):
1118   """VM level representation of a Java class."""
1119 
1120   def __init__(self, jvm, address):
1121     super(InstanceKlass, self).__init__(
1122         jvm, address.cast(jvm.t_instance_klass_ptr))
1123 
1124   def __repr__(self):
1125     return "%s" % self.address.dereference()
1126 
1127   def new_oop(self, address):
1128     return Oop(self.jvm, address.cast(self.jvm.t_oop))
1129 
1130   @property
1131   def source_file_name(self):
1132     return self.constants.symbol_at(self.address["_source_file_name_index"])
1133 
1134   @property
1135   def methods(self):
1136     return MethodArray(self.jvm, self.address["_methods"])
1137 
1138   def method_with_idnum(self, idnum):
1139     """TODO."""
1140     m = None
1141     methods = self.methods
1142     if idnum < methods.length:
1143       m = methods.at(idnum)
1144     if m is None or m.method_idnum != idnum:
1145       for i in range(methods.length):
1146         m = methods.at(i)
1147         if m.method_idnum == idnum:
1148           break
1149     return m
1150 
1151   @property
1152   def constants(self):
1153     return ConstantPool(self.jvm, self.address["_constants"])
1154 
1155   def find_field(self, field_name, field_signature):
1156 
1157     """Finds field.
1158 
1159     Finds field according to JVM spec 5.4.3.2, returns the klass in
1160     which the field is defined (convenience routine).
1161 
1162     Args:
1163       field_name: Field name symbol.
1164       field_signature: Signature encoding field type.
1165 
1166     Returns:
1167       FieldInfo or None
1168     """
1169     symbols = self.jvm.symbol_table
1170     name_symbol = symbols.probe_symbol(field_name)
1171     signature_symbol = symbols.probe_symbol(field_signature)
1172     if name_symbol is None or signature_symbol is None:
1173       return None
1174     return self.find_field_by_symbol(name_symbol, signature_symbol)
1175 
1176   def find_field_by_symbol(self, name_symbol, signature_symbol):
1177     f = self.find_local_field(name_symbol, signature_symbol)
1178     if f is None:
1179       f = self.find_interface_field(name_symbol, signature_symbol)
1180     if f is None:
1181       superclass = self.superclass
1182       if superclass is not None:
1183         f = superclass.find_field_by_symbol(name_symbol, signature_symbol)
1184     return f
1185 
1186   def find_local_field(self, name_symbol, signature_symbol):
1187     for i in range(self.java_fields_count):
1188       field = FieldInfo(self, i)
1189       if (name_symbol == field.name and
1190           signature_symbol == field.signature):
1191         return field
1192     return None
1193 
1194   def find_interface_field(self, name_symbol, signature_symbol):
1195     """TODO."""
1196     local_interfaces = self.address["_local_interfaces"]
1197     for i in range(0, local_interfaces["_length"]):
1198       intf = self.jvm.new_klass(local_interfaces["_data"][i])
1199       f = intf.find_local_field(name_symbol, signature_symbol)
1200       if f is None:
1201         f = intf.find_interface_field(name_symbol, signature_symbol)
1202       if f is not None:
1203         return f
1204     return None
1205 
1206   @property
1207   def java_fields_count(self):
1208     return self.address["_java_fields_count"]
1209 
1210 
1211 class InstanceMirrorKlass(InstanceKlass):
1212   """A specialized InstanceKlass for java.lang.Class instances.
1213 
1214   See src/share/vm/oops/instanceMirrorKlass.{hpp,cpp}
1215   """
1216 
1217   def __init__(self, jvm, address):
1218     super(InstanceMirrorKlass, self).__init__(jvm, address)
1219 
1220 
1221 class InstanceRefKlass(InstanceKlass):
1222   """A specialized InstanceKlass for subclasses of java/lang/ref/Reference.
1223 
1224   See src/share/vm/oops/instanceRefKlass.{hpp,cpp}.
1225   """
1226 
1227   def __init__(self, jvm, address):
1228     super(InstanceRefKlass, self).__init__(jvm, address)
1229 
1230 
1231 class InstanceClassLoaderKlass(InstanceKlass):
1232   """A specialization of the InstanceKlass.
1233 
1234   See src/share/vm/oops/instanceClassLoaderKlass.{hpp,cpp}.
1235   """
1236 
1237   def __init__(self, jvm, address):
1238     super(InstanceClassLoaderKlass, self).__init__(jvm, address)
1239 
1240 
1241 class Method(Metadata):
1242   """Represents Java method.
1243 
1244   See src/share/vm/oops/method.{hpp,cpp}
1245   """
1246 
1247   def __init__(self, jvm, address):
1248     super(Method, self).__init__(jvm, address)
1249 
1250   def __repr__(self):
1251     return "%s" % self.address.dereference()
1252 
1253   @property
1254   def name(self):
1255     return self.constants.symbol_at(self.address["_constMethod"]["_name_index"])
1256 
1257   @property
1258   def klass_fqname(self):
1259     return self.method_holder.name.string().replace("/", ".")
1260 
1261   @property
1262   def const_method(self):
1263     return ConstMethod(self.jvm, self.address["_constMethod"])
1264 
1265   @property
1266   def constants(self):
1267     return ConstantPool(self.jvm, self.address["_constMethod"]["_constants"])
1268 
1269   @property
1270   def method_holder(self):
1271     return InstanceKlass(
1272         self.jvm, self.address["_constMethod"]["_constants"]["_pool_holder"])
1273 
1274   @property
1275   def is_native(self):
1276     return self.access_flags & ClassConstants.JVM_ACC_NATIVE != 0
1277 
1278   @property
1279   def is_static(self):
1280     return self.access_flags & ClassConstants.JVM_ACC_STATIC != 0
1281 
1282   @property
1283   def access_flags(self):
1284     return self.address["_access_flags"].cast(self.jvm.t_int)
1285 
1286   @property
1287   def size_of_parameters(self):
1288     return self.const_method.size_of_parameters
1289 
1290   @property
1291   def signature(self):
1292     return self.constants.symbol_at(self.const_method.signature_index)
1293 
1294   @property
1295   def has_local_variable_table(self):
1296     return self.const_method.has_localvariable_table
1297 
1298   @property
1299   def first_local_variable_ptr(self):
1300     return self.const_method.first_local_variable_ptr
1301 
1302   @property
1303   def localvariable_table_length(self):
1304     return self.const_method.localvariable_table_length
1305 
1306   @property
1307   def method_idnum(self):
1308     return self.address["_constMethod"]["_method_idnum"]
1309 
1310   def line_number_from_bci(self, bci):
1311     return self.const_method.line_number_from_bci(bci)
1312 
1313   def bci_from(self, bcx):
1314     return bcx - self.const_method.code_base
1315 
1316   @property
1317   def max_locals(self):
1318     return self.const_method.max_locals
1319 
1320   def mask_for(self, _):
1321     # entry = OopMapCacheEntry()
1322     # entry.fill(this, bci)
1323     # return entry
1324     return None
1325 
1326 
1327 class MethodData(Metadata):
1328   """TODO."""
1329 
1330   def __init__(self, jvm, address):
1331     super(MethodData, self).__init__(jvm, address)
1332 
1333 
1334 class ConstMethod(VmObject):
1335   """Represents portions of a Java method which do not vary.
1336 
1337   See src/share/vm/oops/constMethod.{hpp,cpp}.
1338   """
1339 
1340   HAS_LINENUMBER_TABLE = None
1341 
1342   def __init__(self, jvm, address):
1343     super(ConstMethod, self).__init__(jvm, address)
1344     if ConstMethod.HAS_LINENUMBER_TABLE is None:
1345       ConstMethod.init_constants()
1346     self._is_native = None
1347 
1348   def __repr__(self):
1349     return "%s" % self.address
1350 
1351   @classmethod
1352   def init_constants(cls):
1353     """TODO."""
1354     t_int = gdb.lookup_type("int")
1355     ConstMethod.HAS_LINENUMBER_TABLE = gdb.parse_and_eval(
1356         "ConstMethod::_has_linenumber_table").cast(t_int)
1357     ConstMethod.HAS_LOCALVARIABLE_TABLE = gdb.parse_and_eval(
1358         "ConstMethod::_has_localvariable_table").cast(t_int)
1359     ConstMethod.HAS_CHECKED_EXCEPTIONS = gdb.parse_and_eval(
1360         "ConstMethod::_has_checked_exceptions").cast(t_int)
1361     ConstMethod.HAS_EXCEPTION_TABLE = gdb.parse_and_eval(
1362         "ConstMethod::_has_exception_table").cast(t_int)
1363     ConstMethod.HAS_METHOD_PARAMETERS = gdb.parse_and_eval(
1364         "ConstMethod::_has_method_parameters").cast(t_int)
1365     ConstMethod.HAS_GENERIC_SIGNATURE = gdb.parse_and_eval(
1366         "ConstMethod::_has_generic_signature").cast(t_int)
1367     ConstMethod.HAS_METHOD_ANNOTATIONS = gdb.parse_and_eval(
1368         "ConstMethod::_has_method_annotations").cast(t_int)
1369     ConstMethod.HAS_PARAMETER_ANNOTATIONS = gdb.parse_and_eval(
1370         "ConstMethod::_has_parameter_annotations").cast(t_int)
1371     ConstMethod.HAS_TYPE_ANNOTATIONS = gdb.parse_and_eval(
1372         "ConstMethod::_has_type_annotations").cast(t_int)
1373     ConstMethod.HAS_DEFAULT_ANNOTATIONS = gdb.parse_and_eval(
1374         "ConstMethod::_has_default_annotations").cast(t_int)
1375     ConstMethod.EXCEPTION_TABLE_STRIDE = gdb.lookup_type(
1376         "ExceptionTableElement").sizeof
1377     ConstMethod.CHECKED_EXCEPTION_STRIDE = gdb.lookup_type(
1378         "CheckedExceptionElement").sizeof
1379     ConstMethod.LOCALVARIABLE_TABLE_STRIDE = gdb.lookup_type(
1380         "LocalVariableTableElement").sizeof
1381 
1382   @property
1383   def name_index(self):
1384     return self.address["_name_index"]
1385 
1386   @property
1387   def signature_index(self):
1388     return self.address["_signature_index"]
1389 
1390   @property
1391   def constants(self):
1392     return ConstantPool(self.jvm, self.address["_constants"])
1393 
1394   def line_number_from_bci(self, bci_needle):
1395     """TODO."""
1396 
1397     if self.is_native or not self.has_line_number_table:
1398       return None
1399     best_bci = -1
1400     best_line = -1
1401     for (bci, line) in self.compressed_line_numbers():
1402       # print "Read bci=%d, line=%d" % (bci, line)
1403       if bci == bci_needle:
1404         return line if line >= 0 else None
1405       if bci < bci_needle and bci > best_bci:
1406         best_bci = bci
1407         best_line = line
1408     return best_line if best_line >= 0 else None
1409 
1410   def compressed_line_numbers(self):
1411     stream = CompressedStream(self.code_end)
1412     bci = 0
1413     line = 0
1414     while True:
1415       byte = stream.read_byte()
1416       if byte == 0:
1417         return
1418       if byte == 255:
1419         bci += stream.read_signed_int()
1420         line += stream.read_signed_int()
1421       else:
1422         bci += byte >> 3
1423         line += byte & 7
1424       yield bci, line
1425 
1426   @property
1427   def is_native(self):
1428     if self._is_native is None:
1429       self._is_native = self.method.is_native
1430     return self._is_native
1431 
1432   @property
1433   def method(self):
1434     klass = InstanceKlass(self.jvm, self.address["_constants"]["_pool_holder"])
1435     return klass.method_with_idnum(self.address["_method_idnum"])
1436 
1437   @property
1438   def has_line_number_table(self):
1439     return self.address["_flags"] & ConstMethod.HAS_LINENUMBER_TABLE
1440 
1441   @property
1442   def has_localvariable_table(self):
1443     return self.address["_flags"] & ConstMethod.HAS_LOCALVARIABLE_TABLE
1444 
1445   @property
1446   def has_exception_table(self):
1447     return self.address["_flags"] & ConstMethod.HAS_EXCEPTION_TABLE
1448 
1449   @property
1450   def has_checked_exceptions(self):
1451     return self.address["_flags"] & ConstMethod.HAS_CHECKED_EXCEPTIONS
1452 
1453   @property
1454   def has_method_parameters(self):
1455     return self.address["_flags"] & ConstMethod.HAS_METHOD_PARAMETERS
1456 
1457   @property
1458   def has_generic_signature(self):
1459     return self.address["_flags"] & ConstMethod.HAS_GENERIC_SIGNATURE
1460 
1461   @property
1462   def has_method_annotations(self):
1463     return self.address["_flags"] & ConstMethod.HAS_METHOD_ANNOTATIONS
1464 
1465   @property
1466   def has_parameter_annotations(self):
1467     return self.address["_flags"] & ConstMethod.HAS_PARAMETER_ANNOTATIONS
1468 
1469   @property
1470   def has_type_annotations(self):
1471     return self.address["_flags"] & ConstMethod.HAS_TYPE_ANNOTATIONS
1472 
1473   @property
1474   def has_default_annotations(self):
1475     return self.address["_flags"] & ConstMethod.HAS_DEFAULT_ANNOTATIONS
1476 
1477   @property
1478   def first_local_variable_ptr(self):
1479     return self.localvariable_table_start.cast(
1480         self.jvm.t_local_variable_table_element_ptr)
1481 
1482   @property
1483   def last_u2_element_address(self):
1484     """TODO."""
1485     offset = 0
1486     if self.has_method_annotations:
1487       offset += 1
1488     if self.has_parameter_annotations:
1489       offset += 1
1490     if self.has_type_annotations:
1491       offset += 1
1492     if self.has_default_annotations:
1493       offset += 1
1494     return self.const_method_end - offset * self.jvm.t_char_ptr.sizeof - 2
1495 
1496   @property
1497   def method_parameters_length_addr(self):
1498     if self.has_generic_signature:
1499       return self.last_u2_element_address - 2
1500     else:
1501       return self.last_u2_element_address
1502 
1503   @property
1504   def checked_exceptions_length_addr(self):
1505     if self.has_method_parameters:
1506       return self.method_parameters_start - 2
1507     elif self.has_generic_signature:
1508       return self.last_u2_element_address - 2
1509     else:
1510       return self.last_u2_element_address
1511 
1512   @property
1513   def exception_table_length_addr(self):
1514     if self.has_checked_exceptions:
1515       return self.checked_exceptions_address - 2
1516     elif self.has_method_parameters:
1517       return self.method_parameters_start - 2
1518     elif self.has_generic_signature:
1519       return self.last_u2_element_address - 2
1520     else:
1521       return self.last_u2_element_address
1522 
1523   @property
1524   def localvariable_table_length_addr(self):
1525     if self.has_exception_table:
1526       return self.exception_table_start - 2
1527     elif self.has_checked_exceptions:
1528       return self.checked_exceptions_start - 2
1529     elif self.has_method_parameters:
1530       return self.method_parameters_start - 2
1531     elif self.has_generic_signature:
1532       return self.last_u2_element_address - 2
1533     else:
1534       return self.last_u2_element_address
1535 
1536   def count_to_address(self, count_address, stride):
1537     count = count_address.cast(self.jvm.t_u2ptr).dereference()
1538     return count_address - count * stride
1539 
1540   @property
1541   def checked_exceptions_start(self):
1542     return self.count_to_address(self.checked_exceptions_length_addr,
1543                                  ConstMethod.CHECKED_EXCEPTION_STRIDE)
1544 
1545   @property
1546   def localvariable_table_start(self):
1547     return self.count_to_address(self.localvariable_table_length_addr,
1548                                  ConstMethod.LOCALVARIABLE_TABLE_STRIDE)
1549 
1550   @property
1551   def exception_table_start(self):
1552     return self.count_to_address(self.exception_table_length_addr,
1553                                  ConstMethod.EXCEPTION_TABLE_STRIDE)
1554 
1555   @property
1556   def localvariable_table_length(self):
1557     return self.localvariable_table_length_addr.cast(
1558         self.jvm.t_u2ptr).dereference()
1559 
1560   @property
1561   def code_base(self):
1562     return (self.address + 1).cast(self.jvm.t_char_ptr)
1563 
1564   @property
1565   def code_size(self):
1566     return self.address["_code_size"]
1567 
1568   @property
1569   def code_end(self):
1570     return self.code_base + self.code_size
1571 
1572   @property
1573   def size_of_parameters(self):
1574     return self.address["_size_of_parameters"]
1575 
1576   @property
1577   def max_locals(self):
1578     return self.address["_max_locals"]
1579 
1580   @property
1581   def const_method_end(self):
1582     return (self.address.cast(self.jvm.t_int_ptr_ptr) +
1583             self.address["_constMethod_size"]).cast(self.jvm.t_char_ptr)
1584 
1585 
1586 class ConstantPool(Metadata):
1587   """Class constants as described in the class file.
1588 
1589   See src/share/vm/oops/constantPool.{hpp,cpp}.
1590   """
1591 
1592   def __init__(self, jvm, address):
1593     super(ConstantPool, self).__init__(jvm, address)
1594 
1595   def __repr__(self):
1596     return "%s" % self.address.dereference()
1597 
1598   def symbol_at(self, index):
1599     base = (self.address + 1).cast(self.jvm.t_symbol_ptr_ptr)
1600     if index < 0 or index >= self.length:
1601       raise RuntimeError("%s: index %d out of bounds" % (self, index))
1602     return Symbol(self.jvm, (base + index).dereference())
1603 
1604   @property
1605   def length(self):
1606     return self.jvm.align_object_size(self.address["_length"])
1607 
1608   @property
1609   def pool_holder(self):
1610     return InstanceKlass(self.jvm, self.address["_pool_holder"])
1611 
1612   @property
1613   def header_size(self):
1614     return self.address.dereference().type.sizeof / self.jvm.heap_word_size
1615 
1616 
1617 class MethodArray(VmObject):
1618   """TODO."""
1619 
1620   def __init__(self, jvm, address):
1621     super(MethodArray, self).__init__(jvm, address)
1622 
1623   def __repr__(self):
1624     return "%s" % self.address
1625 
1626   def at(self, i):
1627     if i < 0 or i >= self.length:
1628       raise RuntimeError("%s index %d out of bounds" % (self, i))
1629     return Method(self.jvm, self.address["_data"][i])
1630 
1631   @property
1632   def length(self):
1633     return self.address["_length"]
1634 
1635 
1636 class Symbol(VmObject):
1637   """A canonicalized string.
1638 
1639   See src/share/vm/oops/symbol.{hpp,cpp}.
1640   """
1641 
1642   def __init__(self, jvm, address):
1643     super(Symbol, self).__init__(jvm, address)
1644 
1645   def __repr__(self):
1646     return "%s" % self.address.dereference()
1647 
1648   def __eq__(self, other):
1649     return self.string() == other.string()
1650 
1651   def string(self):
1652     return self.address["_body"].string(
1653         "utf-8", "replace", self.address["_length"])
1654 
1655 
1656 class MachineFrame(object):
1657   """A frame represents a physical stack frame (an activation).
1658 
1659   Frames can be C or Java frames, and the Java frames can be
1660   interpreted or compiled. In contrast, vframes represent
1661   source-level activations, so that one physical frame can
1662   correspond to multiple source level frames because of inlining.
1663 
1664   For the X64, the layout of the machine frame is as follows (lowest addr
1665   on top):
1666     [expression stack      ] * <- sp
1667     [monitors              ]   \
1668      ...                        | monitor block size
1669     [monitors              ]   /
1670     [monitor block size    ]
1671     [byte code index/pointr]              = bcx()          bcx_offset
1672     [pointer to locals     ]              = locals()       locals_offset
1673     [constant pool cache   ]              = cache()        cache_offset
1674     [methodData            ]              = mdp()          mdx_offset
1675     [methodOop             ]              = method()       method_offset
1676     [last sp               ]              = last_sp()      last_sp_offset
1677     [old stack pointer     ]                (sender_sp)    sender_sp_offset
1678     [old frame pointer     ]   <- fp      = link()
1679     [return pc             ]
1680     [oop temp              ]                     (only for native calls)
1681     [locals and parameters ]
1682                                <- sender sp
1683 
1684   This class mirrors sun.jvm.hotspot.runtime.Frame in SA.
1685   """
1686 
1687   def __init__(self, jvm, fp=None, sp=None, pc=None, unextended_sp=None):
1688     self.jvm = jvm
1689     self.fp = fp
1690     self.sp = sp
1691     self.pc = (pc if pc is not None
1692                else jvm.char_ptr_at(sp - jvm.pointer_size))
1693     self.unextended_sp = sp if unextended_sp is None else unextended_sp
1694     # Adjust unextended SP.
1695     self.deoptimized = False
1696     p_nmethod = self.find_cb()
1697     if p_nmethod is not None and jvm.is_instance_of(p_nmethod, "nmethod"):
1698       nmethod = NMethod(jvm, p_nmethod.cast(jvm.t_nmethod_ptr))
1699       if nmethod.is_deopt_mh_entry(self.pc):
1700         self.unextended_sp = self.fp
1701       elif nmethod.is_deopt_entry(self.pc):
1702         self.pc = jvm.char_ptr_at(
1703             self.unextended_sp + nmethod.orig_pc_offset)
1704         self.deoptimized = True
1705       elif nmethod.is_method_handle_return(self.pc):
1706         self.unextended_sp = self.fp
1707 
1708   def __repr__(self):
1709     return ("FP %s, SP %s, PC %s" %
1710             (hexstr(self.fp), hexstr(self.sp), hexstr(self.pc)))
1711 
1712   def vframe(self, java_thread, use_find_unsafe, allow_imprecise):
1713     """TODO."""
1714 
1715     if self.is_interpreted:
1716       return JvmInterpretedFrame(self, java_thread)
1717 
1718     if use_find_unsafe:
1719       p_code_blob = self.jvm.code_cache.find_blob_unsafe(self.pc)
1720     else:
1721       p_code_blob = self.find_cb()
1722     if self.jvm.is_instance_of(p_code_blob, "nmethod"):
1723       nmethod = NMethod(self.jvm, p_code_blob.cast(self.jvm.t_nmethod_ptr))
1724       if allow_imprecise:
1725         scope_desc = nmethod.scope_desc_near_dbg(self.pc)
1726       else:
1727         scope_desc = nmethod.scope_desc_at(self.pc)
1728       return JvmCompiledFrame(self, java_thread,
1729                               scope_desc, allow_imprecise)
1730     if self.is_runtime_frame:
1731       return self.sender().make_vframe(java_thread, use_find_unsafe,
1732                                        allow_imprecise)
1733     return JvmExternalFrame(self, java_thread, allow_imprecise)
1734 
1735   def find_cb(self):
1736     return self.jvm.code_cache.find_blob(self.pc)
1737 
1738   def sender(self, code_blob=None):
1739     """Returns MachineFrame instance for the frame that called this one."""
1740     # From sun.jvm.hotspot.runtime.x86.X86Frame.sender():
1741     # Default is we don't have to follow them. The sender_for_xxx
1742     # will update it accordingly.
1743     # register_map.setIncludeArgumentOops(false)
1744     if self.is_interpreted:
1745       return self.sender_for_interpreter_frame()
1746     if code_blob is None:
1747       p_code_blob = self.find_cb()
1748     if p_code_blob is None:
1749       raise RuntimeError("Cannot find code blob for the frame %s" % self)
1750     return self.sender_for_compiled_frame(
1751         CodeBlob(self.jvm, p_code_blob.cast(self.jvm.t_codeblob_ptr)))
1752 
1753   def sender_for_interpreter_frame(self):
1754     return MachineFrame(self.jvm, fp=self.link, sp=self.sender_sp,
1755                         pc=self.sender_pc,
1756                         unextended_sp=self.slot_value(-1))
1757 
1758   def sender_for_compiled_frame(self, code_blob):
1759     next_sp = self.unextended_sp + code_blob.frame_size
1760     next_pc = self.jvm.char_ptr_at(next_sp - self.jvm.pointer_size)
1761     next_fp = self.jvm.char_ptr_at(next_sp - 2 * self.jvm.pointer_size)
1762     if _debug > 0:
1763       print("Sender for %s: FP=%s, SP=%s, PC=%x" %
1764             (self, hexstr(next_fp), hexstr(next_sp), next_pc))
1765     return MachineFrame(self.jvm, fp=next_fp, sp=next_sp, pc=next_pc)
1766 
1767   def real_sender(self):
1768     next_machine_frame = self.sender()
1769     if self.jvm.is_core:
1770       return next_machine_frame
1771     while next_machine_frame.is_runtime_frame:
1772       next_machine_frame = next_machine_frame.sender()
1773     return next_machine_frame
1774 
1775   @property
1776   def is_interpreted(self):
1777     return self.jvm.in_interpreter(self.pc)
1778 
1779   @property
1780   def is_runtime_frame(self):
1781     p_code_blob = self.find_cb()
1782     if p_code_blob is None:
1783       return False
1784     return self.jvm.is_instance_of(p_code_blob, "RuntimeStub")
1785 
1786   @property
1787   def is_first_frame(self):
1788     # TODO(asmundak): implement
1789     return False
1790 
1791   @property
1792   def interpreter_frame_method(self):
1793     return self.slot_value(-3).cast(self.jvm.t_method_ptr)
1794 
1795   @property
1796   def interpreter_frame_bci(self):
1797     bcp = self.slot_value(-7)
1798     p_const_method = self.interpreter_frame_method["_constMethod"]
1799     if bcp >= 0 and bcp < p_const_method["_code_size"]:
1800       return bcp.cast(self.jvm.t_int)
1801     return (bcp - self.jvm.const_method_oop_desc_size -
1802             p_const_method.cast(self.jvm.t_char_ptr)).cast(self.jvm.t_int)
1803 
1804   @property
1805   def interpreter_frame_locals(self):
1806     return self.slot_value(-6)
1807 
1808   @property
1809   def sender_pc(self):
1810     return self.slot_value(1)
1811 
1812   @property
1813   def link(self):
1814     return self.slot_value(0)
1815 
1816   @property
1817   def sender_sp(self):
1818     return self.fp + 2 * self.jvm.pointer_size
1819 
1820   def slot_value(self, slot):
1821     return self.jvm.char_ptr_at(self.fp + slot * self.jvm.pointer_size)
1822 
1823 
1824 class JvmFrame(object):
1825   """TODO."""
1826 
1827   def __init__(self, machine_frame, java_thread):
1828     self.machine_frame = machine_frame
1829     self.java_thread = java_thread
1830 
1831   @property
1832   def is_java_vframe(self):
1833     return False
1834 
1835   def sender(self, imprecise):
1836     if self.machine_frame.is_first_frame:
1837       return None
1838     next_machine_frame = self.machine_frame.real_sender()
1839     if next_machine_frame.is_first_frame:
1840       return None
1841     return next_machine_frame.vframe(self.java_thread,
1842                                      self.machine_frame.jvm.is_debugging,
1843                                      imprecise)
1844 
1845 
1846 class JvmCompiledFrame(JvmFrame):
1847   """JVM frame for the JIT-compiled method.
1848 
1849   see src/share/vm/runtime/vframe_hp.{hpp,cpp}.
1850   """
1851 
1852   def __init__(self, machine_frame, java_thread, scope_desc,
1853                allow_imprecise):
1854     super(JvmCompiledFrame, self).__init__(machine_frame, java_thread)
1855     self.scope_desc = scope_desc
1856     self.allow_imprecise = allow_imprecise
1857 
1858   @property
1859   def is_java_vframe(self):
1860     return True
1861 
1862   @property
1863   def local_variables(self):
1864     """TODO."""
1865     if self.scope_desc is None:
1866       return None
1867     if _debug > 0:
1868       for scope_value in self.scope_desc.locals():
1869         print(scope_value)
1870 
1871     result = []
1872     heap = self.machine_frame.jvm.object_heap
1873     for scope_value in self.scope_desc.locals():
1874       if scope_value.is_location and scope_value.location_is_onstack:
1875         result.append(self.machine_frame.unextended_sp +
1876                       scope_value.location_stack_offset)
1877       else:
1878         result.append(None)
1879       if (scope_value.is_location and scope_value.location_is_onstack and
1880           scope_value.location_value_is_oop):
1881         oop = self.machine_frame.jvm.char_ptr_at(result[-1])
1882         if _debug > 0:
1883           print("local: (%s, %s@%s)" %
1884                 (scope_value, heap.klass_name_for_oop_handle(oop), result[-1]))
1885       else:
1886         if _debug > 0:
1887           print("local: %s" % (scope_value))
1888     return StackValueCollection(self.machine_frame.jvm.object_heap, result)
1889 
1890   @property
1891   def bci(self):
1892     raw_bci = 0 if self.scope_desc is None else self.scope_desc.bci
1893     return (0 if raw_bci == self.machine_frame.jvm.invocation_entry_bci
1894             else raw_bci)
1895 
1896 
1897 class JvmInterpretedFrame(JvmFrame):
1898   """JVM frame for the interpreted method.
1899 
1900   See src/share/vm/runtime/vframe.{hpp,cpp}.
1901   """
1902 
1903   def __init__(self, machine_frame, java_thread):
1904     super(JvmInterpretedFrame, self).__init__(machine_frame, java_thread)
1905 
1906   def __repr__(self):
1907     return "JvmInterpretedFrame(%s)" % self.machine_frame
1908 
1909   @property
1910   def is_java_vframe(self):
1911     return True
1912 
1913   @property
1914   def method(self):
1915     return Method(self.machine_frame.jvm,
1916                   self.machine_frame.interpreter_frame_method)
1917 
1918   @property
1919   def bci(self):
1920     return self.machine_frame.interpreter_frame_bci
1921 
1922   @property
1923   def local_variables(self):
1924     """TODO."""
1925     m = self.method
1926     length = m.max_locals if not m.is_native else m.size_of_parameters
1927     result = []
1928     # oop_mask = m.mask_for(self.bci)
1929     for i in range(length):
1930       result.append(self.address_of_local_at(i))
1931       # print "locals[%d] = %s" % (i, hexstr(result[i]))
1932     return StackValueCollection(m.jvm.object_heap, result)
1933 
1934   def address_of_local_at(self, i):
1935     return (self.machine_frame.interpreter_frame_locals -
1936             i * self.machine_frame.jvm.pointer_size)
1937 
1938 
1939 class JvmExternalFrame(JvmFrame):
1940   """TODO(asmundak): what is it?
1941 
1942   See src/share/vm/runtime/vframe.{hpp,cpp}.
1943   """
1944 
1945   def __init__(self, machine_frame, java_thread, allow_imprecise):
1946     super(JvmExternalFrame, self).__init__(machine_frame, java_thread)
1947     self.allow_imprecise = allow_imprecise
1948 
1949 
1950 class ArgumentsAndLocals(object):
1951   """Contains variables from the current java frame."""
1952 
1953   def __init__(self, jvm_java_frame, method):
1954     self.jvm = jvm_java_frame.machine_frame.jvm
1955     self.jvm_java_frame = jvm_java_frame
1956     method_signature = method.signature.string()
1957     self.args = []
1958     first_arg = 0 if method.is_static else Jvm.OBJECT_SIZE
1959     arg_count = method.size_of_parameters
1960     if arg_count + first_arg == 0:
1961       return
1962 
1963     # If debuginfo is present, build an array of local variable names so
1964     # that that can be shown in the argument list.
1965     if method.has_local_variable_table:
1966       lv_table = method.first_local_variable_ptr
1967       slot_name = [0] * int(method.localvariable_table_length)
1968       for i in range(len(slot_name)):
1969         slot_name[int(lv_table[i]["slot"])] = int(lv_table[i]["name_cp_index"])
1970       if _debug > 0:
1971         self.print_localvariable_table(method)
1972     else:
1973       slot_name = None
1974 
1975     local_variables = jvm_java_frame.local_variables
1976     if first_arg > 0:
1977       # TODO(asmundak): this object should be
1978       # local_variables.object_at(0, method.klass_fqname),
1979       # but it does not work.
1980       self.args.append(SymbolValueWrapper(
1981           "this", local_variables.object_at(0, method.klass_fqname)))
1982 
1983     arg_slot = first_arg
1984     arg_nr = 0
1985     for (arg_type, arg_word_size, klass_name) in ArgumentIterator(
1986         method_signature):
1987       arg_name = None
1988       if slot_name is not None:
1989         name_cp_index = slot_name[arg_slot]
1990         if name_cp_index != 0:
1991           arg_name = method.constants.symbol_at(name_cp_index).string()
1992       local_index = first_arg + arg_nr
1993       if local_variables is None or not local_variables.exists_at(local_index):
1994         # TODO(asmundak): should set it to some kind of predefined
1995         # "optimized out" value. Add it to Python API
1996         value = 0
1997       else:
1998         if arg_type == "B":
1999           value = local_variables.byte_at(local_index)
2000         elif arg_type == "C":
2001           value = local_variables.char_at(local_index)
2002         elif arg_type == "D":
2003           value = local_variables.double_at(local_index)
2004         elif arg_type == "F":
2005           value = local_variables.float_at(local_index)
2006         elif arg_type == "I":
2007           value = local_variables.int_at(local_index)
2008         elif arg_type == "J":
2009           value = local_variables.long_at(local_index)
2010         elif arg_type == "S":
2011           value = local_variables.short_at(local_index)
2012         elif arg_type == "Z":
2013           value = local_variables.bool_at(local_index)
2014         elif arg_type == "L":
2015           if arg_name is None:
2016             arg_name = re.sub("/", ".", klass_name)
2017           value = local_variables.object_at(local_index, klass_name)
2018         elif arg_type == "[":
2019           value = local_variables.array_at(local_index, klass_name)
2020       if arg_name is None:
2021         arg_name = self.make_arg_name(arg_type, klass_name)
2022       self.args.append(SymbolValueWrapper(arg_name, value))
2023       arg_slot += arg_word_size
2024       arg_nr += 1
2025 
2026   def make_arg_name(self, arg_type, klass_name):
2027     """Returns arg name.
2028 
2029     Args:
2030       arg_type: type letter ('B'/'C'/'D'/etc. to basic types, 'L' for classes,
2031       '[' for arrays)
2032       klass_name: class name if arg_type is 'L' or '['
2033     Returns:
2034       argument name
2035     """
2036     if arg_type == "L":
2037       return klass_name.replace("/", ".")
2038     elif arg_type == "[":
2039       return self.make_arg_name(klass_name[0], klass_name[1:]) + "[]"
2040     else:
2041       return {
2042           "B": "byte", "C": "char", "D": "double", "F": "float",
2043           "I": "int", "J": "long", "S": "short", "Z": "boolean"
2044           }[klass_name[0]]
2045 
2046   def print_localvariable_table(self, method):
2047     print
2048     lv_table = method.first_local_variable_ptr
2049     cp = method.constants
2050     for i in range(method.localvariable_table_length):
2051       lve = lv_table[i]
2052       print("%s\t%d" %
2053             (cp.symbol_at(lve["name_cp_index"]).string(), lv_table[i]["slot"]))
2054 
2055   @property
2056   def arguments(self):
2057     return self.args
2058 
2059 
2060 class ArgumentIterator(object):
2061   """Iterate over signature string tokens."""
2062 
2063   def __init__(self, signature_string):
2064     # Sanity check: signature always starts with '(' and contains ')'
2065     if not (signature_string[0] == "(" and signature_string.find(")") > 0):
2066       raise RuntimeError("Bad signature string '%s'" % signature_string)
2067 
2068     self.signature_string = signature_string
2069 
2070   def __iter__(self):
2071     i = 1
2072     while i < len(self.signature_string):
2073       c = self.signature_string[i]
2074       i += 1
2075       if c == ")":
2076         return
2077       if c == "B":
2078         yield "B", Jvm.BYTE_SIZE, None
2079       elif c == "C":
2080         yield "C", Jvm.CHAR_SIZE, None
2081       elif c == "D":
2082         yield "D", Jvm.DOUBLE_SIZE, None
2083       elif c == "F":
2084         yield "F", Jvm.FLOAT_SIZE, None
2085       elif c == "I":
2086         yield "I", Jvm.INT_SIZE, None
2087       elif c == "J":
2088         yield "J", Jvm.LONG_SIZE, None
2089       elif c == "S":
2090         yield "S", Jvm.SHORT_SIZE, None
2091       elif c == "Z":
2092         yield "Z", Jvm.BOOLEAN_SIZE, None
2093       elif c == "V":
2094         raise RuntimeError("Bad argument type V at offset %d in %s" %
2095                            (i - 1, self.signature_string))
2096       elif c == "L":
2097         end = self.signature_string.index(";", i)
2098         klass_name = self.signature_string[i : end]
2099         i = end + 1
2100         yield "L", Jvm.OBJECT_SIZE, klass_name
2101       elif c == "[":
2102         while True:
2103           c = self.signature_string[i]
2104           i += 1
2105           if (c < "0" or c > "9") and c != "[":
2106             break
2107         start = i
2108         if c == "L":
2109           end = self.signature_string.index(";", start)
2110           klass_name = self.signature_string[start : end]
2111           i = end + 1
2112         else:
2113           klass_name = None
2114           i += 1
2115         yield "[", Jvm.ARRAY_SIZE, klass_name
2116 
2117 
2118 class ClassConstants(object):
2119   """ClassFile constants."""
2120   JVM_ACC_PUBLIC = 0x0001
2121   JVM_ACC_PRIVATE = 0x0002
2122   JVM_ACC_PROTECTED = 0x0004
2123   JVM_ACC_STATIC = 0x0008
2124   JVM_ACC_FINAL = 0x0010
2125   JVM_ACC_SYNCHRONIZED = 0x0020
2126   JVM_ACC_SUPER = 0x0020
2127   JVM_ACC_VOLATILE = 0x0040
2128   JVM_ACC_BRIDGE = 0x0040
2129   JVM_ACC_TRANSIENT = 0x0080
2130   JVM_ACC_VARARGS = 0x0080
2131   JVM_ACC_NATIVE = 0x0100
2132   JVM_ACC_INTERFACE = 0x0200
2133   JVM_ACC_ABSTRACT = 0x0400
2134   JVM_ACC_STRICT = 0x0800
2135   JVM_ACC_SYNTHETIC = 0x1000
2136   JVM_ACC_ANNOTATION = 0x2000
2137   JVM_ACC_ENUM = 0x4000
2138   JVM_ACC_FIELD_ACCESS_WATCHED = 0x2000
2139   JVM_ACC_FIELD_MODIFICATION_WATCHED = 0x8000
2140   JVM_ACC_FIELD_INTERNAL = 0x0400
2141   JVM_ACC_FIELD_STABLE = 0x0020
2142   JVM_ACC_FIELD_HAS_GENERIC_SIGNATURE = 0x0800
2143 
2144 
2145 class CompressedStream(object):
2146   """An interface to serialize basic types.
2147 
2148   See src/share/vm/code/compressedStream.{hpp,cpp}.
2149   """
2150 
2151   H = 64   # Number of high codes
2152   L = 192  # Number of low codes
2153 
2154   def __init__(self, address):
2155     self.position = address
2156 
2157   def read_byte(self):
2158     b = self.position.dereference()
2159     self.position += 1
2160     return b & 255
2161 
2162   def read_signed_int(self):
2163     return self.decode_sign(self.read_int())
2164 
2165   def read_int(self):
2166     #  This encoding, called UNSIGNED5, is taken from J2SE Pack200.
2167     #  It assumes that most values have lots of leading zeroes.
2168     # Very small values, in the range [0..191], code in one byte.
2169     # Any 32-bit value (including negatives) can be coded, in
2170     # up to five bytes.  The grammar is:
2171     #   low_byte  = [0..191]
2172     #   high_byte = [192..255]
2173     #    any_byte  = low_byte | high_byte
2174     #   coding = low_byte
2175     #          | high_byte low_byte
2176     #           | high_byte high_byte low_byte
2177     #           | high_byte high_byte high_byte low_byte
2178     #           | high_byte high_byte high_byte high_byte any_byte
2179     # Each high_byte contributes six bits of payload.
2180     #  The encoding is one-to-one (except for integer overflow)
2181     # and easy to parse and unparse.
2182     value = self.read_byte()
2183     if value < CompressedStream.L:
2184       return value
2185     shift = 0
2186     while True:
2187       byte = self.read_byte()
2188       shift += 6
2189       value += (byte << 6)
2190       if byte < CompressedStream.L or shift >= 24:
2191         return value
2192 
2193   def encode_sign(self, value):
2194     return (value << 1) ^ ((value >> 31) & 1)
2195 
2196   def decode_sign(self, value):
2197     return ((value >> 1) & 0x7FFFFFFF) ^ -(value & 1)
2198 
2199 
2200 class PcDesc(VmObject):
2201   """Maps an offset from start of nmethod to the source scope and BCI.
2202 
2203   See src/share/vm/code/pcDesc.{hpp,cpp}.
2204   """
2205 
2206   REEXECUTE = None
2207 
2208   @classmethod
2209   def init_constants(cls):
2210     t_int = gdb.lookup_type("int")
2211     PcDesc.REEXECUTE = gdb.parse_and_eval(
2212         "PcDesc::PCDESC_reexecute").cast(t_int)
2213     PcDesc.IS_METHOD_HANDLE_INVOKE = gdb.parse_and_eval(
2214         "PcDesc::PCDESC_is_method_handle_invoke")
2215 
2216   def __init__(self, jvm, address):
2217     super(PcDesc, self).__init__(jvm, address)
2218     if PcDesc.REEXECUTE is None:
2219       PcDesc.init_constants()
2220 
2221   @property
2222   def pc_offset(self):
2223     return self.address["_pc_offset"]
2224 
2225   @property
2226   def scope_decode_offset(self):
2227     return self.address["_scope_decode_offset"]
2228 
2229   @property
2230   def obj_decode_offset(self):
2231     return self.address["_obj_decode_offset"]
2232 
2233   @property
2234   def reexecute(self):
2235     return self.address["_flags"] & PcDesc.REEXECUTE
2236 
2237   @property
2238   def is_method_handle_invoke(self):
2239     return self.address["_flags"] & PcDesc.IS_METHOD_HANDLE_INVOKE
2240 
2241   def real_pc(self, nmethod):
2242     return self.pc_offset + nmethod.code_begin
2243 
2244 
2245 class ScopeDesc(object):
2246   """Scope description.
2247 
2248   ScopeDescs contain information that make source-level debugging of nmethods
2249   possible: each ScopeDesc describes a method activation.
2250   See share/vm/code/scopeDesc.{hpp,cpp}.
2251   """
2252 
2253   def __init__(self, nmethod, decode_offset, object_decode_offset,
2254                reexecute):
2255     self.nmethod = nmethod
2256     self.decode_offset = decode_offset
2257     self.objects = self.decode_object_values(object_decode_offset)
2258     self.reexecute = reexecute
2259     stream = DebugInfoStream(nmethod, self.decode_offset)
2260     self.sender_decode_offset = stream.read_int()
2261     self.method = Method(nmethod.jvm, stream.read_method())
2262     self.bci = stream.read_bci()
2263     self.locals_decode_offset = stream.read_int()
2264     self.expressions_decode_offset = stream.read_int()
2265     self.monitors_decode_offset = stream.read_int()
2266 
2267   def locals(self):
2268     stream = DebugInfoStream(self.nmethod, self.locals_decode_offset)
2269     count = stream.read_int()
2270     if _debug > 0:
2271       print("Debug info stream at %s, nmethod %s with %d items" %
2272             (hexstr(stream.position), hexstr(self.nmethod.address), count))
2273     for _ in range(count):
2274       yield ScopeValue.read_from(stream)
2275 
2276   def decode_object_values(self, _):
2277     return []
2278 
2279 
2280 class ScopeValue(object):
2281   """The value of a variable/expression in a scope.
2282 
2283   See src/share/vm/code/debugInfo.{hpp,cpp}.
2284   """
2285 
2286   LOCATION_CODE = 0
2287   CONSTANT_INT_CODE = 1
2288   CONSTANT_OOP_CODE = 2
2289   CONSTANT_LONG_CODE = 3
2290   CONSTANT_DOUBLE_CODE = 4
2291   CONSTANT_OBJECT_CODE = 5
2292   CONSTANT_OBJECT_ID_CODE = 6
2293   VALUE_NAMES = ("location", "int", "oop", "long", "double",
2294                  "object", "object_id")
2295 
2296   @classmethod
2297   def read_from(cls, stream):
2298     """TODO."""
2299     tag = stream.read_int()
2300     if tag == ScopeValue.LOCATION_CODE:
2301       value = stream.read_int()
2302     elif tag == ScopeValue.CONSTANT_INT_CODE:
2303       value = stream.read_signed_int()
2304     elif tag == ScopeValue.CONSTANT_OOP_CODE:
2305       value = stream.read_oop_handle()
2306     elif tag == ScopeValue.CONSTANT_LONG_CODE:
2307       value = stream.read_long()
2308     elif tag == ScopeValue.CONSTANT_DOUBLE_CODE:
2309       value = stream.read_double()
2310     elif tag == ScopeValue.CONSTANT_OBJECT_CODE:
2311       value = stream.read_object_value()
2312     elif tag == ScopeValue.CONSTANT_OBJECT_ID_CODE:
2313       value = stream.get_cached_object()
2314     else:
2315       raise RuntimeError("Unexpected tag %d" % tag)
2316     return cls(tag, value)
2317 
2318   def __init__(self, tag, value):
2319     self.tag = tag
2320     self.value = value
2321 
2322   def __repr__(self):
2323     if self.tag == ScopeValue.LOCATION_CODE:
2324       loc_type = ("invalid", "normal", "oop", "int_in_long", "lng",
2325                   "float_in_dbl", "dbl", "addr", "narrowoop"
2326                  )[int(self.value & 0x0F)]
2327       if self.value & 0x10 == 0:
2328         # Location on stack
2329         return "Location(%s at +0x%x)" % (loc_type, (self.value >> 5) << 2)
2330       else:
2331         return "Location(%s in register %d)" % (loc_type, self.value >> 5)
2332 
2333     return "ScopeValue(%s, %s)" % (ScopeValue.VALUE_NAMES[int(self.tag)],
2334                                    self.value)
2335 
2336   @property
2337   def is_location(self):
2338     return self.tag == ScopeValue.LOCATION_CODE
2339 
2340   @property
2341   def location_is_onstack(self):
2342     return (self.is_location and (self.value & 0x10 == 0) and
2343             self.value & 0x0f != 0)
2344 
2345   @property
2346   def location_value_is_oop(self):
2347     return self.is_location and (self.value & 0x0f == 2)
2348 
2349   @property
2350   def location_stack_offset(self):
2351     if self.is_location and self.location_is_onstack:
2352       return (self.value >> 5) << 2
2353     return None
2354 
2355   @property
2356   def location_register_number(self):
2357     if self.is_location and not self.location_is_onstack:
2358       return self.value >> 5
2359     return None
2360 
2361 
2362 class ObjectValue(ScopeValue):
2363   """An object eliminated by escape analysis.
2364 
2365   See src/share/vm/code/debugInfo.{hpp,cpp}.
2366   """
2367 
2368   def __init__(self, tag, value):
2369     super(ObjectValue, self).__init__(tag, value)
2370     self.klass = None
2371     self.fields = []
2372 
2373   def __repr__(self):
2374     return "ObjectValue(%d)" % self.value
2375 
2376   def read_object(self, stream):
2377     self.klass = ObjectValue.read_from(stream)
2378     count = stream.read_int()
2379     for _ in range(count):
2380       self.fields.append(self.read_from(stream))
2381 
2382 
2383 class DebugInfoStream(CompressedStream):
2384   """Serialize debug information.
2385 
2386   See src/share/vm/code/debugInfo.hpp.
2387   """
2388 
2389   def __init__(self, nmethod, offset):
2390     super(DebugInfoStream, self).__init__(
2391         nmethod.scopes_data_begin + offset)
2392     self.nmethod = nmethod
2393     self.object_pool = {}
2394     self.invocation_entry_bci = gdb.parse_and_eval("InvocationEntryBci")
2395 
2396   def read_oop_handle(self):
2397     i = self.read_int()
2398     return self.nmethod.oop_at(i)
2399 
2400   def read_object_value(self):
2401     obj_id = self.read_int()
2402     result = ObjectValue(ScopeValue.OBJECT_CODE, obj_id)
2403     result.read_object(self)
2404     self.object_pool[result.value] = result
2405 
2406   def read_bci(self):
2407     return self.invocation_entry_bci + self.read_int()
2408 
2409   def get_cached_object(self):
2410     obj_id = self.read_int()
2411     return self.object_pool[obj_id]
2412 
2413   def read_method(self):
2414     return self.nmethod.method_at(self.read_int())
2415 
2416 
2417 class StackValueCollection(object):
2418   """See src/share/vm/runtime/stackValueCollection.{hpp,cpp}."""
2419 
2420   def __init__(self, object_heap, addresses):
2421     self.object_heap = object_heap
2422     self.jvm = object_heap.jvm
2423     self.addresses = addresses
2424 
2425   def exists_at(self, i):
2426     return self.addresses[i] is not None
2427 
2428   def byte_at(self, i):
2429     return self.addresses[i].cast(self.jvm.t_char_ptr).dereference()
2430 
2431   def char_at(self, i):
2432     return self.addresses[i].cast(self.jvm.t_char_ptr).dereference()
2433 
2434   def double_at(self, i):
2435     return self.addresses[i].cast(self.jvm.t_double_ptr).dereference()
2436 
2437   def float_at(self, i):
2438     return self.addresses[i].cast(self.jvm.t_float_ptr).dereference()
2439 
2440   def int_at(self, i):
2441     return self.addresses[i].cast(self.jvm.t_int_ptr).dereference()
2442 
2443   def long_at(self, i):
2444     return self.addresses[i].cast(self.jvm.t_long_ptr).dereference()
2445 
2446   def short_at(self, i):
2447     return self.addresses[i].cast(self.jvm.t_short_ptr).dereference()
2448 
2449   def bool_at(self, i):
2450     v = self.addresses[i].cast(self.jvm.t_char_ptr).dereference()
2451     return gdb.Value(v != 0).cast(self.jvm.t_bool)
2452 
2453   def object_at(self, i, klass_name):
2454     """An object at a given slot.
2455 
2456     Args:
2457       i: slot.
2458       klass_name: object's expected class name.
2459 
2460     Returns:
2461       String if the object has suitable external representation or void pointer
2462       (oop).
2463     """
2464     if self.addresses[i] is None:
2465       return None
2466     oop_location = self.addresses[i].cast(self.jvm.t_oop_ptr)
2467     if oop_location is None:
2468       return None
2469     oop = oop_location.dereference()
2470     return self.jvm.oop_value(self.jvm.new_oop(oop))
2471 
2472   def array_at(self, i, klass_name):
2473     """An object array at a given slot.
2474 
2475     Args:
2476       i: slot number
2477       klass_name: expected klass name
2478     Returns:
2479       oop
2480     """
2481     if self.addresses[i] is None:
2482       return None
2483     oop_location = self.addresses[i].cast(self.jvm.t_oop_ptr)
2484     if oop_location is None:
2485       return None
2486     oop = oop_location.dereference()
2487     return self.jvm.oop_value(self.jvm.new_oop(oop))
2488 
2489 
2490 class BadOopError(Exception):
2491   """TODO."""
2492 
2493 
2494 _debug = 0
2495 jvms = {}
2496 if _debug > 0:
2497   print("Loaded Java frame filter")
2498 gdb.sniffer.register_sniffer(gdb.current_progspace(), Jdk8Amd64Sniffer())