Cygwin: pinfo: use stpcpy where appropriate
[newlib-cygwin.git] / winsup / cygwin / fhandler / process.cc
blob1e5e83b4ab4359d7c7f118497a93bdce02eb7928
1 /* fhandler_process.cc: fhandler for /proc/<pid> virtual filesystem
3 This file is part of Cygwin.
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
7 details. */
9 #include "winsup.h"
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <sys/cygwin.h>
13 #include "cygerrno.h"
14 #include "security.h"
15 #include "path.h"
16 #include "fhandler.h"
17 #include "fhandler_virtual.h"
18 #include "pinfo.h"
19 #include "shared_info.h"
20 #include "dtable.h"
21 #include "cygheap.h"
22 #include "ntdll.h"
23 #include "cygtls.h"
24 #include "mount.h"
25 #include "tls_pbuf.h"
26 #include <sys/sysmacros.h>
27 #include <sys/param.h>
28 #include <ctype.h>
30 #define _LIBC
31 #include <dirent.h>
33 static off_t format_process_maps (void *, char *&);
34 static off_t format_process_stat (void *, char *&);
35 static off_t format_process_status (void *, char *&);
36 static off_t format_process_statm (void *, char *&);
37 static off_t format_process_winexename (void *, char *&);
38 static off_t format_process_winpid (void *, char *&);
39 static off_t format_process_exename (void *, char *&);
40 static off_t format_process_root (void *, char *&);
41 static off_t format_process_cwd (void *, char *&);
42 static off_t format_process_cmdline (void *, char *&);
43 static off_t format_process_ppid (void *, char *&);
44 static off_t format_process_uid (void *, char *&);
45 static off_t format_process_pgid (void *, char *&);
46 static off_t format_process_sid (void *, char *&);
47 static off_t format_process_gid (void *, char *&);
48 static off_t format_process_ctty (void *, char *&);
49 static off_t format_process_fd (void *, char *&);
50 static off_t format_process_mounts (void *, char *&);
51 static off_t format_process_mountinfo (void *, char *&);
52 static off_t format_process_environ (void *, char *&);
54 static const virt_tab_t process_tab[] =
56 { _VN ("."), FH_PROCESS, virt_directory, NULL },
57 { _VN (".."), FH_PROCESS, virt_directory, NULL },
58 { _VN ("cmdline"), FH_PROCESS, virt_file, format_process_cmdline },
59 { _VN ("ctty"), FH_PROCESS, virt_file, format_process_ctty },
60 { _VN ("cwd"), FH_PROCESS, virt_symlink, format_process_cwd },
61 { _VN ("environ"), FH_PROCESS, virt_file, format_process_environ },
62 { _VN ("exe"), FH_PROCESS, virt_symlink, format_process_exename },
63 { _VN ("exename"), FH_PROCESS, virt_file, format_process_exename },
64 { _VN ("fd"), FH_PROCESSFD, virt_directory, format_process_fd },
65 { _VN ("gid"), FH_PROCESS, virt_file, format_process_gid },
66 { _VN ("maps"), FH_PROCESS, virt_file, format_process_maps },
67 { _VN ("mountinfo"), FH_PROCESS, virt_file, format_process_mountinfo },
68 { _VN ("mounts"), FH_PROCESS, virt_file, format_process_mounts },
69 { _VN ("pgid"), FH_PROCESS, virt_file, format_process_pgid },
70 { _VN ("ppid"), FH_PROCESS, virt_file, format_process_ppid },
71 { _VN ("root"), FH_PROCESS, virt_symlink, format_process_root },
72 { _VN ("sid"), FH_PROCESS, virt_file, format_process_sid },
73 { _VN ("stat"), FH_PROCESS, virt_file, format_process_stat },
74 { _VN ("statm"), FH_PROCESS, virt_file, format_process_statm },
75 { _VN ("status"), FH_PROCESS, virt_file, format_process_status },
76 { _VN ("uid"), FH_PROCESS, virt_file, format_process_uid },
77 { _VN ("winexename"), FH_PROCESS, virt_file, format_process_winexename },
78 { _VN ("winpid"), FH_PROCESS, virt_file, format_process_winpid },
79 { NULL, 0, FH_NADA, virt_none, NULL }
82 static const int PROCESS_LINK_COUNT =
83 (sizeof (process_tab) / sizeof (virt_tab_t)) - 1;
84 int get_process_state (DWORD dwProcessId);
85 static bool get_mem_values (DWORD dwProcessId, size_t &vmsize, size_t &vmrss,
86 size_t &vmtext, size_t &vmdata, size_t &vmlib,
87 size_t &vmshare);
89 virtual_ftype_t
90 fhandler_process::exists ()
92 const char *path = get_name ();
93 debug_printf ("exists (%s)", path);
94 path += proc_len + 1;
95 while (*path != 0 && !isdirsep (*path))
96 path++;
97 if (*path == 0)
98 return virt_rootdir;
100 virt_tab_t *entry = virt_tab_search (path + 1, true, process_tab,
101 PROCESS_LINK_COUNT);
102 if (entry)
104 if (!path[entry->name_len + 1])
106 fileid = entry - process_tab;
107 return entry->type;
109 if (entry->type == virt_directory) /* fd subdir only */
111 fileid = entry - process_tab;
112 if (fill_filebuf ())
113 return fd_type;
114 /* Check for nameless device entries. */
115 path = strrchr (path, '/');
116 if (path && *++path)
118 if (!strncmp (path, "pipe:[", 6))
119 return virt_pipe;
120 else if (!strncmp (path, "socket:[", 8))
121 return virt_socket;
125 return virt_none;
128 fhandler_process::fhandler_process ():
129 fhandler_proc ()
134 fhandler_process::fstat (struct stat *buf)
136 const char *path = get_name ();
137 int file_type = exists ();
138 fhandler_base::fstat (buf);
139 path += proc_len + 1;
140 pid = atoi (path);
142 pinfo p (pid);
143 /* If p->pid != pid, then pid is actually the Windows PID for an execed
144 Cygwin process, and the pinfo entry is the additional entry created
145 at exec time. We don't want to enable the user to access a process
146 entry by using the Win32 PID, though. */
147 if (!p || p->pid != pid)
149 set_errno (ENOENT);
150 return -1;
153 buf->st_mode &= ~_IFMT & NO_W;
155 switch (file_type)
157 case virt_none:
158 set_errno (ENOENT);
159 return -1;
160 case virt_directory:
161 case virt_rootdir:
162 buf->st_ctime = buf->st_mtime = buf->st_birthtime = p->start_time;
163 buf->st_ctim.tv_nsec = buf->st_mtim.tv_nsec
164 = buf->st_birthtim.tv_nsec = 0;
165 time_as_timestruc_t (&buf->st_atim);
166 buf->st_uid = p->uid;
167 buf->st_gid = p->gid;
168 buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
169 if (file_type == virt_directory)
170 buf->st_nlink = 2;
171 else
172 buf->st_nlink = 3;
173 return 0;
174 case virt_symlink:
175 case virt_fdsymlink:
176 buf->st_uid = p->uid;
177 buf->st_gid = p->gid;
178 buf->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
179 return 0;
180 case virt_pipe:
181 buf->st_uid = p->uid;
182 buf->st_gid = p->gid;
183 buf->st_mode = S_IFIFO | S_IRUSR | S_IWUSR;
184 return 0;
185 case virt_socket:
186 buf->st_uid = p->uid;
187 buf->st_gid = p->gid;
188 buf->st_mode = S_IFSOCK | S_IRUSR | S_IWUSR;
189 return 0;
190 case virt_file:
191 default:
192 buf->st_uid = p->uid;
193 buf->st_gid = p->gid;
194 buf->st_mode |= S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
195 return 0;
199 DIR *
200 fhandler_process::opendir (int fd)
202 DIR *dir = fhandler_virtual::opendir (fd);
203 if (dir && process_tab[fileid].fhandler == FH_PROCESSFD)
204 fill_filebuf ();
205 return dir;
209 fhandler_process::closedir (DIR *dir)
211 return fhandler_virtual::closedir (dir);
215 fhandler_process::readdir (DIR *dir, dirent *de)
217 int res = ENMFILE;
218 if (process_tab[fileid].fhandler == FH_PROCESSFD)
220 if ((size_t) dir->__d_position >= 2 + filesize / sizeof (int))
221 goto out;
223 else if (dir->__d_position >= PROCESS_LINK_COUNT)
224 goto out;
225 if (process_tab[fileid].fhandler == FH_PROCESSFD && dir->__d_position > 1)
227 int *p = (int *) filebuf;
228 __small_sprintf (de->d_name, "%d", p[dir->__d_position++ - 2]);
229 de->d_type = DT_LNK;
231 else
233 strcpy (de->d_name, process_tab[dir->__d_position].name);
234 de->d_type = virt_ftype_to_dtype (process_tab[dir->__d_position].type);
235 dir->__d_position++;
237 dir->__flags |= dirent_saw_dot | dirent_saw_dot_dot;
238 res = 0;
239 out:
240 syscall_printf ("%d = readdir(%p, %p) (%s)", res, dir, de, de->d_name);
241 return res;
245 fhandler_process::open (int flags, mode_t mode)
247 int res = fhandler_virtual::open (flags, mode);
248 if (!res)
249 goto out;
251 nohandle (true);
253 const char *path;
254 path = get_name () + proc_len + 1;
255 pid = atoi (path);
256 while (*path != 0 && !isdirsep (*path))
257 path++;
259 if (*path == 0)
261 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
263 set_errno (EEXIST);
264 res = 0;
265 goto out;
267 else if (flags & O_WRONLY)
269 set_errno (EISDIR);
270 res = 0;
271 goto out;
273 else
275 diropen = true;
276 goto success;
280 virt_tab_t *entry;
281 entry = virt_tab_search (path + 1, true, process_tab, PROCESS_LINK_COUNT);
282 if (!entry)
284 set_errno ((flags & O_CREAT) ? EROFS : ENOENT);
285 res = 0;
286 goto out;
288 if (entry->fhandler == FH_PROCESSFD)
290 diropen = true;
291 goto success;
293 if (flags & O_WRONLY)
295 set_errno (EROFS);
296 res = 0;
297 goto out;
300 fileid = entry - process_tab;
301 if (!fill_filebuf ())
303 res = 0;
304 goto out;
307 if (flags & O_APPEND)
308 position = filesize;
309 else
310 position = 0;
312 success:
313 res = 1;
314 set_flags ((flags & ~O_TEXT) | O_BINARY);
315 set_open_status ();
316 out:
317 syscall_printf ("%d = fhandler_proc::open(%y, 0%o)", res, flags, mode);
318 return res;
321 struct process_fd_t {
322 const char *path;
323 _pinfo *p;
324 virtual_ftype_t *fd_type;
327 bool
328 fhandler_process::fill_filebuf ()
330 const char *path;
331 path = get_name () + proc_len + 1;
332 if (!pid)
333 pid = atoi (path);
335 pinfo p (pid);
336 /* If p->pid != pid, then pid is actually the Windows PID for an execed
337 Cygwin process, and the pinfo entry is the additional entry created
338 at exec time. We don't want to enable the user to access a process
339 entry by using the Win32 PID, though. */
340 if (!p || p->pid != pid)
342 set_errno (ENOENT);
343 return false;
346 if (process_tab[fileid].format_func)
348 if (process_tab[fileid].fhandler == FH_PROCESSFD)
350 process_fd_t fd = { path, p , &fd_type };
351 filesize = process_tab[fileid].format_func (&fd, filebuf);
353 else
354 filesize = process_tab[fileid].format_func (p, filebuf);
355 return filesize < 0 ? false : true;
357 return false;
360 static off_t
361 format_process_fd (void *data, char *&destbuf)
363 _pinfo *p = ((process_fd_t *) data)->p;
364 const char *path = ((process_fd_t *) data)->path;
365 size_t fs = 0;
366 /* path looks like "$PID/fd", "$PID/fd/", "$PID/fd/[0-9]*". In the latter
367 case a trailing slash and more followup chars are allowed, provided the
368 descriptor symlink points to a directory. */
369 char *fdp = strchr (path, '/') + 3;
370 /* The "fd" directory itself? */
371 if (fdp[0] =='\0' || (fdp[0] == '/' && fdp[1] == '\0'))
373 if (destbuf)
374 cfree (destbuf);
375 destbuf = p ? p->fds (fs) : NULL;
376 *((process_fd_t *) data)->fd_type = virt_symlink;
378 else
380 char *e;
381 int fd;
383 if (destbuf)
384 cfree (destbuf);
385 fd = strtol (++fdp, &e, 10);
386 if (fd < 0 || e == fdp || (*e != '/' && *e != '\0'))
388 set_errno (ENOENT);
389 return -1;
391 destbuf = p ? p->fd (fd, fs) : NULL;
392 if (!destbuf || !*destbuf)
394 set_errno (ENOENT);
395 return -1;
397 if (*e == '\0')
398 *((process_fd_t *) data)->fd_type = virt_fdsymlink;
399 else /* trailing path */
401 /* Does the descriptor link point to a directory? */
402 bool dir;
403 if (*destbuf != '/') /* e.g., "pipe:[" or "socket:[" */
404 dir = false;
405 else
407 path_conv pc (destbuf);
408 dir = pc.isdir ();
410 if (!dir)
412 set_errno (ENOTDIR);
413 cfree (destbuf);
414 return -1;
416 char *newbuf = (char *) cmalloc_abort (HEAP_STR, strlen (destbuf)
417 + strlen (e) + 1);
418 stpcpy (stpcpy (newbuf, destbuf), e);
419 cfree (destbuf);
420 destbuf = newbuf;
421 *((process_fd_t *) data)->fd_type = virt_fsdir;
424 return fs;
427 static off_t
428 format_process_ppid (void *data, char *&destbuf)
430 _pinfo *p = (_pinfo *) data;
431 destbuf = (char *) crealloc_abort (destbuf, 40);
432 return __small_sprintf (destbuf, "%d\n", p->ppid);
435 static off_t
436 format_process_uid (void *data, char *&destbuf)
438 _pinfo *p = (_pinfo *) data;
439 destbuf = (char *) crealloc_abort (destbuf, 40);
440 return __small_sprintf (destbuf, "%d\n", p->uid);
443 static off_t
444 format_process_pgid (void *data, char *&destbuf)
446 _pinfo *p = (_pinfo *) data;
447 destbuf = (char *) crealloc_abort (destbuf, 40);
448 return __small_sprintf (destbuf, "%d\n", p->pgid);
451 static off_t
452 format_process_sid (void *data, char *&destbuf)
454 _pinfo *p = (_pinfo *) data;
455 destbuf = (char *) crealloc_abort (destbuf, 40);
456 return __small_sprintf (destbuf, "%d\n", p->sid);
459 static off_t
460 format_process_gid (void *data, char *&destbuf)
462 _pinfo *p = (_pinfo *) data;
463 destbuf = (char *) crealloc_abort (destbuf, 40);
464 return __small_sprintf (destbuf, "%d\n", p->gid);
467 static off_t
468 format_process_ctty (void *data, char *&destbuf)
470 _pinfo *p = (_pinfo *) data;
471 if (!CTTY_IS_VALID (p->ctty))
473 destbuf = (char *) crealloc_abort (destbuf, 2);
474 return __small_sprintf (destbuf, "\n");
476 device d;
477 d.parse (p->ctty);
478 destbuf = (char *) crealloc_abort (destbuf, strlen (d.name ()) + 2);
479 return __small_sprintf (destbuf, "%s\n", d.name ());
482 static off_t
483 format_process_root (void *data, char *&destbuf)
485 _pinfo *p = (_pinfo *) data;
486 size_t fs;
488 if (destbuf)
490 cfree (destbuf);
491 destbuf = NULL;
493 destbuf = p ? p->root (fs) : NULL;
494 if (!destbuf || !*destbuf)
496 destbuf = cstrdup ("<defunct>");
497 fs = strlen (destbuf) + 1;
499 return fs;
502 static off_t
503 format_process_cwd (void *data, char *&destbuf)
505 _pinfo *p = (_pinfo *) data;
506 size_t fs;
508 if (destbuf)
510 cfree (destbuf);
511 destbuf = NULL;
513 destbuf = p ? p->cwd (fs) : NULL;
514 if (!destbuf || !*destbuf)
516 destbuf = cstrdup ("<defunct>");
517 fs = strlen (destbuf) + 1;
519 return fs;
522 static off_t
523 format_process_cmdline (void *data, char *&destbuf)
525 _pinfo *p = (_pinfo *) data;
526 size_t fs;
528 if (destbuf)
530 cfree (destbuf);
531 destbuf = NULL;
533 destbuf = p ? p->cmdline (fs) : NULL;
534 if (destbuf && *destbuf)
535 return fs;
536 return format_process_exename (data, destbuf);
539 static off_t
540 format_process_exename (void *data, char *&destbuf)
542 _pinfo *p = (_pinfo *) data;
543 int len;
544 tmp_pathbuf tp;
546 char *buf = tp.c_get ();
547 if (p->process_state & PID_EXITED)
548 stpcpy (buf, "<defunct>");
549 else
551 mount_table->conv_to_posix_path (p->progname, buf, CCP_RELATIVE);
552 len = strlen (buf);
553 if (len > 4)
555 char *s = buf + len - 4;
556 if (ascii_strcasematch (s, ".exe"))
557 *s = 0;
560 destbuf = (char *) crealloc_abort (destbuf, (len = strlen (buf)) + 1);
561 stpcpy (destbuf, buf);
562 return len;
565 static off_t
566 format_process_winpid (void *data, char *&destbuf)
568 _pinfo *p = (_pinfo *) data;
569 destbuf = (char *) crealloc_abort (destbuf, 20);
570 return __small_sprintf (destbuf, "%d\n", p->dwProcessId);
573 static off_t
574 format_process_winexename (void *data, char *&destbuf)
576 _pinfo *p = (_pinfo *) data;
577 size_t len = sys_wcstombs (NULL, 0, p->progname);
578 destbuf = (char *) crealloc_abort (destbuf, len + 1);
579 /* With trailing \0 for backward compat reasons. */
580 sys_wcstombs (destbuf, len + 1, p->progname);
581 return len;
584 static off_t
585 format_process_environ (void *data, char *&destbuf)
587 _pinfo *p = (_pinfo *) data;
588 size_t fs;
590 if (destbuf)
592 cfree (destbuf);
593 destbuf = NULL;
595 destbuf = p ? p->environ (fs) : NULL;
596 if (!destbuf || !*destbuf)
598 destbuf = cstrdup ("<defunct>");
599 fs = strlen (destbuf) + 1;
601 return fs;
604 struct heap_info
606 struct heap
608 heap *next;
609 unsigned heap_id;
610 char *base;
611 char *end;
612 unsigned long flags;
614 heap *heap_vm_chunks;
616 heap_info (DWORD pid)
617 : heap_vm_chunks (NULL)
619 PDEBUG_BUFFER buf;
620 NTSTATUS status;
621 PDEBUG_HEAP_ARRAY harray;
623 buf = RtlCreateQueryDebugBuffer (16 * 65536, FALSE);
624 if (!buf)
625 return;
626 status = RtlQueryProcessDebugInformation (pid, PDI_HEAPS | PDI_HEAP_BLOCKS,
627 buf);
628 if (NT_SUCCESS (status)
629 && (harray = (PDEBUG_HEAP_ARRAY) buf->HeapInformation) != NULL)
630 for (ULONG hcnt = 0; hcnt < harray->Count; ++hcnt)
632 PDEBUG_HEAP_BLOCK barray = (PDEBUG_HEAP_BLOCK)
633 harray->Heaps[hcnt].Blocks;
634 if (!barray)
635 continue;
636 for (ULONG bcnt = 0; bcnt < harray->Heaps[hcnt].BlockCount; ++bcnt)
637 if (barray[bcnt].Flags & 2)
639 heap *h = (heap *) malloc (sizeof (heap));
640 if (h)
642 *h = (heap) { heap_vm_chunks,
643 hcnt, (char *) barray[bcnt].Address,
644 (char *) barray[bcnt].Address
645 + barray[bcnt].Size,
646 harray->Heaps[hcnt].Flags };
647 heap_vm_chunks = h;
651 RtlDestroyQueryDebugBuffer (buf);
654 char *fill_if_match (char *base, ULONG type, char *dest)
656 for (heap *h = heap_vm_chunks; h; h = h->next)
657 if (base >= h->base && base < h->end)
659 char *p = dest + __small_sprintf (dest, "[win heap %ld", h->heap_id);
660 if (!(h->flags & HEAP_FLAG_NONDEFAULT))
661 p = stpcpy (p, " default");
662 if ((h->flags & HEAP_FLAG_SHAREABLE) && (type & MEM_MAPPED))
663 p = stpcpy (p, " shared");
664 if (h->flags & HEAP_FLAG_EXECUTABLE)
665 p = stpcpy (p, " exec");
666 if (h->flags & HEAP_FLAG_GROWABLE)
667 p = stpcpy (p, " grow");
668 if (h->flags & HEAP_FLAG_NOSERIALIZE)
669 p = stpcpy (p, " noserial");
670 if (h->flags == HEAP_FLAG_DEBUGGED)
671 p = stpcpy (p, " debug");
672 stpcpy (p, "]");
673 return dest;
675 return NULL;
678 ~heap_info ()
680 heap *n = 0;
681 for (heap *m = heap_vm_chunks; m; m = n)
683 n = m->next;
684 free (m);
689 struct thread_info
691 struct region
693 region *next;
694 ULONG thread_id;
695 char *start;
696 char *end;
697 bool teb;
699 region *regions;
701 thread_info (DWORD pid, HANDLE process)
702 : regions (NULL)
704 NTSTATUS status;
705 PVOID buf = NULL;
706 ULONG size = 50 * (sizeof (SYSTEM_PROCESS_INFORMATION)
707 + 16 * sizeof (SYSTEM_THREADS));
708 PSYSTEM_PROCESS_INFORMATION proc;
709 PSYSTEM_THREADS thread;
713 buf = realloc (buf, size);
714 status = NtQuerySystemInformation (SystemProcessInformation,
715 buf, size, NULL);
716 size <<= 1;
718 while (status == STATUS_INFO_LENGTH_MISMATCH);
719 if (!NT_SUCCESS (status))
721 if (buf)
722 free (buf);
723 debug_printf ("NtQuerySystemInformation, %y", status);
724 return;
726 proc = (PSYSTEM_PROCESS_INFORMATION) buf;
727 while (true)
729 if ((DWORD) (uintptr_t) proc->UniqueProcessId == pid)
730 break;
731 if (!proc->NextEntryOffset)
733 free (buf);
734 return;
736 proc = (PSYSTEM_PROCESS_INFORMATION) ((PBYTE) proc
737 + proc->NextEntryOffset);
739 thread = proc->Threads;
740 for (ULONG i = 0; i < proc->NumberOfThreads; ++i)
742 THREAD_BASIC_INFORMATION tbi;
743 TEB teb;
744 HANDLE thread_h;
746 thread_h = OpenThread (THREAD_QUERY_LIMITED_INFORMATION, FALSE,
747 (ULONG) (ULONG_PTR) thread[i].ClientId.UniqueThread);
748 if (!thread_h)
749 continue;
750 status = NtQueryInformationThread (thread_h, ThreadBasicInformation,
751 &tbi, sizeof tbi, NULL);
752 CloseHandle (thread_h);
753 if (!NT_SUCCESS (status))
754 continue;
755 region *r = (region *) malloc (sizeof (region));
756 if (r)
758 *r = (region) { regions,
759 (ULONG) (ULONG_PTR) thread[i].ClientId.UniqueThread,
760 (char *) tbi.TebBaseAddress,
761 (char *) tbi.TebBaseAddress
762 + 2 * wincap.page_size (),
763 true };
764 regions = r;
766 if (!ReadProcessMemory (process, (PVOID) tbi.TebBaseAddress,
767 &teb, sizeof teb, NULL))
768 continue;
769 r = (region *) malloc (sizeof (region));
770 if (r)
772 *r = (region) { regions,
773 (ULONG) (ULONG_PTR) thread[i].ClientId.UniqueThread,
774 (char *) (teb.DeallocationStack
775 ?: teb.Tib.StackLimit),
776 (char *) teb.Tib.StackBase,
777 false };
778 regions = r;
781 free (buf);
784 char *fill_if_match (char *base, ULONG type, char *dest)
786 for (region *r = regions; r; r = r->next)
787 if (base >= r->start && base < r->end)
789 char *p = dest + __small_sprintf (dest, "[%s (tid %ld)",
790 r->teb ? "teb" : "stack",
791 r->thread_id);
792 if (type & MEM_MAPPED)
793 p = stpcpy (p, " shared");
794 stpcpy (p, "]");
795 return dest;
797 return NULL;
799 /* Helper to look for TEBs inside single allocated region since W10 1511. */
800 char *fill_if_match (char *start, char *dest)
802 for (region *r = regions; r; r = r->next)
803 if (r->teb && start == r->start)
805 __small_sprintf (dest, "[teb (tid %ld)]", r->thread_id);
806 return r->end;
808 return NULL;
811 ~thread_info ()
813 region *n = 0;
814 for (region *m = regions; m; m = n)
816 n = m->next;
817 free (m);
822 static off_t
823 format_process_maps (void *data, char *&destbuf)
825 _pinfo *p = (_pinfo *) data;
826 HANDLE proc = OpenProcess (PROCESS_QUERY_INFORMATION
827 | PROCESS_VM_READ, FALSE, p->dwProcessId);
828 if (!proc)
830 if (!(p->process_state & PID_EXITED))
832 DWORD error = GetLastError ();
833 __seterrno_from_win_error (error);
834 debug_printf ("OpenProcess: ret %u; pid: %d", error, p->dwProcessId);
835 return -1;
837 else
839 /* Else it's a zombie process; just return an empty string */
840 destbuf = (char *) crealloc_abort (destbuf, 1);
841 destbuf[0] = '\0';
842 return 0;
846 NTSTATUS status;
847 PROCESS_BASIC_INFORMATION pbi;
848 PPEB peb = NULL;
850 memset (&pbi, 0, sizeof (pbi));
851 status = NtQueryInformationProcess (proc, ProcessBasicInformation,
852 &pbi, sizeof pbi, NULL);
853 if (NT_SUCCESS (status))
854 peb = pbi.PebBaseAddress;
855 /* myself is in the same spot in every process, so is the pointer to the
856 procinfo. But make sure the destructor doesn't try to release procinfo! */
857 pinfo proc_pinfo;
858 if (ReadProcessMemory (proc, &myself, &proc_pinfo, sizeof proc_pinfo, NULL))
859 proc_pinfo.preserve ();
860 /* The heap info on the cygheap is also in the same spot in each process
861 because the cygheap is located at the same address. */
862 user_heap_info user_heap;
863 shared_region_info region_info;
864 ReadProcessMemory (proc, &cygheap->user_heap, &user_heap,
865 sizeof user_heap, NULL);
866 ReadProcessMemory (proc, &cygheap->shared_regions, &region_info,
867 sizeof region_info, NULL);
869 off_t len = 0;
871 union access
873 char flags[8];
874 off_t word;
875 } a;
877 struct region {
878 access a;
879 char *abase;
880 char *rbase;
881 char *rend;
882 DWORD state;
883 } cur = {{{'\0'}}, (char *)1, 0, 0};
885 MEMORY_BASIC_INFORMATION mb;
886 dos_drive_mappings drive_maps;
887 heap_info heaps (p->dwProcessId);
888 thread_info threads (p->dwProcessId, proc);
889 struct stat st;
890 long last_pass = 0;
892 tmp_pathbuf tp;
893 PMEMORY_SECTION_NAME msi = (PMEMORY_SECTION_NAME) tp.w_get ();
894 char *posix_modname = tp.c_get ();
895 size_t maxsize = 0;
896 char *peb_teb_abase = NULL;
898 if (destbuf)
900 cfree (destbuf);
901 destbuf = NULL;
904 /* Iterate over each VM region in the address space, coalescing
905 memory regions with the same permissions. Once we run out, do one
906 last_pass to trigger output of the last accumulated region. */
907 for (char *i = 0;
908 VirtualQueryEx (proc, i, &mb, sizeof(mb)) || (1 == ++last_pass);
909 i = cur.rend)
911 if (last_pass)
912 posix_modname[0] = '\0';
913 if (mb.State == MEM_FREE)
914 a.word = 0;
915 else if (mb.State == MEM_RESERVE)
917 char *p = stpcpy (a.flags, "===");
918 stpcpy (p, (mb.Type & MEM_MAPPED) ? "s" : "p");
920 else
922 static DWORD const RO = (PAGE_EXECUTE_READ | PAGE_READONLY);
923 static DWORD const RW = (PAGE_EXECUTE_READWRITE | PAGE_READWRITE
924 | PAGE_EXECUTE_WRITECOPY | PAGE_WRITECOPY);
925 static DWORD const X = (PAGE_EXECUTE | PAGE_EXECUTE_READ
926 | PAGE_EXECUTE_READWRITE
927 | PAGE_EXECUTE_WRITECOPY);
928 static DWORD const WC = (PAGE_EXECUTE_WRITECOPY | PAGE_WRITECOPY);
929 DWORD p = mb.Protect;
930 a = (access) {{
931 (p & (RO | RW)) ? 'r' : '-',
932 (p & (RW)) ? 'w' : '-',
933 (p & (X)) ? 'x' : '-',
934 (mb.Type & MEM_MAPPED) && !(p & (WC)) ? 's'
935 : (p & PAGE_GUARD) ? 'g' : 'p',
936 '\0', // zero-fill the remaining bytes
940 region next = { a,
941 (char *) mb.AllocationBase,
942 (char *) mb.BaseAddress,
943 (char *) mb.BaseAddress+mb.RegionSize,
944 mb.State
947 /* Windows permissions are more fine-grained than the unix rwxp,
948 so we reduce clutter by manually coalescing regions sharing
949 the same allocation base and effective permissions. */
950 bool newbase = (next.abase != cur.abase);
951 if (!last_pass && !newbase && next.a.word == cur.a.word)
952 cur.rend = next.rend; /* merge with previous */
953 else
955 char *peb_teb_end = NULL;
956 peb_teb_rinse_repeat:
957 /* Starting with W10 1511, PEB and TEBs don't get allocated
958 separately. Rather they are created in a single region. Examine
959 the region starting at the PEB address page-wise. */
960 if (wincap.has_new_pebteb_region ())
962 if (peb_teb_abase && !peb_teb_end && cur.abase == peb_teb_abase)
964 posix_modname[0] = '\0';
965 peb_teb_end = cur.rend;
966 if (cur.state == MEM_COMMIT)
967 cur.rend = cur.rbase + wincap.page_size ();
969 if (cur.state == MEM_COMMIT)
971 if (!peb_teb_abase && cur.rbase == (char *) peb)
973 peb_teb_abase = cur.abase;
974 peb_teb_end = cur.rend;
975 cur.rend = cur.rbase + wincap.page_size ();
976 strcpy (posix_modname, "[peb]");
978 else if (peb_teb_end)
980 char *end;
981 posix_modname[0] = '\0';
982 end = threads.fill_if_match (cur.rbase, posix_modname);
984 if (end)
985 cur.rend = end;
986 else
988 char *base = cur.rbase;
991 base += wincap.page_size ();
993 while (!threads.fill_if_match (base, posix_modname)
994 && base < peb_teb_end);
995 if (posix_modname[0])
997 posix_modname[0] = '\0';
998 cur.rend = base;
1000 else
1001 cur.rend = peb_teb_end;
1006 /* output the current region if it's "interesting". */
1007 if (cur.a.word)
1009 size_t newlen = strlen (posix_modname) + 62;
1010 if (len + newlen >= maxsize)
1011 destbuf = (char *)
1012 crealloc_abort (destbuf,
1013 maxsize += roundup2 (newlen, 2048UL));
1014 int written = __small_sprintf (destbuf + len,
1015 "%08lx-%08lx %s %08lx %04x:%04x %U ",
1016 cur.rbase, cur.rend, cur.a.flags,
1017 cur.rbase - cur.abase,
1018 st.st_dev >> 16,
1019 st.st_dev & 0xffff,
1020 st.st_ino);
1021 while (written < 62)
1022 destbuf[len + written++] = ' ';
1023 len += written;
1024 len += __small_sprintf (destbuf + len, "%s\n", posix_modname);
1027 if (peb_teb_end && cur.state == MEM_COMMIT)
1029 cur.rbase = cur.rend;
1030 cur.rend += wincap.page_size ();
1031 if (cur.rbase < peb_teb_end)
1032 goto peb_teb_rinse_repeat;
1034 /* start of a new region (but possibly still the same allocation). */
1035 cur = next;
1036 /* if a new allocation, figure out what kind it is. */
1037 if (newbase && !last_pass && cur.state != MEM_FREE)
1039 /* If the return length pointer is missing, NtQueryVirtualMemory
1040 returns with STATUS_ACCESS_VIOLATION on Windows 2000. */
1041 SIZE_T ret_len = 0;
1043 st.st_dev = 0;
1044 st.st_ino = 0;
1045 if ((mb.Type & (MEM_MAPPED | MEM_IMAGE))
1046 && NT_SUCCESS (status = NtQueryVirtualMemory (proc, cur.abase,
1047 MemorySectionName,
1048 msi, 65536, &ret_len)))
1050 PWCHAR dosname =
1051 drive_maps.fixup_if_match (msi->SectionFileName.Buffer);
1052 if (mount_table->conv_to_posix_path (dosname,
1053 posix_modname, 0))
1054 sys_wcstombs (posix_modname, NT_MAX_PATH, dosname);
1055 stat (posix_modname, &st);
1057 else if (!threads.fill_if_match (cur.abase, mb.Type,
1058 posix_modname)
1059 && !heaps.fill_if_match (cur.abase, mb.Type,
1060 posix_modname))
1062 if (cur.abase == (char *) peb)
1063 strcpy (posix_modname, "[peb]");
1064 else if (cur.abase == (char *) &SharedUserData)
1065 strcpy (posix_modname, "[shared-user-data]");
1066 else if (cur.abase == region_info.cygwin_shared_addr)
1067 strcpy (posix_modname, "[cygwin-shared]");
1068 else if (cur.abase == region_info.user_shared_addr)
1069 strcpy (posix_modname, "[cygwin-user-shared]");
1070 else if (cur.abase == region_info.myself_shared_addr)
1071 strcpy (posix_modname, "[procinfo]");
1072 else if (cur.abase == region_info.console_shared_addr)
1073 strcpy (posix_modname, "[cygwin-shared-console]");
1074 else if (cur.abase == (char *) cygheap)
1075 strcpy (posix_modname, "[cygheap]");
1076 else if (cur.abase == user_heap.base)
1077 strcpy (posix_modname, "[heap]");
1078 else
1079 posix_modname[0] = 0;
1084 CloseHandle (proc);
1085 return len;
1088 static off_t
1089 format_process_stat (void *data, char *&destbuf)
1091 _pinfo *p = (_pinfo *) data;
1092 char cmd[NAME_MAX + 1];
1093 int state = 'R';
1094 unsigned long fault_count = 0UL,
1095 vmsize = 0UL, vmrss = 0UL, vmmaxrss = 0UL;
1096 uint64_t utime = 0ULL, stime = 0ULL, start_time = 0ULL;
1097 int nice = 0;
1098 /* ctty maj is 31:16, min is 15:0; tty_nr s/b maj 15:8, min 31:20, 7:0;
1099 maj is 31:16 >> 16 & fff << 8; min is 15:0 >> 8 & ff << 20 | & ff */
1100 int tty_nr = 0;
1101 if (CTTY_IS_VALID (p->ctty))
1102 tty_nr = (((p->ctty >> 8) & 0xff) << 20)
1103 | (((p->ctty >> 16) & 0xfff) << 8)
1104 | (p->ctty & 0xff);
1106 if (p->process_state & PID_EXITED)
1107 strcpy (cmd, "<defunct>");
1108 else
1110 PWCHAR last_slash = wcsrchr (p->progname, L'\\');
1111 sys_wcstombs (cmd, NAME_MAX + 1,
1112 last_slash ? last_slash + 1 : p->progname);
1113 int len = strlen (cmd);
1114 if (len > 4)
1116 char *s = cmd + len - 4;
1117 if (ascii_strcasematch (s, ".exe"))
1118 *s = 0;
1121 /* Note: under Windows, a process is always running - it's only threads
1122 that get suspended. Therefore the default state is R (runnable). */
1123 if (p->process_state & PID_EXITED)
1124 state = 'Z';
1125 else if (p->process_state & PID_STOPPED)
1126 state = 'T';
1127 else
1128 state = get_process_state (p->dwProcessId);
1130 NTSTATUS status;
1131 HANDLE hProcess;
1132 VM_COUNTERS vmc = { 0 };
1133 KERNEL_USER_TIMES put = { 0 };
1134 QUOTA_LIMITS ql = { 0 };
1135 SYSTEM_TIMEOFDAY_INFORMATION stodi = { 0 };
1137 hProcess = OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION,
1138 FALSE, p->dwProcessId);
1139 if (hProcess == NULL)
1141 if (!(p->process_state & PID_EXITED))
1143 DWORD error = GetLastError ();
1144 __seterrno_from_win_error (error);
1145 debug_printf ("OpenProcess: ret %u; pid: %d", error, p->dwProcessId);
1146 return -1;
1148 /* Else it's a zombie process; just leave each structure zero'd */
1150 else
1152 status = NtQueryInformationProcess (hProcess, ProcessVmCounters,
1153 (PVOID) &vmc, sizeof vmc, NULL);
1154 if (!NT_SUCCESS (status))
1155 debug_printf ("NtQueryInformationProcess(ProcessVmCounters): status %y",
1156 status);
1157 status = NtQueryInformationProcess (hProcess, ProcessTimes,
1158 (PVOID) &put, sizeof put, NULL);
1159 if (!NT_SUCCESS (status))
1160 debug_printf ("NtQueryInformationProcess(ProcessTimes): status %y",
1161 status);
1162 status = NtQueryInformationProcess (hProcess, ProcessQuotaLimits,
1163 (PVOID) &ql, sizeof ql, NULL);
1164 if (!NT_SUCCESS (status))
1165 debug_printf ("NtQueryInformationProcess(ProcessQuotaLimits): "
1166 "status %y", status);
1167 nice = winprio_to_nice (GetPriorityClass (hProcess));
1168 CloseHandle (hProcess);
1170 status = NtQuerySystemInformation (SystemTimeOfDayInformation,
1171 (PVOID) &stodi, sizeof stodi, NULL);
1172 if (!NT_SUCCESS (status))
1173 debug_printf ("NtQuerySystemInformation(SystemTimeOfDayInformation): "
1174 "status %y", status);
1175 fault_count = vmc.PageFaultCount;
1176 utime = put.UserTime.QuadPart * CLOCKS_PER_SEC / NS100PERSEC;
1177 stime = put.KernelTime.QuadPart * CLOCKS_PER_SEC / NS100PERSEC;
1178 if (put.CreateTime.QuadPart)
1179 start_time = (put.CreateTime.QuadPart - stodi.BootTime.QuadPart)
1180 * CLOCKS_PER_SEC / NS100PERSEC;
1181 else
1182 start_time = (p->start_time - to_time_t (&stodi.BootTime)) * CLOCKS_PER_SEC;
1183 unsigned page_size = wincap.page_size ();
1184 vmsize = vmc.PagefileUsage; /* bytes */
1185 vmrss = vmc.WorkingSetSize / page_size; /* pages */
1186 vmmaxrss = ql.MaximumWorkingSetSize; /* bytes */
1188 destbuf = (char *) crealloc_abort (destbuf, strlen (cmd) + 320);
1189 return __small_sprintf (destbuf, "%d (%s) %c "
1190 "%d %d %d %d "
1191 "%d %u %lu %lu %u %u "
1192 "%U %U %U %U "
1193 "%d %d %d %d "
1194 "%U "
1195 "%lu %ld %lu\n",
1196 p->pid, cmd, state,
1197 p->ppid, p->pgid, p->sid, tty_nr,
1198 -1, 0, fault_count, fault_count, 0, 0,
1199 utime, stime, utime, stime,
1200 NZERO + nice, nice, 0, 0,
1201 start_time,
1202 vmsize, vmrss, vmmaxrss
1206 static off_t
1207 format_process_status (void *data, char *&destbuf)
1209 _pinfo *p = (_pinfo *) data;
1210 char cmd[NAME_MAX + 1];
1211 int state = 'R';
1212 const char *state_str = "unknown";
1213 size_t vmsize = 0, vmrss = 0, vmdata = 0, vmlib = 0, vmtext = 0, vmshare = 0;
1214 sigset_t pnd = 0, blk = 0, ign = 0;
1215 bool fetch_siginfo = false;
1217 PWCHAR last_slash = wcsrchr (p->progname, L'\\');
1218 sys_wcstombs (cmd, NAME_MAX + 1, last_slash ? last_slash + 1 : p->progname);
1219 int len = strlen (cmd);
1220 if (len > 4)
1222 char *s = cmd + len - 4;
1223 if (ascii_strcasematch (s, ".exe"))
1224 *s = 0;
1226 /* Note: under Windows, a process is always running - it's only threads
1227 that get suspended. Therefore the default state is R (runnable). */
1228 if (p->process_state & PID_EXITED)
1229 state = 'Z';
1230 else if (p->process_state & PID_STOPPED)
1231 state = 'T';
1232 else
1233 state = get_process_state (p->dwProcessId);
1234 switch (state)
1236 case 'O':
1237 state_str = "running";
1238 fetch_siginfo = true;
1239 break;
1240 case 'D':
1241 case 'S':
1242 state_str = "sleeping";
1243 fetch_siginfo = true;
1244 break;
1245 case 'R':
1246 state_str = "runnable";
1247 fetch_siginfo = true;
1248 break;
1249 case 'Z':
1250 state_str = "zombie";
1251 break;
1252 case 'T':
1253 state_str = "stopped";
1254 break;
1256 get_mem_values (p->dwProcessId, vmsize, vmrss, vmtext, vmdata,
1257 vmlib, vmshare);
1258 if (fetch_siginfo)
1259 p->siginfo (pnd, blk, ign);
1260 /* The real uid value for *this* process is stored at cygheap->user.real_uid
1261 but we can't get at the real uid value for any other process, so
1262 just fake it as p->uid. Similar for p->gid. */
1263 size_t kb_per_page = wincap.allocation_granularity() / 1024;
1264 destbuf = (char *) crealloc_abort (destbuf, strlen (cmd) + 320);
1265 return __small_sprintf (destbuf, "Name:\t%s\n"
1266 "State:\t%c (%s)\n"
1267 "Tgid:\t%d\n"
1268 "Pid:\t%d\n"
1269 "PPid:\t%d\n"
1270 "Uid:\t%d %d %d %d\n"
1271 "Gid:\t%d %d %d %d\n"
1272 "VmSize:\t%8lu kB\n"
1273 "VmLck:\t%8lu kB\n"
1274 "VmRSS:\t%8lu kB\n"
1275 "VmData:\t%8lu kB\n"
1276 "VmStk:\t%8lu kB\n"
1277 "VmExe:\t%8lu kB\n"
1278 "VmLib:\t%8lu kB\n"
1279 "SigPnd:\t%016lx\n"
1280 "SigBlk:\t%016lx\n"
1281 "SigIgn:\t%016lx\n",
1282 cmd,
1283 state, state_str,
1284 p->pgid,
1285 p->pid,
1286 p->ppid,
1287 p->uid, p->uid, p->uid, p->uid,
1288 p->gid, p->gid, p->gid, p->gid,
1289 vmsize * kb_per_page, 0UL, vmrss * kb_per_page,
1290 vmdata * kb_per_page, 0UL, vmtext * kb_per_page,
1291 vmlib * kb_per_page,
1292 pnd, blk, ign
1296 static off_t
1297 format_process_statm (void *data, char *&destbuf)
1299 _pinfo *p = (_pinfo *) data;
1300 size_t vmsize = 0, vmrss = 0, vmtext = 0, vmdata = 0, vmlib = 0, vmshare = 0;
1302 if (!get_mem_values (p->dwProcessId, vmsize, vmrss, vmtext, vmdata,
1303 vmlib, vmshare) && !(p->process_state & PID_EXITED))
1304 return -1; /* Error out unless it's a zombie process */
1306 destbuf = (char *) crealloc_abort (destbuf, 96);
1307 return __small_sprintf (destbuf, "%lu %lu %lu %lu %lu %lu 0\n",
1308 vmsize, vmrss, vmshare, vmtext, vmlib, vmdata);
1311 extern "C" {
1312 FILE *setmntent (const char *, const char *);
1313 struct mntent *getmntent (FILE *);
1316 static off_t
1317 format_process_mountstuff (void *data, char *&destbuf, bool mountinfo)
1319 _pinfo *p = (_pinfo *) data;
1320 user_info *u_shared = NULL;
1321 HANDLE u_hdl = NULL;
1322 off_t len = 0;
1323 struct mntent *mnt;
1325 if (p->uid != myself->uid)
1327 WCHAR sid_string[UNLEN + 1] = L""; /* Large enough for SID */
1329 cygsid p_sid;
1331 if (!p_sid.getfrompw (internal_getpwuid (p->uid)))
1332 return 0;
1333 p_sid.string (sid_string);
1334 u_shared = (user_info *) open_shared (sid_string, USER_VERSION, u_hdl,
1335 sizeof (user_info), SH_JUSTOPEN,
1336 &sec_none_nih);
1337 if (!u_shared)
1338 return 0;
1340 else
1341 u_shared = user_shared;
1342 mount_info *mtab = &u_shared->mountinfo;
1344 /* Store old value of _my_tls.locals here. */
1345 int iteration = _my_tls.locals.iteration;
1346 unsigned available_drives = _my_tls.locals.available_drives;
1347 /* This reinitializes the above values in _my_tls. */
1348 setmntent (NULL, NULL);
1349 /* Restore iteration immediately since it's not used below. We use the
1350 local iteration variable instead*/
1351 _my_tls.locals.iteration = iteration;
1353 for (iteration = 0; (mnt = mtab->getmntent (iteration)); ++iteration)
1355 /* We have no access to the drives mapped into another user session and
1356 _my_tls.locals.available_drives contains the mappings of the current
1357 user. So, when printing the mount table of another user, we check
1358 each cygdrive entry if it's a remote drive. If so, ignore it. */
1359 if (iteration >= mtab->nmounts && u_hdl)
1361 WCHAR drive[3] = { (WCHAR) mnt->mnt_fsname[0], L':', L'\0' };
1362 disk_type dt = get_disk_type (drive);
1364 if (dt == DT_SHARE_SMB || dt == DT_SHARE_NFS)
1365 continue;
1367 destbuf = (char *) crealloc_abort (destbuf, len
1368 + strlen (mnt->mnt_fsname)
1369 + strlen (mnt->mnt_dir)
1370 + strlen (mnt->mnt_type)
1371 + strlen (mnt->mnt_opts)
1372 + 30);
1373 if (mountinfo)
1375 path_conv pc (mnt->mnt_dir, PC_SYM_NOFOLLOW | PC_POSIX);
1376 dev_t dev = pc.exists () ? pc.fs_serial_number () : -1;
1378 len += __small_sprintf (destbuf + len,
1379 "%d %d %d:%d / %s %s - %s %s %s\n",
1380 iteration, iteration,
1381 major (dev), minor (dev),
1382 mnt->mnt_dir, mnt->mnt_opts,
1383 mnt->mnt_type, mnt->mnt_fsname,
1384 (pc.fs_flags () & FILE_READ_ONLY_VOLUME)
1385 ? "ro" : "rw");
1387 else
1388 len += __small_sprintf (destbuf + len, "%s %s %s %s %d %d\n",
1389 mnt->mnt_fsname, mnt->mnt_dir, mnt->mnt_type,
1390 mnt->mnt_opts, mnt->mnt_freq, mnt->mnt_passno);
1393 /* Restore available_drives */
1394 _my_tls.locals.available_drives = available_drives;
1396 if (u_hdl) /* Only not-NULL if open_shared has been called. */
1398 UnmapViewOfFile (u_shared);
1399 CloseHandle (u_hdl);
1401 return len;
1404 static off_t
1405 format_process_mounts (void *data, char *&destbuf)
1407 return format_process_mountstuff (data, destbuf, false);
1410 static off_t
1411 format_process_mountinfo (void *data, char *&destbuf)
1413 return format_process_mountstuff (data, destbuf, true);
1417 get_process_state (DWORD dwProcessId)
1419 /* This isn't really heavy magic - just go through the processes' threads
1420 one by one and return a value accordingly. Errors are silently ignored. */
1421 NTSTATUS status;
1422 PSYSTEM_PROCESS_INFORMATION p, sp;
1423 ULONG n = 0x4000;
1424 int state =' ';
1426 p = (PSYSTEM_PROCESS_INFORMATION) malloc (n);
1427 if (!p)
1428 return state;
1429 while (true)
1431 status = NtQuerySystemInformation (SystemProcessInformation,
1432 (PVOID) p, n, NULL);
1433 if (status != STATUS_INFO_LENGTH_MISMATCH)
1434 break;
1435 n <<= 1;
1436 PSYSTEM_PROCESS_INFORMATION new_p = (PSYSTEM_PROCESS_INFORMATION) realloc (p, n);
1437 if (!new_p)
1438 goto out;
1439 p = new_p;
1441 if (!NT_SUCCESS (status))
1443 debug_printf ("NtQuerySystemInformation: status %y, %u",
1444 status, RtlNtStatusToDosError (status));
1445 goto out;
1447 state = 'Z';
1448 sp = p;
1449 for (;;)
1451 if ((DWORD) (uintptr_t) sp->UniqueProcessId == dwProcessId)
1453 SYSTEM_THREADS *st;
1454 st = &sp->Threads[0];
1455 state = 'S';
1456 for (unsigned i = 0; i < sp->NumberOfThreads; i++)
1458 /* FIXME: at some point we should consider generating 'O' */
1459 if (st->State == StateRunning ||
1460 st->State == StateReady)
1462 state = 'R';
1463 goto out;
1465 st++;
1467 break;
1469 if (!sp->NextEntryOffset)
1470 break;
1471 sp = (PSYSTEM_PROCESS_INFORMATION) ((char *) sp + sp->NextEntryOffset);
1473 out:
1474 free (p);
1475 return state;
1478 static bool
1479 get_mem_values (DWORD dwProcessId, size_t &vmsize, size_t &vmrss,
1480 size_t &vmtext, size_t &vmdata, size_t &vmlib, size_t &vmshare)
1482 bool res = false;
1483 NTSTATUS status;
1484 HANDLE hProcess;
1485 VM_COUNTERS vmc;
1486 PMEMORY_WORKING_SET_LIST p;
1487 SIZE_T n = 0x4000, length;
1488 const size_t page_scale = wincap.allocation_granularity()
1489 / wincap.page_size();
1491 /* This appears to work despite MSDN claiming that QueryWorkingSet requires
1492 PROCESS_QUERY_INFORMATION *and* PROCESS_VM_READ. Since we're trying to do
1493 everything with least perms, we stick to PROCESS_QUERY_INFORMATION only
1494 unless this changes in Windows for some reason. */
1495 hProcess = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, dwProcessId);
1496 if (hProcess == NULL)
1498 __seterrno ();
1499 debug_printf ("OpenProcess, %E");
1500 return false;
1502 p = (PMEMORY_WORKING_SET_LIST) malloc (n);
1503 if (!p)
1504 goto out;
1505 while (true)
1507 status = NtQueryVirtualMemory (hProcess, 0, MemoryWorkingSetList,
1508 (PVOID) p, n,
1509 (length = (SIZE_T) -1, &length));
1510 if (status != STATUS_INFO_LENGTH_MISMATCH)
1511 break;
1512 n <<= 1;
1513 PMEMORY_WORKING_SET_LIST new_p = (PMEMORY_WORKING_SET_LIST)
1514 realloc (p, n);
1515 if (!new_p)
1516 goto out;
1517 p = new_p;
1519 if (!NT_SUCCESS (status))
1521 debug_printf ("NtQueryVirtualMemory: status %y", status);
1522 if (status == STATUS_PROCESS_IS_TERMINATING)
1524 vmsize = vmrss = vmtext = vmdata = vmlib = vmshare = 0;
1525 res = true;
1527 else
1528 __seterrno_from_nt_status (status);
1529 goto out;
1531 for (unsigned long i = 0; i < p->NumberOfPages; i++)
1533 ++vmrss;
1534 unsigned flags = p->WorkingSetList[i] & 0x0FFF;
1535 if ((flags & (WSLE_PAGE_EXECUTE | WSLE_PAGE_SHAREABLE))
1536 == (WSLE_PAGE_EXECUTE | WSLE_PAGE_SHAREABLE))
1537 ++vmlib;
1538 else if (flags & WSLE_PAGE_SHAREABLE)
1539 ++vmshare;
1540 else if (flags & WSLE_PAGE_EXECUTE)
1541 ++vmtext;
1542 else
1543 ++vmdata;
1545 status = NtQueryInformationProcess (hProcess, ProcessVmCounters, (PVOID) &vmc,
1546 sizeof vmc, NULL);
1547 if (!NT_SUCCESS (status))
1549 debug_printf ("NtQueryInformationProcess: status %y", status);
1550 __seterrno_from_nt_status (status);
1551 goto out;
1553 vmsize = vmc.PagefileUsage / wincap.page_size ();
1554 /* Return number of Cygwin pages. Page size in Cygwin is equivalent
1555 to Windows allocation_granularity. */
1556 vmsize = howmany (vmsize, page_scale);
1557 vmrss = howmany (vmrss, page_scale);
1558 vmshare = howmany (vmshare, page_scale);
1559 vmtext = howmany (vmtext, page_scale);
1560 vmlib = howmany (vmlib, page_scale);
1561 vmdata = howmany (vmdata, page_scale);
1562 res = true;
1563 out:
1564 free (p);
1565 CloseHandle (hProcess);
1566 return res;