diff options
Diffstat (limited to 'gdb/inf-ttrace.c')
-rw-r--r-- | gdb/inf-ttrace.c | 1224 |
1 files changed, 0 insertions, 1224 deletions
diff --git a/gdb/inf-ttrace.c b/gdb/inf-ttrace.c deleted file mode 100644 index 8957ca2..0000000 --- a/gdb/inf-ttrace.c +++ /dev/null @@ -1,1224 +0,0 @@ -/* Low-level child interface to ttrace. - - Copyright (C) 2004-2015 Free Software Foundation, Inc. - - This file is part of GDB. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#include "defs.h" - -/* The ttrace(2) system call didn't exist before HP-UX 10.30. Don't - try to compile this code unless we have it. */ -#ifdef HAVE_TTRACE - -#include "command.h" -#include "gdbcore.h" -#include "gdbthread.h" -#include "inferior.h" -#include "terminal.h" -#include "target.h" -#include <sys/mman.h> -#include <sys/ttrace.h> -#include <signal.h> - -#include "inf-child.h" -#include "inf-ttrace.h" -#include "common/filestuff.h" - - - -/* HP-UX uses a threading model where each user-space thread - corresponds to a kernel thread. These kernel threads are called - lwps. The ttrace(2) interface gives us almost full control over - the threads, which makes it very easy to support them in GDB. We - identify the threads by process ID and lwp ID. The ttrace(2) also - provides us with a thread's user ID (in the `tts_user_tid' member - of `ttstate_t') but we don't use that (yet) as it isn't necessary - to uniquely label the thread. */ - -/* Number of active lwps. */ -static int inf_ttrace_num_lwps; - - -/* On HP-UX versions that have the ttrace(2) system call, we can - implement "hardware" watchpoints by fiddling with the protection of - pages in the address space that contain the variable being watched. - In order to implement this, we keep a dictionary of pages for which - we have changed the protection. */ - -struct inf_ttrace_page -{ - CORE_ADDR addr; /* Page address. */ - int prot; /* Protection. */ - int refcount; /* Reference count. */ - struct inf_ttrace_page *next; - struct inf_ttrace_page *prev; -}; - -struct inf_ttrace_page_dict -{ - struct inf_ttrace_page buckets[128]; - int pagesize; /* Page size. */ - int count; /* Number of pages in this dictionary. */ -} inf_ttrace_page_dict; - -struct inf_ttrace_private_thread_info -{ - int dying; -}; - -/* Number of lwps that are currently in a system call. */ -static int inf_ttrace_num_lwps_in_syscall; - -/* Flag to indicate whether we should re-enable page protections after - the next wait. */ -static int inf_ttrace_reenable_page_protections; - -/* Enable system call events for process PID. */ - -static void -inf_ttrace_enable_syscall_events (pid_t pid) -{ - ttevent_t tte; - ttstate_t tts; - - gdb_assert (inf_ttrace_num_lwps_in_syscall == 0); - - if (ttrace (TT_PROC_GET_EVENT_MASK, pid, 0, - (uintptr_t)&tte, sizeof tte, 0) == -1) - perror_with_name (("ttrace")); - - tte.tte_events |= (TTEVT_SYSCALL_ENTRY | TTEVT_SYSCALL_RETURN); - - if (ttrace (TT_PROC_SET_EVENT_MASK, pid, 0, - (uintptr_t)&tte, sizeof tte, 0) == -1) - perror_with_name (("ttrace")); - - if (ttrace (TT_PROC_GET_FIRST_LWP_STATE, pid, 0, - (uintptr_t)&tts, sizeof tts, 0) == -1) - perror_with_name (("ttrace")); - - if (tts.tts_flags & TTS_INSYSCALL) - inf_ttrace_num_lwps_in_syscall++; - - /* FIXME: Handle multiple threads. */ -} - -/* Disable system call events for process PID. */ - -static void -inf_ttrace_disable_syscall_events (pid_t pid) -{ - ttevent_t tte; - - gdb_assert (inf_ttrace_page_dict.count == 0); - - if (ttrace (TT_PROC_GET_EVENT_MASK, pid, 0, - (uintptr_t)&tte, sizeof tte, 0) == -1) - perror_with_name (("ttrace")); - - tte.tte_events &= ~(TTEVT_SYSCALL_ENTRY | TTEVT_SYSCALL_RETURN); - - if (ttrace (TT_PROC_SET_EVENT_MASK, pid, 0, - (uintptr_t)&tte, sizeof tte, 0) == -1) - perror_with_name (("ttrace")); - - inf_ttrace_num_lwps_in_syscall = 0; -} - -/* Get information about the page at address ADDR for process PID from - the dictionary. */ - -static struct inf_ttrace_page * -inf_ttrace_get_page (pid_t pid, CORE_ADDR addr) -{ - const int num_buckets = ARRAY_SIZE (inf_ttrace_page_dict.buckets); - const int pagesize = inf_ttrace_page_dict.pagesize; - int bucket; - struct inf_ttrace_page *page; - - bucket = (addr / pagesize) % num_buckets; - page = &inf_ttrace_page_dict.buckets[bucket]; - while (page) - { - if (page->addr == addr) - break; - - page = page->next; - } - - return page; -} - -/* Add the page at address ADDR for process PID to the dictionary. */ - -static struct inf_ttrace_page * -inf_ttrace_add_page (pid_t pid, CORE_ADDR addr) -{ - const int num_buckets = ARRAY_SIZE (inf_ttrace_page_dict.buckets); - const int pagesize = inf_ttrace_page_dict.pagesize; - int bucket; - struct inf_ttrace_page *page; - struct inf_ttrace_page *prev = NULL; - - bucket = (addr / pagesize) % num_buckets; - page = &inf_ttrace_page_dict.buckets[bucket]; - while (page) - { - if (page->addr == addr) - break; - - prev = page; - page = page->next; - } - - if (!page) - { - int prot; - - if (ttrace (TT_PROC_GET_MPROTECT, pid, 0, - addr, 0, (uintptr_t)&prot) == -1) - perror_with_name (("ttrace")); - - page = XNEW (struct inf_ttrace_page); - page->addr = addr; - page->prot = prot; - page->refcount = 0; - page->next = NULL; - - page->prev = prev; - prev->next = page; - - inf_ttrace_page_dict.count++; - if (inf_ttrace_page_dict.count == 1) - inf_ttrace_enable_syscall_events (pid); - - if (inf_ttrace_num_lwps_in_syscall == 0) - { - if (ttrace (TT_PROC_SET_MPROTECT, pid, 0, - addr, pagesize, prot & ~PROT_WRITE) == -1) - perror_with_name (("ttrace")); - } - } - - return page; -} - -/* Insert the page at address ADDR of process PID to the dictionary. */ - -static void -inf_ttrace_insert_page (pid_t pid, CORE_ADDR addr) -{ - struct inf_ttrace_page *page; - - page = inf_ttrace_get_page (pid, addr); - if (!page) - page = inf_ttrace_add_page (pid, addr); - - page->refcount++; -} - -/* Remove the page at address ADDR of process PID from the dictionary. */ - -static void -inf_ttrace_remove_page (pid_t pid, CORE_ADDR addr) -{ - const int pagesize = inf_ttrace_page_dict.pagesize; - struct inf_ttrace_page *page; - - page = inf_ttrace_get_page (pid, addr); - page->refcount--; - - gdb_assert (page->refcount >= 0); - - if (page->refcount == 0) - { - if (inf_ttrace_num_lwps_in_syscall == 0) - { - if (ttrace (TT_PROC_SET_MPROTECT, pid, 0, - addr, pagesize, page->prot) == -1) - perror_with_name (("ttrace")); - } - - inf_ttrace_page_dict.count--; - if (inf_ttrace_page_dict.count == 0) - inf_ttrace_disable_syscall_events (pid); - - page->prev->next = page->next; - if (page->next) - page->next->prev = page->prev; - - xfree (page); - } -} - -/* Mask the bits in PROT from the page protections that are currently - in the dictionary for process PID. */ - -static void -inf_ttrace_mask_page_protections (pid_t pid, int prot) -{ - const int num_buckets = ARRAY_SIZE (inf_ttrace_page_dict.buckets); - const int pagesize = inf_ttrace_page_dict.pagesize; - int bucket; - - for (bucket = 0; bucket < num_buckets; bucket++) - { - struct inf_ttrace_page *page; - - page = inf_ttrace_page_dict.buckets[bucket].next; - while (page) - { - if (ttrace (TT_PROC_SET_MPROTECT, pid, 0, - page->addr, pagesize, page->prot & ~prot) == -1) - perror_with_name (("ttrace")); - - page = page->next; - } - } -} - -/* Write-protect the pages in the dictionary for process PID. */ - -static void -inf_ttrace_enable_page_protections (pid_t pid) -{ - inf_ttrace_mask_page_protections (pid, PROT_WRITE); -} - -/* Restore the protection of the pages in the dictionary for process - PID. */ - -static void -inf_ttrace_disable_page_protections (pid_t pid) -{ - inf_ttrace_mask_page_protections (pid, 0); -} - -/* Insert a "hardware" watchpoint for LEN bytes at address ADDR of - type TYPE. */ - -static int -inf_ttrace_insert_watchpoint (struct target_ops *self, - CORE_ADDR addr, int len, int type, - struct expression *cond) -{ - const int pagesize = inf_ttrace_page_dict.pagesize; - pid_t pid = ptid_get_pid (inferior_ptid); - CORE_ADDR page_addr; - int num_pages; - int page; - - gdb_assert (type == hw_write); - - page_addr = (addr / pagesize) * pagesize; - num_pages = (len + pagesize - 1) / pagesize; - - for (page = 0; page < num_pages; page++, page_addr += pagesize) - inf_ttrace_insert_page (pid, page_addr); - - return 1; -} - -/* Remove a "hardware" watchpoint for LEN bytes at address ADDR of - type TYPE. */ - -static int -inf_ttrace_remove_watchpoint (struct target_ops *self, - CORE_ADDR addr, int len, int type, - struct expression *cond) -{ - const int pagesize = inf_ttrace_page_dict.pagesize; - pid_t pid = ptid_get_pid (inferior_ptid); - CORE_ADDR page_addr; - int num_pages; - int page; - - gdb_assert (type == hw_write); - - page_addr = (addr / pagesize) * pagesize; - num_pages = (len + pagesize - 1) / pagesize; - - for (page = 0; page < num_pages; page++, page_addr += pagesize) - inf_ttrace_remove_page (pid, page_addr); - - return 1; -} - -static int -inf_ttrace_can_use_hw_breakpoint (struct target_ops *self, - int type, int len, int ot) -{ - return (type == bp_hardware_watchpoint); -} - -static int -inf_ttrace_region_ok_for_hw_watchpoint (struct target_ops *self, - CORE_ADDR addr, int len) -{ - return 1; -} - -/* Return non-zero if the current inferior was (potentially) stopped - by hitting a "hardware" watchpoint. */ - -static int -inf_ttrace_stopped_by_watchpoint (struct target_ops *ops) -{ - pid_t pid = ptid_get_pid (inferior_ptid); - lwpid_t lwpid = ptid_get_lwp (inferior_ptid); - ttstate_t tts; - - if (inf_ttrace_page_dict.count > 0) - { - if (ttrace (TT_LWP_GET_STATE, pid, lwpid, - (uintptr_t)&tts, sizeof tts, 0) == -1) - perror_with_name (("ttrace")); - - if (tts.tts_event == TTEVT_SIGNAL - && tts.tts_u.tts_signal.tts_signo == SIGBUS) - { - const int pagesize = inf_ttrace_page_dict.pagesize; - void *addr = tts.tts_u.tts_signal.tts_siginfo.si_addr; - CORE_ADDR page_addr = ((uintptr_t)addr / pagesize) * pagesize; - - if (inf_ttrace_get_page (pid, page_addr)) - return 1; - } - } - - return 0; -} - - -/* Target hook for follow_fork. On entry and at return inferior_ptid - is the ptid of the followed inferior. */ - -static int -inf_ttrace_follow_fork (struct target_ops *ops, int follow_child, - int detach_fork) -{ - struct thread_info *tp = inferior_thread (); - - gdb_assert (tp->pending_follow.kind == TARGET_WAITKIND_FORKED - || tp->pending_follow.kind == TARGET_WAITKIND_VFORKED); - - if (follow_child) - { - struct thread_info *ti; - - /* The child will start out single-threaded. */ - inf_ttrace_num_lwps = 1; - inf_ttrace_num_lwps_in_syscall = 0; - - ti = inferior_thread (); - ti->priv = - xmalloc (sizeof (struct inf_ttrace_private_thread_info)); - memset (ti->priv, 0, - sizeof (struct inf_ttrace_private_thread_info)); - } - else - { - pid_t child_pid; - - /* Following parent. Detach child now. */ - child_pid = ptid_get_pid (tp->pending_follow.value.related_pid); - if (ttrace (TT_PROC_DETACH, child_pid, 0, 0, 0, 0) == -1) - perror_with_name (("ttrace")); - } - - return 0; -} - - -/* File descriptors for pipes used as semaphores during initial - startup of an inferior. */ -static int inf_ttrace_pfd1[2]; -static int inf_ttrace_pfd2[2]; - -static void -do_cleanup_pfds (void *dummy) -{ - close (inf_ttrace_pfd1[0]); - close (inf_ttrace_pfd1[1]); - close (inf_ttrace_pfd2[0]); - close (inf_ttrace_pfd2[1]); - - unmark_fd_no_cloexec (inf_ttrace_pfd1[0]); - unmark_fd_no_cloexec (inf_ttrace_pfd1[1]); - unmark_fd_no_cloexec (inf_ttrace_pfd2[0]); - unmark_fd_no_cloexec (inf_ttrace_pfd2[1]); -} - -static void -inf_ttrace_prepare (void) -{ - if (pipe (inf_ttrace_pfd1) == -1) - perror_with_name (("pipe")); - - if (pipe (inf_ttrace_pfd2) == -1) - { - close (inf_ttrace_pfd1[0]); - close (inf_ttrace_pfd2[0]); - perror_with_name (("pipe")); - } - - mark_fd_no_cloexec (inf_ttrace_pfd1[0]); - mark_fd_no_cloexec (inf_ttrace_pfd1[1]); - mark_fd_no_cloexec (inf_ttrace_pfd2[0]); - mark_fd_no_cloexec (inf_ttrace_pfd2[1]); -} - -/* Prepare to be traced. */ - -static void -inf_ttrace_me (void) -{ - struct cleanup *old_chain = make_cleanup (do_cleanup_pfds, 0); - char c; - - /* "Trace me, Dr. Memory!" */ - if (ttrace (TT_PROC_SETTRC, 0, 0, 0, TT_VERSION, 0) == -1) - perror_with_name (("ttrace")); - - /* Tell our parent that we are ready to be traced. */ - if (write (inf_ttrace_pfd1[1], &c, sizeof c) != sizeof c) - perror_with_name (("write")); - - /* Wait until our parent has set the initial event mask. */ - if (read (inf_ttrace_pfd2[0], &c, sizeof c) != sizeof c) - perror_with_name (("read")); - - do_cleanups (old_chain); -} - -/* Start tracing PID. */ - -static void -inf_ttrace_him (struct target_ops *ops, int pid) -{ - struct cleanup *old_chain = make_cleanup (do_cleanup_pfds, 0); - ttevent_t tte; - char c; - - /* Wait until our child is ready to be traced. */ - if (read (inf_ttrace_pfd1[0], &c, sizeof c) != sizeof c) - perror_with_name (("read")); - - /* Set the initial event mask. */ - memset (&tte, 0, sizeof (tte)); - tte.tte_events |= TTEVT_EXEC | TTEVT_EXIT | TTEVT_FORK | TTEVT_VFORK; - tte.tte_events |= TTEVT_LWP_CREATE | TTEVT_LWP_EXIT | TTEVT_LWP_TERMINATE; -#ifdef TTEVT_BPT_SSTEP - tte.tte_events |= TTEVT_BPT_SSTEP; -#endif - tte.tte_opts |= TTEO_PROC_INHERIT; - if (ttrace (TT_PROC_SET_EVENT_MASK, pid, 0, - (uintptr_t)&tte, sizeof tte, 0) == -1) - perror_with_name (("ttrace")); - - /* Tell our child that we have set the initial event mask. */ - if (write (inf_ttrace_pfd2[1], &c, sizeof c) != sizeof c) - perror_with_name (("write")); - - do_cleanups (old_chain); - - if (!target_is_pushed (ops)) - push_target (ops); - - startup_inferior (START_INFERIOR_TRAPS_EXPECTED); - - /* On some targets, there must be some explicit actions taken after - the inferior has been started up. */ - target_post_startup_inferior (pid_to_ptid (pid)); -} - -static void -inf_ttrace_create_inferior (struct target_ops *ops, char *exec_file, - char *allargs, char **env, int from_tty) -{ - int pid; - - gdb_assert (inf_ttrace_num_lwps == 0); - gdb_assert (inf_ttrace_num_lwps_in_syscall == 0); - gdb_assert (inf_ttrace_page_dict.count == 0); - gdb_assert (inf_ttrace_reenable_page_protections == 0); - - pid = fork_inferior (exec_file, allargs, env, inf_ttrace_me, NULL, - inf_ttrace_prepare, NULL, NULL); - - inf_ttrace_him (ops, pid); -} - -static void -inf_ttrace_mourn_inferior (struct target_ops *ops) -{ - const int num_buckets = ARRAY_SIZE (inf_ttrace_page_dict.buckets); - int bucket; - - inf_ttrace_num_lwps = 0; - inf_ttrace_num_lwps_in_syscall = 0; - - for (bucket = 0; bucket < num_buckets; bucket++) - { - struct inf_ttrace_page *page; - struct inf_ttrace_page *next; - - page = inf_ttrace_page_dict.buckets[bucket].next; - while (page) - { - next = page->next; - xfree (page); - page = next; - } - } - inf_ttrace_page_dict.count = 0; - - inf_child_mourn_inferior (ops); -} - -/* Assuming we just attached the debugger to a new inferior, create - a new thread_info structure for each thread, and add it to our - list of threads. */ - -static void -inf_ttrace_create_threads_after_attach (int pid) -{ - int status; - ptid_t ptid; - ttstate_t tts; - struct thread_info *ti; - - status = ttrace (TT_PROC_GET_FIRST_LWP_STATE, pid, 0, - (uintptr_t) &tts, sizeof (ttstate_t), 0); - if (status < 0) - perror_with_name (_("TT_PROC_GET_FIRST_LWP_STATE ttrace call failed")); - gdb_assert (tts.tts_pid == pid); - - /* Add the stopped thread. */ - ptid = ptid_build (pid, tts.tts_lwpid, 0); - ti = add_thread (ptid); - ti->priv = xzalloc (sizeof (struct inf_ttrace_private_thread_info)); - inf_ttrace_num_lwps++; - - /* We use the "first stopped thread" as the currently active thread. */ - inferior_ptid = ptid; - - /* Iterative over all the remaining threads. */ - - for (;;) - { - ptid_t ptid; - - status = ttrace (TT_PROC_GET_NEXT_LWP_STATE, pid, 0, - (uintptr_t) &tts, sizeof (ttstate_t), 0); - if (status < 0) - perror_with_name (_("TT_PROC_GET_NEXT_LWP_STATE ttrace call failed")); - if (status == 0) - break; /* End of list. */ - - ptid = ptid_build (tts.tts_pid, tts.tts_lwpid, 0); - ti = add_thread (ptid); - ti->priv = xzalloc (sizeof (struct inf_ttrace_private_thread_info)); - inf_ttrace_num_lwps++; - } -} - -static void -inf_ttrace_attach (struct target_ops *ops, const char *args, int from_tty) -{ - char *exec_file; - pid_t pid; - ttevent_t tte; - struct inferior *inf; - - pid = parse_pid_to_attach (args); - - if (pid == getpid ()) /* Trying to masturbate? */ - error (_("I refuse to debug myself!")); - - if (from_tty) - { - exec_file = get_exec_file (0); - - if (exec_file) - printf_unfiltered (_("Attaching to program: %s, %s\n"), exec_file, - target_pid_to_str (pid_to_ptid (pid))); - else - printf_unfiltered (_("Attaching to %s\n"), - target_pid_to_str (pid_to_ptid (pid))); - - gdb_flush (gdb_stdout); - } - - gdb_assert (inf_ttrace_num_lwps == 0); - gdb_assert (inf_ttrace_num_lwps_in_syscall == 0); - - if (ttrace (TT_PROC_ATTACH, pid, 0, TT_KILL_ON_EXIT, TT_VERSION, 0) == -1) - perror_with_name (("ttrace")); - - inf = current_inferior (); - inferior_appeared (inf, pid); - inf->attach_flag = 1; - - /* Set the initial event mask. */ - memset (&tte, 0, sizeof (tte)); - tte.tte_events |= TTEVT_EXEC | TTEVT_EXIT | TTEVT_FORK | TTEVT_VFORK; - tte.tte_events |= TTEVT_LWP_CREATE | TTEVT_LWP_EXIT | TTEVT_LWP_TERMINATE; -#ifdef TTEVT_BPT_SSTEP - tte.tte_events |= TTEVT_BPT_SSTEP; -#endif - tte.tte_opts |= TTEO_PROC_INHERIT; - if (ttrace (TT_PROC_SET_EVENT_MASK, pid, 0, - (uintptr_t)&tte, sizeof tte, 0) == -1) - perror_with_name (("ttrace")); - - if (!target_is_pushed (ops)) - push_target (ops); - - inf_ttrace_create_threads_after_attach (pid); -} - -static void -inf_ttrace_detach (struct target_ops *ops, const char *args, int from_tty) -{ - pid_t pid = ptid_get_pid (inferior_ptid); - int sig = 0; - - if (from_tty) - { - char *exec_file = get_exec_file (0); - if (exec_file == 0) - exec_file = ""; - printf_unfiltered (_("Detaching from program: %s, %s\n"), exec_file, - target_pid_to_str (pid_to_ptid (pid))); - gdb_flush (gdb_stdout); - } - if (args) - sig = atoi (args); - - /* ??? The HP-UX 11.0 ttrace(2) manual page doesn't mention that we - can pass a signal number here. Does this really work? */ - if (ttrace (TT_PROC_DETACH, pid, 0, 0, sig, 0) == -1) - perror_with_name (("ttrace")); - - inf_ttrace_num_lwps = 0; - inf_ttrace_num_lwps_in_syscall = 0; - - inferior_ptid = null_ptid; - detach_inferior (pid); - - inf_child_maybe_unpush_target (ops); -} - -static void -inf_ttrace_kill (struct target_ops *ops) -{ - pid_t pid = ptid_get_pid (inferior_ptid); - - if (pid == 0) - return; - - if (ttrace (TT_PROC_EXIT, pid, 0, 0, 0, 0) == -1) - perror_with_name (("ttrace")); - /* ??? Is it necessary to call ttrace_wait() here? */ - - target_mourn_inferior (); -} - -/* Check is a dying thread is dead by now, and delete it from GDBs - thread list if so. */ -static int -inf_ttrace_delete_dead_threads_callback (struct thread_info *info, void *arg) -{ - lwpid_t lwpid; - struct inf_ttrace_private_thread_info *p; - - if (is_exited (info->ptid)) - return 0; - - lwpid = ptid_get_lwp (info->ptid); - p = (struct inf_ttrace_private_thread_info *) info->priv; - - /* Check if an lwp that was dying is still there or not. */ - if (p->dying && (kill (lwpid, 0) == -1)) - /* It's gone now. */ - delete_thread (info->ptid); - - return 0; -} - -/* Resume the lwp pointed to by INFO, with REQUEST, and pass it signal - SIG. */ - -static void -inf_ttrace_resume_lwp (struct thread_info *info, ttreq_t request, int sig) -{ - pid_t pid = ptid_get_pid (info->ptid); - lwpid_t lwpid = ptid_get_lwp (info->ptid); - - if (ttrace (request, pid, lwpid, TT_NOPC, sig, 0) == -1) - { - struct inf_ttrace_private_thread_info *p - = (struct inf_ttrace_private_thread_info *) info->priv; - if (p->dying && errno == EPROTO) - /* This is expected, it means the dying lwp is really gone - by now. If ttrace had an event to inform the debugger - the lwp is really gone, this wouldn't be needed. */ - delete_thread (info->ptid); - else - /* This was really unexpected. */ - perror_with_name (("ttrace")); - } -} - -/* Callback for iterate_over_threads. */ - -static int -inf_ttrace_resume_callback (struct thread_info *info, void *arg) -{ - if (!ptid_equal (info->ptid, inferior_ptid) && !is_exited (info->ptid)) - inf_ttrace_resume_lwp (info, TT_LWP_CONTINUE, 0); - - return 0; -} - -static void -inf_ttrace_resume (struct target_ops *ops, - ptid_t ptid, int step, enum gdb_signal signal) -{ - int resume_all; - ttreq_t request = step ? TT_LWP_SINGLE : TT_LWP_CONTINUE; - int sig = gdb_signal_to_host (signal); - struct thread_info *info; - - /* A specific PTID means `step only this process id'. */ - resume_all = (ptid_equal (ptid, minus_one_ptid)); - - /* If resuming all threads, it's the current thread that should be - handled specially. */ - if (resume_all) - ptid = inferior_ptid; - - info = find_thread_ptid (ptid); - inf_ttrace_resume_lwp (info, request, sig); - - if (resume_all) - /* Let all the other threads run too. */ - iterate_over_threads (inf_ttrace_resume_callback, NULL); -} - -static ptid_t -inf_ttrace_wait (struct target_ops *ops, - ptid_t ptid, struct target_waitstatus *ourstatus, int options) -{ - pid_t pid = ptid_get_pid (ptid); - lwpid_t lwpid = ptid_get_lwp (ptid); - ttstate_t tts; - struct thread_info *ti; - ptid_t related_ptid; - - /* Until proven otherwise. */ - ourstatus->kind = TARGET_WAITKIND_SPURIOUS; - - if (pid == -1) - pid = lwpid = 0; - - gdb_assert (pid != 0 || lwpid == 0); - - do - { - set_sigint_trap (); - - if (ttrace_wait (pid, lwpid, TTRACE_WAITOK, &tts, sizeof tts) == -1) - perror_with_name (("ttrace_wait")); - - clear_sigint_trap (); - } - while (tts.tts_event == TTEVT_NONE); - - /* Now that we've waited, we can re-enable the page protections. */ - if (inf_ttrace_reenable_page_protections) - { - gdb_assert (inf_ttrace_num_lwps_in_syscall == 0); - inf_ttrace_enable_page_protections (tts.tts_pid); - inf_ttrace_reenable_page_protections = 0; - } - - ptid = ptid_build (tts.tts_pid, tts.tts_lwpid, 0); - - if (inf_ttrace_num_lwps == 0) - { - struct thread_info *ti; - - inf_ttrace_num_lwps = 1; - - /* This is the earliest we hear about the lwp member of - INFERIOR_PTID, after an attach or fork_inferior. */ - gdb_assert (ptid_get_lwp (inferior_ptid) == 0); - - /* We haven't set the private member on the main thread yet. Do - it now. */ - ti = find_thread_ptid (inferior_ptid); - gdb_assert (ti != NULL && ti->priv == NULL); - ti->priv = - xmalloc (sizeof (struct inf_ttrace_private_thread_info)); - memset (ti->priv, 0, - sizeof (struct inf_ttrace_private_thread_info)); - - /* Notify the core that this ptid changed. This changes - inferior_ptid as well. */ - thread_change_ptid (inferior_ptid, ptid); - } - - switch (tts.tts_event) - { -#ifdef TTEVT_BPT_SSTEP - case TTEVT_BPT_SSTEP: - /* Make it look like a breakpoint. */ - ourstatus->kind = TARGET_WAITKIND_STOPPED; - ourstatus->value.sig = GDB_SIGNAL_TRAP; - break; -#endif - - case TTEVT_EXEC: - ourstatus->kind = TARGET_WAITKIND_EXECD; - ourstatus->value.execd_pathname = - xmalloc (tts.tts_u.tts_exec.tts_pathlen + 1); - if (ttrace (TT_PROC_GET_PATHNAME, tts.tts_pid, 0, - (uintptr_t)ourstatus->value.execd_pathname, - tts.tts_u.tts_exec.tts_pathlen, 0) == -1) - perror_with_name (("ttrace")); - ourstatus->value.execd_pathname[tts.tts_u.tts_exec.tts_pathlen] = 0; - - /* At this point, all inserted breakpoints are gone. Doing this - as soon as we detect an exec prevents the badness of deleting - a breakpoint writing the current "shadow contents" to lift - the bp. That shadow is NOT valid after an exec. */ - mark_breakpoints_out (); - break; - - case TTEVT_EXIT: - store_waitstatus (ourstatus, tts.tts_u.tts_exit.tts_exitcode); - inf_ttrace_num_lwps = 0; - break; - - case TTEVT_FORK: - related_ptid = ptid_build (tts.tts_u.tts_fork.tts_fpid, - tts.tts_u.tts_fork.tts_flwpid, 0); - - ourstatus->kind = TARGET_WAITKIND_FORKED; - ourstatus->value.related_pid = related_ptid; - - /* Make sure the other end of the fork is stopped too. */ - if (ttrace_wait (tts.tts_u.tts_fork.tts_fpid, - tts.tts_u.tts_fork.tts_flwpid, - TTRACE_WAITOK, &tts, sizeof tts) == -1) - perror_with_name (("ttrace_wait")); - - gdb_assert (tts.tts_event == TTEVT_FORK); - if (tts.tts_u.tts_fork.tts_isparent) - { - related_ptid = ptid_build (tts.tts_u.tts_fork.tts_fpid, - tts.tts_u.tts_fork.tts_flwpid, 0); - ptid = ptid_build (tts.tts_pid, tts.tts_lwpid, 0); - ourstatus->value.related_pid = related_ptid; - } - break; - - case TTEVT_VFORK: - if (tts.tts_u.tts_fork.tts_isparent) - ourstatus->kind = TARGET_WAITKIND_VFORK_DONE; - else - { - related_ptid = ptid_build (tts.tts_u.tts_fork.tts_fpid, - tts.tts_u.tts_fork.tts_flwpid, 0); - - ourstatus->kind = TARGET_WAITKIND_VFORKED; - ourstatus->value.related_pid = related_ptid; - } - break; - - case TTEVT_LWP_CREATE: - lwpid = tts.tts_u.tts_thread.tts_target_lwpid; - ptid = ptid_build (tts.tts_pid, lwpid, 0); - ti = add_thread (ptid); - ti->priv = - xmalloc (sizeof (struct inf_ttrace_private_thread_info)); - memset (ti->priv, 0, - sizeof (struct inf_ttrace_private_thread_info)); - inf_ttrace_num_lwps++; - ptid = ptid_build (tts.tts_pid, tts.tts_lwpid, 0); - /* Let the lwp_create-caller thread continue. */ - ttrace (TT_LWP_CONTINUE, ptid_get_pid (ptid), - ptid_get_lwp (ptid), TT_NOPC, 0, 0); - /* Return without stopping the whole process. */ - ourstatus->kind = TARGET_WAITKIND_IGNORE; - return ptid; - - case TTEVT_LWP_EXIT: - if (print_thread_events) - printf_unfiltered (_("[%s exited]\n"), target_pid_to_str (ptid)); - ti = find_thread_ptid (ptid); - gdb_assert (ti != NULL); - ((struct inf_ttrace_private_thread_info *)ti->priv)->dying = 1; - inf_ttrace_num_lwps--; - /* Let the thread really exit. */ - ttrace (TT_LWP_CONTINUE, ptid_get_pid (ptid), - ptid_get_lwp (ptid), TT_NOPC, 0, 0); - /* Return without stopping the whole process. */ - ourstatus->kind = TARGET_WAITKIND_IGNORE; - return ptid; - - case TTEVT_LWP_TERMINATE: - lwpid = tts.tts_u.tts_thread.tts_target_lwpid; - ptid = ptid_build (tts.tts_pid, lwpid, 0); - if (print_thread_events) - printf_unfiltered(_("[%s has been terminated]\n"), - target_pid_to_str (ptid)); - ti = find_thread_ptid (ptid); - gdb_assert (ti != NULL); - ((struct inf_ttrace_private_thread_info *)ti->priv)->dying = 1; - inf_ttrace_num_lwps--; - - /* Resume the lwp_terminate-caller thread. */ - ptid = ptid_build (tts.tts_pid, tts.tts_lwpid, 0); - ttrace (TT_LWP_CONTINUE, ptid_get_pid (ptid), - ptid_get_lwp (ptid), TT_NOPC, 0, 0); - /* Return without stopping the whole process. */ - ourstatus->kind = TARGET_WAITKIND_IGNORE; - return ptid; - - case TTEVT_SIGNAL: - ourstatus->kind = TARGET_WAITKIND_STOPPED; - ourstatus->value.sig = - gdb_signal_from_host (tts.tts_u.tts_signal.tts_signo); - break; - - case TTEVT_SYSCALL_ENTRY: - gdb_assert (inf_ttrace_reenable_page_protections == 0); - inf_ttrace_num_lwps_in_syscall++; - if (inf_ttrace_num_lwps_in_syscall == 1) - { - /* A thread has just entered a system call. Disable any - page protections as the kernel can't deal with them. */ - inf_ttrace_disable_page_protections (tts.tts_pid); - } - ourstatus->kind = TARGET_WAITKIND_SYSCALL_ENTRY; - ourstatus->value.syscall_number = tts.tts_scno; - break; - - case TTEVT_SYSCALL_RETURN: - if (inf_ttrace_num_lwps_in_syscall > 0) - { - /* If the last thread has just left the system call, this - would be a logical place to re-enable the page - protections, but that doesn't work. We can't re-enable - them until we've done another wait. */ - inf_ttrace_reenable_page_protections = - (inf_ttrace_num_lwps_in_syscall == 1); - inf_ttrace_num_lwps_in_syscall--; - } - ourstatus->kind = TARGET_WAITKIND_SYSCALL_RETURN; - ourstatus->value.syscall_number = tts.tts_scno; - break; - - default: - gdb_assert (!"Unexpected ttrace event"); - break; - } - - /* Make sure all threads within the process are stopped. */ - if (ttrace (TT_PROC_STOP, tts.tts_pid, 0, 0, 0, 0) == -1) - perror_with_name (("ttrace")); - - /* Now that the whole process is stopped, check if any dying thread - is really dead by now. If a dying thread is still alive, it will - be stopped too, and will still show up in `info threads', tagged - with "(Exiting)". We could make `info threads' prune dead - threads instead via inf_ttrace_thread_alive, but doing this here - has the advantage that a frontend is notificed sooner of thread - exits. Note that a dying lwp is still alive, it still has to be - resumed, like any other lwp. */ - iterate_over_threads (inf_ttrace_delete_dead_threads_callback, NULL); - - return ptid; -} - -/* Transfer LEN bytes from ADDR in the inferior's memory into READBUF, - and transfer LEN bytes from WRITEBUF into the inferior's memory at - ADDR. Either READBUF or WRITEBUF may be null, in which case the - corresponding transfer doesn't happen. Return the number of bytes - actually transferred (which may be zero if an error occurs). */ - -static LONGEST -inf_ttrace_xfer_memory (CORE_ADDR addr, ULONGEST len, - void *readbuf, const void *writebuf) -{ - pid_t pid = ptid_get_pid (inferior_ptid); - - /* HP-UX treats text space and data space differently. GDB however, - doesn't really know the difference. Therefore we try both. Try - text space before data space though because when we're writing - into text space the instruction cache might need to be flushed. */ - - if (readbuf - && ttrace (TT_PROC_RDTEXT, pid, 0, addr, len, (uintptr_t)readbuf) == -1 - && ttrace (TT_PROC_RDDATA, pid, 0, addr, len, (uintptr_t)readbuf) == -1) - return 0; - - if (writebuf - && ttrace (TT_PROC_WRTEXT, pid, 0, addr, len, (uintptr_t)writebuf) == -1 - && ttrace (TT_PROC_WRDATA, pid, 0, addr, len, (uintptr_t)writebuf) == -1) - return 0; - - return len; -} - -static enum target_xfer_status -inf_ttrace_xfer_partial (struct target_ops *ops, enum target_object object, - const char *annex, gdb_byte *readbuf, - const gdb_byte *writebuf, - ULONGEST offset, ULONGEST len, ULONGEST *xfered_len) -{ - switch (object) - { - case TARGET_OBJECT_MEMORY: - { - LONGEST val = inf_ttrace_xfer_memory (offset, len, readbuf, writebuf); - - if (val == 0) - return TARGET_XFER_EOF; - else - { - *xfered_len = (ULONGEST) val; - return TARGET_XFER_OK; - } - } - - case TARGET_OBJECT_UNWIND_TABLE: - return TARGET_XFER_E_IO; - - case TARGET_OBJECT_AUXV: - return TARGET_XFER_E_IO; - - case TARGET_OBJECT_WCOOKIE: - return TARGET_XFER_E_IO; - - default: - return TARGET_XFER_E_IO; - } -} - -/* Print status information about what we're accessing. */ - -static void -inf_ttrace_files_info (struct target_ops *ignore) -{ - struct inferior *inf = current_inferior (); - printf_filtered (_("\tUsing the running image of %s %s.\n"), - inf->attach_flag ? "attached" : "child", - target_pid_to_str (inferior_ptid)); -} - -static int -inf_ttrace_thread_alive (struct target_ops *ops, ptid_t ptid) -{ - return 1; -} - -/* Return a string describing the state of the thread specified by - INFO. */ - -static char * -inf_ttrace_extra_thread_info (struct target_ops *self, - struct thread_info *info) -{ - struct inf_ttrace_private_thread_info* priv = - (struct inf_ttrace_private_thread_info *) info->priv; - - if (priv != NULL && priv->dying) - return "Exiting"; - - return NULL; -} - -static char * -inf_ttrace_pid_to_str (struct target_ops *ops, ptid_t ptid) -{ - pid_t pid = ptid_get_pid (ptid); - lwpid_t lwpid = ptid_get_lwp (ptid); - static char buf[128]; - - if (lwpid == 0) - xsnprintf (buf, sizeof buf, "process %ld", - (long) pid); - else - xsnprintf (buf, sizeof buf, "process %ld, lwp %ld", - (long) pid, (long) lwpid); - return buf; -} - - -/* Implement the get_ada_task_ptid target_ops method. */ - -static ptid_t -inf_ttrace_get_ada_task_ptid (struct target_ops *self, long lwp, long thread) -{ - return ptid_build (ptid_get_pid (inferior_ptid), lwp, 0); -} - - -struct target_ops * -inf_ttrace_target (void) -{ - struct target_ops *t = inf_child_target (); - - t->to_attach = inf_ttrace_attach; - t->to_detach = inf_ttrace_detach; - t->to_resume = inf_ttrace_resume; - t->to_wait = inf_ttrace_wait; - t->to_files_info = inf_ttrace_files_info; - t->to_can_use_hw_breakpoint = inf_ttrace_can_use_hw_breakpoint; - t->to_insert_watchpoint = inf_ttrace_insert_watchpoint; - t->to_remove_watchpoint = inf_ttrace_remove_watchpoint; - t->to_stopped_by_watchpoint = inf_ttrace_stopped_by_watchpoint; - t->to_region_ok_for_hw_watchpoint = - inf_ttrace_region_ok_for_hw_watchpoint; - t->to_kill = inf_ttrace_kill; - t->to_create_inferior = inf_ttrace_create_inferior; - t->to_follow_fork = inf_ttrace_follow_fork; - t->to_mourn_inferior = inf_ttrace_mourn_inferior; - t->to_thread_alive = inf_ttrace_thread_alive; - t->to_extra_thread_info = inf_ttrace_extra_thread_info; - t->to_pid_to_str = inf_ttrace_pid_to_str; - t->to_xfer_partial = inf_ttrace_xfer_partial; - t->to_get_ada_task_ptid = inf_ttrace_get_ada_task_ptid; - - return t; -} -#endif - - -/* Prevent warning from -Wmissing-prototypes. */ -void _initialize_inf_ttrace (void); - -void -_initialize_inf_ttrace (void) -{ -#ifdef HAVE_TTRACE - inf_ttrace_page_dict.pagesize = getpagesize(); -#endif -} |