Path: blob/master/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c
41152 views
/*1* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*22*/2324#include <stdio.h>25#include <stdlib.h>26#include <string.h>27#include <signal.h>28#include <errno.h>29#include <elf.h>30#include <dirent.h>31#include <ctype.h>32#include <sys/types.h>33#include <sys/wait.h>34#include <sys/ptrace.h>35#include <sys/uio.h>36#include "libproc_impl.h"3738#if defined(x86_64) && !defined(amd64)39#define amd64 140#endif4142#ifndef __WALL43#define __WALL 0x40000000 // Copied from /usr/include/linux/wait.h44#endif4546// This file has the libproc implementation specific to live process47// For core files, refer to ps_core.c4849typedef enum {50ATTACH_SUCCESS,51ATTACH_FAIL,52ATTACH_THREAD_DEAD53} attach_state_t;5455static inline uintptr_t align(uintptr_t ptr, size_t size) {56return (ptr & ~(size - 1));57}5859// ---------------------------------------------60// ptrace functions61// ---------------------------------------------6263// read "size" bytes of data from "addr" within the target process.64// unlike the standard ptrace() function, process_read_data() can handle65// unaligned address - alignment check, if required, should be done66// before calling process_read_data.6768static bool process_read_data(struct ps_prochandle* ph, uintptr_t addr, char *buf, size_t size) {69long rslt;70size_t i, words;71uintptr_t end_addr = addr + size;72uintptr_t aligned_addr = align(addr, sizeof(long));7374if (aligned_addr != addr) {75char *ptr = (char *)&rslt;76errno = 0;77rslt = ptrace(PTRACE_PEEKDATA, ph->pid, aligned_addr, 0);78if (errno) {79print_debug("ptrace(PTRACE_PEEKDATA, ..) failed for %d bytes @ %lx\n", size, addr);80return false;81}82for (; aligned_addr != addr; aligned_addr++, ptr++);83for (; ((intptr_t)aligned_addr % sizeof(long)) && aligned_addr < end_addr;84aligned_addr++)85*(buf++) = *(ptr++);86}8788words = (end_addr - aligned_addr) / sizeof(long);8990// assert((intptr_t)aligned_addr % sizeof(long) == 0);91for (i = 0; i < words; i++) {92errno = 0;93rslt = ptrace(PTRACE_PEEKDATA, ph->pid, aligned_addr, 0);94if (errno) {95print_debug("ptrace(PTRACE_PEEKDATA, ..) failed for %d bytes @ %lx\n", size, addr);96return false;97}98*(long *)buf = rslt;99buf += sizeof(long);100aligned_addr += sizeof(long);101}102103if (aligned_addr != end_addr) {104char *ptr = (char *)&rslt;105errno = 0;106rslt = ptrace(PTRACE_PEEKDATA, ph->pid, aligned_addr, 0);107if (errno) {108print_debug("ptrace(PTRACE_PEEKDATA, ..) failed for %d bytes @ %lx\n", size, addr);109return false;110}111for (; aligned_addr != end_addr; aligned_addr++)112*(buf++) = *(ptr++);113}114return true;115}116117// null implementation for write118static bool process_write_data(struct ps_prochandle* ph,119uintptr_t addr, const char *buf , size_t size) {120return false;121}122123// "user" should be a pointer to a user_regs_struct124static bool process_get_lwp_regs(struct ps_prochandle* ph, pid_t pid, struct user_regs_struct *user) {125// we have already attached to all thread 'pid's, just use ptrace call126// to get regset now. Note that we don't cache regset upfront for processes.127// Linux on x86 and sparc are different. On x86 ptrace(PTRACE_GETREGS, ...)128// uses pointer from 4th argument and ignores 3rd argument. On sparc it uses129// pointer from 3rd argument and ignores 4th argument130#define ptrace_getregs(request, pid, addr, data) ptrace(request, pid, data, addr)131132#if defined(_LP64) && defined(PTRACE_GETREGS64)133#define PTRACE_GETREGS_REQ PTRACE_GETREGS64134#elif defined(PTRACE_GETREGS)135#define PTRACE_GETREGS_REQ PTRACE_GETREGS136#elif defined(PT_GETREGS)137#define PTRACE_GETREGS_REQ PT_GETREGS138#endif139140#ifdef PTRACE_GETREGS_REQ141if (ptrace_getregs(PTRACE_GETREGS_REQ, pid, user, NULL) < 0) {142print_debug("ptrace(PTRACE_GETREGS, ...) failed for lwp(%d) errno(%d) \"%s\"\n", pid,143errno, strerror(errno));144return false;145}146return true;147#elif defined(PTRACE_GETREGSET)148struct iovec iov;149iov.iov_base = user;150iov.iov_len = sizeof(*user);151if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, (void*) &iov) < 0) {152print_debug("ptrace(PTRACE_GETREGSET, ...) failed for lwp %d\n", pid);153return false;154}155return true;156#else157print_debug("ptrace(PTRACE_GETREGS, ...) not supported\n");158return false;159#endif160161}162163static bool ptrace_continue(pid_t pid, int signal) {164// pass the signal to the process so we don't swallow it165if (ptrace(PTRACE_CONT, pid, NULL, signal) < 0) {166print_debug("ptrace(PTRACE_CONT, ..) failed for %d\n", pid);167return false;168}169return true;170}171172// waits until the ATTACH has stopped the process173// by signal SIGSTOP174static attach_state_t ptrace_waitpid(pid_t pid) {175int ret;176int status;177errno = 0;178while (true) {179// Wait for debuggee to stop.180ret = waitpid(pid, &status, 0);181if (ret == -1 && errno == ECHILD) {182// try cloned process.183ret = waitpid(pid, &status, __WALL);184}185if (ret >= 0) {186if (WIFSTOPPED(status)) {187// Any signal will stop the thread, make sure it is SIGSTOP. Otherwise SIGSTOP188// will still be pending and delivered when the process is DETACHED and the process189// will go to sleep.190if (WSTOPSIG(status) == SIGSTOP) {191// Debuggee stopped by SIGSTOP.192return ATTACH_SUCCESS;193}194if (!ptrace_continue(pid, WSTOPSIG(status))) {195print_error("Failed to correctly attach to VM. VM might HANG! [PTRACE_CONT failed, stopped by %d]\n", WSTOPSIG(status));196return ATTACH_FAIL;197}198} else {199print_debug("waitpid(): Child process %d exited/terminated (status = 0x%x)\n", pid, status);200return ATTACH_THREAD_DEAD;201}202} else {203switch (errno) {204case EINTR:205continue;206break;207case ECHILD:208print_debug("waitpid() failed. Child process pid (%d) does not exist \n", pid);209return ATTACH_THREAD_DEAD;210case EINVAL:211print_error("waitpid() failed. Invalid options argument.\n");212return ATTACH_FAIL;213default:214print_error("waitpid() failed. Unexpected error %d\n", errno);215return ATTACH_FAIL;216}217} // else218} // while219}220221// checks the state of the thread/process specified by "pid", by reading222// in the 'State:' value from the /proc/<pid>/status file. From the proc223// man page, "Current state of the process. One of "R (running)",224// "S (sleeping)", "D (disk sleep)", "T (stopped)", "T (tracing stop)",225// "Z (zombie)", or "X (dead)"." Assumes that the thread is dead if we226// don't find the status file or if the status is 'X' or 'Z'.227static bool process_doesnt_exist(pid_t pid) {228char fname[32];229char buf[30];230FILE *fp = NULL;231const char state_string[] = "State:";232233sprintf(fname, "/proc/%d/status", pid);234fp = fopen(fname, "r");235if (fp == NULL) {236print_debug("can't open /proc/%d/status file\n", pid);237// Assume the thread does not exist anymore.238return true;239}240bool found_state = false;241size_t state_len = strlen(state_string);242while (fgets(buf, sizeof(buf), fp) != NULL) {243char *state = NULL;244if (strncmp (buf, state_string, state_len) == 0) {245found_state = true;246state = buf + state_len;247// Skip the spaces248while (isspace(*state)) {249state++;250}251// A state value of 'X' indicates that the thread is dead. 'Z'252// indicates that the thread is a zombie.253if (*state == 'X' || *state == 'Z') {254fclose (fp);255return true;256}257break;258}259}260// If the state value is not 'X' or 'Z', the thread exists.261if (!found_state) {262// We haven't found the line beginning with 'State:'.263// Assuming the thread exists.264print_error("Could not find the 'State:' string in the /proc/%d/status file\n", pid);265}266fclose (fp);267return false;268}269270// attach to a process/thread specified by "pid"271static attach_state_t ptrace_attach(pid_t pid, char* err_buf, size_t err_buf_len) {272errno = 0;273if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) {274if (errno == EPERM || errno == ESRCH) {275// Check if the process/thread is exiting or is a zombie276if (process_doesnt_exist(pid)) {277print_debug("Thread with pid %d does not exist\n", pid);278return ATTACH_THREAD_DEAD;279}280}281282// strerror_r() API function is not compatible in different implementations:283// GNU-specific: char *strerror_r(int errnum, char *buf, size_t buflen);284// XSI-compliant: int strerror_r(int errnum, char *buf, size_t buflen);285char buf[200];286#if defined(__GLIBC__) && defined(_GNU_SOURCE)287char* msg = strerror_r(errno, buf, sizeof(buf));288#else289int rc = strerror_r(errno, buf, sizeof(buf));290char* msg = (rc == 0) ? (char*)buf : "Unknown";291#endif292snprintf(err_buf, err_buf_len, "ptrace(PTRACE_ATTACH, ..) failed for %d: %s", pid, msg);293print_error("%s\n", err_buf);294return ATTACH_FAIL;295} else {296attach_state_t wait_ret = ptrace_waitpid(pid);297if (wait_ret == ATTACH_THREAD_DEAD) {298print_debug("Thread with pid %d does not exist\n", pid);299}300return wait_ret;301}302}303304// -------------------------------------------------------305// functions for obtaining library information306// -------------------------------------------------------307308/*309* splits a string _str_ into substrings with delimiter _delim_ by replacing old * delimiters with _new_delim_ (ideally, '\0'). the address of each substring310* is stored in array _ptrs_ as the return value. the maximum capacity of _ptrs_ * array is specified by parameter _n_.311* RETURN VALUE: total number of substrings (always <= _n_)312* NOTE: string _str_ is modified if _delim_!=_new_delim_313*/314static int split_n_str(char * str, int n, char ** ptrs, char delim, char new_delim)315{316int i;317for(i = 0; i < n; i++) ptrs[i] = NULL;318if (str == NULL || n < 1 ) return 0;319320i = 0;321322// skipping leading blanks323while(*str&&*str==delim) str++;324325while(*str&&i<n){326ptrs[i++] = str;327while(*str&&*str!=delim) str++;328while(*str&&*str==delim) *(str++) = new_delim;329}330331return i;332}333334/*335* fgets without storing '\n' at the end of the string336*/337static char * fgets_no_cr(char * buf, int n, FILE *fp)338{339char * rslt = fgets(buf, n, fp);340if (rslt && buf && *buf){341char *p = strchr(buf, '\0');342if (*--p=='\n') *p='\0';343}344return rslt;345}346347static bool read_lib_info(struct ps_prochandle* ph) {348char fname[32];349char buf[PATH_MAX];350FILE *fp = NULL;351352sprintf(fname, "/proc/%d/maps", ph->pid);353fp = fopen(fname, "r");354if (fp == NULL) {355print_debug("can't open /proc/%d/maps file\n", ph->pid);356return false;357}358359while(fgets_no_cr(buf, PATH_MAX, fp)){360char * word[7];361int nwords = split_n_str(buf, 7, word, ' ', '\0');362363if (nwords < 6) {364// not a shared library entry. ignore.365continue;366}367368// SA does not handle the lines with patterns:369// "[stack]", "[heap]", "[vdso]", "[vsyscall]", etc.370if (word[5][0] == '[') {371// not a shared library entry. ignore.372continue;373}374375if (nwords > 6) {376// prelink altered mapfile when the program is running.377// Entries like one below have to be skipped378// /lib64/libc-2.15.so (deleted)379// SO name in entries like one below have to be stripped.380// /lib64/libpthread-2.15.so.#prelink#.EECVts381char *s = strstr(word[5],".#prelink#");382if (s == NULL) {383// No prelink keyword. skip deleted library384print_debug("skip shared object %s deleted by prelink\n", word[5]);385continue;386}387388// Fall through389print_debug("rectifying shared object name %s changed by prelink\n", word[5]);390*s = 0;391}392393if (find_lib(ph, word[5]) == false) {394intptr_t base;395lib_info* lib;396#ifdef _LP64397sscanf(word[0], "%lx", &base);398#else399sscanf(word[0], "%x", &base);400#endif401if ((lib = add_lib_info(ph, word[5], (uintptr_t)base)) == NULL)402continue; // ignore, add_lib_info prints error403404// we don't need to keep the library open, symtab is already405// built. Only for core dump we need to keep the fd open.406close(lib->fd);407lib->fd = -1;408}409}410fclose(fp);411return true;412}413414// detach a given pid415static bool ptrace_detach(pid_t pid) {416if (pid && ptrace(PTRACE_DETACH, pid, NULL, NULL) < 0) {417print_debug("ptrace(PTRACE_DETACH, ..) failed for %d\n", pid);418return false;419} else {420return true;421}422}423424// detach all pids of a ps_prochandle425static void detach_all_pids(struct ps_prochandle* ph) {426thread_info* thr = ph->threads;427while (thr) {428ptrace_detach(thr->lwp_id);429thr = thr->next;430}431}432433static void process_cleanup(struct ps_prochandle* ph) {434detach_all_pids(ph);435}436437static ps_prochandle_ops process_ops = {438.release= process_cleanup,439.p_pread= process_read_data,440.p_pwrite= process_write_data,441.get_lwp_regs= process_get_lwp_regs442};443444// attach to the process. One and only one exposed stuff445JNIEXPORT struct ps_prochandle* JNICALL446Pgrab(pid_t pid, char* err_buf, size_t err_buf_len) {447struct ps_prochandle* ph = NULL;448thread_info* thr = NULL;449attach_state_t attach_status = ATTACH_SUCCESS;450451if ( (ph = (struct ps_prochandle*) calloc(1, sizeof(struct ps_prochandle))) == NULL) {452snprintf(err_buf, err_buf_len, "can't allocate memory for ps_prochandle");453print_debug("%s\n", err_buf);454return NULL;455}456457if ((attach_status = ptrace_attach(pid, err_buf, err_buf_len)) != ATTACH_SUCCESS) {458if (attach_status == ATTACH_THREAD_DEAD) {459print_error("The process with pid %d does not exist.\n", pid);460}461free(ph);462return NULL;463}464465// initialize ps_prochandle466ph->pid = pid;467add_thread_info(ph, ph->pid);468469// initialize vtable470ph->ops = &process_ops;471472// read library info and symbol tables, must do this before attaching threads,473// as the symbols in the pthread library will be used to figure out474// the list of threads within the same process.475read_lib_info(ph);476477/*478* Read thread info.479* SA scans all tasks in /proc/<PID>/task to read all threads info.480*/481char taskpath[PATH_MAX];482DIR *dirp;483struct dirent *entry;484485snprintf(taskpath, PATH_MAX, "/proc/%d/task", ph->pid);486dirp = opendir(taskpath);487int lwp_id;488while ((entry = readdir(dirp)) != NULL) {489if (*entry->d_name == '.') {490continue;491}492lwp_id = atoi(entry->d_name);493if (lwp_id == ph->pid) {494continue;495}496if (!process_doesnt_exist(lwp_id)) {497add_thread_info(ph, lwp_id);498}499}500closedir(dirp);501502// attach to the threads503thr = ph->threads;504505while (thr) {506thread_info* current_thr = thr;507thr = thr->next;508// don't attach to the main thread again509if (ph->pid != current_thr->lwp_id) {510if ((attach_status = ptrace_attach(current_thr->lwp_id, err_buf, err_buf_len)) != ATTACH_SUCCESS) {511if (attach_status == ATTACH_THREAD_DEAD) {512// Remove this thread from the threads list513delete_thread_info(ph, current_thr);514}515else {516Prelease(ph);517return NULL;518} // ATTACH_THREAD_DEAD519} // !ATTACH_SUCCESS520}521}522return ph;523}524525526