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