View Javadoc
1   /*
2    * Copyright (c) 2002, 2013, 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.bsd;
26  
27  import java.io.File;
28  import java.util.ArrayList;
29  import java.util.List;
30  
31  import sun.jvm.hotspot.debugger.Address;
32  import sun.jvm.hotspot.debugger.DebuggerBase;
33  import sun.jvm.hotspot.debugger.DebuggerException;
34  import sun.jvm.hotspot.debugger.DebuggerUtilities;
35  import sun.jvm.hotspot.debugger.MachineDescription;
36  import sun.jvm.hotspot.debugger.NotInHeapException;
37  import sun.jvm.hotspot.debugger.OopHandle;
38  import sun.jvm.hotspot.debugger.ReadResult;
39  import sun.jvm.hotspot.debugger.ThreadProxy;
40  import sun.jvm.hotspot.debugger.UnalignedAddressException;
41  import sun.jvm.hotspot.debugger.UnmappedAddressException;
42  import sun.jvm.hotspot.debugger.cdbg.CDebugger;
43  import sun.jvm.hotspot.debugger.cdbg.ClosestSymbol;
44  import sun.jvm.hotspot.debugger.cdbg.LoadObject;
45  import sun.jvm.hotspot.runtime.JavaThread;
46  import sun.jvm.hotspot.runtime.Threads;
47  import sun.jvm.hotspot.runtime.VM;
48  import sun.jvm.hotspot.utilities.PlatformInfo;
49  
50  /** <P> An implementation of the JVMDebugger interface. The basic debug
51      facilities are implemented through ptrace interface in the JNI code
52      (libsaproc.so). Library maps and symbol table management are done in
53      JNI. </P>
54  
55      <P> <B>NOTE</B> that since we have the notion of fetching "Java
56      primitive types" from the remote process (which might have
57      different sizes than we expect) we have a bootstrapping
58      problem. We need to know the sizes of these types before we can
59      fetch them. The current implementation solves this problem by
60      requiring that it be configured with these type sizes before they
61      can be fetched. The readJ(Type) routines here will throw a
62      RuntimeException if they are called before the debugger is
63      configured with the Java primitive type sizes. </P> */
64  
65  public class BsdDebuggerLocal extends DebuggerBase implements BsdDebugger {
66      private boolean useGCC32ABI;
67      private boolean attached;
68      private long    p_ps_prochandle;      // native debugger handle
69      private long    symbolicator;         // macosx symbolicator handle
70      private long    task;                 // macosx task handle
71      private boolean isCore;
72      private boolean isDarwin;             // variant for bsd
73  
74      // CDebugger support
75      private BsdCDebugger cdbg;
76  
77      // threadList and loadObjectList are filled by attach0 method
78      private List threadList;
79      private List loadObjectList;
80  
81      // called by native method lookupByAddress0
82      private ClosestSymbol createClosestSymbol(String name, long offset) {
83         return new ClosestSymbol(name, offset);
84      }
85  
86      // called by native method attach0
87      private LoadObject createLoadObject(String fileName, long textsize,
88                                          long base) {
89         File f = new File(fileName);
90         Address baseAddr = newAddress(base);
91         return new SharedObject(this, fileName, f.length(), baseAddr);
92      }
93  
94      // native methods
95  
96      private native static void init0()
97                                  throws DebuggerException;
98      private native void attach0(int pid)
99                                  throws DebuggerException;
100     private native void attach0(String execName, String coreName)
101                                 throws DebuggerException;
102     private native void detach0()
103                                 throws DebuggerException;
104     private native long lookupByName0(String objectName, String symbol)
105                                 throws DebuggerException;
106     private native ClosestSymbol lookupByAddress0(long address)
107                                 throws DebuggerException;
108     private native long[] getThreadIntegerRegisterSet0(long unique_thread_id)
109                                 throws DebuggerException;
110     private native byte[] readBytesFromProcess0(long address, long numBytes)
111                                 throws DebuggerException;
112     public native static int  getAddressSize() ;
113 
114     // Note on Bsd threads are really processes. When target process is
115     // attached by a serviceability agent thread, only that thread can do
116     // ptrace operations on the target. This is because from kernel's point
117     // view, other threads are just separate processes and they are not
118     // attached to the target. When they attempt to make ptrace calls,
119     // an ESRCH error will be returned as kernel believes target is not
120     // being traced by the caller.
121     // To work around the problem, we use a worker thread here to handle
122     // all JNI functions that are making ptrace calls.
123 
124     interface WorkerThreadTask {
125        public void doit(BsdDebuggerLocal debugger) throws DebuggerException;
126     }
127 
128     class BsdDebuggerLocalWorkerThread extends Thread {
129        BsdDebuggerLocal debugger;
130        WorkerThreadTask task;
131        DebuggerException lastException;
132 
133        public BsdDebuggerLocalWorkerThread(BsdDebuggerLocal debugger) {
134          this.debugger = debugger;
135          setDaemon(true);
136        }
137 
138        public void run() {
139           synchronized (workerThread) {
140              for (;;) {
141                 if (task != null) {
142                    lastException = null;
143                    try {
144                       task.doit(debugger);
145                    } catch (DebuggerException exp) {
146                       lastException = exp;
147                    }
148                    task = null;
149                    workerThread.notifyAll();
150                 }
151 
152                 try {
153                    workerThread.wait();
154                 } catch (InterruptedException x) {}
155              }
156           }
157        }
158 
159        public WorkerThreadTask execute(WorkerThreadTask task) throws DebuggerException {
160           synchronized (workerThread) {
161              this.task = task;
162              workerThread.notifyAll();
163              while (this.task != null) {
164                 try {
165                    workerThread.wait();
166                 } catch (InterruptedException x) {}
167              }
168              if (lastException != null) {
169                 throw new DebuggerException(lastException);
170              } else {
171                 return task;
172              }
173           }
174        }
175     }
176 
177     private BsdDebuggerLocalWorkerThread workerThread = null;
178 
179     //----------------------------------------------------------------------
180     // Implementation of Debugger interface
181     //
182 
183     /** <P> machDesc may not be null. </P>
184 
185     <P> useCache should be set to true if debugging is being done
186     locally, and to false if the debugger is being created for the
187     purpose of supporting remote debugging. </P> */
188     public BsdDebuggerLocal(MachineDescription machDesc,
189                               boolean useCache) throws DebuggerException {
190         this.machDesc = machDesc;
191         utils = new DebuggerUtilities(machDesc.getAddressSize(),
192                                       machDesc.isBigEndian()) {
193            public void checkAlignment(long address, long alignment) {
194              // Need to override default checkAlignment because we need to
195              // relax alignment constraints on Bsd/x86
196              if ( (address % alignment != 0)
197                 &&(alignment != 8 || address % 4 != 0)) {
198                 throw new UnalignedAddressException(
199                         "Trying to read at address: "
200                       + addressValueToString(address)
201                       + " with alignment: " + alignment,
202                         address);
203              }
204            }
205         };
206 
207         if (useCache) {
208             // FIXME: re-test necessity of cache on Bsd, where data
209             // fetching is faster
210             // Cache portion of the remote process's address space.
211             // Fetching data over the socket connection to dbx is slow.
212             // Might be faster if we were using a binary protocol to talk to
213             // dbx, but would have to test. For now, this cache works best
214             // if it covers the entire heap of the remote process. FIXME: at
215             // least should make this tunable from the outside, i.e., via
216             // the UI. This is a cache of 4096 4K pages, or 16 MB. The page
217             // size must be adjusted to be the hardware's page size.
218             // (FIXME: should pick this up from the debugger.)
219             if (getCPU().equals("ia64")) {
220               initCache(16384, parseCacheNumPagesProperty(1024));
221             } else {
222               initCache(4096, parseCacheNumPagesProperty(4096));
223             }
224         }
225 
226         isDarwin = getOS().equals("darwin");
227         workerThread = new BsdDebuggerLocalWorkerThread(this);
228         workerThread.start();
229     }
230 
231     /** From the Debugger interface via JVMDebugger */
232     public boolean hasProcessList() throws DebuggerException {
233         return false;
234     }
235 
236     /** From the Debugger interface via JVMDebugger */
237     public List getProcessList() throws DebuggerException {
238         throw new DebuggerException("getProcessList not implemented yet");
239     }
240 
241     private void checkAttached() throws DebuggerException {
242         if (attached) {
243             if (isCore) {
244                 throw new DebuggerException("attached to a core dump already");
245             } else {
246                 throw new DebuggerException("attached to a process already");
247             }
248         }
249     }
250 
251     private void requireAttach() {
252         if (! attached) {
253             throw new RuntimeException("not attached to a process or a core!");
254         }
255     }
256 
257     /* called from attach methods */
258     private void findABIVersion() throws DebuggerException {
259         String libjvmName = isDarwin ? "libjvm.dylib" : "libjvm.so";
260         String javaThreadVt = isDarwin ? "_vt_10JavaThread" : "__vt_10JavaThread";
261         if (lookupByName0(libjvmName, javaThreadVt) != 0) {
262             // old C++ ABI
263             useGCC32ABI = false;
264         } else {
265             // new C++ ABI
266             useGCC32ABI = true;
267         }
268     }
269 
270     /** From the Debugger interface via JVMDebugger */
271     public synchronized void attach(int processID) throws DebuggerException {
272         checkAttached();
273         threadList = new ArrayList();
274         loadObjectList = new ArrayList();
275         class AttachTask implements WorkerThreadTask {
276            int pid;
277            public void doit(BsdDebuggerLocal debugger) {
278               debugger.attach0(pid);
279               debugger.attached = true;
280               debugger.isCore = false;
281               findABIVersion();
282            }
283         }
284 
285         AttachTask task = new AttachTask();
286         task.pid = processID;
287         workerThread.execute(task);
288     }
289 
290     /** From the Debugger interface via JVMDebugger */
291     public synchronized void attach(String execName, String coreName) {
292         checkAttached();
293         threadList = new ArrayList();
294         loadObjectList = new ArrayList();
295         attach0(execName, coreName);
296         attached = true;
297         isCore = true;
298         findABIVersion();
299     }
300 
301     /** From the Debugger interface via JVMDebugger */
302     public synchronized boolean detach() {
303         if (!attached) {
304             return false;
305         }
306 
307         threadList = null;
308         loadObjectList = null;
309 
310         if (isCore) {
311             detach0();
312             attached = false;
313             return true;
314         } else {
315             class DetachTask implements WorkerThreadTask {
316                 boolean result = false;
317 
318                 public void doit(BsdDebuggerLocal debugger) {
319                     debugger.detach0();
320                     debugger.attached = false;
321                     result = true;
322                 }
323             }
324 
325             DetachTask task = new DetachTask();
326             workerThread.execute(task);
327             return task.result;
328         }
329     }
330 
331     /** From the Debugger interface via JVMDebugger */
332     public Address parseAddress(String addressString)
333             throws NumberFormatException {
334         long addr = utils.scanAddress(addressString);
335         if (addr == 0) {
336             return null;
337         }
338         return new BsdAddress(this, addr);
339     }
340 
341     /** From the Debugger interface via JVMDebugger */
342     public String getOS() {
343         return PlatformInfo.getOS();
344     }
345 
346     /** From the Debugger interface via JVMDebugger */
347     public String getCPU() {
348         return PlatformInfo.getCPU();
349     }
350 
351     public boolean hasConsole() throws DebuggerException {
352         return false;
353     }
354 
355     public String consoleExecuteCommand(String cmd) throws DebuggerException {
356         throw new DebuggerException("No debugger console available on Bsd");
357     }
358 
359     public String getConsolePrompt() throws DebuggerException {
360         return null;
361     }
362 
363     /* called from lookup */
364     private long handleGCC32ABI(long addr, String symbol) throws DebuggerException {
365         if (useGCC32ABI && symbol.startsWith("_ZTV")) {
366             return addr + (2 * machDesc.getAddressSize());
367         } else {
368             return addr;
369         }
370     }
371 
372     /** From the SymbolLookup interface via Debugger and JVMDebugger */
373     public synchronized Address lookup(String objectName, String symbol) {
374         requireAttach();
375         if (!attached) {
376             return null;
377         }
378 
379         if (isCore) {
380             // MacOSX symbol with "_" as leading
381             long addr = lookupByName0(objectName, isDarwin ? "_" + symbol : symbol);
382             return (addr == 0)? null : new BsdAddress(this, handleGCC32ABI(addr, symbol));
383         } else {
384             class LookupByNameTask implements WorkerThreadTask {
385                 String objectName, symbol;
386                 Address result;
387 
388                 public void doit(BsdDebuggerLocal debugger) {
389                     long addr = debugger.lookupByName0(objectName, symbol);
390                     result = (addr == 0 ? null : new BsdAddress(debugger, handleGCC32ABI(addr, symbol)));
391                 }
392             }
393 
394             LookupByNameTask task = new LookupByNameTask();
395             task.objectName = objectName;
396             task.symbol = symbol;
397             workerThread.execute(task);
398             return task.result;
399         }
400     }
401 
402     /** From the SymbolLookup interface via Debugger and JVMDebugger */
403     public synchronized OopHandle lookupOop(String objectName, String symbol) {
404         Address addr = lookup(objectName, symbol);
405         if (addr == null) {
406             return null;
407         }
408         return addr.addOffsetToAsOopHandle(0);
409     }
410 
411     /** From the Debugger interface */
412     public MachineDescription getMachineDescription() {
413         return machDesc;
414     }
415 
416     //----------------------------------------------------------------------
417     // Implementation of ThreadAccess interface
418     //
419 
420     /** From the ThreadAccess interface via Debugger and JVMDebugger */
421     public ThreadProxy getThreadForIdentifierAddress(Address threadIdAddr, Address uniqueThreadIdAddr) {
422         return new BsdThread(this, threadIdAddr, uniqueThreadIdAddr);
423     }
424 
425     @Override
426     public ThreadProxy getThreadForIdentifierAddress(Address addr) {
427         throw new RuntimeException("unimplemented");
428     }
429 
430     /** From the ThreadAccess interface via Debugger and JVMDebugger */
431     public ThreadProxy getThreadForThreadId(long id) {
432         return new BsdThread(this, id);
433     }
434 
435     //----------------------------------------------------------------------
436     // Internal routines (for implementation of BsdAddress).
437     // These must not be called until the MachineDescription has been set up.
438     //
439 
440     /** From the BsdDebugger interface */
441     public String addressValueToString(long address) {
442         return utils.addressValueToString(address);
443     }
444 
445     /** From the BsdDebugger interface */
446     public BsdAddress readAddress(long address)
447             throws UnmappedAddressException, UnalignedAddressException {
448         long value = readAddressValue(address);
449         return (value == 0 ? null : new BsdAddress(this, value));
450     }
451     public BsdAddress readCompOopAddress(long address)
452             throws UnmappedAddressException, UnalignedAddressException {
453         long value = readCompOopAddressValue(address);
454         return (value == 0 ? null : new BsdAddress(this, value));
455     }
456 
457     public BsdAddress readCompKlassAddress(long address)
458             throws UnmappedAddressException, UnalignedAddressException {
459         long value = readCompKlassAddressValue(address);
460         return (value == 0 ? null : new BsdAddress(this, value));
461     }
462 
463     /** From the BsdDebugger interface */
464     public BsdOopHandle readOopHandle(long address)
465             throws UnmappedAddressException, UnalignedAddressException,
466                 NotInHeapException {
467         long value = readAddressValue(address);
468         return (value == 0 ? null : new BsdOopHandle(this, value));
469     }
470     public BsdOopHandle readCompOopHandle(long address)
471             throws UnmappedAddressException, UnalignedAddressException,
472                 NotInHeapException {
473         long value = readCompOopAddressValue(address);
474         return (value == 0 ? null : new BsdOopHandle(this, value));
475     }
476 
477     //----------------------------------------------------------------------
478     // Thread context access
479     //
480 
481     public synchronized long[] getThreadIntegerRegisterSet(long unique_thread_id)
482                                             throws DebuggerException {
483         requireAttach();
484         if (isCore) {
485             return getThreadIntegerRegisterSet0(unique_thread_id);
486         } else {
487             class GetThreadIntegerRegisterSetTask implements WorkerThreadTask {
488                 long unique_thread_id;
489                 long[] result;
490                 public void doit(BsdDebuggerLocal debugger) {
491                     result = debugger.getThreadIntegerRegisterSet0(unique_thread_id);
492                 }
493             }
494 
495             GetThreadIntegerRegisterSetTask task = new GetThreadIntegerRegisterSetTask();
496             task.unique_thread_id = unique_thread_id;
497             workerThread.execute(task);
498             return task.result;
499         }
500     }
501 
502     /** Need to override this to relax alignment checks on x86. */
503     public long readCInteger(long address, long numBytes, boolean isUnsigned)
504         throws UnmappedAddressException, UnalignedAddressException {
505         // Only slightly relaxed semantics -- this is a hack, but is
506         // necessary on x86 where it seems the compiler is
507         // putting some global 64-bit data on 32-bit boundaries
508         if (numBytes == 8) {
509             utils.checkAlignment(address, 4);
510         } else {
511             utils.checkAlignment(address, numBytes);
512         }
513         byte[] data = readBytes(address, numBytes);
514         return utils.dataToCInteger(data, isUnsigned);
515     }
516 
517     // Overridden from DebuggerBase because we need to relax alignment
518     // constraints on x86
519     public long readJLong(long address)
520         throws UnmappedAddressException, UnalignedAddressException {
521         utils.checkAlignment(address, jintSize);
522         byte[] data = readBytes(address, jlongSize);
523         return utils.dataToJLong(data, jlongSize);
524     }
525 
526     //----------------------------------------------------------------------
527     // Address access. Can not be package private, but should only be
528     // accessed by the architecture-specific subpackages.
529 
530     /** From the BsdDebugger interface */
531     public long getAddressValue(Address addr) {
532       if (addr == null) return 0;
533       return ((BsdAddress) addr).getValue();
534     }
535 
536     /** From the BsdDebugger interface */
537     public Address newAddress(long value) {
538       if (value == 0) return null;
539       return new BsdAddress(this, value);
540     }
541 
542     /** From the BsdCDebugger interface */
543     public List/*<ThreadProxy>*/ getThreadList() {
544       requireAttach();
545       return threadList;
546     }
547 
548     /** From the BsdCDebugger interface */
549     public List/*<LoadObject>*/ getLoadObjectList() {
550       requireAttach();
551       return loadObjectList;
552     }
553 
554     /** From the BsdCDebugger interface */
555     public synchronized ClosestSymbol lookup(long addr) {
556        requireAttach();
557        if (isCore) {
558           return lookupByAddress0(addr);
559        } else {
560           class LookupByAddressTask implements WorkerThreadTask {
561              long addr;
562              ClosestSymbol result;
563 
564              public void doit(BsdDebuggerLocal debugger) {
565                  result = debugger.lookupByAddress0(addr);
566              }
567           }
568 
569           LookupByAddressTask task = new LookupByAddressTask();
570           task.addr = addr;
571           workerThread.execute(task);
572           return task.result;
573        }
574     }
575 
576     public CDebugger getCDebugger() {
577       if (cdbg == null) {
578          String cpu = getCPU();
579          if (cpu.equals("ia64") ) {
580             // IA-64 is not supported because of stack-walking issues
581             return null;
582          }
583          cdbg = new BsdCDebugger(this);
584       }
585       return cdbg;
586     }
587 
588     /** This reads bytes from the remote process. */
589     public synchronized ReadResult readBytesFromProcess(long address,
590             long numBytes) throws UnmappedAddressException, DebuggerException {
591         requireAttach();
592         if (isCore) {
593             byte[] res = readBytesFromProcess0(address, numBytes);
594             return (res != null)? new ReadResult(res) : new ReadResult(address);
595         } else {
596             class ReadBytesFromProcessTask implements WorkerThreadTask {
597                 long address, numBytes;
598                 ReadResult result;
599                 public void doit(BsdDebuggerLocal debugger) {
600                     byte[] res = debugger.readBytesFromProcess0(address, numBytes);
601                     if (res != null)
602                         result = new ReadResult(res);
603                     else
604                         result = new ReadResult(address);
605                 }
606             }
607 
608             ReadBytesFromProcessTask task = new ReadBytesFromProcessTask();
609             task.address = address;
610             task.numBytes = numBytes;
611             workerThread.execute(task);
612             return task.result;
613         }
614     }
615 
616     public void writeBytesToProcess(long address, long numBytes, byte[] data)
617         throws UnmappedAddressException, DebuggerException {
618         // FIXME
619         throw new DebuggerException("Unimplemented");
620     }
621 
622     /** this functions used for core file reading and called from native attach0,
623         it returns an array of long integers as
624         [thread_id, stack_start, stack_end, thread_id, stack_start, stack_end, ....] for
625         all java threads recorded in Threads. Also adds the ThreadProxy to threadList */
626     public long[] getJavaThreadsInfo() {
627         requireAttach();
628         Threads threads = VM.getVM().getThreads();
629         int len = threads.getNumberOfThreads();
630         long[] result = new long[len * 3];    // triple
631         JavaThread t = threads.first();
632         long beg, end;
633         int i = 0;
634         while (t != null) {
635             end = t.getStackBaseValue();
636             beg = end - t.getStackSize();
637             BsdThread bsdt = (BsdThread)t.getThreadProxy();
638             long uid = bsdt.getUniqueThreadId();
639             if (threadList != null) threadList.add(bsdt);
640             result[i] = uid;
641             result[i + 1] = beg;
642             result[i + 2] = end;
643             t = t.next();
644             i += 3;
645         }
646         return result;
647     }
648 
649     static {
650         System.loadLibrary("saproc");
651         init0();
652     }
653 }