Nicholas Nethercote: Measuring memory used by third-party code |
Firefox’s memory reporting infrastructure, which underlies about:memory, is great. And when it lacks coverage — causing the “heap-unclassified” number to get large — we can use DMD to identify where the unreported allocations are coming from. Using this information, we can extend existing memory reporters or write new ones to cover the missing heap blocks.
But there is one exception: third-party code. Well… some libraries support custom allocators, which is great, because it lets us provide a counting allocator. And if we have a copy of the third-party code within Firefox, we can even use some pre-processor hacks to forcibly provide custom counting allocators for code that doesn’t support them.
But some heap allocations are done by code we have no control over, like OpenGL drivers. For example, after opening a simple WebGL demo on my Linux box, I have over 50% “heap-unclassified”.
208.11 MB (100.0%) -- explicit +--107.76 MB (51.78%) -- heap-unclassified
DMD’s output makes it clear that the OpenGL drivers are responsible. The following record is indicative.
Unreported: 1 block in stack trace record 2 of 3,597 15,486,976 bytes (15,482,896 requested / 4,080 slop) 6.92% of the heap (20.75% cumulative); 10.56% of unreported (31.67% cumulative) Allocated at replace_malloc (/home/njn/moz/mi8/co64dmd/memory/replace/dmd/../../../../memory/replace/dmd/DMD.cpp:1245) 0x7bf895f1 _swrast_CreateContext (??:?) 0x3c907f03 ??? (/usr/lib/x86_64-linux-gnu/dri/i965_dri.so) 0x3cd84fa8 ??? (/usr/lib/x86_64-linux-gnu/dri/i965_dri.so) 0x3cd9fa2c ??? (/usr/lib/x86_64-linux-gnu/dri/i965_dri.so) 0x3cd8b996 ??? (/usr/lib/x86_64-linux-gnu/dri/i965_dri.so) 0x3ce1f790 ??? (/usr/lib/x86_64-linux-gnu/dri/i965_dri.so) 0x3ce1f935 glXGetDriverConfig (??:?) 0x3dce1827 glXDestroyGLXPixmap (??:?) 0x3dcbc213 glXCreateNewContext (??:?) 0x3dcbc48a mozilla::gl::GLContextGLX::CreateGLContext(mozilla::gfx::SurfaceCaps const&, mozilla::gl::GLContextGLX*, bool, _XDisplay*, unsigned long, __GLXFBConfigRec*, bool, gfxXlibSurface*) (/home/njn/moz/mi8/co64dmd/gfx/gl/../../../gfx/gl/GLContextProviderGLX.cpp:783) 0x753c99f4
The bottom-most frame is for a function (CreateGLContext
) within Firefox’s codebase, and then control passes to the OpenGL driver, which eventually does a heap allocation, which ends up in DMD’s replace_malloc
function.
The following DMD report is a similar case that shows up on Firefox OS.
Unreported: 1 block in stack trace record 1 of 463 1,454,080 bytes (1,454,080 requested / 0 slop) 9.75% of the heap (9.75% cumulative); 21.20% of unreported (21.20% cumulative) Allocated at replace_calloc /Volumes/firefoxos/B2G/gecko/memory/replace/dmd/DMD.cpp:1264 (0xb6f90744 libdmd.so+0x5744) os_calloc (0xb25aba16 libgsl.so+0xda16) (no addr2line) rb_alloc_primitive_lists (0xb1646ebc libGLESv2_adreno.so+0x74ebc) (no addr2line) rb_context_create (0xb16446c6 libGLESv2_adreno.so+0x726c6) (no addr2line) gl2_context_create (0xb16216f6 libGLESv2_adreno.so+0x4f6f6) (no addr2line) eglCreateClientApiContext (0xb25d3048 libEGL_adreno.so+0x1a048) (no addr2line) qeglDrvAPI_eglCreateContext (0xb25c931c libEGL_adreno.so+0x1031c) (no addr2line) eglCreateContext (0xb25bfb58 libEGL_adreno.so+0x6b58) (no addr2line) eglCreateContext /Volumes/firefoxos/B2G/frameworks/native/opengl/libs/EGL/eglApi.cpp:527 (0xb423dda2 libEGL.so+0xeda2) mozilla::gl::GLLibraryEGL::fCreateContext(void*, void*, void*, int const*) /Volumes/firefoxos/B2G/gecko/gfx/gl/GLLibraryEGL.h:180 (discriminator 3) (0xb4e88f4c libxul.so+0x789f4c)
We can’t traverse these allocations in the usual manner to measure them, because we have no idea about the layout of the relevant data structures. And we can’t provide a custom counting allocator to code outside of Firefox’s codebase.
However, although we pass control to the driver, control eventually comes back to the heap allocator, and that is something that we do have some power to change. So I had an idea to toggle some kind of mode that records all the allocations that occur within a section of code, as the following code snippet demonstrates.
SetHeapBlockTagForThread("webgl-create-new-context"); context = glx.xCreateNewContext(display, cfg, LOCAL_GLX_RGBA_TYPE, glxContext, True); ClearHeapBlockTagForThread();
The calls on either side of glx.xCreateNewContext
tell the allocator that it should tag all allocations done within that call. And later on, the relevant memory reporter can ask the allocator how many of these allocations remain and how big they are. I’ve implemented a draft version of this, and it basically works, as the following about:memory output shows.
216.97 MB (100.0%) -- explicit +---78.50 MB (36.18%) -- webgl-contexts +---32.37 MB (14.92%) -- heap-unclassified
The implementation is fairly simple.
SetHeapBlockTagForThread
is called, the given tag is stored in thread-local storage. When ClearHeapBlockTagForThread
is called, the tag is cleared.Unfortunately, the implementation isn’t suitable for landing in Firefox’s code, for several reasons.
replace_malloc
infrastructure to wrap the default allocator (jemalloc). This works well — DMD does the same thing — but using it requires doing a special build and then setting some environment variables at start-up. This is ok for an occasional-use tool that’s only used by Firefox developers, but it’s important that about:memory works in vanilla builds without any additional effort.This issue of OpenGL drivers and other kinds of third-party code has been a long-term shortcoming with about:memory. For the first time I have a partial solution, though it still has major problems. I’d love to hear if anyone has additional ideas on how to make it better.
https://blog.mozilla.org/nnethercote/2014/07/07/measuring-memory-used-by-third-party-code/
Комментировать | « Пред. запись — К дневнику — След. запись » | Страницы: [1] [Новые] |