Extending /proc/*/maps
Ryan Johnson
ryan.johnson@cs.utoronto.ca
Thu May 12 16:31:00 GMT 2011
On 12/05/2011 11:09 AM, Corinna Vinschen wrote:
> On May 12 14:10, Corinna Vinschen wrote:
>> On May 11 21:31, Corinna Vinschen wrote:
>>> On May 11 13:46, Ryan Johnson wrote:
>>>> Given that Heap32* has already been reverse-engineered by others,
>>>> the main challenge would involve sorting the set of heap block
>>>> addresses and distilling them down to a set of allocation bases. We
>>>> don't want to do repeated linear searches over 50k+ heap blocks.
>>> While the base address of the heap is available in
>>> DEBUG_HEAP_INFORMATION, I don't see the size of the heap. Maybe it's in
>>> the block of 7 ULONGs marked as "Reserved"? It must be somewhere.
>>> Assuming just that, you could scan the list of blocks once and drop
>>> those within the orignal heap allocation. The remaining blocks are big
>>> blocks which have been allocated by additional calls to VirtualAlloc.
>> After some debugging, I now have the solution. [...]
> Here's a prelimiary patch to fhandler_process.cc which takes everything
> into account I have learned in the meantime. For instance, there are
> actually heaps marked as shareable. Please have a look. What's missing
> is the flag for low-fragmentation heaps, but I'm just hunting for it.
I like it. Detailed comments below.
> +/* Known heap flags */
> +#define HEAP_FLAG_NOSERIALIZE 0x1
> +#define HEAP_FLAG_GROWABLE 0x2
> +#define HEAP_FLAG_EXCEPTIONS 0x4
> +#define HEAP_FLAG_NONDEFAULT 0x1000
> +#define HEAP_FLAG_SHAREABLE 0x8000
> +#define HEAP_FLAG_EXECUTABLE 0x40000
Would it make sense to put those in ntdll.h along with the heap structs
that use them?
> struct heap
> {
> heap *next;
> - void *base;
> + unsigned heap_id;
> + uintptr_t base;
> + uintptr_t end;
> + unsigned long flags;
> };
We don't actually need the end pointer: we're trying to match an unknown
allocation base against heap region bases. The code which traverses VM
allocations should query heap_info at most once per allocation (for
example, it only looks up the file name of cygwin1.dll once even though
the latter has 12 entries in /proc/*/maps).
> heap *heaps;
This is a misnomer now -- it's really a list of heap regions/blocks.
> + heap *h = (heap *) cmalloc (HEAP_FHANDLER, sizeof (heap));
> + *h = (heap) {heaps, hcnt, barray[bcnt].Address,
> + barray[bcnt].Address + barray[bcnt].Size,
> + harray->Heaps[hcnt].Flags};
> + heaps = h;
Given that the number of heap blocks is potentially large, I think it
makes sense to build a sorted list. That way, each query examines only
one heap block (deleting it unless it was above the queried address). I
have ready-but-unsent a patch which does this to the checked-in version
of the code. Shall I send it?
> - char *fill_if_match (void *base, char *dest )
> + char *fill_if_match (void *base, ULONG type, char *dest )
> {
> - long count = 0;
> - for (heap *h = heaps; h&& ++count; h = h->next)
> - if (base == h->base)
> + for (heap *h = heaps; h; h = h->next)
> + if ((uintptr_t) base>= h->base&& (uintptr_t) base< h->end)
> {
> - __small_sprintf (dest, "[heap %ld]", count);
> + char *p;
> + __small_sprintf (dest, "[heap %ld", h->heap_id);
> + p = strchr (dest, '\0');
> + if (!(h->flags& HEAP_FLAG_NONDEFAULT))
> + p = stpcpy (p, " default");
> + if ((h->flags& HEAP_FLAG_SHAREABLE)&& (type& MEM_MAPPED))
> + p = stpcpy (p, " share");
> + if (h->flags& HEAP_FLAG_EXECUTABLE)
> + p = stpcpy (p, " exec");
> + if (h->flags& HEAP_FLAG_GROWABLE)
> + p = stpcpy (p, " grow");
> + if (h->flags& HEAP_FLAG_NOSERIALIZE)
> + p = stpcpy (p, " noserial");
> + stpcpy (p, "]");
> return dest;
> }
> return 0;
Do you actually encounter requests which fall inside a heap region
rather than at its start? I have not seen this in my experiments, and if
you have it is almost certainly a bug in format_process_maps' allocation
traversal.
Also, are there ever shareable-but-not-mem_mapped segments? If not, we
could probably remove 'type.'
Ryan
More information about the Cygwin-patches
mailing list