This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: PR/2386 [2/2]: MinGW attach to process without an exec file


On Sat, Dec 29, 2007 at 10:43:47PM +0000, Pedro Alves wrote:
> Christopher Faylor wrote:
>> On Fri, 28 Dec 2007 22:34:08 +0000 , Pedro Alves wrote:
>>> Not useful in this case, as the pid gdb holds is the native Windows pid.
>>> Hence current usage of cygwin_internal (CW_GETPINFO, ...).
>> I do find this less intrusive but I don't understand the above comment.
>> Did you actually try using /proc and found it wanting?  Finding a cygwin
>> exename should be as simple as just doing a readlink
>> ("/proc/<pid>/exe").  I just tried this on a top-level process created
>> by cygwin and on the pid returned by typing "sleep 300&" in bash.  The
>> sleep process does have two pids associated with it due to the oddities
>> of the way that cygwin emulates exec but "ls -l /proc/<pid>/exe" showed
>> "/bin/sleep.exe" in each case.
>
> /* Try to find the process path using the Cygwin internal process list
>    pid isn't a valid pid, unfortunately.  Use current_event.dwProcessId
>    instead.  */
>
> (That's the I-have-a-patch-to-clean-that-up part.  But even then,
>  PIDGET(inferior_ptid) will be the winpid.  We have room for it in the
>  ptid_t, though, in the form of a lwp.  Note, *that* patch will
> be invasive :-) )
>
>What's stored in current_event.dwProcessId is a Windows PID, not a
>Cygwin pid.  In that case, /proc/<pid>/exe isn't helpful, unless we can
>query Cygwin for the cygpid associated with this pid, or we look though
>all the /proc/<pid>s looking at a matching `cat /proc/<cygpid>/winpid`.

I'm familiar with the difference between windows pids and Cygwin pids.
In many cases they are the same and, when they aren't, there should
still be a /proc entry for the windows pid associated with the Cygwin
process, which is what I was saying above.

  bash-3.2$ sleep 300&
  [1] 1560
  bash-3.2$ ps
	PID    PPID    PGID     WINPID  TTY  UID    STIME COMMAND
       2332       1    2332       2332  con 1003 12:53:53 /bin/bash
       1560    2332    1560       2576  con 1003 18:42:59 /bin/sleep
       2920    2332    2920       3108  con 1003 18:43:01 /bin/ps
  bash-3.2$ ls -l /proc/1560/exe
  lrwxrwxrwx 1 cgf None 0 Nov 30  2006 exe -> /bin/sleep.exe
  bash-3.2$ ls -l /proc/2576/exe
  lrwxrwxrwx 1 cgf None 0 Nov 30  2006 exe -> /bin/sleep.exe

For exec'ed stubs you won't see the windows pid entry if you 'ls /proc'
but it is there if you query it.

/proc wasn't available when win32-nat.c was first written but it is now
so it should be used rather than querying an internal cygwin function.
The comment that you are referring to and the code around it is
out-of-date.

> Please do note that I'm not writting that part of the patch.
> That Cygwin specific code is already there.  That is not to say
> that I won't change it if you tell me how to get the winpid from
> a cygwin pid.

I didn't catch that before but I'd prefer if you are going to be
refactoring the code that it use /proc.  This interface has been
available in cygwin for about five years so gdb is overdue for this.

>> We use NtQuerySystemInformation in cygwin to return the list of
>> processes.  See pinfo.cc.
>
> Not fully useable, I did try it.  The ProcessName field in the
> SYSTEM_PROCESSES structure doesn't have a path, only the filename ...
>it isn't found.

Ok.  After some additional digging and memory jogging, I see that Cygwin
actually uses GetModuleFileNameEx to retrieve this information after
iterating over the processes using NtQuerySystemInformation and
GetModuleFileNameEx comes from psapi.dll.  So it seems like psapi.dll is
still the best solution.

I don't think that finding people with an app that doesn't load because
there are wrong versions of the DLL being loaded is a great
advertisement for psapi.dll.  However, I don't recall anyone complaining
about problems in Cygwin with "ps -W" not working and gdb has been using
psapi for a while now so it probably isn't a big deal.

Rather than go back and forth on this, I've taken a stab at implementing
what I'm trying to get across here.  I don't think there is any reason to
now add Windows 9x support for features that have been missing for years
so I didn't do that.  I did issue an error when psapi.dll was not found
however.  I also added some of those slash-star/star-slash things.

I'll check this in unless there are serious objections.

cgf

2007-12-29  Christopher Faylor  <me+gdb@cgf.cx>

	* win32-nat.c (psapi_module_handle): Remove static.
	(get_module_name): Rename from psapi_get_dll_name.  Revamp slightly to
	return first module found if base_address is zero.  Don't initialize
	psapi function pointers here.  Convert to cygwin paths when
	appropriate.
	(win32_pid_to_exec_file): Use Cygwin's /proc interface to determine
	executable name.  Use get_module_name when that fails or when
	!__CYGWIN__.
	(_initialize_psapi): New function.  Initialize psapi stuff before it is
	needed or issue a warning if it is not found.  Move psapi_module_handle
	here.

