| /* Machine independent support for Solaris /proc (process file system) for GDB. | 
 |  | 
 |    Copyright (C) 1999-2023 Free Software Foundation, Inc. | 
 |  | 
 |    Written by Michael Snyder at Cygnus Solutions. | 
 |    Based on work by Fred Fish, Stu Grossman, Geoff Noer, and others. | 
 |  | 
 |    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/>.  */ | 
 |  | 
 | /* | 
 |  * Pretty-print trace of api calls to the /proc api | 
 |  */ | 
 |  | 
 | #include "defs.h" | 
 | #include "gdbcmd.h" | 
 | #include "completer.h" | 
 |  | 
 | #include <sys/types.h> | 
 | #include <sys/procfs.h> | 
 | #include <sys/proc.h>	/* for struct proc */ | 
 | #include <sys/user.h>	/* for struct user */ | 
 | #include <fcntl.h>	/* for O_RDWR etc.  */ | 
 | #include "gdbsupport/gdb_wait.h" | 
 |  | 
 | #include "proc-utils.h" | 
 |  | 
 | /*  Much of the information used in the /proc interface, particularly for | 
 |     printing status information, is kept as tables of structures of the | 
 |     following form.  These tables can be used to map numeric values to | 
 |     their symbolic names and to a string that describes their specific use.  */ | 
 |  | 
 | struct trans { | 
 |   long value;                   /* The numeric value */ | 
 |   const char *name;             /* The equivalent symbolic value */ | 
 |   const char *desc;             /* Short description of value */ | 
 | }; | 
 |  | 
 | static bool  procfs_trace   = false; | 
 | static FILE *procfs_file     = NULL; | 
 | static std::string procfs_filename = "procfs_trace"; | 
 |  | 
 | static void | 
 | prepare_to_trace (void) | 
 | { | 
 |   if (procfs_trace)			/* if procfs tracing turned on */ | 
 |     if (procfs_file == NULL)		/* if output file not yet open */ | 
 |       procfs_file = fopen (procfs_filename.c_str (), "a");	/* open output file */ | 
 | } | 
 |  | 
 | static void | 
 | set_procfs_trace_cmd (const char *args, | 
 | 		      int from_tty, struct cmd_list_element *c) | 
 | { | 
 | #if 0	/* not sure what I might actually need to do here, if anything */ | 
 |   if (procfs_file) | 
 |     fflush (procfs_file); | 
 | #endif | 
 | } | 
 |  | 
 | static void | 
 | set_procfs_file_cmd (const char *args, | 
 | 		     int from_tty, struct cmd_list_element *c) | 
 | { | 
 |   /* Just changed the filename for procfs tracing. | 
 |      If a file was already open, close it.  */ | 
 |   if (procfs_file) | 
 |     fclose (procfs_file); | 
 |   procfs_file = NULL; | 
 | } | 
 |  | 
 | static struct trans rw_table[] = { | 
 |   { PCAGENT,  "PCAGENT",  "create agent lwp with regs from argument" }, | 
 |   { PCCFAULT, "PCCFAULT", "clear current fault" }, | 
 |   { PCCSIG,   "PCCSIG",   "clear current signal" }, | 
 |   { PCDSTOP,  "PCDSTOP",  "post stop request" }, | 
 |   { PCKILL,   "PCKILL",   "post a signal" }, | 
 |   { PCNICE,   "PCNICE",   "set nice priority" }, | 
 |   { PCREAD,   "PCREAD",   "read from the address space" }, | 
 |   { PCWRITE,  "PCWRITE",  "write to the address space" }, | 
 |   { PCRUN,    "PCRUN",    "make process/lwp runnable" }, | 
 |   { PCSASRS,  "PCSASRS",  "set ancillary state registers" }, | 
 |   { PCSCRED,  "PCSCRED",  "set process credentials" }, | 
 |   { PCSENTRY, "PCSENTRY", "set traced syscall entry set" }, | 
 |   { PCSET,    "PCSET",    "set modes" }, | 
 |   { PCSEXIT,  "PCSEXIT",  "set traced syscall exit  set" }, | 
 |   { PCSFAULT, "PCSFAULT", "set traced fault set" }, | 
 |   { PCSFPREG, "PCSFPREG", "set floating point registers" }, | 
 |   { PCSHOLD,  "PCSHOLD",  "set signal mask" }, | 
 |   { PCSREG,   "PCSREG",   "set general registers" }, | 
 |   { PCSSIG,   "PCSSIG",   "set current signal" }, | 
 |   { PCSTOP,   "PCSTOP",   "post stop request and wait" }, | 
 |   { PCSTRACE, "PCSTRACE", "set traced signal set" }, | 
 |   { PCSVADDR, "PCSVADDR", "set pc virtual address" }, | 
 |   { PCSXREG,  "PCSXREG",  "set extra registers" }, | 
 |   { PCTWSTOP, "PCTWSTOP", "wait for stop, with timeout arg" }, | 
 |   { PCUNKILL, "PCUNKILL", "delete a pending signal" }, | 
 |   { PCUNSET,  "PCUNSET",  "unset modes" }, | 
 |   { PCWATCH,  "PCWATCH",  "set/unset watched memory area" }, | 
 |   { PCWSTOP,  "PCWSTOP",  "wait for process/lwp to stop, no timeout" }, | 
 |   { 0,        NULL,      NULL } | 
 | }; | 
 |  | 
 | static off_t lseek_offset; | 
 |  | 
 | int | 
 | write_with_trace (int fd, void *varg, size_t len, char *file, int line) | 
 | { | 
 |   int i = ARRAY_SIZE (rw_table) - 1; | 
 |   int ret; | 
 |   procfs_ctl_t *arg = (procfs_ctl_t *) varg; | 
 |  | 
 |   prepare_to_trace (); | 
 |   if (procfs_trace) | 
 |     { | 
 |       procfs_ctl_t opcode = arg[0]; | 
 |       for (i = 0; rw_table[i].name != NULL; i++) | 
 | 	if (rw_table[i].value == opcode) | 
 | 	  break; | 
 |  | 
 |       if (info_verbose) | 
 | 	fprintf (procfs_file ? procfs_file : stdout,  | 
 | 		 "%s:%d -- ", file, line); | 
 |       switch (opcode) { | 
 |       case PCSET: | 
 | 	fprintf (procfs_file ? procfs_file : stdout,  | 
 | 		 "write (PCSET,   %s) %s\n",  | 
 | 		 arg[1] == PR_FORK  ? "PR_FORK"  : | 
 | 		 arg[1] == PR_RLC   ? "PR_RLC"   : | 
 | 		 arg[1] == PR_ASYNC ? "PR_ASYNC" : | 
 | 		 "<unknown flag>", | 
 | 		 info_verbose ? rw_table[i].desc : ""); | 
 | 	break; | 
 |       case PCUNSET: | 
 | 	fprintf (procfs_file ? procfs_file : stdout,  | 
 | 		 "write (PCRESET, %s) %s\n",  | 
 | 		 arg[1] == PR_FORK  ? "PR_FORK"  : | 
 | 		 arg[1] == PR_RLC   ? "PR_RLC"   : | 
 | 		 arg[1] == PR_ASYNC ? "PR_ASYNC" : | 
 | 		 "<unknown flag>", | 
 | 		 info_verbose ? rw_table[i].desc : ""); | 
 | 	break; | 
 |       case PCSTRACE: | 
 | 	fprintf (procfs_file ? procfs_file : stdout,  | 
 | 		 "write (PCSTRACE) "); | 
 | 	proc_prettyfprint_signalset (procfs_file ? procfs_file : stdout, | 
 | 				     (sigset_t *) &arg[1], 0); | 
 | 	break; | 
 |       case PCSFAULT: | 
 | 	fprintf (procfs_file ? procfs_file : stdout,  | 
 | 		 "write (PCSFAULT) "); | 
 | 	proc_prettyfprint_faultset (procfs_file ? procfs_file : stdout, | 
 | 				    (fltset_t *) &arg[1], 0); | 
 | 	break; | 
 |       case PCSENTRY: | 
 | 	fprintf (procfs_file ? procfs_file : stdout,  | 
 | 		 "write (PCSENTRY) "); | 
 | 	proc_prettyfprint_syscalls (procfs_file ? procfs_file : stdout, | 
 | 				    (sysset_t *) &arg[1], 0); | 
 | 	break; | 
 |       case PCSEXIT: | 
 | 	fprintf (procfs_file ? procfs_file : stdout,  | 
 | 		 "write (PCSEXIT) "); | 
 | 	proc_prettyfprint_syscalls (procfs_file ? procfs_file : stdout, | 
 | 				    (sysset_t *) &arg[1], 0); | 
 | 	break; | 
 |       case PCSHOLD: | 
 | 	fprintf (procfs_file ? procfs_file : stdout,  | 
 | 		 "write (PCSHOLD) "); | 
 | 	proc_prettyfprint_signalset (procfs_file ? procfs_file : stdout, | 
 | 				     (sigset_t *) &arg[1], 0); | 
 | 	break; | 
 |       case PCSSIG: | 
 | 	fprintf (procfs_file ? procfs_file : stdout,  | 
 | 		 "write (PCSSIG) "); | 
 | 	proc_prettyfprint_signal (procfs_file ? procfs_file : stdout, | 
 | 				  arg[1] ? ((siginfo_t *) &arg[1])->si_signo  | 
 | 					 : 0,  | 
 | 				  0); | 
 | 	fprintf (procfs_file ? procfs_file : stdout, "\n"); | 
 | 	break; | 
 |       case PCRUN: | 
 | 	fprintf (procfs_file ? procfs_file : stdout,  | 
 | 		 "write (PCRUN) "); | 
 | 	if (arg[1] & PRCSIG) | 
 | 	  fprintf (procfs_file ? procfs_file : stdout, "clearSig "); | 
 | 	if (arg[1] & PRCFAULT) | 
 | 	  fprintf (procfs_file ? procfs_file : stdout, "clearFlt "); | 
 | 	if (arg[1] & PRSTEP) | 
 | 	  fprintf (procfs_file ? procfs_file : stdout, "step "); | 
 | 	if (arg[1] & PRSABORT) | 
 | 	  fprintf (procfs_file ? procfs_file : stdout, "syscallAbort "); | 
 | 	if (arg[1] & PRSTOP) | 
 | 	  fprintf (procfs_file ? procfs_file : stdout, "stopReq "); | 
 | 	   | 
 | 	fprintf (procfs_file ? procfs_file : stdout, "\n"); | 
 | 	break; | 
 |       case PCKILL: | 
 | 	fprintf (procfs_file ? procfs_file : stdout,  | 
 | 		 "write (PCKILL) "); | 
 | 	proc_prettyfprint_signal (procfs_file ? procfs_file : stdout, | 
 | 				  arg[1], 0); | 
 | 	fprintf (procfs_file ? procfs_file : stdout, "\n"); | 
 | 	break; | 
 |       default: | 
 | 	{ | 
 | 	  if (rw_table[i].name) | 
 | 	    fprintf (procfs_file ? procfs_file : stdout,  | 
 | 		     "write (%s) %s\n",  | 
 | 		     rw_table[i].name,  | 
 | 		     info_verbose ? rw_table[i].desc : ""); | 
 | 	  else | 
 | 	    { | 
 | 	      if (lseek_offset != -1) | 
 | 		fprintf (procfs_file ? procfs_file : stdout,  | 
 | 			 "write (<unknown>, %lud bytes at 0x%08lx) \n",  | 
 | 			 (unsigned long) len, (unsigned long) lseek_offset); | 
 | 	      else | 
 | 		fprintf (procfs_file ? procfs_file : stdout,  | 
 | 			 "write (<unknown>, %lud bytes) \n",  | 
 | 			 (unsigned long) len); | 
 | 	    } | 
 | 	  break; | 
 | 	} | 
 |       } | 
 |       if (procfs_file) | 
 | 	fflush (procfs_file); | 
 |     } | 
 |   errno = 0; | 
 |   ret = write (fd, (void *) arg, len); | 
 |   if (procfs_trace && ret != len) | 
 |     { | 
 |       fprintf (procfs_file ? procfs_file : stdout,  | 
 | 	       "[write (%s) FAILED! (%s)]\n", | 
 | 	       rw_table[i].name != NULL ?  | 
 | 	       rw_table[i].name : "<unknown>",  | 
 | 	       safe_strerror (errno)); | 
 |       if (procfs_file) | 
 | 	fflush (procfs_file); | 
 |     } | 
 |  | 
 |   lseek_offset = -1; | 
 |   return ret; | 
 | } | 
 |  | 
 | off_t | 
 | lseek_with_trace (int fd, off_t offset, int whence, char *file, int line) | 
 | { | 
 |   off_t ret; | 
 |  | 
 |   prepare_to_trace (); | 
 |   errno = 0; | 
 |   ret = lseek (fd, offset, whence); | 
 |   lseek_offset = ret; | 
 |   if (procfs_trace && (ret == -1 || errno != 0)) | 
 |     { | 
 |       fprintf (procfs_file ? procfs_file : stdout,  | 
 | 	       "[lseek (0x%08lx) FAILED! (%s)]\n",  | 
 | 	       (unsigned long) offset, safe_strerror (errno)); | 
 |       if (procfs_file) | 
 | 	fflush (procfs_file); | 
 |     } | 
 |  | 
 |   return ret; | 
 | } | 
 |  | 
 | int | 
 | open_with_trace (char *filename, int mode, char *file, int line) | 
 | { | 
 |   int ret; | 
 |  | 
 |   prepare_to_trace (); | 
 |   errno = 0; | 
 |   ret = open (filename, mode); | 
 |   if (procfs_trace) | 
 |     { | 
 |       if (info_verbose) | 
 | 	fprintf (procfs_file ? procfs_file : stdout,  | 
 | 		 "%s:%d -- ", file, line); | 
 |  | 
 |       if (errno) | 
 | 	{ | 
 | 	  fprintf (procfs_file ? procfs_file : stdout,  | 
 | 		   "[open FAILED! (%s) line %d]\\n",  | 
 | 		   safe_strerror (errno), line); | 
 | 	} | 
 |       else | 
 | 	{ | 
 | 	  fprintf (procfs_file ? procfs_file : stdout,  | 
 | 		   "%d = open (%s, ", ret, filename); | 
 | 	  if (mode == O_RDONLY) | 
 | 	    fprintf (procfs_file ? procfs_file : stdout, "O_RDONLY) %d\n", | 
 | 		     line); | 
 | 	  else if (mode == O_WRONLY) | 
 | 	    fprintf (procfs_file ? procfs_file : stdout, "O_WRONLY) %d\n", | 
 | 		     line); | 
 | 	  else if (mode == O_RDWR) | 
 | 	    fprintf (procfs_file ? procfs_file : stdout, "O_RDWR)   %d\n", | 
 | 		     line); | 
 | 	} | 
 |       if (procfs_file) | 
 | 	fflush (procfs_file); | 
 |     } | 
 |  | 
 |   return ret; | 
 | } | 
 |  | 
 | int | 
 | close_with_trace (int fd, char *file, int line) | 
 | { | 
 |   int ret; | 
 |  | 
 |   prepare_to_trace (); | 
 |   errno = 0; | 
 |   ret = close (fd); | 
 |   if (procfs_trace) | 
 |     { | 
 |       if (info_verbose) | 
 | 	fprintf (procfs_file ? procfs_file : stdout,  | 
 | 		 "%s:%d -- ", file, line); | 
 |       if (errno) | 
 | 	fprintf (procfs_file ? procfs_file : stdout,  | 
 | 		 "[close FAILED! (%s)]\n", safe_strerror (errno)); | 
 |       else | 
 | 	fprintf (procfs_file ? procfs_file : stdout,  | 
 | 		 "%d = close (%d)\n", ret, fd); | 
 |       if (procfs_file) | 
 | 	fflush (procfs_file); | 
 |     } | 
 |  | 
 |   return ret; | 
 | } | 
 |  | 
 | pid_t | 
 | wait_with_trace (int *wstat, char *file, int line) | 
 | { | 
 |   int ret, lstat = 0; | 
 |  | 
 |   prepare_to_trace (); | 
 |   if (procfs_trace) | 
 |     { | 
 |       if (info_verbose) | 
 | 	fprintf (procfs_file ? procfs_file : stdout,  | 
 | 		 "%s:%d -- ", file, line); | 
 |       fprintf (procfs_file ? procfs_file : stdout,  | 
 | 	       "wait (line %d) ", line); | 
 |       if (procfs_file) | 
 | 	fflush (procfs_file); | 
 |     } | 
 |   errno = 0; | 
 |   ret = wait (&lstat); | 
 |   if (procfs_trace) | 
 |     { | 
 |       if (errno) | 
 | 	fprintf (procfs_file ? procfs_file : stdout,  | 
 | 		 "[wait FAILED! (%s)]\n", safe_strerror (errno)); | 
 |       else | 
 | 	fprintf (procfs_file ? procfs_file : stdout,  | 
 | 		 "returned pid %d, status 0x%x\n", ret, lstat); | 
 |       if (procfs_file) | 
 | 	fflush (procfs_file); | 
 |     } | 
 |   if (wstat) | 
 |     *wstat = lstat; | 
 |  | 
 |   return ret; | 
 | } | 
 |  | 
 | void | 
 | procfs_note (const char *msg, const char *file, int line) | 
 | { | 
 |   prepare_to_trace (); | 
 |   if (procfs_trace) | 
 |     { | 
 |       if (info_verbose) | 
 | 	fprintf (procfs_file ? procfs_file : stdout,  | 
 | 		 "%s:%d -- ", file, line); | 
 |       fprintf (procfs_file ? procfs_file : stdout, "%s", msg); | 
 |       if (procfs_file) | 
 | 	fflush (procfs_file); | 
 |     } | 
 | } | 
 |  | 
 | void | 
 | proc_prettyfprint_status (long flags, int why, int what, int thread) | 
 | { | 
 |   prepare_to_trace (); | 
 |   if (procfs_trace) | 
 |     { | 
 |       if (thread) | 
 | 	fprintf (procfs_file ? procfs_file : stdout, | 
 | 		 "Thread %d: ", thread); | 
 |  | 
 |       proc_prettyfprint_flags (procfs_file ? procfs_file : stdout,  | 
 | 			       flags, 0); | 
 |  | 
 |       if (flags & (PR_STOPPED | PR_ISTOP)) | 
 | 	proc_prettyfprint_why (procfs_file ? procfs_file : stdout,  | 
 | 			       why, what, 0); | 
 |       if (procfs_file) | 
 | 	fflush (procfs_file); | 
 |     } | 
 | } | 
 |  | 
 | void _initialize_proc_api (); | 
 | void | 
 | _initialize_proc_api () | 
 | { | 
 |   add_setshow_boolean_cmd ("procfs-trace", no_class, &procfs_trace, _("\ | 
 | Set tracing for /proc api calls."), _("\ | 
 | Show tracing for /proc api calls."), NULL, | 
 | 			   set_procfs_trace_cmd, | 
 | 			   NULL, /* FIXME: i18n: */ | 
 | 			   &setlist, &showlist); | 
 |  | 
 |   add_setshow_filename_cmd ("procfs-file", no_class, &procfs_filename, _("\ | 
 | Set filename for /proc tracefile."), _("\ | 
 | Show filename for /proc tracefile."), NULL, | 
 | 			    set_procfs_file_cmd, | 
 | 			    NULL, /* FIXME: i18n: */ | 
 | 			    &setlist, &showlist); | 
 | } |