agent/src/os/linux/ps_proc.c

Wed, 02 Jul 2008 12:55:16 -0700

author
xdono
date
Wed, 02 Jul 2008 12:55:16 -0700
changeset 631
d1605aabd0a1
parent 485
485d403e94e1
child 1907
c18cbe5936b8
permissions
-rw-r--r--

6719955: Update copyright year
Summary: Update copyright year for files that have been modified in 2008
Reviewed-by: ohair, tbell

duke@435 1 /*
xdono@631 2 * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved.
duke@435 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
duke@435 4 *
duke@435 5 * This code is free software; you can redistribute it and/or modify it
duke@435 6 * under the terms of the GNU General Public License version 2 only, as
duke@435 7 * published by the Free Software Foundation.
duke@435 8 *
duke@435 9 * This code is distributed in the hope that it will be useful, but WITHOUT
duke@435 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
duke@435 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
duke@435 12 * version 2 for more details (a copy is included in the LICENSE file that
duke@435 13 * accompanied this code).
duke@435 14 *
duke@435 15 * You should have received a copy of the GNU General Public License version
duke@435 16 * 2 along with this work; if not, write to the Free Software Foundation,
duke@435 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
duke@435 18 *
duke@435 19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
duke@435 20 * CA 95054 USA or visit www.sun.com if you need additional information or
duke@435 21 * have any questions.
duke@435 22 *
duke@435 23 */
duke@435 24
duke@435 25 #include <stdio.h>
duke@435 26 #include <stdlib.h>
duke@435 27 #include <string.h>
duke@435 28 #include <errno.h>
duke@435 29 #include <sys/ptrace.h>
duke@435 30 #include "libproc_impl.h"
duke@435 31
duke@435 32 #if defined(x86_64) && !defined(amd64)
duke@435 33 #define amd64 1
duke@435 34 #endif
duke@435 35
duke@435 36 #ifndef __WALL
duke@435 37 #define __WALL 0x40000000 // Copied from /usr/include/linux/wait.h
duke@435 38 #endif
duke@435 39
duke@435 40 // This file has the libproc implementation specific to live process
duke@435 41 // For core files, refer to ps_core.c
duke@435 42
duke@435 43 static inline uintptr_t align(uintptr_t ptr, size_t size) {
duke@435 44 return (ptr & ~(size - 1));
duke@435 45 }
duke@435 46
duke@435 47 // ---------------------------------------------
duke@435 48 // ptrace functions
duke@435 49 // ---------------------------------------------
duke@435 50
duke@435 51 // read "size" bytes of data from "addr" within the target process.
duke@435 52 // unlike the standard ptrace() function, process_read_data() can handle
duke@435 53 // unaligned address - alignment check, if required, should be done
duke@435 54 // before calling process_read_data.
duke@435 55
duke@435 56 static bool process_read_data(struct ps_prochandle* ph, uintptr_t addr, char *buf, size_t size) {
duke@435 57 long rslt;
duke@435 58 size_t i, words;
duke@435 59 uintptr_t end_addr = addr + size;
duke@435 60 uintptr_t aligned_addr = align(addr, sizeof(long));
duke@435 61
duke@435 62 if (aligned_addr != addr) {
duke@435 63 char *ptr = (char *)&rslt;
duke@435 64 errno = 0;
duke@435 65 rslt = ptrace(PTRACE_PEEKDATA, ph->pid, aligned_addr, 0);
duke@435 66 if (errno) {
duke@435 67 print_debug("ptrace(PTRACE_PEEKDATA, ..) failed for %d bytes @ %lx\n", size, addr);
duke@435 68 return false;
duke@435 69 }
duke@435 70 for (; aligned_addr != addr; aligned_addr++, ptr++);
duke@435 71 for (; ((intptr_t)aligned_addr % sizeof(long)) && aligned_addr < end_addr;
duke@435 72 aligned_addr++)
duke@435 73 *(buf++) = *(ptr++);
duke@435 74 }
duke@435 75
duke@435 76 words = (end_addr - aligned_addr) / sizeof(long);
duke@435 77
duke@435 78 // assert((intptr_t)aligned_addr % sizeof(long) == 0);
duke@435 79 for (i = 0; i < words; i++) {
duke@435 80 errno = 0;
duke@435 81 rslt = ptrace(PTRACE_PEEKDATA, ph->pid, aligned_addr, 0);
duke@435 82 if (errno) {
duke@435 83 print_debug("ptrace(PTRACE_PEEKDATA, ..) failed for %d bytes @ %lx\n", size, addr);
duke@435 84 return false;
duke@435 85 }
duke@435 86 *(long *)buf = rslt;
duke@435 87 buf += sizeof(long);
duke@435 88 aligned_addr += sizeof(long);
duke@435 89 }
duke@435 90
duke@435 91 if (aligned_addr != end_addr) {
duke@435 92 char *ptr = (char *)&rslt;
duke@435 93 errno = 0;
duke@435 94 rslt = ptrace(PTRACE_PEEKDATA, ph->pid, aligned_addr, 0);
duke@435 95 if (errno) {
duke@435 96 print_debug("ptrace(PTRACE_PEEKDATA, ..) failed for %d bytes @ %lx\n", size, addr);
duke@435 97 return false;
duke@435 98 }
duke@435 99 for (; aligned_addr != end_addr; aligned_addr++)
duke@435 100 *(buf++) = *(ptr++);
duke@435 101 }
duke@435 102 return true;
duke@435 103 }
duke@435 104
duke@435 105 // null implementation for write
duke@435 106 static bool process_write_data(struct ps_prochandle* ph,
duke@435 107 uintptr_t addr, const char *buf , size_t size) {
duke@435 108 return false;
duke@435 109 }
duke@435 110
duke@435 111 // "user" should be a pointer to a user_regs_struct
duke@435 112 static bool process_get_lwp_regs(struct ps_prochandle* ph, pid_t pid, struct user_regs_struct *user) {
duke@435 113 // we have already attached to all thread 'pid's, just use ptrace call
duke@435 114 // to get regset now. Note that we don't cache regset upfront for processes.
duke@435 115 // Linux on x86 and sparc are different. On x86 ptrace(PTRACE_GETREGS, ...)
duke@435 116 // uses pointer from 4th argument and ignores 3rd argument. On sparc it uses
duke@435 117 // pointer from 3rd argument and ignores 4th argument
duke@435 118 #if defined(sparc) || defined(sparcv9)
duke@435 119 #define ptrace_getregs(request, pid, addr, data) ptrace(request, pid, addr, data)
duke@435 120 #else
duke@435 121 #define ptrace_getregs(request, pid, addr, data) ptrace(request, pid, data, addr)
duke@435 122 #endif
duke@435 123
duke@435 124 #ifdef _LP64
duke@435 125 #ifdef PTRACE_GETREGS64
duke@435 126 #define PTRACE_GETREGS_REQ PTRACE_GETREGS64
duke@435 127 #endif
duke@435 128 #else
duke@435 129 #if defined(PTRACE_GETREGS) || defined(PT_GETREGS)
duke@435 130 #define PTRACE_GETREGS_REQ PTRACE_GETREGS
duke@435 131 #endif
duke@435 132 #endif /* _LP64 */
duke@435 133
duke@435 134 #ifdef PTRACE_GETREGS_REQ
duke@435 135 if (ptrace_getregs(PTRACE_GETREGS_REQ, pid, user, NULL) < 0) {
duke@435 136 print_debug("ptrace(PTRACE_GETREGS, ...) failed for lwp %d\n", pid);
duke@435 137 return false;
duke@435 138 }
duke@435 139 return true;
duke@435 140 #else
duke@435 141 print_debug("ptrace(PTRACE_GETREGS, ...) not supported\n");
duke@435 142 return false;
duke@435 143 #endif
duke@435 144
duke@435 145 }
duke@435 146
duke@435 147 // attach to a process/thread specified by "pid"
duke@435 148 static bool ptrace_attach(pid_t pid) {
duke@435 149 if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) {
duke@435 150 print_debug("ptrace(PTRACE_ATTACH, ..) failed for %d\n", pid);
duke@435 151 return false;
duke@435 152 } else {
duke@435 153 int ret;
duke@435 154 int status;
duke@435 155 do {
duke@435 156 // Wait for debuggee to stop.
duke@435 157 ret = waitpid(pid, &status, 0);
duke@435 158 if (ret == -1 && errno == ECHILD) {
duke@435 159 // try cloned process.
duke@435 160 ret = waitpid(pid, &status, __WALL);
duke@435 161 }
duke@435 162 if (ret >= 0) {
duke@435 163 if (WIFSTOPPED(status)) {
duke@435 164 // Debuggee stopped.
duke@435 165 return true;
duke@435 166 } else {
duke@435 167 print_debug("waitpid(): Child process exited/terminated (status = 0x%x)\n", status);
duke@435 168 return false;
duke@435 169 }
duke@435 170 } else {
duke@435 171 switch (errno) {
duke@435 172 case EINTR:
duke@435 173 continue;
duke@435 174 break;
duke@435 175 case ECHILD:
duke@435 176 print_debug("waitpid() failed. Child process pid (%d) does not exist \n", pid);
duke@435 177 break;
duke@435 178 case EINVAL:
duke@435 179 print_debug("waitpid() failed. Invalid options argument.\n");
duke@435 180 break;
duke@435 181 default:
duke@435 182 print_debug("waitpid() failed. Unexpected error %d\n",errno);
duke@435 183 }
duke@435 184 return false;
duke@435 185 }
duke@435 186 } while(true);
duke@435 187 }
duke@435 188 }
duke@435 189
duke@435 190 // -------------------------------------------------------
duke@435 191 // functions for obtaining library information
duke@435 192 // -------------------------------------------------------
duke@435 193
duke@435 194 /*
duke@435 195 * splits a string _str_ into substrings with delimiter _delim_ by replacing old * delimiters with _new_delim_ (ideally, '\0'). the address of each substring
duke@435 196 * is stored in array _ptrs_ as the return value. the maximum capacity of _ptrs_ * array is specified by parameter _n_.
duke@435 197 * RETURN VALUE: total number of substrings (always <= _n_)
duke@435 198 * NOTE: string _str_ is modified if _delim_!=_new_delim_
duke@435 199 */
duke@435 200 static int split_n_str(char * str, int n, char ** ptrs, char delim, char new_delim)
duke@435 201 {
duke@435 202 int i;
duke@435 203 for(i = 0; i < n; i++) ptrs[i] = NULL;
duke@435 204 if (str == NULL || n < 1 ) return 0;
duke@435 205
duke@435 206 i = 0;
duke@435 207
duke@435 208 // skipping leading blanks
duke@435 209 while(*str&&*str==delim) str++;
duke@435 210
duke@435 211 while(*str&&i<n){
duke@435 212 ptrs[i++] = str;
duke@435 213 while(*str&&*str!=delim) str++;
duke@435 214 while(*str&&*str==delim) *(str++) = new_delim;
duke@435 215 }
duke@435 216
duke@435 217 return i;
duke@435 218 }
duke@435 219
duke@435 220 /*
duke@435 221 * fgets without storing '\n' at the end of the string
duke@435 222 */
duke@435 223 static char * fgets_no_cr(char * buf, int n, FILE *fp)
duke@435 224 {
duke@435 225 char * rslt = fgets(buf, n, fp);
duke@435 226 if (rslt && buf && *buf){
duke@435 227 char *p = strchr(buf, '\0');
duke@435 228 if (*--p=='\n') *p='\0';
duke@435 229 }
duke@435 230 return rslt;
duke@435 231 }
duke@435 232
duke@435 233 // callback for read_thread_info
duke@435 234 static bool add_new_thread(struct ps_prochandle* ph, pthread_t pthread_id, lwpid_t lwp_id) {
duke@435 235 return add_thread_info(ph, pthread_id, lwp_id) != NULL;
duke@435 236 }
duke@435 237
duke@435 238 static bool read_lib_info(struct ps_prochandle* ph) {
duke@435 239 char fname[32];
duke@435 240 char buf[256];
duke@435 241 FILE *fp = NULL;
duke@435 242
duke@435 243 sprintf(fname, "/proc/%d/maps", ph->pid);
duke@435 244 fp = fopen(fname, "r");
duke@435 245 if (fp == NULL) {
duke@435 246 print_debug("can't open /proc/%d/maps file\n", ph->pid);
duke@435 247 return false;
duke@435 248 }
duke@435 249
duke@435 250 while(fgets_no_cr(buf, 256, fp)){
duke@435 251 char * word[6];
duke@435 252 int nwords = split_n_str(buf, 6, word, ' ', '\0');
duke@435 253 if (nwords > 5 && find_lib(ph, word[5]) == false) {
duke@435 254 intptr_t base;
duke@435 255 lib_info* lib;
duke@435 256 sscanf(word[0], "%lx", &base);
duke@435 257 if ((lib = add_lib_info(ph, word[5], (uintptr_t)base)) == NULL)
duke@435 258 continue; // ignore, add_lib_info prints error
duke@435 259
duke@435 260 // we don't need to keep the library open, symtab is already
duke@435 261 // built. Only for core dump we need to keep the fd open.
duke@435 262 close(lib->fd);
duke@435 263 lib->fd = -1;
duke@435 264 }
duke@435 265 }
duke@435 266 fclose(fp);
duke@435 267 return true;
duke@435 268 }
duke@435 269
duke@435 270 // detach a given pid
duke@435 271 static bool ptrace_detach(pid_t pid) {
duke@435 272 if (pid && ptrace(PTRACE_DETACH, pid, NULL, NULL) < 0) {
duke@435 273 print_debug("ptrace(PTRACE_DETACH, ..) failed for %d\n", pid);
duke@435 274 return false;
duke@435 275 } else {
duke@435 276 return true;
duke@435 277 }
duke@435 278 }
duke@435 279
duke@435 280 // detach all pids of a ps_prochandle
duke@435 281 static void detach_all_pids(struct ps_prochandle* ph) {
duke@435 282 thread_info* thr = ph->threads;
duke@435 283 while (thr) {
duke@435 284 ptrace_detach(thr->lwp_id);
duke@435 285 thr = thr->next;
duke@435 286 }
duke@435 287 }
duke@435 288
duke@435 289 static void process_cleanup(struct ps_prochandle* ph) {
duke@435 290 detach_all_pids(ph);
duke@435 291 }
duke@435 292
duke@435 293 static ps_prochandle_ops process_ops = {
dcubed@485 294 .release= process_cleanup,
dcubed@485 295 .p_pread= process_read_data,
dcubed@485 296 .p_pwrite= process_write_data,
dcubed@485 297 .get_lwp_regs= process_get_lwp_regs
duke@435 298 };
duke@435 299
duke@435 300 // attach to the process. One and only one exposed stuff
duke@435 301 struct ps_prochandle* Pgrab(pid_t pid) {
duke@435 302 struct ps_prochandle* ph = NULL;
duke@435 303 thread_info* thr = NULL;
duke@435 304
duke@435 305 if ( (ph = (struct ps_prochandle*) calloc(1, sizeof(struct ps_prochandle))) == NULL) {
duke@435 306 print_debug("can't allocate memory for ps_prochandle\n");
duke@435 307 return NULL;
duke@435 308 }
duke@435 309
duke@435 310 if (ptrace_attach(pid) != true) {
duke@435 311 free(ph);
duke@435 312 return NULL;
duke@435 313 }
duke@435 314
duke@435 315 // initialize ps_prochandle
duke@435 316 ph->pid = pid;
duke@435 317
duke@435 318 // initialize vtable
duke@435 319 ph->ops = &process_ops;
duke@435 320
duke@435 321 // read library info and symbol tables, must do this before attaching threads,
duke@435 322 // as the symbols in the pthread library will be used to figure out
duke@435 323 // the list of threads within the same process.
duke@435 324 read_lib_info(ph);
duke@435 325
duke@435 326 // read thread info
duke@435 327 read_thread_info(ph, add_new_thread);
duke@435 328
duke@435 329 // attach to the threads
duke@435 330 thr = ph->threads;
duke@435 331 while (thr) {
duke@435 332 // don't attach to the main thread again
duke@435 333 if (ph->pid != thr->lwp_id && ptrace_attach(thr->lwp_id) != true) {
duke@435 334 // even if one attach fails, we get return NULL
duke@435 335 Prelease(ph);
duke@435 336 return NULL;
duke@435 337 }
duke@435 338 thr = thr->next;
duke@435 339 }
duke@435 340 return ph;
duke@435 341 }

mercurial