8181313: SA: Remove libthread_db dependency on Linux

Reviewed-by: jgeorge, sballal
This commit is contained in:
Yasumasa Suenaga 2019-01-18 14:43:25 +09:00
parent e7d44449f0
commit ef3336ec27
9 changed files with 50 additions and 172 deletions

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2015, 2019, 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,7 +66,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBSA, \
CXXFLAGS := $(CXXFLAGS_JDKLIB) $(SA_CFLAGS) $(SA_CXXFLAGS), \
EXTRA_SRC := $(LIBSA_EXTRA_SRC), \
LDFLAGS := $(LDFLAGS_JDKLIB) $(SA_LDFLAGS), \
LIBS_linux := -lthread_db $(LIBDL), \
LIBS_linux := $(LIBDL), \
LIBS_solaris := -ldl -ldemangle -lthread -lproc, \
LIBS_macosx := -framework Foundation -framework JavaNativeFoundation \
-framework JavaRuntimeSupport -framework Security -framework CoreFoundation, \

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2019, 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
@ -24,6 +24,7 @@
#include <jni.h>
#include "libproc.h"
#include "proc_service.h"
#include <elf.h>
#include <sys/types.h>
@ -241,10 +242,10 @@ JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_se
/*
* Class: sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal
* Method: attach0
* Signature: (IZ)V
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_attach0__IZ
(JNIEnv *env, jobject this_obj, jint jpid, jboolean is_in_container) {
JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_attach0__I
(JNIEnv *env, jobject this_obj, jint jpid) {
// For bitness checking, locate binary at /proc/jpid/exe
char buf[PATH_MAX];
@ -254,7 +255,7 @@ JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_at
char err_buf[200];
struct ps_prochandle* ph;
if ((ph = Pgrab(jpid, err_buf, sizeof(err_buf), is_in_container)) == NULL) {
if ((ph = Pgrab(jpid, err_buf, sizeof(err_buf))) == NULL) {
char msg[230];
snprintf(msg, sizeof(msg), "Can't attach to the process: %s", err_buf);
THROW_NEW_DEBUGGER_EXCEPTION(msg);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2019, 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
@ -28,41 +28,10 @@
#include <jni.h>
#include <unistd.h>
#include <stdint.h>
#include "proc_service.h"
#include <sys/procfs.h>
#include <sys/ptrace.h>
/************************************************************************************
0. This is very minimal subset of Solaris libproc just enough for current application.
Please note that the bulk of the functionality is from proc_service interface. This
adds Pgrab__ and some missing stuff. We hide the difference b/w live process and core
file by this interface.
1. pthread_id unique in both NPTL & LinuxThreads. We store this in
OSThread::_pthread_id in JVM code.
2. All threads see the same pid when they call getpid() under NPTL.
Threads receive different pid under LinuxThreads. We used to save the result of
::getpid() call in OSThread::_thread_id. This way uniqueness of OSThread::_thread_id
was lost under NPTL. Now, we store the result of ::gettid() call in
OSThread::_thread_id. Because gettid returns actual pid of thread (lwp id), this is
unique again. We therefore use OSThread::_thread_id as unique identifier.
3. There is a unique LWP id under both thread libraries. libthread_db maps pthread_id
to its underlying lwp_id under both the thread libraries. thread_info.lwp_id stores
lwp_id of the thread. The lwp id is nothing but the actual pid of clone'd processes. But
unfortunately libthread_db does not work very well for core dumps. So, we get pthread_id
only for processes. For core dumps, we don't use libthread_db at all (like gdb).
4. ptrace operates on this LWP id under both the thread libraries. When we say 'pid' for
ptrace call, we refer to lwp_id of the thread.
5. for core file, we parse ELF files and read data from them. For processes we use
combination of ptrace and /proc calls.
*************************************************************************************/
#if defined(sparc) || defined(sparcv9) || defined(ppc64) || defined(ppc64le)
#include <asm/ptrace.h>
@ -87,7 +56,7 @@ struct ps_prochandle;
// attach to a process
JNIEXPORT struct ps_prochandle* JNICALL
Pgrab(pid_t pid, char* err_buf, size_t err_buf_len, bool is_in_container);
Pgrab(pid_t pid, char* err_buf, size_t err_buf_len);
// attach to a core dump
JNIEXPORT struct ps_prochandle* JNICALL

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2019, 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
@ -26,8 +26,9 @@
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <thread_db.h>
#include <sys/procfs.h>
#include "libproc_impl.h"
#include "proc_service.h"
#define SA_ALTROOT "SA_ALTROOT"
@ -116,13 +117,6 @@ JNIEXPORT bool JNICALL
init_libproc(bool debug) {
// init debug mode
_libsaproc_debug = debug;
// initialize the thread_db library
if (td_init() != TD_OK) {
print_debug("libthread_db's td_init failed\n");
return false;
}
return true;
}
@ -256,7 +250,7 @@ const char* symbol_for_pc(struct ps_prochandle* ph, uintptr_t addr, uintptr_t* p
}
// add a thread to ps_prochandle
thread_info* add_thread_info(struct ps_prochandle* ph, pthread_t pthread_id, lwpid_t lwp_id) {
thread_info* add_thread_info(struct ps_prochandle* ph, lwpid_t lwp_id) {
thread_info* newthr;
if ( (newthr = (thread_info*) calloc(1, sizeof(thread_info))) == NULL) {
print_debug("can't allocate memory for thread_info\n");
@ -264,7 +258,6 @@ thread_info* add_thread_info(struct ps_prochandle* ph, pthread_t pthread_id, lwp
}
// initialize thread info
newthr->pthread_id = pthread_id;
newthr->lwp_id = lwp_id;
// add new thread to the list
@ -295,64 +288,6 @@ void delete_thread_info(struct ps_prochandle* ph, thread_info* thr_to_be_removed
free(current_thr);
}
// struct used for client data from thread_db callback
struct thread_db_client_data {
struct ps_prochandle* ph;
thread_info_callback callback;
};
// callback function for libthread_db
static int thread_db_callback(const td_thrhandle_t *th_p, void *data) {
struct thread_db_client_data* ptr = (struct thread_db_client_data*) data;
td_thrinfo_t ti;
td_err_e err;
memset(&ti, 0, sizeof(ti));
err = td_thr_get_info(th_p, &ti);
if (err != TD_OK) {
print_debug("libthread_db : td_thr_get_info failed, can't get thread info\n");
return err;
}
print_debug("thread_db : pthread %d (lwp %d)\n", ti.ti_tid, ti.ti_lid);
if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE) {
print_debug("Skipping pthread %d (lwp %d)\n", ti.ti_tid, ti.ti_lid);
return TD_OK;
}
if (ptr->callback(ptr->ph, ti.ti_tid, ti.ti_lid) != true)
return TD_ERR;
return TD_OK;
}
// read thread_info using libthread_db
bool read_thread_info(struct ps_prochandle* ph, thread_info_callback cb) {
struct thread_db_client_data mydata;
td_thragent_t* thread_agent = NULL;
if (td_ta_new(ph, &thread_agent) != TD_OK) {
print_debug("can't create libthread_db agent\n");
return false;
}
mydata.ph = ph;
mydata.callback = cb;
// we use libthread_db iterator to iterate thru list of threads.
if (td_ta_thr_iter(thread_agent, thread_db_callback, &mydata,
TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS) != TD_OK) {
td_ta_delete(thread_agent);
return false;
}
// delete thread agent
td_ta_delete(thread_agent);
return true;
}
// get number of threads
int get_num_threads(struct ps_prochandle* ph) {
return ph->num_threads;
@ -484,9 +419,3 @@ ps_lgetregs(struct ps_prochandle *ph, lwpid_t lid, prgregset_t gregset) {
return PS_OK;
}
// new libthread_db of NPTL seem to require this symbol
JNIEXPORT ps_err_e JNICALL
ps_get_thread_area() {
print_debug("ps_get_thread_area not implemented\n");
return PS_OK;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2019, 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
@ -46,7 +46,6 @@ typedef struct lib_info {
// list of threads
typedef struct thread_info {
lwpid_t lwp_id;
pthread_t pthread_id; // not used cores, always -1
struct user_regs_struct regs; // not for process, core uses for caching regset
struct thread_info* next;
} thread_info;
@ -108,11 +107,6 @@ void print_debug(const char* format,...);
void print_error(const char* format,...);
bool is_debug();
typedef bool (*thread_info_callback)(struct ps_prochandle* ph, pthread_t pid, lwpid_t lwpid);
// reads thread info using libthread_db and calls above callback for each thread
bool read_thread_info(struct ps_prochandle* ph, thread_info_callback cb);
// deletes a thread from the thread list
void delete_thread_info(struct ps_prochandle* ph, thread_info* thr);
@ -124,7 +118,7 @@ lib_info* add_lib_info_fd(struct ps_prochandle* ph, const char* libname, int fd,
uintptr_t base);
// adds a new thread to threads list, returns NULL on failure
thread_info* add_thread_info(struct ps_prochandle* ph, pthread_t pthread_id, lwpid_t lwp_id);
thread_info* add_thread_info(struct ps_prochandle* ph, lwpid_t lwp_id);
// a test for ELF signature without using libelf
bool is_elf_file(int fd);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2019, 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
@ -26,12 +26,10 @@
#define _PROC_SERVICE_H_
#include <stdio.h>
#include <thread_db.h>
#include <sys/procfs.h>
#include "jni.h"
#include "libproc.h"
// Linux does not have the proc service library, though it does provide the
// thread_db library which can be used to manipulate threads without having
// to know the details of NPTL
// copied from Solaris "proc_service.h"
typedef enum {
@ -79,8 +77,4 @@ ps_lgetfpregs(struct ps_prochandle *ph, lwpid_t lid, prfpregset_t *fpregs);
JNIEXPORT ps_err_e JNICALL
ps_lgetregs(struct ps_prochandle *ph, lwpid_t lid, prgregset_t gregset);
// new libthread_db of NPTL seem to require this symbol
JNIEXPORT ps_err_e JNICALL
ps_get_thread_area();
#endif /* _PROC_SERVICE_H_ */

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2019, 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
@ -31,6 +31,7 @@
#include <elf.h>
#include <link.h>
#include "libproc_impl.h"
#include "proc_service.h"
#include "salibelf.h"
#include "cds.h"
@ -510,8 +511,7 @@ static bool core_handle_prstatus(struct ps_prochandle* ph, const char* buf, size
prstatus_t* prstat = (prstatus_t*) buf;
thread_info* newthr;
print_debug("got integer regset for lwp %d\n", prstat->pr_pid);
// we set pthread_t to -1 for core dump
if((newthr = add_thread_info(ph, (pthread_t) -1, prstat->pr_pid)) == NULL)
if((newthr = add_thread_info(ph, prstat->pr_pid)) == NULL)
return false;
// copy regs

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2019, 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
@ -339,11 +339,6 @@ static char * fgets_no_cr(char * buf, int n, FILE *fp)
return rslt;
}
// callback for read_thread_info
static bool add_new_thread(struct ps_prochandle* ph, pthread_t pthread_id, lwpid_t lwp_id) {
return add_thread_info(ph, pthread_id, lwp_id) != NULL;
}
static bool read_lib_info(struct ps_prochandle* ph) {
char fname[32];
char buf[PATH_MAX];
@ -443,7 +438,7 @@ static ps_prochandle_ops process_ops = {
// 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, bool is_in_container) {
Pgrab(pid_t pid, char* err_buf, size_t err_buf_len) {
struct ps_prochandle* ph = NULL;
thread_info* thr = NULL;
attach_state_t attach_status = ATTACH_SUCCESS;
@ -464,6 +459,7 @@ Pgrab(pid_t pid, char* err_buf, size_t err_buf_len, bool is_in_container) {
// initialize ps_prochandle
ph->pid = pid;
add_thread_info(ph, ph->pid);
// initialize vtable
ph->ops = &process_ops;
@ -473,33 +469,30 @@ Pgrab(pid_t pid, char* err_buf, size_t err_buf_len, bool is_in_container) {
// the list of threads within the same process.
read_lib_info(ph);
// read thread info
if (is_in_container) {
/*
* If the process is running in the container, SA scans all tasks in
* /proc/<PID>/task to read all threads info.
*/
char taskpath[PATH_MAX];
DIR *dirp;
struct dirent *entry;
/*
* Read thread info.
* SA scans all tasks in /proc/<PID>/task to read all threads info.
*/
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);
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;
}
if (!process_doesnt_exist(lwp_id)) {
add_thread_info(ph, lwp_id);
}
closedir(dirp);
} else {
read_thread_info(ph, add_new_thread);
}
closedir(dirp);
// attach to the threads
thr = ph->threads;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2019, 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
@ -102,7 +102,7 @@ public class LinuxDebuggerLocal extends DebuggerBase implements LinuxDebugger {
private native static void init0()
throws DebuggerException;
private native void setSAAltRoot0(String altroot);
private native void attach0(int pid, boolean isInContainer)
private native void attach0(int pid)
throws DebuggerException;
private native void attach0(String execName, String coreName)
throws DebuggerException;
@ -321,9 +321,8 @@ public class LinuxDebuggerLocal extends DebuggerBase implements LinuxDebugger {
class AttachTask implements WorkerThreadTask {
int pid;
boolean isInContainer;
public void doit(LinuxDebuggerLocal debugger) {
debugger.attach0(pid, isInContainer);
debugger.attach0(pid);
debugger.attached = true;
debugger.isCore = false;
findABIVersion();
@ -332,7 +331,6 @@ public class LinuxDebuggerLocal extends DebuggerBase implements LinuxDebugger {
AttachTask task = new AttachTask();
task.pid = processID;
task.isInContainer = (processID != NSpid);
workerThread.execute(task);
}