Repository navigation
jcmd jmap_attach
ceragon edited this page Jun 17, 2022
·
2 revisions
public class HotSpotAgent {
private JVMDebugger debugger;
private TypeDataBase db;
public synchronized void attach(int processID)
throws DebuggerException {
pid = processID;
startupMode = PROCESS_MODE;
go();
}
private void go() {
setupDebugger();
setupVM();
}
private void setupDebugger() {
setupDebuggerLinux();
}
private void setupDebuggerLinux() {
LinuxDebuggerLocal dbg = new LinuxDebuggerLocal(machDesc, !isServer);
debugger = dbg;
attachDebugger();
}
private void attachDebugger() {
debugger.attach(pid);
}
private void setupVM() {
if (os.equals("linux")) {
db = new HotSpotTypeDataBase(machDesc,
new LinuxVtblAccess(debugger, jvmLibNames),
debugger, jvmLibNames);
}
VM.initialize(db, debugger);
}
}public class VM {
private static VM soleInstance;
public static void initialize(TypeDataBase db, JVMDebugger debugger) {
soleInstance = new VM(db, debugger,
debugger.getMachineDescription().isBigEndian());
}
}public class LinuxDebuggerLocal extends DebuggerBase implements LinuxDebugger {
private LinuxDebuggerLocalWorkerThread workerThread = null;
public synchronized void attach(int processID) throws DebuggerException {
class AttachTask implements WorkerThreadTask {
int pid;
public void doit(LinuxDebuggerLocal debugger) {
debugger.attach0(pid);
debugger.attached = true;
debugger.isCore = false;
findABIVersion();
}
}
AttachTask task = new AttachTask();
task.pid = processID;
workerThread.execute(task);
}
private native void attach0(int pid) throws DebuggerException;
}void Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_attach0__I
(JNIEnv *env, jobject this_obj, jint jpid) {
char err_buf[200];
struct ps_prochandle* ph;
if ((ph = Pgrab(jpid, err_buf, sizeof(err_buf))) == NULL) {
// 失败
}
}struct ps_prochandle* Pgrab(pid_t pid, char* err_buf, size_t err_buf_len) {
struct ps_prochandle* ph = NULL;
attach_state_t attach_status = ATTACH_SUCCESS;
// 先 attach 主线程
if ((attach_status = ptrace_attach(pid, err_buf, err_buf_len)) != ATTACH_SUCCESS) {
return NULL;
}
ph->pid = pid;
// 从 /proc/pid/task 目录下检索所有线程
char taskpath[PATH_MAX];
struct dirent *entry;
snprintf(taskpath, PATH_MAX, "/proc/%d/task", ph->pid);
dirp = opendir(taskpath);
int lwp_id;
while ((entry = readdir(dirp)) != NULL) {
lwp_id = atoi(entry->d_name);
if (lwp_id == ph->pid) {
continue;
}
add_thread_info(ph, lwp_id);
}
// 遍历所有线程,并依次 attach
thr = ph->threads;
while (thr) {
thread_info* current_thr = thr;
thr = thr->next;
// don't attach to the main thread again
if (ph->pid != current_thr->lwp_id) {
if ((attach_status = ptrace_attach(current_thr->lwp_id, err_buf, err_buf_len)) != ATTACH_SUCCESS) {
return NULL:
}
}
}
// 最终所有 jvm 的线程都被 attach 方法给挂起了
return ph;
}static attach_state_t ptrace_attach(pid_t pid, char* err_buf, size_t err_buf_len) {
// 先调用 attach,这样 pid 线程就会挂起
if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) {
return ATTACH_FAIL;
} else {
// 线程挂起了,此时 pid 所有的 signal(除了 KILL)都会被当前线程拦截
// 调用 waitpid 可以在线程停止后让线程正常退出
attach_state_t wait_ret = ptrace_waitpid(pid);
return wait_ret;
}
}static attach_state_t ptrace_waitpid(pid_t pid) {
int ret;
int status;
while (true) {
ret = waitpid(pid, &status, 0);
if (ret >= 0) {
if (WIFSTOPPED(status)) {
if (WSTOPSIG(status) == SIGSTOP) {
// Debuggee stopped by SIGSTOP.
return ATTACH_SUCCESS;
}
}
return ATTACH_FAIL;
}
}
}void Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_detach0
(JNIEnv *env, jobject this_obj) {
struct ps_prochandle* ph = get_proc_handle(env, this_obj);
if (ph != NULL) {
Prelease(ph);
}
}void Prelease(struct ps_prochandle* ph) {
ph->ops->release(ph);
destroy_lib_info(ph);
destroy_thread_info(ph);
free(ph);
}static void process_cleanup(struct ps_prochandle* ph) {
detach_all_pids(ph);
}static void detach_all_pids(struct ps_prochandle* ph) {
thread_info* thr = ph->threads;
while (thr) {
ptrace_detach(thr->lwp_id);
thr = thr->next;
}
}static bool ptrace_detach(pid_t pid) {
// detach
if (pid && ptrace(PTRACE_DETACH, pid, NULL, NULL) < 0) {
return false;
} else {
return true;
}
}