⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 linux-low.c

📁 这个是LINUX下的GDB调度工具的源码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* Low level interface to ptrace, for the remote server for GDB.   Copyright 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004   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 2 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, write to the Free Software   Foundation, Inc., 59 Temple Place - Suite 330,   Boston, MA 02111-1307, USA.  */#include "server.h"#include "linux-low.h"#include <sys/wait.h>#include <stdio.h>#include <sys/param.h>#include <sys/dir.h>#include <sys/ptrace.h>#include <sys/user.h>#include <signal.h>#include <sys/ioctl.h>#include <fcntl.h>#include <string.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>#include <sys/syscall.h>/* ``all_threads'' is keyed by the LWP ID - it should be the thread ID instead,   however.  This requires changing the ID in place when we go from !using_threads   to using_threads, immediately.   ``all_processes'' is keyed by the process ID - which on Linux is (presently)   the same as the LWP ID.  */struct inferior_list all_processes;/* FIXME this is a bit of a hack, and could be removed.  */int stopping_threads;/* FIXME make into a target method?  */int using_threads;static void linux_resume_one_process (struct inferior_list_entry *entry,				      int step, int signal);static void linux_resume (struct thread_resume *resume_info);static void stop_all_processes (void);static int linux_wait_for_event (struct thread_info *child);struct pending_signals{  int signal;  struct pending_signals *prev;};#define PTRACE_ARG3_TYPE long#define PTRACE_XFER_TYPE long#ifdef HAVE_LINUX_REGSETSstatic int use_regsets_p = 1;#endifint debug_threads = 0;#define pid_of(proc) ((proc)->head.id)/* FIXME: Delete eventually.  */#define inferior_pid (pid_of (get_thread_process (current_inferior)))/* This function should only be called if the process got a SIGTRAP.   The SIGTRAP could mean several things.   On i386, where decr_pc_after_break is non-zero:   If we were single-stepping this process using PTRACE_SINGLESTEP,   we will get only the one SIGTRAP (even if the instruction we   stepped over was a breakpoint).  The value of $eip will be the   next instruction.   If we continue the process using PTRACE_CONT, we will get a   SIGTRAP when we hit a breakpoint.  The value of $eip will be   the instruction after the breakpoint (i.e. needs to be   decremented).  If we report the SIGTRAP to GDB, we must also   report the undecremented PC.  If we cancel the SIGTRAP, we   must resume at the decremented PC.   (Presumably, not yet tested) On a non-decr_pc_after_break machine   with hardware or kernel single-step:   If we single-step over a breakpoint instruction, our PC will   point at the following instruction.  If we continue and hit a   breakpoint instruction, our PC will point at the breakpoint   instruction.  */static CORE_ADDRget_stop_pc (void){  CORE_ADDR stop_pc = (*the_low_target.get_pc) ();  if (get_thread_process (current_inferior)->stepping)    return stop_pc;  else    return stop_pc - the_low_target.decr_pc_after_break;}static void *add_process (int pid){  struct process_info *process;  process = (struct process_info *) malloc (sizeof (*process));  memset (process, 0, sizeof (*process));  process->head.id = pid;  /* Default to tid == lwpid == pid.  */  process->tid = pid;  process->lwpid = pid;  add_inferior_to_list (&all_processes, &process->head);  return process;}/* Start an inferior process and returns its pid.   ALLARGS is a vector of program-name and args. */static intlinux_create_inferior (char *program, char **allargs){  void *new_process;  int pid;  pid = fork ();  if (pid < 0)    perror_with_name ("fork");  if (pid == 0)    {      ptrace (PTRACE_TRACEME, 0, 0, 0);      signal (__SIGRTMIN + 1, SIG_DFL);      setpgid (0, 0);      execv (program, allargs);      fprintf (stderr, "Cannot exec %s: %s.\n", program,	       strerror (errno));      fflush (stderr);      _exit (0177);    }  new_process = add_process (pid);  add_thread (pid, new_process);  return pid;}/* Attach to an inferior process.  */voidlinux_attach_lwp (int pid, int tid){  struct process_info *new_process;  if (ptrace (PTRACE_ATTACH, pid, 0, 0) != 0)    {      fprintf (stderr, "Cannot attach to process %d: %s (%d)\n", pid,	       strerror (errno), errno);      fflush (stderr);      /* If we fail to attach to an LWP, just return.  */      if (!using_threads)	_exit (0177);      return;    }  new_process = (struct process_info *) add_process (pid);  add_thread (tid, new_process);  /* The next time we wait for this LWP we'll see a SIGSTOP as PTRACE_ATTACH     brings it to a halt.  We should ignore that SIGSTOP and resume the process     (unless this is the first process, in which case the flag will be cleared     in linux_attach).     On the other hand, if we are currently trying to stop all threads, we     should treat the new thread as if we had sent it a SIGSTOP.  This works     because we are guaranteed that add_process added us to the end of the     list, and so the new thread has not yet reached wait_for_sigstop (but     will).  */  if (! stopping_threads)    new_process->stop_expected = 1;}intlinux_attach (int pid){  struct process_info *process;  linux_attach_lwp (pid, pid);  /* Don't ignore the initial SIGSTOP if we just attached to this process.  */  process = (struct process_info *) find_inferior_id (&all_processes, pid);  process->stop_expected = 0;  return 0;}/* Kill the inferior process.  Make us have no inferior.  */static voidlinux_kill_one_process (struct inferior_list_entry *entry){  struct thread_info *thread = (struct thread_info *) entry;  struct process_info *process = get_thread_process (thread);  int wstat;  /* We avoid killing the first thread here, because of a Linux kernel (at     least 2.6.0-test7 through 2.6.8-rc4) bug; if we kill the parent before     the children get a chance to be reaped, it will remain a zombie     forever.  */  if (entry == all_threads.head)    return;  do    {      ptrace (PTRACE_KILL, pid_of (process), 0, 0);      /* Make sure it died.  The loop is most likely unnecessary.  */      wstat = linux_wait_for_event (thread);    } while (WIFSTOPPED (wstat));}static voidlinux_kill (void){  struct thread_info *thread = (struct thread_info *) all_threads.head;  struct process_info *process = get_thread_process (thread);  int wstat;  for_each_inferior (&all_threads, linux_kill_one_process);  /* See the comment in linux_kill_one_process.  We did not kill the first     thread in the list, so do so now.  */  do    {      ptrace (PTRACE_KILL, pid_of (process), 0, 0);      /* Make sure it died.  The loop is most likely unnecessary.  */      wstat = linux_wait_for_event (thread);    } while (WIFSTOPPED (wstat));}static voidlinux_detach_one_process (struct inferior_list_entry *entry){  struct thread_info *thread = (struct thread_info *) entry;  struct process_info *process = get_thread_process (thread);  ptrace (PTRACE_DETACH, pid_of (process), 0, 0);}static voidlinux_detach (void){  for_each_inferior (&all_threads, linux_detach_one_process);}/* Return nonzero if the given thread is still alive.  */static intlinux_thread_alive (int tid){  if (find_inferior_id (&all_threads, tid) != NULL)    return 1;  else    return 0;}/* Return nonzero if this process stopped at a breakpoint which   no longer appears to be inserted.  Also adjust the PC   appropriately to resume where the breakpoint used to be.  */static intcheck_removed_breakpoint (struct process_info *event_child){  CORE_ADDR stop_pc;  struct thread_info *saved_inferior;  if (event_child->pending_is_breakpoint == 0)    return 0;  if (debug_threads)    fprintf (stderr, "Checking for breakpoint.\n");  saved_inferior = current_inferior;  current_inferior = get_process_thread (event_child);  stop_pc = get_stop_pc ();  /* If the PC has changed since we stopped, then we shouldn't do     anything.  This happens if, for instance, GDB handled the     decr_pc_after_break subtraction itself.  */  if (stop_pc != event_child->pending_stop_pc)    {      if (debug_threads)	fprintf (stderr, "Ignoring, PC was changed.\n");      event_child->pending_is_breakpoint = 0;      current_inferior = saved_inferior;      return 0;    }  /* If the breakpoint is still there, we will report hitting it.  */  if ((*the_low_target.breakpoint_at) (stop_pc))    {      if (debug_threads)	fprintf (stderr, "Ignoring, breakpoint is still present.\n");      current_inferior = saved_inferior;      return 0;    }  if (debug_threads)    fprintf (stderr, "Removed breakpoint.\n");  /* For decr_pc_after_break targets, here is where we perform the     decrement.  We go immediately from this function to resuming,     and can not safely call get_stop_pc () again.  */  if (the_low_target.set_pc != NULL)    (*the_low_target.set_pc) (stop_pc);  /* We consumed the pending SIGTRAP.  */  event_child->pending_is_breakpoint = 0;  event_child->status_pending_p = 0;  event_child->status_pending = 0;  current_inferior = saved_inferior;  return 1;}/* Return 1 if this process has an interesting status pending.  This function   may silently resume an inferior process.  */static intstatus_pending_p (struct inferior_list_entry *entry, void *dummy){  struct process_info *process = (struct process_info *) entry;  if (process->status_pending_p)    if (check_removed_breakpoint (process))      {	/* This thread was stopped at a breakpoint, and the breakpoint	   is now gone.  We were told to continue (or step...) all threads,	   so GDB isn't trying to single-step past this breakpoint.	   So instead of reporting the old SIGTRAP, pretend we got to	   the breakpoint just after it was removed instead of just	   before; resume the process.  */	linux_resume_one_process (&process->head, 0, 0);	return 0;      }  return process->status_pending_p;}static voidlinux_wait_for_process (struct process_info **childp, int *wstatp){  int ret;  int to_wait_for = -1;  if (*childp != NULL)    to_wait_for = (*childp)->lwpid;  while (1)    {      ret = waitpid (to_wait_for, wstatp, WNOHANG);      if (ret == -1)	{	  if (errno != ECHILD)	    perror_with_name ("waitpid");	}      else if (ret > 0)	break;      ret = waitpid (to_wait_for, wstatp, WNOHANG | __WCLONE);      if (ret == -1)	{	  if (errno != ECHILD)	    perror_with_name ("waitpid (WCLONE)");	}      else if (ret > 0)	break;      usleep (1000);    }  if (debug_threads      && (!WIFSTOPPED (*wstatp)	  || (WSTOPSIG (*wstatp) != 32	      && WSTOPSIG (*wstatp) != 33)))    fprintf (stderr, "Got an event from %d (%x)\n", ret, *wstatp);  if (to_wait_for == -1)    *childp = (struct process_info *) find_inferior_id (&all_processes, ret);  (*childp)->stopped = 1;  (*childp)->pending_is_breakpoint = 0;  if (debug_threads      && WIFSTOPPED (*wstatp))    {      current_inferior = (struct thread_info *)	find_inferior_id (&all_threads, (*childp)->tid);      /* For testing only; i386_stop_pc prints out a diagnostic.  */      if (the_low_target.get_pc != NULL)	get_stop_pc ();    }}static intlinux_wait_for_event (struct thread_info *child){  CORE_ADDR stop_pc;  struct process_info *event_child;  int wstat;  /* Check for a process with a pending status.  */  /* It is possible that the user changed the pending task's registers since     it stopped.  We correctly handle the change of PC if we hit a breakpoint     (in check_removed_breakpoint); signals should be reported anyway.  */  if (child == NULL)    {      event_child = (struct process_info *)	find_inferior (&all_processes, status_pending_p, NULL);      if (debug_threads && event_child)	fprintf (stderr, "Got a pending child %d\n", event_child->lwpid);    }  else    {      event_child = get_thread_process (child);      if (event_child->status_pending_p	  && check_removed_breakpoint (event_child))	event_child = NULL;    }  if (event_child != NULL)    {      if (event_child->status_pending_p)	{	  if (debug_threads)	    fprintf (stderr, "Got an event from pending child %d (%04x)\n",		     event_child->lwpid, event_child->status_pending);	  wstat = event_child->status_pending;	  event_child->status_pending_p = 0;	  event_child->status_pending = 0;	  current_inferior = get_process_thread (event_child);	  return wstat;	}    }  /* We only enter this loop if no process has a pending wait status.  Thus     any action taken in response to a wait status inside this loop is     responding as soon as we detect the status, not after any pending     events.  */  while (1)    {      if (child == NULL)	event_child = NULL;      else	event_child = get_thread_process (child);      linux_wait_for_process (&event_child, &wstat);      if (event_child == NULL)	error ("event from unknown child");      current_inferior = (struct thread_info *)	find_inferior_id (&all_threads, event_child->tid);      if (using_threads)	{	  /* Check for thread exit.  */	  if (! WIFSTOPPED (wstat))	    {	      if (debug_threads)		fprintf (stderr, "Thread %d (LWP %d) exiting\n",			 event_child->tid, event_child->head.id);	      /* If the last thread is exiting, just return.  */	      if (all_threads.head == all_threads.tail)		return wstat;	      dead_thread_notify (event_child->tid);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -