This section gives an overview of how to use Dr. Memory, divided into the following sub-sections:
See also the full reference of Dr. Memory's runtime options:
This section describes requirements, setup instructions, and other concerns prior to running Dr. Memory on Linux.
Ensure your Linux machine has the following utilities installed:
This section describes requirements, setup instructions, and other concerns prior to running Dr. Memory on Windows.
Please note that Dr. Memory does not yet have full support for the Windows graphical system calls, leading to extra false positives, particularly on Windows Vista and Windows 7. Future releases will eliminate these; for now, suppression files can be used.
Dr. Memory displays results using notepad
by default. If it is not on the PATH a message pointing to the file containing the results will be displayed. The option -batch
can be used to disable the launch of notepad
at application exit.
Dr. Memory supports applications compiled with the MinGW gcc or g++ compilers, but will only provide line number information if DWARF2 debugging information is present. The default for MinGW gcc prior to version 4.3 is to use the stabs format, which is not supported by Dr. Memory. Pass the -ggdb option to gcc to generate DWARF2 format instead.
For MinGW C++ applications, Dr. Memory also requires the use of static libstdc++ libraries. Pass "-static-libgcc -static-libstdc++" to g++ to request this. Since these static libraries contain frame pointer optimizations, we recommend passing the "-no_callstack_use_top_fp" runtime option to Dr. Memory. This option is also discussed below. Dr. Memory attempts to automatically use this setting when it executes an application built with MinGW.
Currently there is no support for applications built with Cygwin gcc.
The regular Dr. Memory build can be executed just fine from a Cygwin shell when targeting non-Cygwin applications.
The first step is to unzip or untar the Dr. Memory package.
In order to obtain line number information, compile your target application with debugging information enabled (e.g., "/Zi" for cl.exe or "-g" for gcc or g++).
On Linux, Dr. Memory supports ELF files with DWARF2 line information. On Windows, Dr. Memory supports PDB debug information (produced by Visual Studio). The Windows release of Dr. Memory does not support symbols for applications built with gcc or g++ on Windows.
If you want to run your application with stripped libraries or binaries and have the unstripped versions in separate locations, you can point at those locations using the DRMEMORY_LIB_PATH
environment variable, which is a colon-separated list of directories.
On Windows, the debug DLL C library (specified with the "/MDd" flag) is currently not supported. Instead, build your application with either the release DLL ("/MD") or the release or debug static library ("/MT" or "/MTd"), although "/MTd" is not recommended (see below). When using Visual Studio, the release library can be selected from Visual Studio's configuration dialog (press Alt-F7 to bring it up) under the C/C++ | Code Generation | Runtime Library settings. Change "Multi-threaded Debug DLL (/MDd)" to "Multi-threaded DLL (/MD)". If your application is C++, another step is necessary when using /MD to avoid the C++ Debug DLL: under Configuration Properties | C/C++ | Preprocessor, remove "_DEBUG" from the list of "Preprocessor Definitions".
On Windows, when using debug versions of the C library (when building with the "/MTd" flag ("/MDd" is not supported at all), you will not be able to control the redzone size. The debug heap checks will mostly be disabled as they overlap and can conflict with Dr. Memory's checks. It is recommended that the release version of the heap routines be used. Additionally, when using static debug versions of msvcrt (the "/MTd" flag), if your application does not have symbols then Dr. Memory will not be able to identify and avoid conflicts with msvcrt's own heap debugging facilities, which may lead to false positives and/or false negatives. Be sure to build with debugging information included.
You'll get better callstacks in error reports if you disable inlining. For cl.exe, "/Ob0", or for gcc or g++, "-fno-inline". Dr. Memory should be able to determine callstacks in the presence of frame pointer optimizations (FPO) in most cases, but if you see strange callstacks in an optimized application consider disabling FPO: for cl.exe, "/Oy-"; for gcc or g++, "-fno-omit-frame-pointer".
If your application links with a static library that was built with frame pointer optimizations and you observe missing frames on your callstacks, try running with the option "-no_callstack_use_top_fp". This will add some additional overhead on malloc-intensive applications, but should eliminate skipped frames.
The Visual Studio compiler's /RTC1 flag can prevent Dr. Memory from reporting uninitialized reads of local variables, and the /RTC1 checks for uninitialized reads themselves may not catch everything that Dr. Memory finds. However, /RTC1 does perform additional stack checks that Dr. Memory does not, so for best results, your application should be run under Dr. Memory without /RTC1, and run natively with /RTC1.
Run your application as you normally would from a command prompt (on Windows, either the cmd
shell or a Cygwin prompt), with drmemory.exe
and "--" prefixed to the command line (the "--" separates any arguments to Dr. Memory from the application being run).
~/DrMemory-Windows-1.4.6-1/bin/drmemory.exe -- c:/path/to/my/app args to my app
On Windows, if you used the installer and checked the box to add drmemory.exe
to your PATH, you can simply type into your shell:
drmemory.exe -- c:/path/to/my/app args to my app
You can also drag-and-drop your application onto drmemory.exe
in Windows Explorer or on the Desktop if you requested a Desktop shorcut upon installation.
When running scripts it is best to explicitly invoke the interpreter rather than invoking the script directly. For example:
~/DrMemory-Windows-1.4.6-1/bin/drmemory.exe -- /usr/bin/perl ./myscript.pl
Dr. Memory's results are written to a file called results.txt
inside a subdirectory created for each invocation of Dr. Memory, inside Dr. Memory's log directory. The log directory by default is the logs
subdirectory of the unpacked installation, or the Dr. Memory directory inside your profile directory when Dr. Memory is installed into Program Files on Windows. It can be changed with the -logdir
option:
~/DrMemory-Windows-1.4.6-1/bin/drmemory.exe -logdir c:/logs -- myapp
When an application is executed under Dr. Memory's control, it creates a subdirectory in the base log directory named "DrMemory-<appname>.<pid>.NNN", where NNN is a counter that is incremented to ensure unique names.
On Windows, the results files is automatically opened in notepad
when the application exits. This only occurs for the top-level process; if it created child processes, they will be followed by Dr. Memory and have their own result directories and files, but these will not be automatically displayed. The option -batch
can be used to disable the launch of notepad
at application exit.
For full details on the errors reported by Dr. Memory, see Error Types Reported by Dr. Memory.
If you would like to attach a debugger at the point an error is first detected, use the -pause_at_unaddressable
or -pause_at_uninitialized
options (see Dr. Memory Runtime Option Reference).
By default, Dr. Memory monitors all child processes. To disable this behavior and not monitor any children, use this option:
~/DrMemory-Windows-1.4.6-1/bin/drmemory.exe -no_follow_children
To follow all children except certain applications, run this command with the executable name of each application you do not wish to monitor, prior to running Dr. Memory with default options:
~/DrMemory-Windows-1.4.6-1/bin/drconfig.exe -quiet -reg myapp -norun
This -norun
request is permanent and affects all subsequent Dr. Memory executions by the user that issued the -norun
request. To undo it, use this command:
~/DrMemory-Windows-1.4.6-1/bin/drconfig.exe -quiet -unreg myapp
Dr. Memory supports aggregating results from multiple log directories, whether from multiple processes of a multi-process application or from multiple runs of the same application. Simply use the -aggregate
option and supply the list of log directories, or a single directory containing the log directories as sub-directories to aggregate all of them:
~/DrMemory-Windows-1.4.6-1/bin/drmemory.exe -aggregate DrMemory-myapp.1234.000 DrMemory-myapp.1235.000
~/DrMemory-Windows-1.4.6-1/bin/drmemory.exe -aggregate /parent/logdir/
While Dr. Memory updates the results.txt
file as the application runs, it does not perform leak checking or produce a summary of errors until the end of the run. For applications that do not have a normal exit, such as daemons, Dr. Memory provides a method of forcing end-of-run actions.
Run drmemory.exe
with the -nudge
option and the process identifier of the application in order to request leak checking and other end-of-run actions:
~/DrMemory-Windows-1.4.6-1/bin/drmemory.exe -nudge processid
Some applications have very large symbol files. To reduce resource usage during an application run, symbol processing can be disabled while the application executes and instead be performed after the run. Use the -skip_results
option when running the application. Once finished, re-run Dr. Memory with the -results
option and the log directory created during the run (which contains the raw data):
~/DrMemory-Windows-1.4.6-1/bin/drmemory.exe -skip_results -- myapp myargs
~/DrMemory-Windows-1.4.6-1/bin/drmemory.exe -results DrMemory-myapp.9876.000
If -results
is invoked on a different machine where the application executable is not located at the same path as when it was executed with -skip_results
, use the -results_app
parameter to specify the executable along with the -results
parameter.
The -skip_results
option is not currently available on Windows.
On Linux, Dr. Memory will look in the default debug directories for symbols for a library: a .debug
subdirectory of the library's directory or in /usr/lib/debug
.
On Windows, the _NT_SYMBOL_PATH
environment variable is honored by Dr. Memory as a local cache of pdb
files. However, Dr. Memory does not support symbol store paths (those that contain srv
).
Dr. Memory generates a file named suppress.txt
alongside the results.txt
file. To suppress errors from being reported in future runs, edit this file to contain just the errors you wish to suppress. Then pass the file to drmemory.exe
with the -suppress option:
~/DrMemory-Windows-1.4.6-1/bin/drmemory.exe -suppress c:/suppress-custom.txt -- myapp
The suppress.txt generated by Dr. Memory will contain suppression information only for those errors that weren't suppressed using the -suppress option. For each error reported in suppress.txt, there will be two types of call stacks, one showing <module+offset>
type frames and the other module!function
type frames. The offset in <module+offset>
must be a lower-case hexadecimal constant (e.g., 0x4af
) and is the offset from the start of the module. Either type of call stack can be used and it is enough if one of those two is specified. The module!function
are more general and more robust across different versions of a module.
In order to simplify writing suppressions, callstacks support "*" and "?" wildcards in either the module or function name (or offset for <module+offset>
). A "*" matches any number of characters of any kind. A "?" matches a single character of any kind. C++ functions should be in their unmangled form. If a literal "*" is desired, a "?" should usually be used instead to avoid the "*" wildcard expansion.
To match any frame at all, use a single "*". To match any module but not any non-module (and thus not match a system call or non-module code, such as generated code), use "*!*".
Callstacks automatically employ prefix matching. Prefix matching means that the callstack in the suppression file is considered a match if it matches the top of any actual callstack during a run. This allows specifying only the first few frames of a callstack and have it match any callstack whose first frames match those frames, regardless of subsequent frames.
A final type of wildcard frame is supported: "...". A callstack frame consisting of the string "..." matches zero or more frames in the callstack.
As a variation on the "..." ellipsis frame, "module!..." matches one or more frames in a given module. Importantly, it will not match zero frames like a plain ellipsis. This form is most useful for suppressing reports through system libraries that are missing symbols, because oftentimes such reports can be identified by the way they call back and forth across system library boundaries.
Suppression files can have empty lines and comment lines (begining with #). There should be no leading white space for the actual suppression information. Suppressions should not end with "...", which is unnecessary due to the implicit prefix matching.
A suppression can be given an identifier with a name=
line after the error type line. Suppressions that were matched are printed in the results file with a count of how many unique callstacks were matched. Duplicate callstacks are not included. Here is an example:
LEAK name=bug #456 (deliberately leaked object) mylib.dll!LeakMe mylib.dll!*
On Windows, for unaddressable accesses and uninitialized reads, a suppression can be further restricted by the actual instruction involved. The instruction is printed out in an error report in a Note:
field. For example:
Error #8: UNADDRESSABLE ACCESS: reading 0x001338a8-0x001338ac 4 byte(s) ... Note: instruction: mov (%eax) -> %eax
A suppression can contain an instruction=
line after the error type line which will be matched against this note field. It can contain wildcards. The instruction=
line is currently ignored on Linux. Here is an example:
UNINITIALIZED READ name=bug #123 (deliberate uninitialized read to generate random number) instruction=test * $0x00000?00 myranlib.dll!GenRanHelper* myranlib.dll!GenRan*
For users with existing Valgrind Memcheck suppression files, Dr. Memory supports Valgrind-style suppressions, but not with mangled C++ symbols. On Linux, a script called valgrind2drmemory.pl
is provided in the bin directory that can be used to convert a legacy Valgrind suppression file to the Dr. Memory format. It uses heuristics to convert mangled C++ symbols that contain wildcards and may not succeed on complex types (it prints a warning for those it fails on). Use it like this:
bin/valgrind2drmemory.pl /path/to/old-supp-file /path/to/new-drmem-towrite
The Dr. Memory suppression format is more powerful then the Valgrind format and matches the Windows module!function standards used by other tools. We recommend converting to the Dr. Memory format from a legacy format.
Another method of ignoring errors is to filter the reported errors to focus on particular source files. Use the -srcfilter
option to do this:
~/DrMemory-Windows-1.4.6-1/bin/drmemory.exe -srcfilter hello.c -- ./hello
The -srcfilter
option is not available in the Windows version of Dr. Memory.
Dr. Memory comes with a set of default suppressions to avoid known false positives in system libraries. These can be disabled with the option -no_default_suppress.
Dr. Memory replaces several string and memory routines in the C library, executable, and other libraries, in order to avoid false positives from extremely-optimized versions of these routines. So if an application expects a crash inside a routine like strlen
, do not be alarmed if it shows up as replace_strlen
in the Dr. Memory library rather than strlen
in the C library.
Each error found by Dr. Memory is listed in the results.txt
file (see Examining the Results). The error report includes an error number, the type of error, the address and size of the memory access in question (if applicable to the error type), a timestamp and thread identifier indicating when and where the error was detected, and a callstack of the application at the point of the error. The thread identifier can be correlated with the function of a particular thread by locating the thread creation callstack in the global.pid.log
file in the same directory as the results.txt
file.
The -brief
option can be used to request simpler and in some cases easier to read (i.e., more suited to novices) error reports that hide STL and CRT paths, simplify source paths for the executable by removing the path containing the executable itself, and omit absolute addresses, thread timestamps, and instruction disassembly.
The following subsections describe each type of error. Leaks are described in Memory Leaks.
Dr. Memory considers any read or write of a memory location that was not allocated as an "unaddressable access". An allocation is:
An unaddressable access is an access to an invalid memory address. Examples include a buffer overflow, reading off the end of an array, reading or writing to memory that has been freed (often referred to as a "use-after-free" error), reading beyond the top of the stack, etc.
The heap allocator may pad the size requested by the application. The padded region beyond what the application asked for is considered unaddressable by Dr. Memory.
Consider this example code:
char *x = malloc(8); char c = *(x+8);
Here is a diagram of the resulting heap allocation:
malloc header: | unaddressable |
x: | uninitialized |
x + 8: | unaddressable |
Dr. Memory will report an unaddressable access error on the second line of the example code, because x+8
is not an addressable location.
When an unaddressable access occurs in a heap region, Dr. Memory attempts to provide additional information about nearby valid allocations and whether the target address is inside a region that has been freed. For example, here is a sample error report:
Error #1: UNADDRESSABLE ACCESS: reading 0x000a720b-0x000a720c 1 byte(s)
# 0 main [e:\derek\drmemory\git\src\tests\malloc.c:96]
Note: @0:00:01.500 in thread 3808
Note: refers to 1 byte(s) beyond last valid byte in prior malloc
Note: prev lower malloc: 0x000a7208-0x000a720b
Note: instruction: mov 0x03(%edx) -> %al
Note how the "prev lower malloc" ends at 0x000a720b just before the invalid address, indicating a read that went one byte too far, as indicated.
Here is another example, using the -delay_frees_stack
option to obtain the callstack of the freed memory:
Error #8: UNADDRESSABLE ACCESS: reading 0x001338a8-0x001338ac 4 byte(s) # 0 unaddr_test1 [e:\derek\drmemory\git\src\tests\suppress.c:110] # 1 test [e:\derek\drmemory\git\src\tests\suppress.c:269] # 2 main [e:\derek\drmemory\git\src\tests\suppress.c:297] Note: @0:00:02.141 in thread 3024 Note: next higher malloc: 0x001338e8-0x00133938 Note: prev lower malloc: 0x001337e8-0x00133820 Note: 0x001338a8-0x001338ac overlaps memory 0x001338a8-0x001338c4 that was freed here: Note: # 0 test [e:\derek\drmemory\git\src\tests\suppress.c:269] Note: # 1 main [e:\derek\drmemory\git\src\tests\suppress.c:297] Note: instruction: mov (%eax) -> %eax
The -brief
option provides less information but may be easier to read:
Error #8: UNADDRESSABLE ACCESS: reading 4 byte(s) # 0 unaddr_test1 [e:\derek\drmemory\git\src\tests\suppress.c:110] # 1 test [e:\derek\drmemory\git\src\tests\suppress.c:269] # 2 main [e:\derek\drmemory\git\src\tests\suppress.c:297] Note: refers to memory that was freed here: Note: # 0 test [e:\derek\drmemory\git\src\tests\suppress.c:269] Note: # 1 main [e:\derek\drmemory\git\src\tests\suppress.c:297]
The callstack_style
option can be used to customize how callstacks are printed out. Here is another style for the same callstack (without -brief):
Error #8: UNADDRESSABLE ACCESS: reading 0x001338a8-0x001338ac 4 byte(s) # 0 unaddr_test1 (0x0040130b <suppress.exe+0x130b>) e:\derek\drmemory\git\src\tests\suppress.c:110 # 1 test (0x0040112d <suppress.exe+0x112d>) e:\derek\drmemory\git\src\tests\suppress.c:269 # 2 main (0x0040107e <suppress.exe+0x107e>) e:\derek\drmemory\git\src\tests\suppress.c:297 Note: @0:00:01.453 in thread 4088 Note: next higher malloc: 0x001338e8-0x00133938 Note: prev lower malloc: 0x001337e8-0x00133820 Note: 0x001338a8-0x001338ac overlaps memory 0x001338a8-0x001338c4 that was freed here: Note: # 0 test (0x00401121 <suppress.exe+0x1121>) Note: e:\derek\drmemory\git\src\tests\suppress.c:269 Note: # 1 main (0x0040107e <suppress.exe+0x107e>) Note: e:\derek\drmemory\git\src\tests\suppress.c:297 Note: instruction: mov (%eax) -> %eax
In this case Dr. Memory is able to report that the target address is inside a malloc area that has been freed and has not been re-allocated since.
This additional information, and the addresses accessed, only apply to the first error with that callstack that Dr. Memory sees. Any subsequent errors with the same callstack will increment the duplicate count for that error but further individual information about each duplicate is not provided.
If the application reads from addressable memory that has not been written to since it was allocated, Dr. Memory reports an "uninitialized read" error. In order to avoid false positives, Dr. Memory does not report the use of uninitialized memory until something "meaningful" is done with that memory, such as a comparison or conditional branch or passing it to a system call. Variables or fields smaller than a word are often initialized without their containing word (variables and fields are typically word-aligned) being initialized. When these variables or fields are then copied, the uninitialized portion of each word is technically being read as an uninitialized value, but reporting such reads as errors would result in far too many errors.
When passing data structures to a system call, if the structure is initialized field-by-field then padding bytes may be left uninitialized. Dr. Memory will report errors on these as it does not know whether the kernel or a receipient on the other end might read it. To avoid these errors, memset the entire structure, or use a Dr. Memory error suppression (see Suppressing Errors) to ignore the error.
Here is an example of an uninitialized read error:
Error #2: UNINITIALIZED READ: reading 0xffbae108-0xffbae114 12 byte(s) within 0xffbae100-0xffbae114 Elapsed time = 0:00:00.214 in thread 19298 system call socketcall setsockopt args <system call> 0x08049a65 <my-socket-test+0x1a65> my-socket-test!main ??:0 0x0092dbb6 <libc.so.6+0x16bb6> libc.so.6<nosyms>!__libc_start_main ??:0 0x080489b1 <my-socket-test+0x9b1> my-socket-test!_start ??:0
When only part of a larger region is uninitialized, Dr. Memory reports the containing range to make it easier to track down the problem. This typically happens with buffers or structures passed to system calls. Note also in this example how Dr. Memory reports which part of the socketcall system call checks discovered this error.
Whenever a pointer that does not refer to a valid malloc region is passed to free() or other malloc-related routines, Dr. Memory reports an "invalid heap argument" error. Here the problem is immediately apparent as 0x00001234 is not a valid heap address at all:
Error #4: INVALID HEAP ARGUMENT: free 0x00001234 Elapsed time = 0:00:00.180 in thread 21848 # 0 malloc!main [/home/bruening/drmemory/git/src/tests/malloc.c:164] # 1 libc.so.6!__libc_start_main [/build/buildd/eglibc-2.11.1/csu/libc-start.c:226] # 2 malloc!_start
Another common example of an invalid argument is a mismatch in calling free() versus operator delete versus operator delete[]. This will be reported as:
Error #4: INVALID HEAP ARGUMENT: allocated with operator new[], freed with operator delete # 0 test_mismatch [cs2bug.cpp:122] # 1 main [cs2bug.cpp:139] Note: memory was allocated here: Note: # 0 test_mismatch [cs2bug.cpp:121] Note: # 1 main [cs2bug.cpp:139]
For code compiled with Visual Studio, detecting such mismatches relies on having debug information. Certain Visual Studio optimizations can also preclude the ability to detect mismatches when using a static C library, so using either a Debug build of your application or linking to the dynamic C library may be required to identify these bugs. Also note that if leak counting is completely disabled via -no-count_leaks
then the callstack of the allocation will not be reported on a mismatch.
Dr. Memory divides all memory that is still allocated at the time it does its leak scan into 3 categories:
-no_midchunk_new_ok
runtime option.-no_midchunk_inheritance_ok
runtime option.char
[] array in the middle of an allocation and points directly at it. You can suppress Dr. Memory's identification of such mid-allocation pointers, causing them to show up as possible leaks, with the -no_midchunk_string_ok
runtime option.-no_midchunk_size_ok
runtime option.The two categories of leaks ("leaks" and "possible leaks") are further broken down into direct and indirect leaks. An indirect leak is a heap object that is reachable by a pointer to its start address, but with all such pointers originating in leaked objects. Leaks can be thought of as trees, with the top-level object the direct leaks and all child objects indirect leaks. Dr. Memory reports the size of all the child indirect leaks for each direct leak, but does not report a detailed callstack for indirect leaks. If the direct leak is addressed, the indirect leaks should no longer be leaked, making their details unnecessary.
By default, Dr. Memory performs leak checking at application exit, or when a mid-run check is requested via -nudge
(see Applications That Do Not Exit). Nudges can be used to help determine when the last pointer to an allocation was lost, if the callstack of the allocation is not sufficient to pinpoint the error in the source code. Each nudge will perform a full leak scan and by nudging periodically the first instance of a particular leak can be used to identify when the leak occurred.
On Windows, when HeapDestroy is called, any live allocations inside are reported as possible leaks. This can be disabled, since some applications may consider it correct behavior, with the -no_check_leaks_on_destroy
option.
Dr. Memory reports the number of leaks, possible leaks, and still-reachable allocations. The callstack for the allocation of each leak and possible leak is gathered by default and listed with other errors in the results file. To also see all of the reachable allocations, use the -show_reachable
runtime option. Reachable allocations will be in the global.pid.log
file inside the log directory, labeled as REACHABLE LEAK
.
Here are example lines from Dr. Memory's output summary:
~~Dr.M~~ 9 unique, 282 total, 5860 byte(s) of leak(s) ~~Dr.M~~ 0 unique, 0 total, 0 byte(s) of possible leak(s) ~~Dr.M~~ 125 still-reachable allocation(s) ~~Dr.M~~ (re-run with "-show_reachable" for details)
There are known sources of false positives where a memory allocation may actually be reachable by the application, but Dr. Memory's algorithm will determine that it is unreachable. These cases include:
One known problem comes from using glib's memory allocator. To avoid false positives when using glib, set the following environment variables when running your application:
Dr. Memory reports warnings about unusual memory-related conditions that an application developer might like to know about. For example:
Error #12: WARNING: heap allocation failed @0:00:01.500 in thread 3748 # 0 suppress.exe!warning_test1 [e:\derek\drmemory\git\src\tests\suppress.c:179] # 1 suppress.exe!test [e:\derek\drmemory\git\src\tests\suppress.c:282] # 2 suppress.exe!main [e:\derek\drmemory\git\src\tests\suppress.c:297] # 3 suppress.exe!__tmainCRTStartup [f:\sp\vctools\crt_bld\self_x86\crt\src\crt0.c:327] # 4 KERNEL32.dll!BaseProcessStart
Dr. Memory also identifies common errors in using the Windows GDI API and reports these errors as warning. For example:
Error #15: WARNING: GDI usage error: DC 0x2d011230 that contains selected object being deleted # 0 system call NtGdiDeleteObjectApp # 1 GDI32.dll!DeleteDC +0x11 (0x76b458c5 <GDI32.dll+0x158c5>) # 2 chrome.dll!skia::BitmapPlatformDevice::BitmapPlatformDeviceData::ReleaseBitmapDC [c:\src\chromium\src\skia\ext\bitmap_platform_device_win.cc:58] # 3 chrome.dll!SkRefCnt::unref [c:\src\chromium\src\third_party\skia\include\core\skrefcnt.h:60] # 4 chrome.dll!SkRefCnt::unref [c:\src\chromium\src\third_party\skia\include\core\skrefcnt.h:60] # 5 chrome.dll!SkCanvas::internalRestore [c:\src\chromium\src\third_party\skia\src\core\skcanvas.cpp:824] # 6 chrome.dll!SkCanvas::~SkCanvas [c:\src\chromium\src\third_party\skia\src\core\skcanvas.cpp:449]
Note that some of these GDI usage errors are best programming practices and may not result in detrimental behavior in actual usage on more recent versions of Windows. Detection of these errors can be disabled with the -no_check_gdi
runtime option.