mirror of
git://sourceware.org/git/newlib-cygwin.git
synced 2025-01-18 12:29:32 +08:00
* fhandler_process.cc (format_process_maps): Rework to report
all mapped address space in a process (committed or reserved), identifying the nature of the mapping (mapped file/image, heap, shared memory) when possible. (dos_drive_mappings): New helper classes. (heap_info): Ditto. * ntdll.h (struct _MEMORY_SECTION_NAME): Define.
This commit is contained in:
parent
34a6eeabff
commit
8285dae540
@ -1,3 +1,13 @@
|
||||
2011-05-11 Ryan Johnson <ryan.johnson@cs.utoronto.ca>
|
||||
|
||||
* fhandler_process.cc (format_process_maps): Rework to report
|
||||
all mapped address space in a process (committed or reserved),
|
||||
identifying the nature of the mapping (mapped file/image, heap,
|
||||
shared memory) when possible.
|
||||
(dos_drive_mappings): New helper classes.
|
||||
(heap_info): Ditto.
|
||||
* ntdll.h (struct _MEMORY_SECTION_NAME): Define.
|
||||
|
||||
2011-05-11 Corinna Vinschen <corinna@vinschen.de>
|
||||
|
||||
* autoload.cc (GetProcessMemoryInfo): Remove.
|
||||
|
@ -29,6 +29,7 @@ details. */
|
||||
#include <sys/param.h>
|
||||
#include <ctype.h>
|
||||
#include <psapi.h>
|
||||
#include <tlhelp32.h>
|
||||
|
||||
#define _COMPILING_NEWLIB
|
||||
#include <dirent.h>
|
||||
@ -527,6 +528,136 @@ format_process_winexename (void *data, char *&destbuf)
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
struct dos_drive_mappings
|
||||
{
|
||||
struct mapping
|
||||
{
|
||||
mapping *next;
|
||||
int len;
|
||||
wchar_t drive_letter;
|
||||
wchar_t mapping[1];
|
||||
};
|
||||
mapping *mappings;
|
||||
|
||||
dos_drive_mappings ()
|
||||
: mappings(0)
|
||||
{
|
||||
/* The logical drive strings buffer holds a list of (at most 26)
|
||||
drive names separated by nulls and terminated by a double-null:
|
||||
|
||||
"a:\\\0b:\\\0...z:\\\0"
|
||||
|
||||
The annoying part is, QueryDosDeviceW wants only "x:" rather
|
||||
than the "x:\" we get back from GetLogicalDriveStringsW, so
|
||||
we'll have to strip out the trailing slash for each mapping.
|
||||
|
||||
The returned mapping a native NT pathname (\Device\...) which
|
||||
we can use to fix up the output of GetMappedFileNameW
|
||||
*/
|
||||
static unsigned const DBUFLEN = 26 * 4;
|
||||
wchar_t dbuf[DBUFLEN + 1];
|
||||
wchar_t pbuf[NT_MAX_PATH];
|
||||
wchar_t drive[] = {L'x', L':', 0};
|
||||
unsigned result = GetLogicalDriveStringsW (DBUFLEN * sizeof (wchar_t),
|
||||
dbuf);
|
||||
if (!result)
|
||||
debug_printf ("Failed to get logical DOS drive names: %lu",
|
||||
GetLastError ());
|
||||
else if (result > DBUFLEN)
|
||||
debug_printf ("Too many mapped drive letters: %u", result);
|
||||
else
|
||||
for (wchar_t *cur = dbuf; (*drive = *cur); cur = wcschr (cur, L'\0')+1)
|
||||
if (QueryDosDeviceW (drive, pbuf, NT_MAX_PATH))
|
||||
{
|
||||
size_t plen = wcslen (pbuf);
|
||||
size_t psize = plen * sizeof (wchar_t);
|
||||
debug_printf ("DOS drive %ls maps to %ls", drive, pbuf);
|
||||
mapping *m = (mapping*) cmalloc (HEAP_FHANDLER,
|
||||
sizeof (mapping) + psize);
|
||||
m->next = mappings;
|
||||
m->len = plen;
|
||||
m->drive_letter = *drive;
|
||||
memcpy (m->mapping, pbuf, psize + sizeof (wchar_t));
|
||||
mappings = m;
|
||||
}
|
||||
else
|
||||
debug_printf ("Unable to determine the native mapping for %ls "
|
||||
"(error %lu)", drive, GetLastError ());
|
||||
}
|
||||
|
||||
wchar_t *fixup_if_match (wchar_t *path)
|
||||
{
|
||||
for (mapping *m = mappings; m; m = m->next)
|
||||
if (!wcsncmp (m->mapping, path, m->len))
|
||||
{
|
||||
path += m->len - 2;
|
||||
path[0] = m->drive_letter;
|
||||
path[1] = L':';
|
||||
break;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
~dos_drive_mappings ()
|
||||
{
|
||||
mapping *n = 0;
|
||||
for (mapping *m = mappings; m; m = n)
|
||||
{
|
||||
n = m->next;
|
||||
cfree (m);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct heap_info
|
||||
{
|
||||
struct heap
|
||||
{
|
||||
heap *next;
|
||||
void *base;
|
||||
};
|
||||
heap *heaps;
|
||||
|
||||
heap_info (DWORD pid)
|
||||
: heaps (0)
|
||||
{
|
||||
HANDLE hHeapSnap = CreateToolhelp32Snapshot (TH32CS_SNAPHEAPLIST, pid);
|
||||
HEAPLIST32 hl;
|
||||
hl.dwSize = sizeof(hl);
|
||||
|
||||
if (hHeapSnap != INVALID_HANDLE_VALUE && Heap32ListFirst (hHeapSnap, &hl))
|
||||
do
|
||||
{
|
||||
heap *h = (heap *) cmalloc (HEAP_FHANDLER, sizeof (heap));
|
||||
*h = (heap) {heaps, (void*) hl.th32HeapID};
|
||||
heaps = h;
|
||||
} while (Heap32ListNext (hHeapSnap, &hl));
|
||||
CloseHandle (hHeapSnap);
|
||||
}
|
||||
|
||||
char *fill_if_match (void *base, char *dest )
|
||||
{
|
||||
long count = 0;
|
||||
for (heap *h = heaps; h && ++count; h = h->next)
|
||||
if (base == h->base)
|
||||
{
|
||||
__small_sprintf (dest, "[heap %ld]", count);
|
||||
return dest;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
~heap_info ()
|
||||
{
|
||||
heap *n = 0;
|
||||
for (heap *m = heaps; m; m = n)
|
||||
{
|
||||
n = m->next;
|
||||
cfree (m);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static _off64_t
|
||||
format_process_maps (void *data, char *&destbuf)
|
||||
{
|
||||
@ -538,14 +669,28 @@ format_process_maps (void *data, char *&destbuf)
|
||||
return 0;
|
||||
|
||||
_off64_t len = 0;
|
||||
HMODULE *modules;
|
||||
DWORD needed, i;
|
||||
DWORD_PTR wset_size;
|
||||
DWORD_PTR *workingset = NULL;
|
||||
MODULEINFO info;
|
||||
|
||||
union access
|
||||
{
|
||||
char flags[8];
|
||||
_off64_t word;
|
||||
} a;
|
||||
|
||||
struct region {
|
||||
access a;
|
||||
char *abase;
|
||||
char *rbase;
|
||||
char *rend;
|
||||
} cur = {{{'\0'}}, (char *)1, 0, 0};
|
||||
|
||||
MEMORY_BASIC_INFORMATION mb;
|
||||
dos_drive_mappings drive_maps;
|
||||
heap_info heaps (p->dwProcessId);
|
||||
struct __stat64 st;
|
||||
long last_pass = 0;
|
||||
|
||||
tmp_pathbuf tp;
|
||||
PWCHAR modname = tp.w_get ();
|
||||
PMEMORY_SECTION_NAME msi = (PMEMORY_SECTION_NAME) tp.w_get ();
|
||||
char *posix_modname = tp.c_get ();
|
||||
size_t maxsize = 0;
|
||||
|
||||
@ -554,75 +699,92 @@ format_process_maps (void *data, char *&destbuf)
|
||||
cfree (destbuf);
|
||||
destbuf = NULL;
|
||||
}
|
||||
if (!EnumProcessModules (proc, NULL, 0, &needed))
|
||||
|
||||
/* Iterate over each VM region in the address space, coalescing
|
||||
memory regions with the same permissions. Once we run out, do one
|
||||
last_pass to trigger output of the last accumulated region. */
|
||||
for (char *i = 0;
|
||||
VirtualQueryEx (proc, i, &mb, sizeof(mb)) || (1 == ++last_pass);
|
||||
i = cur.rend)
|
||||
{
|
||||
__seterrno ();
|
||||
len = -1;
|
||||
goto out;
|
||||
}
|
||||
modules = (HMODULE*) alloca (needed);
|
||||
if (!EnumProcessModules (proc, modules, needed, &needed))
|
||||
{
|
||||
__seterrno ();
|
||||
len = -1;
|
||||
goto out;
|
||||
}
|
||||
if (mb.State == MEM_FREE)
|
||||
a.word = 0;
|
||||
else
|
||||
{
|
||||
static DWORD const RW = (PAGE_EXECUTE_READWRITE | PAGE_READWRITE
|
||||
| PAGE_EXECUTE_WRITECOPY | PAGE_WRITECOPY);
|
||||
DWORD p = mb.Protect;
|
||||
a = (access) {{
|
||||
(p & (RW | PAGE_EXECUTE_READ | PAGE_READONLY))? 'r' : '-',
|
||||
(p & (RW))? 'w' : '-',
|
||||
(p & (PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ
|
||||
| PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE))? 'x' : '-',
|
||||
(p & (PAGE_GUARD))? 's' : 'p',
|
||||
'\0', // zero-fill the remaining bytes
|
||||
}};
|
||||
}
|
||||
|
||||
QueryWorkingSet (proc, (void *) &wset_size, sizeof wset_size);
|
||||
if (GetLastError () == ERROR_BAD_LENGTH)
|
||||
{
|
||||
workingset = (DWORD_PTR *) alloca (sizeof (DWORD_PTR) * ++wset_size);
|
||||
if (!QueryWorkingSet (proc, (void *) workingset,
|
||||
sizeof (DWORD_PTR) * wset_size))
|
||||
workingset = NULL;
|
||||
}
|
||||
for (i = 0; i < needed / sizeof (HMODULE); i++)
|
||||
if (GetModuleInformation (proc, modules[i], &info, sizeof info)
|
||||
&& GetModuleFileNameExW (proc, modules[i], modname, NT_MAX_PATH))
|
||||
{
|
||||
char access[5];
|
||||
strcpy (access, "r--p");
|
||||
struct __stat64 st;
|
||||
if (mount_table->conv_to_posix_path (modname, posix_modname, 0))
|
||||
sys_wcstombs (posix_modname, NT_MAX_PATH, modname);
|
||||
if (stat64 (posix_modname, &st))
|
||||
{
|
||||
st.st_dev = 0;
|
||||
st.st_ino = 0;
|
||||
}
|
||||
size_t newlen = strlen (posix_modname) + 62;
|
||||
if (len + newlen >= maxsize)
|
||||
destbuf = (char *) crealloc_abort (destbuf,
|
||||
maxsize += roundup2 (newlen, 2048));
|
||||
if (workingset)
|
||||
for (unsigned i = 1; i <= wset_size; ++i)
|
||||
region next = { a,
|
||||
(char *) mb.AllocationBase,
|
||||
(char *) mb.BaseAddress,
|
||||
(char *) mb.BaseAddress+mb.RegionSize
|
||||
};
|
||||
|
||||
/* Windows permissions are more fine-grained than the unix rwxp,
|
||||
so we reduce clutter by manually coalescing regions sharing
|
||||
the same allocation base and effective permissions. */
|
||||
bool newbase = (next.abase != cur.abase);
|
||||
if (!last_pass && !newbase && next.a.word == cur.a.word)
|
||||
cur.rend = next.rend; // merge with previous
|
||||
else
|
||||
{
|
||||
// output the current region if it's "interesting"
|
||||
if (cur.a.word)
|
||||
{
|
||||
DWORD_PTR addr = workingset[i] & 0xfffff000UL;
|
||||
if ((char *)addr >= info.lpBaseOfDll
|
||||
&& (char *)addr < (char *)info.lpBaseOfDll + info.SizeOfImage)
|
||||
{
|
||||
access[0] = (workingset[i] & 0x5) ? 'r' : '-';
|
||||
access[1] = (workingset[i] & 0x4) ? 'w' : '-';
|
||||
access[2] = (workingset[i] & 0x2) ? 'x' : '-';
|
||||
access[3] = (workingset[i] & 0x100) ? 's' : 'p';
|
||||
}
|
||||
size_t newlen = strlen (posix_modname) + 62;
|
||||
if (len + newlen >= maxsize)
|
||||
destbuf = (char *) crealloc_abort (destbuf,
|
||||
maxsize += roundup2 (newlen,
|
||||
2048));
|
||||
int written = __small_sprintf (destbuf + len,
|
||||
"%08lx-%08lx %s %08lx %04x:%04x %U ",
|
||||
cur.rbase, cur.rend, cur.a.flags,
|
||||
cur.rbase - cur.abase,
|
||||
st.st_dev >> 16,
|
||||
st.st_dev & 0xffff,
|
||||
st.st_ino);
|
||||
while (written < 62)
|
||||
destbuf[len + written++] = ' ';
|
||||
len += written;
|
||||
len += __small_sprintf (destbuf + len, "%s\n", posix_modname);
|
||||
}
|
||||
int written = __small_sprintf (destbuf + len,
|
||||
"%08lx-%08lx %s %08lx %04x:%04x %U ",
|
||||
info.lpBaseOfDll,
|
||||
(unsigned long)info.lpBaseOfDll
|
||||
+ info.SizeOfImage,
|
||||
access,
|
||||
info.EntryPoint,
|
||||
st.st_dev >> 16,
|
||||
st.st_dev & 0xffff,
|
||||
st.st_ino);
|
||||
while (written < 62)
|
||||
destbuf[len + written++] = ' ';
|
||||
len += written;
|
||||
len += __small_sprintf (destbuf + len, "%s\n", posix_modname);
|
||||
}
|
||||
out:
|
||||
// start of a new region (but possibly still the same allocation)
|
||||
cur = next;
|
||||
// if a new allocation, figure out what kind it is
|
||||
if (newbase && !last_pass)
|
||||
{
|
||||
st.st_dev = 0;
|
||||
st.st_ino = 0;
|
||||
if ((mb.Type & (MEM_MAPPED | MEM_IMAGE))
|
||||
&& NT_SUCCESS (NtQueryVirtualMemory (proc, cur.abase,
|
||||
MemorySectionName,
|
||||
msi, 65536, NULL)))
|
||||
{
|
||||
PWCHAR dosname =
|
||||
drive_maps.fixup_if_match (msi->SectionFileName.Buffer);
|
||||
if (mount_table->conv_to_posix_path (dosname,
|
||||
posix_modname, 0))
|
||||
sys_wcstombs (posix_modname, NT_MAX_PATH, dosname);
|
||||
stat64 (posix_modname, &st);
|
||||
}
|
||||
else if (mb.Type & MEM_MAPPED)
|
||||
strcpy (posix_modname, "[shareable]");
|
||||
else if (!(mb.Type & MEM_PRIVATE
|
||||
&& heaps.fill_if_match (cur.abase, posix_modname)))
|
||||
posix_modname[0] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
CloseHandle (proc);
|
||||
return len;
|
||||
}
|
||||
|
@ -645,6 +645,11 @@ typedef struct _MEMORY_WORKING_SET_LIST
|
||||
ULONG WorkingSetList[1];
|
||||
} MEMORY_WORKING_SET_LIST, *PMEMORY_WORKING_SET_LIST;
|
||||
|
||||
typedef struct _MEMORY_SECTION_NAME
|
||||
{
|
||||
UNICODE_STRING SectionFileName;
|
||||
} MEMORY_SECTION_NAME, *PMEMORY_SECTION_NAME;
|
||||
|
||||
typedef struct _FILE_BASIC_INFORMATION {
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
|
Loading…
x
Reference in New Issue
Block a user