1 /* 2 * Copyright (c) 2014, Oracle and/or its affiliates. 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 package sun.jvm.hotspot.debugger.proc; 26 27 import java.io.*; 28 import java.net.*; 29 import java.util.*; 30 import java.lang.reflect.*; 31 import sun.jvm.hotspot.debugger.*; 32 import sun.jvm.hotspot.debugger.cdbg.*; 33 import sun.jvm.hotspot.debugger.proc.amd64.*; 34 import sun.jvm.hotspot.debugger.proc.aarch64.*; 35 import sun.jvm.hotspot.debugger.proc.sparc.*; 36 import sun.jvm.hotspot.debugger.proc.ppc64.*; 37 import sun.jvm.hotspot.debugger.proc.x86.*; 38 import sun.jvm.hotspot.debugger.ppc64.*; 39 import sun.jvm.hotspot.debugger.amd64.*; 40 import sun.jvm.hotspot.debugger.aarch64.*; 41 import sun.jvm.hotspot.debugger.sparc.*; 42 import sun.jvm.hotspot.debugger.x86.*; 43 import sun.jvm.hotspot.utilities.*; 44 45 /** <P> An implementation of the JVMDebugger interface which sits on 46 * top of proc and relies on the SA's proc import module for 47 * communication with the debugger. </P> 48 * 49 * <P> <B>NOTE</B> that since we have the notion of fetching "Java 50 * primitive types" from the remote process (which might have 51 * different sizes than we expect) we have a bootstrapping 52 * problem. We need to know the sizes of these types before we can 53 * fetch them. The current implementation solves this problem by 54 * requiring that it be configured with these type sizes before they 55 * can be fetched. The readJ(Type) routines here will throw a 56 * RuntimeException if they are called before the debugger is 57 * configured with the Java primitive type sizes. </P> 58 */ 59 60 public class ProcDebuggerLocal extends DebuggerBase implements ProcDebugger { 61 protected static final int cacheSize = 16 * 1024 * 1024; // 16 MB 62 63 //------------------------------------------------------------------------ 64 // Implementation of Debugger interface 65 // 66 67 /** <P> machDesc may be null if it couldn't be determined yet; i.e., 68 * if we're on SPARC, we need to ask the remote process whether 69 * we're in 32- or 64-bit mode. </P> 70 * 71 * <P> useCache should be set to true if debugging is being done 72 * locally, and to false if the debugger is being created for the 73 * purpose of supporting remote debugging. </P> */ 74 public ProcDebuggerLocal(MachineDescription machDesc, boolean useCache) { 75 this.machDesc = machDesc; 76 int cacheNumPages; 77 int cachePageSize; 78 79 final String cpu = PlatformInfo.getCPU(); 80 if (cpu.equals("sparc")) { 81 threadFactory = new ProcSPARCThreadFactory(this); 82 pcRegIndex = SPARCThreadContext.R_PC; 83 fpRegIndex = SPARCThreadContext.R_I6; 84 } else if (cpu.equals("x86")) { 85 threadFactory = new ProcX86ThreadFactory(this); 86 pcRegIndex = X86ThreadContext.EIP; 87 fpRegIndex = X86ThreadContext.EBP; 88 unalignedAccessesOkay = true; 89 } else if (cpu.equals("amd64") || cpu.equals("x86_64")) { 90 threadFactory = new ProcAMD64ThreadFactory(this); 91 pcRegIndex = AMD64ThreadContext.RIP; 92 fpRegIndex = AMD64ThreadContext.RBP; 93 } else if (cpu.equals("aarch64")) { 94 threadFactory = new ProcAARCH64ThreadFactory(this); 95 pcRegIndex = AARCH64ThreadContext.PC; 96 fpRegIndex = AARCH64ThreadContext.FP; 97 } else if (cpu.equals("ppc64")) { 98 threadFactory = new ProcPPC64ThreadFactory(this); 99 pcRegIndex = PPC64ThreadContext.PC; 100 fpRegIndex = PPC64ThreadContext.SP; 101 } else { 102 try { 103 Class tfc = Class.forName("sun.jvm.hotspot.debugger.proc." + 104 cpu.toLowerCase() + ".Proc" + cpu.toUpperCase() + 105 "ThreadFactory"); 106 Constructor[] ctfc = tfc.getConstructors(); 107 threadFactory = (ProcThreadFactory)ctfc[0].newInstance(this); 108 } catch (Exception e) { 109 throw new RuntimeException("Thread access for CPU architecture " + PlatformInfo.getCPU() + " not yet supported"); 110 // Note: pcRegIndex and fpRegIndex do not appear to be referenced 111 } 112 } 113 if (useCache) { 114 // Cache portion of the remote process's address space. 115 // For now, this cache works best if it covers the entire 116 // heap of the remote process. FIXME: at least should make this 117 // tunable from the outside, i.e., via the UI. This is a 16 MB 118 // cache divided on SPARC into 2048 8K pages and on x86 into 119 // 4096 4K pages; the page size must be adjusted to be the OS's 120 // page size. 121 122 cachePageSize = getPageSize(); 123 cacheNumPages = parseCacheNumPagesProperty(cacheSize / cachePageSize); 124 initCache(cachePageSize, cacheNumPages); 125 } 126 127 resetNativePointers(); 128 clearCacheFields(); 129 } 130 131 /** FIXME: implement this with a Runtime.exec() of ps followed by 132 * parsing of its output */ 133 public boolean hasProcessList() throws DebuggerException { 134 return false; 135 } 136 137 public List getProcessList() throws DebuggerException { 138 throw new DebuggerException("Not yet supported"); 139 } 140 141 142 /** From the Debugger interface via JVMDebugger */ 143 public synchronized void attach(int processID) throws DebuggerException { 144 checkAttached(); 145 isCore = false; 146 attach0(new Integer(processID).toString()); 147 attached = true; 148 suspended = true; 149 } 150 151 /** From the Debugger interface via JVMDebugger */ 152 public synchronized void attach 153 (String executableName, String coreFileName) throws DebuggerException { 154 checkAttached(); 155 isCore = true; 156 topFrameCache = new HashMap(); 157 attach0(executableName, coreFileName); 158 attached = true; 159 suspended = true; 160 } 161 162 /** From the Debugger interface via JVMDebugger */ 163 public synchronized boolean detach() { 164 if (! attached) { 165 return false; 166 } 167 168 try { 169 if (p_ps_prochandle == 0L) { 170 return false; 171 } 172 detach0(); 173 clearCache(); 174 return true; 175 } catch (Exception e) { 176 e.printStackTrace(); 177 return false; 178 } finally { 179 resetNativePointers(); 180 clearCacheFields(); 181 suspended = false; 182 attached = false; 183 } 184 } 185 186 public synchronized void suspend() throws DebuggerException { 187 requireAttach(); 188 if (suspended) { 189 throw new DebuggerException("Process already suspended"); 190 } 191 suspend0(); 192 suspended = true; 193 enableCache(); 194 reresolveLoadObjects(); 195 } 196 197 public synchronized void resume() throws DebuggerException { 198 requireAttach(); 199 if (!suspended) { 200 throw new DebuggerException("Process not suspended"); 201 } 202 resume0(); 203 disableCache(); 204 suspended = false; 205 } 206 207 public synchronized boolean isSuspended() throws DebuggerException { 208 requireAttach(); 209 return suspended; 210 } 211 212 /** From the Debugger interface via JVMDebugger */ 213 public Address parseAddress(String addressString) throws NumberFormatException { 214 long addr = utils.scanAddress(addressString); 215 if (addr == 0) { 216 return null; 217 } 218 return new ProcAddress(this, addr); 219 } 220 221 /** From the Debugger interface via JVMDebugger */ 222 public String getOS() { 223 return PlatformInfo.getOS(); 224 } 225 226 /** From the Debugger interface via JVMDebugger */ 227 public String getCPU() { 228 return PlatformInfo.getCPU(); 229 } 230 231 public boolean hasConsole() throws DebuggerException { 232 return false; 233 } 234 235 public String consoleExecuteCommand(String cmd) throws DebuggerException { 236 throw new DebuggerException("Can't execute console commands"); 237 } 238 239 public String getConsolePrompt() throws DebuggerException { 240 return ""; 241 } 242 243 public CDebugger getCDebugger() throws DebuggerException { 244 if (cdbg == null) { 245 cdbg = new ProcCDebugger(this); 246 } 247 return cdbg; 248 } 249 250 /** From the SymbolLookup interface via Debugger and JVMDebugger */ 251 public synchronized Address lookup(String objectName, String symbol) { 252 requireAttach(); 253 long addr = lookupByName0(objectName, symbol); 254 if (addr == 0) { 255 return null; 256 } 257 return new ProcAddress(this, addr); 258 } 259 260 /** From the SymbolLookup interface via Debugger and JVMDebugger */ 261 public synchronized OopHandle lookupOop(String objectName, String symbol) { 262 Address addr = lookup(objectName, symbol); 263 if (addr == null) { 264 return null; 265 } 266 return addr.addOffsetToAsOopHandle(0); 267 } 268 269 /** From the ProcDebugger interface */ 270 public MachineDescription getMachineDescription() { 271 return machDesc; 272 } 273 274 /** Internal routine supporting lazy setting of MachineDescription, 275 * since on SPARC we will need to query the remote process to ask 276 * it what its data model is (32- or 64-bit). 277 */ 278 279 public void setMachineDescription(MachineDescription machDesc) { 280 this.machDesc = machDesc; 281 setBigEndian(machDesc.isBigEndian()); 282 utils = new DebuggerUtilities(machDesc.getAddressSize(), machDesc.isBigEndian()); 283 } 284 285 public synchronized int getRemoteProcessAddressSize() 286 throws DebuggerException { 287 requireAttach(); 288 return getRemoteProcessAddressSize0(); 289 } 290 291 //-------------------------------------------------------------------------------- 292 // Implementation of ThreadAccess interface 293 // 294 295 /** From the ThreadAccess interface via Debugger and JVMDebugger */ 296 public ThreadProxy getThreadForIdentifierAddress(Address addr) { 297 return threadFactory.createThreadWrapper(addr); 298 } 299 300 public ThreadProxy getThreadForThreadId(long id) { 301 return threadFactory.createThreadWrapper(id); 302 } 303 304 //---------------------------------------------------------------------- 305 // Overridden from DebuggerBase because we need to relax alignment 306 // constraints on x86 307 308 public long readJLong(long address) 309 throws UnmappedAddressException, UnalignedAddressException { 310 checkJavaConfigured(); 311 // FIXME: allow this to be configurable. Undesirable to add a 312 // dependency on the runtime package here, though, since this 313 // package should be strictly underneath it. 314 if (unalignedAccessesOkay) { 315 utils.checkAlignment(address, jintSize); 316 } else { 317 utils.checkAlignment(address, jlongSize); 318 } 319 byte[] data = readBytes(address, jlongSize); 320 return utils.dataToJLong(data, jlongSize); 321 } 322 323 //-------------------------------------------------------------------------------- 324 // Internal routines (for implementation of ProcAddress). 325 // These must not be called until the MachineDescription has been set up. 326 // 327 328 /** From the ProcDebugger interface */ 329 public String addressValueToString(long address) { 330 return utils.addressValueToString(address); 331 } 332 333 /** Need to override this to relax alignment checks on Solaris/x86. */ 334 public long readCInteger(long address, long numBytes, boolean isUnsigned) 335 throws UnmappedAddressException, UnalignedAddressException { 336 checkConfigured(); 337 if (!unalignedAccessesOkay) { 338 utils.checkAlignment(address, numBytes); 339 } else { 340 // Only slightly relaxed semantics -- this is a hack, but is 341 // necessary on Solaris/x86 where it seems the compiler is 342 // putting some global 64-bit data on 32-bit boundaries 343 if (numBytes == 8) { 344 utils.checkAlignment(address, 4); 345 } else { 346 utils.checkAlignment(address, numBytes); 347 } 348 } 349 byte[] data = readBytes(address, numBytes); 350 return utils.dataToCInteger(data, isUnsigned); 351 } 352 353 /** From the ProcDebugger interface */ 354 public ProcAddress readAddress(long address) 355 throws UnmappedAddressException, UnalignedAddressException { 356 long value = readAddressValue(address); 357 return (value == 0 ? null : new ProcAddress(this, value)); 358 } 359 360 public ProcAddress readCompOopAddress(long address) 361 throws UnmappedAddressException, UnalignedAddressException { 362 long value = readCompOopAddressValue(address); 363 return (value == 0 ? null : new ProcAddress(this, value)); 364 } 365 366 public ProcAddress readCompKlassAddress(long address) 367 throws UnmappedAddressException, UnalignedAddressException { 368 long value = readCompKlassAddressValue(address); 369 return (value == 0 ? null : new ProcAddress(this, value)); 370 } 371 372 /** From the ProcDebugger interface */ 373 public ProcOopHandle readOopHandle(long address) 374 throws UnmappedAddressException, UnalignedAddressException, NotInHeapException { 375 long value = readAddressValue(address); 376 return (value == 0 ? null : new ProcOopHandle(this, value)); 377 } 378 379 public ProcOopHandle readCompOopHandle(long address) { 380 long value = readCompOopAddressValue(address); 381 return (value == 0 ? null : new ProcOopHandle(this, value)); 382 } 383 384 public void writeBytesToProcess(long address, long numBytes, byte[] data) 385 throws UnmappedAddressException, DebuggerException { 386 if (isCore) { 387 throw new DebuggerException("Attached to a core file!"); 388 } 389 writeBytesToProcess0(address, numBytes, data); 390 } 391 392 public synchronized ReadResult readBytesFromProcess(long address, long numBytes) 393 throws DebuggerException { 394 requireAttach(); 395 byte[] res = readBytesFromProcess0(address, numBytes); 396 if(res != null) 397 return new ReadResult(res); 398 else 399 return new ReadResult(address); 400 } 401 402 protected int getPageSize() { 403 int pagesize = getPageSize0(); 404 if (pagesize == -1) { 405 // return the hard coded default value. 406 if (PlatformInfo.getCPU().equals("sparc") || 407 PlatformInfo.getCPU().equals("amd64") ) 408 pagesize = 8196; 409 else 410 pagesize = 4096; 411 } 412 return pagesize; 413 } 414 415 //-------------------------------------------------------------------------------- 416 // Thread context access. Can not be package private, but should 417 // only be accessed by the architecture-specific subpackages. 418 419 /** From the ProcDebugger interface. May have to redefine this later. */ 420 public synchronized long[] getThreadIntegerRegisterSet(int tid) { 421 requireAttach(); 422 return getThreadIntegerRegisterSet0(tid); 423 } 424 425 //-------------------------------------------------------------------------------- 426 // Address access. Can not be package private, but should only be 427 // accessed by the architecture-specific subpackages. 428 429 /** From the ProcDebugger interface */ 430 public long getAddressValue(Address addr) { 431 if (addr == null) return 0; 432 return ((ProcAddress) addr).getValue(); 433 } 434 435 /** From the ProcDebugger interface */ 436 public Address newAddress(long value) { 437 if (value == 0) return null; 438 return new ProcAddress(this, value); 439 } 440 441 /** From the ProcDebugger interface */ 442 public synchronized List getThreadList() throws DebuggerException { 443 requireAttach(); 444 List res = null; 445 if (isCore && (threadListCache != null)) { 446 res = threadListCache; 447 } else { 448 res = new ArrayList(); 449 fillThreadList0(res); 450 if (isCore) { 451 threadListCache = res; 452 } 453 } 454 return res; 455 } 456 457 /** From the ProcDebugger interface */ 458 public synchronized List getLoadObjectList() throws DebuggerException { 459 requireAttach(); 460 if (!suspended) { 461 throw new DebuggerException("Process not suspended"); 462 } 463 464 if (loadObjectCache == null) { 465 updateLoadObjectCache(); 466 } 467 return loadObjectCache; 468 } 469 470 /** From the ProcDebugger interface */ 471 public synchronized CFrame topFrameForThread(ThreadProxy thread) 472 throws DebuggerException { 473 requireAttach(); 474 CFrame res = null; 475 if (isCore && ((res = (CFrame) topFrameCache.get(thread)) != null)) { 476 return res; 477 } else { 478 ThreadContext context = thread.getContext(); 479 int numRegs = context.getNumRegisters(); 480 long[] regs = new long[numRegs]; 481 for (int i = 0; i < numRegs; i++) { 482 regs[i] = context.getRegister(i); 483 } 484 res = fillCFrameList0(regs); 485 if (isCore) { 486 topFrameCache.put(thread, res); 487 } 488 return res; 489 } 490 } 491 492 /** From the ProcDebugger interface */ 493 public synchronized ClosestSymbol lookup(long address) { 494 requireAttach(); 495 return lookupByAddress0(address); 496 } 497 498 /** From the ProcDebugger interface */ 499 public String demangle(String name) { 500 return demangle0(name); 501 } 502 503 //------------- Internals only below this point -------------------- 504 // 505 // 506 507 private void updateLoadObjectCache() { 508 List res = new ArrayList(); 509 nameToDsoMap = new HashMap(); 510 fillLoadObjectList0(res); 511 loadObjectCache = sortLoadObjects(res); 512 } 513 514 // sort load objects by base address 515 private static List sortLoadObjects(List in) { 516 // sort the list by base address 517 Object[] arr = in.toArray(); 518 Arrays.sort(arr, loadObjectComparator); 519 return Arrays.asList(arr); 520 } 521 522 private long lookupByName(String objectName, String symbolName) 523 throws DebuggerException { 524 // NOTE: this assumes that process is suspended (which is probably 525 // necessary assumption given that DSOs can be loaded/unloaded as 526 // process runs). Should update documentation. 527 if (nameToDsoMap == null) { 528 getLoadObjectList(); 529 } 530 SharedObject dso = (SharedObject) nameToDsoMap.get(objectName); 531 // The DSO can be null because we use this to search through known 532 // DSOs in HotSpotTypeDataBase (for example) 533 if (dso != null) { 534 ProcAddress addr = (ProcAddress) dso.lookupSymbol(symbolName); 535 if (addr != null) { 536 return addr.getValue(); 537 } 538 } 539 return 0; 540 } 541 542 private SharedObject findDSOByName(String fullPathName) { 543 if (loadObjectCache == null) 544 return null; 545 for (Iterator iter = loadObjectCache.iterator(); iter.hasNext(); ) { 546 SharedObject dso = (SharedObject) iter.next(); 547 if (dso.getName().equals(fullPathName)) { 548 return dso; 549 } 550 } 551 return null; 552 } 553 554 private void reresolveLoadObjects() throws DebuggerException { 555 if (loadObjectCache == null) { 556 return; 557 } 558 updateLoadObjectCache(); 559 } 560 561 562 private void checkAttached() { 563 if (attached) { 564 if (isCore) { 565 throw new DebuggerException("already attached to a core file!"); 566 } else { 567 throw new DebuggerException("already attached to a process!"); 568 } 569 } 570 } 571 572 private void requireAttach() { 573 if (! attached) { 574 throw new RuntimeException("not attached to a process or core file!"); 575 } 576 } 577 578 private void clearCacheFields() { 579 loadObjectCache = null; 580 nameToDsoMap = null; 581 threadListCache = null; 582 topFrameCache = null; 583 } 584 585 private void resetNativePointers() { 586 p_ps_prochandle = 0L; 587 588 // reset thread_db pointers 589 libthread_db_handle = 0L; 590 p_td_thragent_t = 0L; 591 p_td_init = 0L; 592 p_td_ta_new = 0L; 593 p_td_ta_delete = 0L; 594 p_td_ta_thr_iter = 0L; 595 p_td_thr_get_info = 0L; 596 p_td_ta_map_id2thr = 0L; 597 p_td_thr_getgregs = 0L; 598 599 // part of class sharing workaround 600 classes_jsa_fd = -1; 601 p_file_map_header = 0L; 602 } 603 604 // native methods and native helpers 605 606 // attach, detach 607 private native void attach0(String pid) throws DebuggerException; 608 private native void attach0(String executableFile, String coreFileName) throws DebuggerException; 609 private native void detach0() throws DebuggerException; 610 611 // address size, page size 612 private native int getRemoteProcessAddressSize0() throws DebuggerException; 613 private native int getPageSize0() throws DebuggerException; 614 615 // threads, stacks 616 private native long[] getThreadIntegerRegisterSet0(long tid) throws DebuggerException; 617 private native void fillThreadList0(List l) throws DebuggerException; 618 619 // fills stack frame list given reg set of the top frame and top frame 620 private native ProcCFrame fillCFrameList0(long[] regs) throws DebuggerException; 621 622 // helper called by fillCFrameList0 623 private ProcCFrame createSenderFrame(ProcCFrame f, long pc, long fp) { 624 ProcCFrame sender = new ProcCFrame(this, newAddress(pc), newAddress(fp)); 625 if (f != null) { 626 f.setSender(sender); 627 } 628 return sender; 629 } 630 631 // shared objects 632 private native void fillLoadObjectList0(List l) throws DebuggerException; 633 634 // helper called by fillLoadObjectList0 635 private LoadObject createLoadObject(String fileName, long textsize, long base) { 636 File f = new File(fileName); 637 Address baseAddr = newAddress(base); 638 SharedObject res = findDSOByName(fileName); 639 if (res != null) { 640 // already in cache. just change the base, if needed 641 Address oldBase = res.getBase(); 642 if (! baseAddr.equals(oldBase)) { 643 res.setBase(baseAddr); 644 } 645 } else { 646 // new shared object. 647 res = new SharedObject(this, fileName, f.length(), baseAddr); 648 } 649 nameToDsoMap.put(f.getName(), res); 650 return res; 651 } 652 653 // symbol-to-pc 654 private native long lookupByName0(String objectName, String symbolName) throws DebuggerException; 655 private native ClosestSymbol lookupByAddress0(long address) throws DebuggerException; 656 657 // helper called by lookupByAddress0 658 private ClosestSymbol createClosestSymbol(String name, long offset) { 659 return new ClosestSymbol(name, offset); 660 } 661 662 // process read/write 663 private native byte[] readBytesFromProcess0(long address, long numBytes) throws DebuggerException; 664 private native void writeBytesToProcess0(long address, long numBytes, byte[] data) throws DebuggerException; 665 666 // process control 667 private native void suspend0() throws DebuggerException; 668 private native void resume0() throws DebuggerException; 669 670 // demangle a C++ name 671 private native String demangle0(String name); 672 673 // init JNI ids to fields, methods 674 private native static void initIDs() throws DebuggerException; 675 private static LoadObjectComparator loadObjectComparator; 676 677 static { 678 System.loadLibrary("saproc"); 679 initIDs(); 680 loadObjectComparator = new LoadObjectComparator(); 681 } 682 683 private boolean unalignedAccessesOkay; 684 private ProcThreadFactory threadFactory; 685 686 // indices of PC and FP registers in gregset 687 private int pcRegIndex; 688 private int fpRegIndex; 689 690 // Symbol lookup support 691 // This is a map of library names to DSOs 692 private Map nameToDsoMap; // Map<String, SharedObject> 693 694 // C/C++ debugging support 695 private List/*<LoadObject>*/ loadObjects; 696 private CDebugger cdbg; 697 698 // ProcessControl support 699 private boolean suspended; 700 701 // libproc handle 702 private long p_ps_prochandle; 703 704 // libthread.so's dlopen handle, thread agent 705 // and function pointers 706 private long libthread_db_handle; 707 private long p_td_thragent_t; 708 private long p_td_init; 709 private long p_td_ta_new; 710 private long p_td_ta_delete; 711 private long p_td_ta_thr_iter; 712 private long p_td_thr_get_info; 713 private long p_td_ta_map_id2thr; 714 private long p_td_thr_getgregs; 715 716 // part of class sharing workaround 717 private int classes_jsa_fd; 718 private long p_file_map_header; 719 720 private boolean attached = false; 721 private boolean isCore; 722 723 // for core files, we cache load object list, thread list, top frames etc. 724 // for processes we cache load object list and sync. it during suspend. 725 private List threadListCache; 726 private List loadObjectCache; 727 private Map topFrameCache; // Map<ThreadProxy, CFrame> 728 }