|  | /* Machine independent support for Solaris /proc (process file system) for GDB. | 
|  |  | 
|  | Copyright (C) 1999-2022 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); | 
|  | } |