--- old/src/jdk.hotspot.agent/linux/native/libsaproc/LinuxDebuggerLocal.c 2018-06-28 21:55:38.541364491 +0900 +++ new/src/jdk.hotspot.agent/linux/native/libsaproc/LinuxDebuggerLocal.c 2018-06-28 21:55:38.344369146 +0900 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -66,6 +66,8 @@ static jmethodID getThreadForThreadId_ID = 0; static jmethodID listAdd_ID = 0; +static char *saaltroot = NULL; + #define CHECK_EXCEPTION_(value) if ((*env)->ExceptionOccurred(env)) { return value; } #define CHECK_EXCEPTION if ((*env)->ExceptionOccurred(env)) { return;} #define THROW_NEW_DEBUGGER_EXCEPTION_(str, value) { throw_new_debugger_exception(env, str); return value; } @@ -211,11 +213,34 @@ /* * Class: sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal + * Method: setSAAltRoot0 + * Signature: (Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_setSAAltRoot0 + (JNIEnv *env, jobject this_obj, jstring altroot) { + if (saaltroot != NULL) { + free(saaltroot); + } + const char *path = (*env)->GetStringUTFChars(env, altroot, JNI_FALSE); + /* + * `saaltroot` is used for putenv(). + * So we need to keep this memory. + */ + static const char *PREFIX = "SA_ALTROOT="; + size_t len = strlen(PREFIX) + strlen(path) + 1; + saaltroot = (char *)malloc(len); + snprintf(saaltroot, len, "%s%s", PREFIX, path); + putenv(saaltroot); + (*env)->ReleaseStringUTFChars(env, altroot, path); +} + +/* + * Class: sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal * Method: attach0 - * Signature: (I)V + * Signature: (IZ)V */ -JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_attach0__I - (JNIEnv *env, jobject this_obj, jint jpid) { +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_attach0__IZ + (JNIEnv *env, jobject this_obj, jint jpid, jboolean is_in_container) { // For bitness checking, locate binary at /proc/jpid/exe char buf[PATH_MAX]; @@ -225,7 +250,7 @@ char err_buf[200]; struct ps_prochandle* ph; - if ( (ph = Pgrab(jpid, err_buf, sizeof(err_buf))) == NULL) { + if ( (ph = Pgrab(jpid, err_buf, sizeof(err_buf), is_in_container)) == NULL) { char msg[230]; snprintf(msg, sizeof(msg), "Can't attach to the process: %s", err_buf); THROW_NEW_DEBUGGER_EXCEPTION(msg); @@ -276,6 +301,10 @@ if (ph != NULL) { Prelease(ph); } + if (saaltroot != NULL) { + free(saaltroot); + saaltroot = NULL; + } } /* --- old/src/jdk.hotspot.agent/linux/native/libsaproc/libproc.h 2018-06-28 21:55:39.091351494 +0900 +++ new/src/jdk.hotspot.agent/linux/native/libsaproc/libproc.h 2018-06-28 21:55:38.894356149 +0900 @@ -87,7 +87,7 @@ // attach to a process JNIEXPORT struct ps_prochandle* JNICALL -Pgrab(pid_t pid, char* err_buf, size_t err_buf_len); +Pgrab(pid_t pid, char* err_buf, size_t err_buf_len, bool is_in_container); // attach to a core dump JNIEXPORT struct ps_prochandle* JNICALL --- old/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c 2018-06-28 21:55:39.633338687 +0900 +++ new/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c 2018-06-28 21:55:39.436343342 +0900 @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -374,7 +375,7 @@ // attach to the process. One and only one exposed stuff JNIEXPORT struct ps_prochandle* JNICALL -Pgrab(pid_t pid, char* err_buf, size_t err_buf_len) { +Pgrab(pid_t pid, char* err_buf, size_t err_buf_len, bool is_in_container) { struct ps_prochandle* ph = NULL; thread_info* thr = NULL; @@ -401,7 +402,28 @@ read_lib_info(ph); // read thread info - read_thread_info(ph, add_new_thread); + if (is_in_container) { + char taskpath[PATH_MAX]; + DIR *dirp; + struct dirent *entry; + + snprintf(taskpath, PATH_MAX, "/proc/%d/task", ph->pid); + dirp = opendir(taskpath); + int lwp_id; + while ((entry = readdir(dirp)) != NULL) { + if (*entry->d_name == '.') { + continue; + } + lwp_id = atoi(entry->d_name); + if (lwp_id == ph->pid) { + continue; + } + add_new_thread(ph, -1, lwp_id); + } + closedir(dirp); + } else { + read_thread_info(ph, add_new_thread); + } // attach to the threads thr = ph->threads; --- old/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java 2018-06-28 21:55:40.176325855 +0900 +++ new/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java 2018-06-28 21:55:39.979330511 +0900 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,8 +25,16 @@ package sun.jvm.hotspot.debugger.linux; import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.stream.Collectors; import sun.jvm.hotspot.debugger.Address; import sun.jvm.hotspot.debugger.DebuggerBase; @@ -72,6 +80,9 @@ private List threadList; private List loadObjectList; + // PID namespace support + private Map nspidMap; + // called by native method lookupByAddress0 private ClosestSymbol createClosestSymbol(String name, long offset) { return new ClosestSymbol(name, offset); @@ -89,7 +100,8 @@ private native static void init0() throws DebuggerException; - private native void attach0(int pid) + private native void setSAAltRoot0(String altroot); + private native void attach0(int pid, boolean isInContainer) throws DebuggerException; private native void attach0(String execName, String coreName) throws DebuggerException; @@ -254,15 +266,59 @@ } } + private int getNamespacePID(Path statusPath) { + try (var lines = Files.lines(statusPath)) { + return lines.map(s -> s.split("\\s+")) + .filter(a -> a.length == 3) + .filter(a -> a[0].equals("NSpid:")) + .mapToInt(a -> Integer.valueOf(a[2])) + .findFirst() + .getAsInt(); + } catch (IOException | NoSuchElementException e) { + return Integer.valueOf(statusPath.getParent() + .toFile() + .getName()); + } + } + + public int getHostPID(int id) { + try { + return nspidMap.get(id); + } catch (NullPointerException e) { + return -1; + } + } + + private void fillNSpidMap(Path proc) { + Path task = Paths.get(proc.toString(), "task"); + try { + nspidMap = Files.list(task) + .filter(p -> !p.toString().startsWith(".")) + .collect(Collectors.toMap(p -> Integer.valueOf(getNamespacePID(Paths.get(p.toString(), "status"))), + p -> Integer.valueOf(p.toFile().getName()))); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + /** From the Debugger interface via JVMDebugger */ public synchronized void attach(int processID) throws DebuggerException { checkAttached(); threadList = new ArrayList(); loadObjectList = new ArrayList(); + + Path proc = Paths.get("/proc", Integer.toString(processID)); + int NSpid = getNamespacePID(Paths.get(proc.toString(), "status")); + if (NSpid != processID) { + setSAAltRoot0(Paths.get(proc.toString(), "root").toString()); + fillNSpidMap(proc); + } + class AttachTask implements WorkerThreadTask { int pid; + boolean isInContainer; public void doit(LinuxDebuggerLocal debugger) { - debugger.attach0(pid); + debugger.attach0(pid, isInContainer); debugger.attached = true; debugger.isCore = false; findABIVersion(); @@ -271,6 +327,7 @@ AttachTask task = new AttachTask(); task.pid = processID; + task.isInContainer = (processID != NSpid); workerThread.execute(task); } --- old/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxThread.java 2018-06-28 21:55:40.724312906 +0900 +++ new/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxThread.java 2018-06-28 21:55:40.527317561 +0900 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,15 @@ // FIXME: size of data fetched here should be configurable. // However, making it so would produce a dependency on the "types" // package from the debugger package, which is not desired. - this.lwp_id = (int) addr.getCIntegerAt(0, 4, true); + int pid = (int)addr.getCIntegerAt(0, 4, true); + if (debugger instanceof LinuxDebuggerLocal) { + int hostPID = ((LinuxDebuggerLocal)debugger).getHostPID(pid); + if (hostPID != -1) { + pid = hostPID; + } + } + this.lwp_id = pid; + } LinuxThread(LinuxDebugger debugger, long id) {