Index: win32-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/win32-nat.c,v
retrieving revision 1.144
diff -d -u -r1.144 win32-nat.c
--- win32-nat.c	22 Dec 2007 22:09:56 -0000	1.144
+++ win32-nat.c	30 Dec 2007 02:57:11 -0000
@@ -457,88 +457,78 @@
 }
 
 static int psapi_loaded = 0;
-static HMODULE psapi_module_handle = NULL;
-static BOOL WINAPI (*psapi_EnumProcessModules) (HANDLE, HMODULE *, DWORD, LPDWORD) = NULL;
-static BOOL WINAPI (*psapi_GetModuleInformation) (HANDLE, HMODULE, LPMODULEINFO, DWORD) = NULL;
-static DWORD WINAPI (*psapi_GetModuleFileNameExA) (HANDLE, HMODULE, LPSTR, DWORD) = NULL;
+static BOOL WINAPI (*psapi_EnumProcessModules) (HANDLE, HMODULE *, DWORD,
+						LPDWORD);
+static BOOL WINAPI (*psapi_GetModuleInformation) (HANDLE, HMODULE, LPMODULEINFO,
+						  DWORD);
+static DWORD WINAPI (*psapi_GetModuleFileNameExA) (HANDLE, HMODULE, LPSTR,
+						   DWORD);
 
+/* Get the name of a given module at at given base address.  If base_address
+   is zero return the first loaded module (which is always the name of the
+   executable).  */
 static int
-psapi_get_dll_name (DWORD BaseAddress, char *dll_name_ret)
+get_module_name (DWORD base_address, char *dll_name_ret)
 {
   DWORD len;
   MODULEINFO mi;
   int i;
   HMODULE dh_buf[1];
-  HMODULE *DllHandle = dh_buf;
+  HMODULE *DllHandle = dh_buf;	/* Set to temporary storage for initial query */
   DWORD cbNeeded;
-  BOOL ok;
+#ifdef __CYGWIN__
+  char pathbuf[PATH_MAX + 1];	/* Temporary storage prior to converting to
+				   posix form */
+#else
+  char *pathbuf = dll_name_ret;	/* Just copy directly to passed-in arg */
+#endif
 
-  if (!psapi_loaded ||
-      psapi_EnumProcessModules == NULL ||
-      psapi_GetModuleInformation == NULL ||
-      psapi_GetModuleFileNameExA == NULL)
-    {
-      if (psapi_loaded)
-	goto failed;
-      psapi_loaded = 1;
-      psapi_module_handle = LoadLibrary ("psapi.dll");
-      if (!psapi_module_handle)
-	{
-	  /* printf_unfiltered ("error loading psapi.dll: %u", GetLastError ()); */
-	  goto failed;
-	}
-      psapi_EnumProcessModules = GetProcAddress (psapi_module_handle, "EnumProcessModules");
-      psapi_GetModuleInformation = GetProcAddress (psapi_module_handle, "GetModuleInformation");
-      psapi_GetModuleFileNameExA = (void *) GetProcAddress (psapi_module_handle,
-						    "GetModuleFileNameExA");
-      if (psapi_EnumProcessModules == NULL ||
-	  psapi_GetModuleInformation == NULL ||
-	  psapi_GetModuleFileNameExA == NULL)
-	goto failed;
-    }
+  /* If psapi_loaded < 0 either psapi.dll is not available or it does not contain
+     the needed functions. */
+  if (psapi_loaded <= 0)
+    goto failed;
 
   cbNeeded = 0;
-  ok = (*psapi_EnumProcessModules) (current_process_handle,
-				    DllHandle,
-				    sizeof (HMODULE),
-				    &cbNeeded);
-
-  if (!ok || !cbNeeded)
+  /* Find size of buffer needed to handle list of modules loaded in inferior */
+  if (!psapi_EnumProcessModules (current_process_handle, DllHandle,
+				 sizeof (HMODULE), &cbNeeded) || !cbNeeded)
     goto failed;
 
+  /* Allocate correct amount of space for module list */
   DllHandle = (HMODULE *) alloca (cbNeeded);
   if (!DllHandle)
     goto failed;
 
-  ok = (*psapi_EnumProcessModules) (current_process_handle,
-				    DllHandle,
-				    cbNeeded,
-				    &cbNeeded);
-  if (!ok)
+  /* Get the list of modules */
+  if (!psapi_EnumProcessModules (current_process_handle, DllHandle, cbNeeded,
+				 &cbNeeded))
     goto failed;
 
   for (i = 0; i < (int) (cbNeeded / sizeof (HMODULE)); i++)
     {
-      if (!(*psapi_GetModuleInformation) (current_process_handle,
-					  DllHandle[i],
-					  &mi,
-					  sizeof (mi)))
+      /* Get information on this module */
+      if (!psapi_GetModuleInformation (current_process_handle, DllHandle[i],
+				       &mi, sizeof (mi)))
 	error (_("Can't get module info"));
 
-      len = (*psapi_GetModuleFileNameExA) (current_process_handle,
-					   DllHandle[i],
-					   dll_name_ret,
-					   MAX_PATH);
-      if (len == 0)
-	error (_("Error getting dll name: %u."), (unsigned) GetLastError ());
-
-      if ((DWORD) (mi.lpBaseOfDll) == BaseAddress)
-	return 1;
+      if (!base_address || (DWORD) (mi.lpBaseOfDll) == base_address)
+	{
+	  /* Try to find the name of the given module */
+	  len = psapi_GetModuleFileNameExA (current_process_handle,
+					    DllHandle[i], pathbuf, MAX_PATH);
+	  if (len == 0)
+	    error (_("Error getting dll name: %u."), (unsigned) GetLastError ());
+#ifdef __CYGWIN__
+	  /* Cygwin prefers that the path be in /x/y/z format */
+	  cygwin_conv_to_full_posix_path (pathbuf, dll_name_ret);
+#endif
+	  return 1;	/* success */
+	}
     }
 
 failed:
   dll_name_ret[0] = '\0';
-  return 0;
+  return 0;		/* failure */
 }
 
 /* Encapsulate the information required in a call to
@@ -745,7 +735,7 @@
 
   dll_buf[0] = dll_buf[sizeof (dll_buf) - 1] = '\0';
 
-  if (!psapi_get_dll_name ((DWORD) (event->lpBaseOfDll), dll_buf))
+  if (!get_module_name ((DWORD) event->lpBaseOfDll, dll_buf))
     dll_buf[0] = dll_buf[sizeof (dll_buf) - 1] = '\0';
 
   dll_name = dll_buf;
@@ -1730,35 +1720,27 @@
 static char *
 win32_pid_to_exec_file (int pid)
 {
-  /* Try to find the process path using the Cygwin internal process list
-     pid isn't a valid pid, unfortunately.  Use current_event.dwProcessId
-     instead.  */
-
   static char path[MAX_PATH + 1];
