1 /* fhandler_process.cc: fhandler for /proc/<pid> virtual filesystem
3 Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
4 2010, 2011 Red Hat, Inc.
6 This file is part of Cygwin.
8 This software is a copyrighted work licensed under the terms of the
9 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
15 #include <sys/cygwin.h>
20 #include "fhandler_virtual.h"
22 #include "shared_info.h"
29 #include <sys/param.h>
34 #define _COMPILING_NEWLIB
37 static _off64_t format_process_maps (void *, char *&);
38 static _off64_t format_process_stat (void *, char *&);
39 static _off64_t format_process_status (void *, char *&);
40 static _off64_t format_process_statm (void *, char *&);
41 static _off64_t format_process_winexename (void *, char *&);
42 static _off64_t format_process_winpid (void *, char *&);
43 static _off64_t format_process_exename (void *, char *&);
44 static _off64_t format_process_root (void *, char *&);
45 static _off64_t format_process_cwd (void *, char *&);
46 static _off64_t format_process_cmdline (void *, char *&);
47 static _off64_t format_process_ppid (void *, char *&);
48 static _off64_t format_process_uid (void *, char *&);
49 static _off64_t format_process_pgid (void *, char *&);
50 static _off64_t format_process_sid (void *, char *&);
51 static _off64_t format_process_gid (void *, char *&);
52 static _off64_t format_process_ctty (void *, char *&);
53 static _off64_t format_process_fd (void *, char *&);
54 static _off64_t format_process_mounts (void *, char *&);
56 static const virt_tab_t process_tab[] =
58 { _VN ("."), FH_PROCESS, virt_directory, NULL },
59 { _VN (".."), FH_PROCESS, virt_directory, NULL },
60 { _VN ("cmdline"), FH_PROCESS, virt_file, format_process_cmdline },
61 { _VN ("ctty"), FH_PROCESS, virt_file, format_process_ctty },
62 { _VN ("cwd"), FH_PROCESS, virt_symlink, format_process_cwd },
63 { _VN ("exe"), FH_PROCESS, virt_symlink, format_process_exename },
64 { _VN ("exename"), FH_PROCESS, virt_file, format_process_exename },
65 { _VN ("fd"), FH_PROCESSFD, virt_directory, format_process_fd },
66 { _VN ("gid"), FH_PROCESS, virt_file, format_process_gid },
67 { _VN ("maps"), FH_PROCESS, virt_file, format_process_maps },
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, unsigned long *vmsize,
86 unsigned long *vmrss, unsigned long *vmtext,
87 unsigned long *vmdata, unsigned long *vmlib,
88 unsigned long *vmshare);
90 /* Returns 0 if path doesn't exist, >0 if path is a directory,
91 * -1 if path is a file, -2 if path is a symlink, -3 if path is a pipe,
92 * -4 if path is a socket.
95 fhandler_process::exists ()
97 const char *path = get_name ();
98 debug_printf ("exists (%s)", path);
100 while (*path != 0 && !isdirsep (*path))
105 virt_tab_t *entry = virt_tab_search (path + 1, true, process_tab,
109 if (!path[entry->name_len + 1])
111 fileid = entry - process_tab;
114 if (entry->type == virt_directory)
116 fileid = entry - process_tab;
119 /* Check for nameless device entries. */
120 path = strrchr (path, '/');
123 if (!strncmp (path, "pipe:[", 6))
125 else if (!strncmp (path, "socket:[", 8))
133 fhandler_process::fhandler_process ():
139 fhandler_process::fstat (struct __stat64 *buf)
141 const char *path = get_name ();
142 int file_type = exists ();
143 fhandler_base::fstat (buf);
144 path += proc_len + 1;
148 /* If p->pid != pid, then pid is actually the Windows PID for an execed
149 Cygwin process, and the pinfo entry is the additional entry created
150 at exec time. We don't want to enable the user to access a process
151 entry by using the Win32 PID, though. */
152 if (!p || p->pid != pid)
158 buf->st_mode &= ~_IFMT & NO_W;
167 buf->st_ctime = buf->st_mtime = buf->st_birthtime = p->start_time;
168 buf->st_ctim.tv_nsec = buf->st_mtim.tv_nsec
169 = buf->st_birthtim.tv_nsec = 0;
170 time_as_timestruc_t (&buf->st_atim);
171 buf->st_uid = p->uid;
172 buf->st_gid = p->gid;
173 buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
180 buf->st_uid = p->uid;
181 buf->st_gid = p->gid;
182 buf->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
185 buf->st_uid = p->uid;
186 buf->st_gid = p->gid;
187 buf->st_mode = S_IFIFO | S_IRUSR | S_IWUSR;
190 buf->st_uid = p->uid;
191 buf->st_gid = p->gid;
192 buf->st_mode = S_IFSOCK | S_IRUSR | S_IWUSR;
196 buf->st_uid = p->uid;
197 buf->st_gid = p->gid;
198 buf->st_mode |= S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
204 fhandler_process::opendir (int fd)
206 DIR *dir = fhandler_virtual::opendir (fd);
207 if (dir && process_tab[fileid].fhandler == FH_PROCESSFD)
213 fhandler_process::closedir (DIR *dir)
215 return fhandler_virtual::closedir (dir);
219 fhandler_process::readdir (DIR *dir, dirent *de)
222 if (process_tab[fileid].fhandler == FH_PROCESSFD)
224 if (dir->__d_position >= 2 + filesize / sizeof (int))
227 else if (dir->__d_position >= PROCESS_LINK_COUNT)
229 if (process_tab[fileid].fhandler == FH_PROCESSFD && dir->__d_position > 1)
231 int *p = (int *) filebuf;
232 __small_sprintf (de->d_name, "%d", p[dir->__d_position++ - 2]);
235 strcpy (de->d_name, process_tab[dir->__d_position++].name);
236 dir->__flags |= dirent_saw_dot | dirent_saw_dot_dot;
239 syscall_printf ("%d = readdir (%p, %p) (%s)", res, dir, de, de->d_name);
244 fhandler_process::open (int flags, mode_t mode)
246 int res = fhandler_virtual::open (flags, mode);
253 path = get_name () + proc_len + 1;
255 while (*path != 0 && !isdirsep (*path))
260 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
266 else if (flags & O_WRONLY)
280 entry = virt_tab_search (path + 1, true, process_tab, PROCESS_LINK_COUNT);
283 set_errno ((flags & O_CREAT) ? EROFS : ENOENT);
287 if (entry->fhandler == FH_PROCESSFD)
292 if (flags & O_WRONLY)
299 fileid = entry - process_tab;
300 if (!fill_filebuf ())
306 if (flags & O_APPEND)
313 set_flags ((flags & ~O_TEXT) | O_BINARY);
316 syscall_printf ("%d = fhandler_proc::open (%p, %d)", res, flags, mode);
320 struct process_fd_t {
326 fhandler_process::fill_filebuf ()
329 path = get_name () + proc_len + 1;
334 /* If p->pid != pid, then pid is actually the Windows PID for an execed
335 Cygwin process, and the pinfo entry is the additional entry created
336 at exec time. We don't want to enable the user to access a process
337 entry by using the Win32 PID, though. */
338 if (!p || p->pid != pid)
344 if (process_tab[fileid].format_func)
346 if (process_tab[fileid].fhandler == FH_PROCESSFD)
348 process_fd_t fd = { path, p };
349 filesize = process_tab[fileid].format_func (&fd, filebuf);
352 filesize = process_tab[fileid].format_func (p, filebuf);
353 return !filesize ? false : true;
359 format_process_fd (void *data, char *&destbuf)
361 _pinfo *p = ((process_fd_t *) data)->p;
362 const char *path = ((process_fd_t *) data)->path;
364 char *fdp = strrchr (path, '/');
366 if (!fdp || *++fdp == 'f') /* The "fd" directory itself. */
370 destbuf = p->fds (fs);
377 if (fd < 0 || (fd == 0 && !isdigit (*fdp)))
382 destbuf = p->fd (fd, fs);
383 if (!destbuf || !*destbuf)
393 format_process_ppid (void *data, char *&destbuf)
395 _pinfo *p = (_pinfo *) data;
396 destbuf = (char *) crealloc_abort (destbuf, 40);
397 return __small_sprintf (destbuf, "%d\n", p->ppid);
401 format_process_uid (void *data, char *&destbuf)
403 _pinfo *p = (_pinfo *) data;
404 destbuf = (char *) crealloc_abort (destbuf, 40);
405 return __small_sprintf (destbuf, "%d\n", p->uid);
409 format_process_pgid (void *data, char *&destbuf)
411 _pinfo *p = (_pinfo *) data;
412 destbuf = (char *) crealloc_abort (destbuf, 40);
413 return __small_sprintf (destbuf, "%d\n", p->pgid);
417 format_process_sid (void *data, char *&destbuf)
419 _pinfo *p = (_pinfo *) data;
420 destbuf = (char *) crealloc_abort (destbuf, 40);
421 return __small_sprintf (destbuf, "%d\n", p->sid);
425 format_process_gid (void *data, char *&destbuf)
427 _pinfo *p = (_pinfo *) data;
428 destbuf = (char *) crealloc_abort (destbuf, 40);
429 return __small_sprintf (destbuf, "%d\n", p->gid);
433 format_process_ctty (void *data, char *&destbuf)
436 _pinfo *p = (_pinfo *) data;
438 destbuf = (char *) crealloc_abort (destbuf, strlen (d.name) + 2);
439 return __small_sprintf (destbuf, "%s\n", d.name);
443 format_process_root (void *data, char *&destbuf)
445 _pinfo *p = (_pinfo *) data;
453 destbuf = p->root (fs);
454 if (!destbuf || !*destbuf)
456 destbuf = cstrdup ("<defunct>");
457 fs = strlen (destbuf) + 1;
463 format_process_cwd (void *data, char *&destbuf)
465 _pinfo *p = (_pinfo *) data;
473 destbuf = p->cwd (fs);
474 if (!destbuf || !*destbuf)
476 destbuf = cstrdup ("<defunct>");
477 fs = strlen (destbuf) + 1;
483 format_process_cmdline (void *data, char *&destbuf)
485 _pinfo *p = (_pinfo *) data;
493 destbuf = p->cmdline (fs);
494 if (!destbuf || !*destbuf)
496 destbuf = cstrdup ("<defunct>");
497 fs = strlen (destbuf) + 1;
503 format_process_exename (void *data, char *&destbuf)
505 _pinfo *p = (_pinfo *) data;
509 char *buf = tp.c_get ();
510 if (p->process_state & PID_EXITED)
511 stpcpy (buf, "<defunct>");
514 mount_table->conv_to_posix_path (p->progname, buf, 1);
518 char *s = buf + len - 4;
519 if (ascii_strcasematch (s, ".exe"))
523 destbuf = (char *) crealloc_abort (destbuf, (len = strlen (buf)) + 1);
524 stpcpy (destbuf, buf);
529 format_process_winpid (void *data, char *&destbuf)
531 _pinfo *p = (_pinfo *) data;
532 destbuf = (char *) crealloc_abort (destbuf, 20);
533 return __small_sprintf (destbuf, "%d\n", p->dwProcessId);
537 format_process_winexename (void *data, char *&destbuf)
539 _pinfo *p = (_pinfo *) data;
540 size_t len = sys_wcstombs (NULL, 0, p->progname);
541 destbuf = (char *) crealloc_abort (destbuf, len + 1);
542 sys_wcstombs (destbuf, len, p->progname);
547 struct dos_drive_mappings
553 wchar_t drive_letter;
558 dos_drive_mappings ()
561 /* The logical drive strings buffer holds a list of (at most 26)
562 drive names separated by nulls and terminated by a double-null:
564 "a:\\\0b:\\\0...z:\\\0"
566 The annoying part is, QueryDosDeviceW wants only "x:" rather
567 than the "x:\" we get back from GetLogicalDriveStringsW, so
568 we'll have to strip out the trailing slash for each mapping.
570 The returned mapping a native NT pathname (\Device\...) which
571 we can use to fix up the output of GetMappedFileNameW
573 static unsigned const DBUFLEN = 26 * 4;
574 wchar_t dbuf[DBUFLEN + 1];
575 wchar_t pbuf[NT_MAX_PATH];
576 wchar_t drive[] = {L'x', L':', 0};
577 unsigned result = GetLogicalDriveStringsW (DBUFLEN * sizeof (wchar_t),
580 debug_printf ("Failed to get logical DOS drive names: %lu",
582 else if (result > DBUFLEN)
583 debug_printf ("Too many mapped drive letters: %u", result);
585 for (wchar_t *cur = dbuf; (*drive = *cur); cur = wcschr (cur, L'\0')+1)
586 if (QueryDosDeviceW (drive, pbuf, NT_MAX_PATH))
588 size_t plen = wcslen (pbuf);
589 size_t psize = plen * sizeof (wchar_t);
590 debug_printf ("DOS drive %ls maps to %ls", drive, pbuf);
591 mapping *m = (mapping*) malloc (sizeof (mapping) + psize);
596 m->drive_letter = *drive;
597 memcpy (m->mapping, pbuf, psize + sizeof (wchar_t));
602 debug_printf ("Unable to determine the native mapping for %ls "
603 "(error %lu)", drive, GetLastError ());
606 wchar_t *fixup_if_match (wchar_t *path)
608 /* Check for network drive first. */
609 if (!wcsncmp (path, L"\\Device\\Mup\\", 12))
615 /* Then test local drives. */
616 for (mapping *m = mappings; m; m = m->next)
617 if (!wcsncmp (m->mapping, path, m->len))
620 path[0] = m->drive_letter;
627 ~dos_drive_mappings ()
630 for (mapping *m = mappings; m; m = n)
648 heap *heap_vm_chunks;
650 heap_info (DWORD pid)
651 : heap_vm_chunks (NULL)
655 PDEBUG_HEAP_ARRAY harray;
657 buf = RtlCreateQueryDebugBuffer (0, FALSE);
660 status = RtlQueryProcessDebugInformation (pid, PDI_HEAPS | PDI_HEAP_BLOCKS,
662 if (NT_SUCCESS (status)
663 && (harray = (PDEBUG_HEAP_ARRAY) buf->HeapInformation) != NULL)
664 for (ULONG hcnt = 0; hcnt < harray->Count; ++hcnt)
666 PDEBUG_HEAP_BLOCK barray = (PDEBUG_HEAP_BLOCK)
667 harray->Heaps[hcnt].Blocks;
670 for (ULONG bcnt = 0; bcnt < harray->Heaps[hcnt].BlockCount; ++bcnt)
671 if (barray[bcnt].Flags & 2)
673 heap *h = (heap *) malloc (sizeof (heap));
676 *h = (heap) { heap_vm_chunks,
677 hcnt, (char *) barray[bcnt].Address,
678 (char *) barray[bcnt].Address
680 harray->Heaps[hcnt].Flags };
685 RtlDestroyQueryDebugBuffer (buf);
688 char *fill_if_match (char *base, ULONG type, char *dest)
690 for (heap *h = heap_vm_chunks; h; h = h->next)
691 if (base >= h->base && base < h->end)
693 char *p = dest + __small_sprintf (dest, "[win heap %ld", h->heap_id);
694 if (!(h->flags & HEAP_FLAG_NONDEFAULT))
695 p = stpcpy (p, " default");
696 if ((h->flags & HEAP_FLAG_SHAREABLE) && (type & MEM_MAPPED))
697 p = stpcpy (p, " shared");
698 if (h->flags & HEAP_FLAG_EXECUTABLE)
699 p = stpcpy (p, " exec");
700 if (h->flags & HEAP_FLAG_GROWABLE)
701 p = stpcpy (p, " grow");
702 if (h->flags & HEAP_FLAG_NOSERIALIZE)
703 p = stpcpy (p, " noserial");
704 if (h->flags == HEAP_FLAG_DEBUGGED)
705 p = stpcpy (p, " debug");
715 for (heap *m = heap_vm_chunks; m; m = n)
735 thread_info (DWORD pid, HANDLE process)
740 size_t size = 50 * (sizeof (SYSTEM_PROCESSES)
741 + 16 * sizeof (SYSTEM_THREADS));
742 PSYSTEM_PROCESSES proc;
743 PSYSTEM_THREADS thread;
747 buf = realloc (buf, size);
748 status = NtQuerySystemInformation (SystemProcessesAndThreadsInformation,
752 while (status == STATUS_INFO_LENGTH_MISMATCH);
753 if (!NT_SUCCESS (status))
757 debug_printf ("NtQuerySystemInformation, %p", status);
760 proc = (PSYSTEM_PROCESSES) buf;
763 if (proc->ProcessId == pid)
765 if (!proc->NextEntryDelta)
770 proc = (PSYSTEM_PROCESSES) ((PBYTE) proc + proc->NextEntryDelta);
772 thread = proc->Threads;
773 for (ULONG i = 0; i < proc->ThreadCount; ++i)
775 THREAD_BASIC_INFORMATION tbi;
779 if (!(thread_h = OpenThread (THREAD_QUERY_INFORMATION, FALSE,
780 (ULONG) thread[i].ClientId.UniqueThread)))
782 status = NtQueryInformationThread (thread_h, ThreadBasicInformation,
783 &tbi, sizeof tbi, NULL);
784 CloseHandle (thread_h);
785 if (!NT_SUCCESS (status))
787 region *r = (region *) malloc (sizeof (region));
790 *r = (region) { regions, (ULONG) thread[i].ClientId.UniqueThread,
791 (char *) tbi.TebBaseAddress,
792 (char *) tbi.TebBaseAddress + wincap.page_size (),
796 if (!ReadProcessMemory (process, (PVOID) tbi.TebBaseAddress,
797 &teb, sizeof teb, NULL))
799 r = (region *) malloc (sizeof (region));
802 *r = (region) { regions, (ULONG) thread[i].ClientId.UniqueThread,
803 (char *) (teb.DeallocationStack
804 ?: teb.Tib.StackLimit),
805 (char *) teb.Tib.StackBase,
813 char *fill_if_match (char *base, ULONG type, char *dest)
815 for (region *r = regions; r; r = r->next)
816 if ((base >= r->start && base < r->end)
817 /* Special case WOW64. The TEB is 8K within the region reserved
818 for it. No idea what the lower 8K are used for. */
819 || (r->teb && wincap.is_wow64 ()
820 && r->start == base + 2 * wincap.page_size ()))
822 char *p = dest + __small_sprintf (dest, "[%s (tid %ld)",
823 r->teb ? "teb" : "stack",
825 if (type & MEM_MAPPED)
826 p = stpcpy (p, " shared");
836 for (region *m = regions; m; m = n)
845 format_process_maps (void *data, char *&destbuf)
847 _pinfo *p = (_pinfo *) data;
848 HANDLE proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
849 FALSE, p->dwProcessId);
854 PROCESS_BASIC_INFORMATION pbi;
857 memset (&pbi, 0, sizeof (pbi));
858 status = NtQueryInformationProcess (proc, ProcessBasicInformation,
859 &pbi, sizeof pbi, NULL);
860 if (NT_SUCCESS (status))
861 peb = pbi.PebBaseAddress;
862 /* myself is in the same spot in every process, so is the pointer to the
863 procinfo. But make sure the destructor doesn't try to release procinfo! */
865 if (ReadProcessMemory (proc, &myself, &proc_pinfo, sizeof proc_pinfo, NULL))
866 proc_pinfo.preserve ();
867 /* The heap info on the cygheap is also in the same spot in each process
868 because the cygheap is located at the same address. */
869 user_heap_info user_heap;
870 ReadProcessMemory (proc, &cygheap->user_heap, &user_heap,
871 sizeof user_heap, NULL);
886 } cur = {{{'\0'}}, (char *)1, 0, 0};
888 MEMORY_BASIC_INFORMATION mb;
889 dos_drive_mappings drive_maps;
890 heap_info heaps (p->dwProcessId);
891 thread_info threads (p->dwProcessId, proc);
896 PMEMORY_SECTION_NAME msi = (PMEMORY_SECTION_NAME) tp.w_get ();
897 char *posix_modname = tp.c_get ();
906 /* Iterate over each VM region in the address space, coalescing
907 memory regions with the same permissions. Once we run out, do one
908 last_pass to trigger output of the last accumulated region. */
910 VirtualQueryEx (proc, i, &mb, sizeof(mb)) || (1 == ++last_pass);
914 posix_modname[0] = '\0';
915 if (mb.State == MEM_FREE)
917 else if (mb.State == MEM_RESERVE)
919 char *p = stpcpy (a.flags, "===");
920 stpcpy (p, (mb.Type & MEM_MAPPED) ? "s" : "p");
924 static DWORD const RO = (PAGE_EXECUTE_READ | PAGE_READONLY);
925 static DWORD const RW = (PAGE_EXECUTE_READWRITE | PAGE_READWRITE
926 | PAGE_EXECUTE_WRITECOPY | PAGE_WRITECOPY);
927 static DWORD const X = (PAGE_EXECUTE | PAGE_EXECUTE_READ
928 | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY);
929 static DWORD const WC = (PAGE_EXECUTE_WRITECOPY | PAGE_WRITECOPY);
930 DWORD p = mb.Protect;
932 (p & (RO | RW)) ? 'r' : '-',
933 (p & (RW)) ? 'w' : '-',
934 (p & (X)) ? 'x' : '-',
935 (mb.Type & MEM_MAPPED) && !(p & (WC)) ? 's'
936 : (p & PAGE_GUARD) ? 'g' : 'p',
937 '\0', // zero-fill the remaining bytes
942 (char *) mb.AllocationBase,
943 (char *) mb.BaseAddress,
944 (char *) mb.BaseAddress+mb.RegionSize
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
955 // output the current region if it's "interesting"
958 size_t newlen = strlen (posix_modname) + 62;
959 if (len + newlen >= maxsize)
960 destbuf = (char *) crealloc_abort (destbuf,
961 maxsize += roundup2 (newlen,
963 int written = __small_sprintf (destbuf + len,
964 "%08lx-%08lx %s %08lx %04x:%04x %U ",
965 cur.rbase, cur.rend, cur.a.flags,
966 cur.rbase - cur.abase,
971 destbuf[len + written++] = ' ';
973 len += __small_sprintf (destbuf + len, "%s\n", posix_modname);
975 // start of a new region (but possibly still the same allocation)
977 // if a new allocation, figure out what kind it is
978 if (newbase && !last_pass && mb.State != MEM_FREE)
980 /* If the return length pointer is missing, NtQueryVirtualMemory
981 returns with STATUS_ACCESS_VIOLATION on Windows 2000. */
986 if ((mb.Type & (MEM_MAPPED | MEM_IMAGE))
987 && NT_SUCCESS (status = NtQueryVirtualMemory (proc, cur.abase,
989 msi, 65536, &ret_len)))
992 drive_maps.fixup_if_match (msi->SectionFileName.Buffer);
993 if (mount_table->conv_to_posix_path (dosname,
995 sys_wcstombs (posix_modname, NT_MAX_PATH, dosname);
996 stat64 (posix_modname, &st);
998 else if (!threads.fill_if_match (cur.abase, mb.Type,
1000 && !heaps.fill_if_match (cur.abase, mb.Type,
1003 if (cur.abase == (char *) peb)
1004 strcpy (posix_modname, "[peb]");
1005 else if (cur.abase == (char *) &SharedUserData)
1006 strcpy (posix_modname, "[shared-user-data]");
1007 else if (cur.abase == (char *) cygwin_shared)
1008 strcpy (posix_modname, "[cygwin-shared]");
1009 else if (cur.abase == (char *) user_shared)
1010 strcpy (posix_modname, "[cygwin-user-shared]");
1011 else if (cur.abase == (char *) *proc_pinfo)
1012 strcpy (posix_modname, "[procinfo]");
1013 else if (cur.abase == user_heap.base)
1014 strcpy (posix_modname, "[heap]");
1016 posix_modname[0] = 0;
1026 format_process_stat (void *data, char *&destbuf)
1028 _pinfo *p = (_pinfo *) data;
1029 char cmd[NAME_MAX + 1];
1030 WCHAR wcmd[NAME_MAX + 1];
1032 unsigned long fault_count = 0UL,
1033 utime = 0UL, stime = 0UL,
1035 vmsize = 0UL, vmrss = 0UL, vmmaxrss = 0UL;
1037 if (p->process_state & PID_EXITED)
1038 strcpy (cmd, "<defunct>");
1041 PWCHAR last_slash = wcsrchr (p->progname, L'\\');
1042 wcscpy (wcmd, last_slash ? last_slash + 1 : p->progname);
1043 sys_wcstombs (cmd, NAME_MAX + 1, wcmd);
1044 int len = strlen (cmd);
1047 char *s = cmd + len - 4;
1048 if (ascii_strcasematch (s, ".exe"))
1053 * Note: under Windows, a _process_ is always running - it's only _threads_
1054 * that get suspended. Therefore the default state is R (runnable).
1056 if (p->process_state & PID_EXITED)
1058 else if (p->process_state & PID_STOPPED)
1061 state = get_process_state (p->dwProcessId);
1062 start_time = (GetTickCount () / 1000 - time (NULL) + p->start_time) * HZ;
1067 KERNEL_USER_TIMES put;
1068 PROCESS_BASIC_INFORMATION pbi;
1070 SYSTEM_TIME_OF_DAY_INFORMATION stodi;
1071 SYSTEM_PROCESSOR_TIMES spt;
1072 hProcess = OpenProcess (PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
1073 FALSE, p->dwProcessId);
1074 if (hProcess != NULL)
1076 ret = NtQueryInformationProcess (hProcess,
1080 if (ret == STATUS_SUCCESS)
1081 ret = NtQueryInformationProcess (hProcess,
1085 if (ret == STATUS_SUCCESS)
1086 ret = NtQueryInformationProcess (hProcess,
1087 ProcessBasicInformation,
1090 if (ret == STATUS_SUCCESS)
1091 ret = NtQueryInformationProcess (hProcess,
1095 CloseHandle (hProcess);
1099 DWORD error = GetLastError ();
1100 __seterrno_from_win_error (error);
1101 debug_printf ("OpenProcess: ret %d", error);
1104 if (ret == STATUS_SUCCESS)
1105 ret = NtQuerySystemInformation (SystemTimeOfDayInformation,
1107 sizeof stodi, NULL);
1108 if (ret == STATUS_SUCCESS)
1109 ret = NtQuerySystemInformation (SystemProcessorTimes,
1112 if (ret != STATUS_SUCCESS)
1114 __seterrno_from_nt_status (ret);
1115 debug_printf ("NtQueryInformationProcess: ret %d, Dos(ret) %E", ret);
1118 fault_count = vmc.PageFaultCount;
1119 utime = put.UserTime.QuadPart * HZ / 10000000ULL;
1120 stime = put.KernelTime.QuadPart * HZ / 10000000ULL;
1122 if (stodi.CurrentTime.QuadPart > put.CreateTime.QuadPart)
1123 start_time = (spt.KernelTime.QuadPart + spt.UserTime.QuadPart -
1124 stodi.CurrentTime.QuadPart + put.CreateTime.QuadPart) * HZ / 10000000ULL;
1127 * sometimes stodi.CurrentTime is a bit behind
1128 * Note: some older versions of procps are broken and can't cope
1129 * with process start times > time(NULL).
1131 start_time = (spt.KernelTme.QuadPart + spt.UserTime.QuadPart) * HZ / 10000000ULL;
1133 priority = pbi.BasePriority;
1134 unsigned page_size = getsystempagesize ();
1135 vmsize = vmc.PagefileUsage;
1136 vmrss = vmc.WorkingSetSize / page_size;
1137 vmmaxrss = ql.MaximumWorkingSetSize / page_size;
1139 destbuf = (char *) crealloc_abort (destbuf, strlen (cmd) + 320);
1140 return __small_sprintf (destbuf, "%d (%s) %c "
1142 "%lu %lu %lu %lu %lu %lu %lu "
1143 "%ld %ld %ld %ld %ld %ld "
1149 p->ppid, p->pgid, p->sid, p->ctty,
1150 -1, 0, fault_count, fault_count, 0, 0, utime, stime,
1151 utime, stime, priority, 0, 0, 0,
1158 format_process_status (void *data, char *&destbuf)
1160 _pinfo *p = (_pinfo *) data;
1161 char cmd[NAME_MAX + 1];
1162 WCHAR wcmd[NAME_MAX + 1];
1164 const char *state_str = "unknown";
1165 unsigned long vmsize = 0UL, vmrss = 0UL, vmdata = 0UL, vmlib = 0UL, vmtext = 0UL,
1167 PWCHAR last_slash = wcsrchr (p->progname, L'\\');
1168 wcscpy (wcmd, last_slash ? last_slash + 1 : p->progname);
1169 sys_wcstombs (cmd, NAME_MAX + 1, wcmd);
1170 int len = strlen (cmd);
1173 char *s = cmd + len - 4;
1174 if (ascii_strcasematch (s, ".exe"))
1178 * Note: under Windows, a _process_ is always running - it's only _threads_
1179 * that get suspended. Therefore the default state is R (runnable).
1181 if (p->process_state & PID_EXITED)
1183 else if (p->process_state & PID_STOPPED)
1186 state = get_process_state (p->dwProcessId);
1190 state_str = "running";
1194 state_str = "sleeping";
1197 state_str = "runnable";
1200 state_str = "zombie";
1203 state_str = "stopped";
1206 if (!get_mem_values (p->dwProcessId, &vmsize, &vmrss, &vmtext, &vmdata,
1209 unsigned page_size = getsystempagesize ();
1210 vmsize *= page_size; vmrss *= page_size; vmdata *= page_size;
1211 vmtext *= page_size; vmlib *= page_size;
1212 // The real uid value for *this* process is stored at cygheap->user.real_uid
1213 // but we can't get at the real uid value for any other process, so
1214 // just fake it as p->uid. Similar for p->gid.
1215 destbuf = (char *) crealloc_abort (destbuf, strlen (cmd) + 320);
1216 return __small_sprintf (destbuf, "Name:\t%s\n"
1221 "Uid:\t%d %d %d %d\n"
1222 "Gid:\t%d %d %d %d\n"
1238 p->uid, p->uid, p->uid, p->uid,
1239 p->gid, p->gid, p->gid, p->gid,
1240 vmsize >> 10, 0, vmrss >> 10, vmdata >> 10, 0,
1241 vmtext >> 10, vmlib >> 10,
1242 0, 0, _my_tls.sigmask
1247 format_process_statm (void *data, char *&destbuf)
1249 _pinfo *p = (_pinfo *) data;
1250 unsigned long vmsize = 0UL, vmrss = 0UL, vmtext = 0UL, vmdata = 0UL,
1251 vmlib = 0UL, vmshare = 0UL;
1252 if (!get_mem_values (p->dwProcessId, &vmsize, &vmrss, &vmtext, &vmdata,
1255 destbuf = (char *) crealloc_abort (destbuf, 96);
1256 return __small_sprintf (destbuf, "%ld %ld %ld %ld %ld %ld %ld",
1257 vmsize, vmrss, vmshare, vmtext, vmlib, vmdata, 0);
1261 FILE *setmntent (const char *, const char *);
1262 struct mntent *getmntent (FILE *);
1266 format_process_mounts (void *data, char *&destbuf)
1268 _pinfo *p = (_pinfo *) data;
1269 user_info *u_shared = NULL;
1270 HANDLE u_hdl = NULL;
1274 if (p->pid != myself->pid)
1276 WCHAR sid_string[UNLEN + 1] = L""; /* Large enough for SID */
1280 if (!p_sid.getfrompw (internal_getpwuid (p->uid)))
1282 p_sid.string (sid_string);
1283 u_shared = (user_info *) open_shared (sid_string, USER_VERSION, u_hdl,
1284 sizeof (user_info), SH_JUSTOPEN,
1290 u_shared = user_shared;
1292 /* Store old value of _my_tls.locals here. */
1293 int iteration = _my_tls.locals.iteration;
1294 unsigned available_drives = _my_tls.locals.available_drives;
1295 /* This reinitializes the above values in _my_tls. */
1296 setmntent (NULL, NULL);
1297 while ((mnt = getmntent (NULL)))
1299 destbuf = (char *) crealloc_abort (destbuf, len
1300 + strlen (mnt->mnt_fsname)
1301 + strlen (mnt->mnt_dir)
1302 + strlen (mnt->mnt_type)
1303 + strlen (mnt->mnt_opts)
1305 len += __small_sprintf (destbuf + len, "%s %s %s %s %d %d\n",
1306 mnt->mnt_fsname, mnt->mnt_dir, mnt->mnt_type,
1307 mnt->mnt_opts, mnt->mnt_freq, mnt->mnt_passno);
1309 /* Restore old value of _my_tls.locals here. */
1310 _my_tls.locals.iteration = iteration;
1311 _my_tls.locals.available_drives = available_drives;
1313 if (u_hdl) /* Only not-NULL if open_shared has been called. */
1315 UnmapViewOfFile (u_shared);
1316 CloseHandle (u_hdl);
1322 get_process_state (DWORD dwProcessId)
1325 * This isn't really heavy magic - just go through the processes'
1326 * threads one by one and return a value accordingly
1327 * Errors are silently ignored.
1330 SYSTEM_PROCESSES *sp;
1332 PULONG p = new ULONG[n];
1334 while (STATUS_INFO_LENGTH_MISMATCH ==
1335 (ret = NtQuerySystemInformation (SystemProcessesAndThreadsInformation,
1337 n * sizeof *p, NULL)))
1338 delete [] p, p = new ULONG[n *= 2];
1339 if (ret != STATUS_SUCCESS)
1341 debug_printf ("NtQuerySystemInformation: ret %d, Dos(ret) %d",
1342 ret, RtlNtStatusToDosError (ret));
1346 sp = (SYSTEM_PROCESSES *) p;
1349 if (sp->ProcessId == dwProcessId)
1352 st = &sp->Threads[0];
1354 for (unsigned i = 0; i < sp->ThreadCount; i++)
1356 /* FIXME: at some point we should consider generating 'O' */
1357 if (st->State == StateRunning ||
1358 st->State == StateReady)
1367 if (!sp->NextEntryDelta)
1369 sp = (SYSTEM_PROCESSES *) ((char *) sp + sp->NextEntryDelta);
1377 get_mem_values (DWORD dwProcessId, unsigned long *vmsize, unsigned long *vmrss,
1378 unsigned long *vmtext, unsigned long *vmdata,
1379 unsigned long *vmlib, unsigned long *vmshare)
1385 MEMORY_WORKING_SET_LIST *mwsl;
1386 ULONG n = 0x4000, length;
1387 PMEMORY_WORKING_SET_LIST p = (PMEMORY_WORKING_SET_LIST) malloc (n);
1388 unsigned page_size = getsystempagesize ();
1389 hProcess = OpenProcess (PROCESS_QUERY_INFORMATION,
1390 FALSE, dwProcessId);
1391 if (hProcess == NULL)
1394 debug_printf ("OpenProcess, %E");
1399 ret = NtQueryVirtualMemory (hProcess, 0, MemoryWorkingSetList,
1400 (PVOID) p, n, (length = ULONG_MAX, &length));
1401 if (ret != STATUS_INFO_LENGTH_MISMATCH)
1404 PMEMORY_WORKING_SET_LIST new_p = (PMEMORY_WORKING_SET_LIST)
1410 if (!NT_SUCCESS (ret))
1412 debug_printf ("NtQueryVirtualMemory: ret %p", ret);
1413 if (ret == STATUS_PROCESS_IS_TERMINATING)
1415 *vmsize = *vmrss = *vmtext = *vmdata = *vmlib = *vmshare = 0;
1419 __seterrno_from_nt_status (ret);
1422 mwsl = (MEMORY_WORKING_SET_LIST *) p;
1423 for (unsigned long i = 0; i < mwsl->NumberOfPages; i++)
1426 unsigned flags = mwsl->WorkingSetList[i] & 0x0FFF;
1427 if ((flags & (WSLE_PAGE_EXECUTE | WSLE_PAGE_SHAREABLE))
1428 == (WSLE_PAGE_EXECUTE | WSLE_PAGE_SHAREABLE))
1430 else if (flags & WSLE_PAGE_SHAREABLE)
1432 else if (flags & WSLE_PAGE_EXECUTE)
1437 ret = NtQueryInformationProcess (hProcess, ProcessVmCounters, (PVOID) &vmc,
1439 if (!NT_SUCCESS (ret))
1441 debug_printf ("NtQueryInformationProcess: ret %p", ret);
1442 __seterrno_from_nt_status (ret);
1445 *vmsize = vmc.PagefileUsage / page_size;
1449 CloseHandle (hProcess);