-  char *path_ptr = NULL;
 
 #ifdef __CYGWIN__
-  /* TODO: Also find native Windows processes using CW_GETPINFO_FULL.  */
-  int cpid;
-  struct external_pinfo *pinfo;
-
-  cygwin_internal (CW_LOCK_PINFO, 1000);
-  for (cpid = 0;
-       (pinfo = (struct external_pinfo *)
-	cygwin_internal (CW_GETPINFO, cpid | CW_NEXTPID));
-       cpid = pinfo->pid)
+  /* Try to find exe name as symlink target of /proc/<pid>/exe */
+  int nchars;
+  char procexe[sizeof ("/proc/4294967295/exe")];
+  sprintf (procexe, "/proc/%lu/exe", current_event.dwProcessId);
+  nchars = readlink (procexe, path, sizeof(path));
+  if (nchars > 0 && nchars < sizeof (path))
     {
-      if (pinfo->dwProcessId == current_event.dwProcessId) /* Got it */
-       {
-	 cygwin_conv_to_full_posix_path (pinfo->progname, path);
-	 path_ptr = path;
-	 break;
-       }
+      path[nchars] = '\0';	/* Got it */
+      return path;
     }
-  cygwin_internal (CW_UNLOCK_PINFO);
 #endif
 
-  return path_ptr;
+  /* If we get here then either Cygwin is hosed, this isn't a Cygwin version
+     of gdb, or we're trying to debug a non-Cygwin windows executable. */
+  if (!get_module_name (0, path))
+    path[0] = '\0';
+
+  return path;
 }
 
 /* Print status information about what we're accessing.  */
@@ -2282,3 +2264,34 @@
 	}
     }
 }
+
+void
+_initialize_psapi (void)
+{
+  /* Load optional functions used for retrieving filename information
+     associated with the currently debugged process or its dlls. */
+  if (!psapi_loaded)
+    {
+      HMODULE psapi_module_handle;
+
+      psapi_loaded = -1;
+
+      psapi_module_handle = LoadLibrary ("psapi.dll");
+      if (psapi_module_handle)
+	{
+	  psapi_EnumProcessModules = (void *) GetProcAddress (psapi_module_handle, "EnumProcessModules");
+	  psapi_GetModuleInformation = (void *) GetProcAddress (psapi_module_handle, "GetModuleInformation");
+	  psapi_GetModuleFileNameExA = (void *) GetProcAddress (psapi_module_handle, "GetModuleFileNameExA");
+
+	  if (psapi_EnumProcessModules != NULL
+	      && psapi_GetModuleInformation != NULL
+	      && psapi_GetModuleFileNameExA != NULL)
+	    psapi_loaded = 1;
+	}
+    }
+
+  /* This will probably fail on Windows 9x/Me.  Let the user know that we're
+     missing some functionality. */
+  if (psapi_loaded < 0)
+    warning(_("working version of psapi.dll not found.  DLL and executable filenames may not be available."));
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]