The MASM Forum

Projects => MasmBasic & the RichMasm IDE => Topic started by: jj2007 on October 04, 2017, 09:14:03 AM

Title: MemState helps to find leaks in your code
Post by: jj2007 on October 04, 2017, 09:14:03 AM
From MbGuide.rtf:

MemState("leaked kBytes: %i")          ; prints to console the leaked bytes using Str (http://www.webalice.it/jj2006/MasmBasicQuickReference.htm#Mb1186)$("...") format
MemState(dump)                         ; prints sums and averages for all monitoring points
MemState(reset)                        ; resets sums and counters


MemState checks if the memory usage of the process has changed between two code points. Here is a practical example of the source that inspired me to write this macro:

  msUse=1                               ; yes, we want to generate MemState code
  MemState("leaked kBytes A: \t%i\n")
  .if StringsDiffer(cbTime$, Time$)
        .if StringsDiffer(cbDate$, Date$)
                deb 4, "DATE", $cbDate$, $Date$, $Dt$(0)
                Let cbDate$=Date$
                call DailyReset
        .endif
        MemState("leaked kBytes A0: \t%i\n")
        SetWin$ hwnd=Str$(" %i dates found", JFT(?))
        MemState("leaked kB SetWin$ A: \t%i\n")
        Let cbTime$=Time$       ; Left$(Time$, 8)
  .endif
  MemState("leaked kBytes B: \t%i\n")


The application has over 2,000 lines and a timer: It does numerous API calls every 200ms, and it definitely leaks a bit. Not dramatically, but the initial 5MB may increase to 20MB or so if it runs all day long.

So I distributed about 20 MemState monitoring points around "suspicious" code parts. Typical console output:
leaked kBytes TT out:   4
leaked kB SetWin$ C:    4
leaked kBytes TT out:   68
leaked kBytes TT out:   64
leaked kBytes TT out:   68
leaked kBytes TT out:   68
leaked kBytes A:        4
leaked kB SetWin$ C:    4
leaked kBytes TT out:   88
leaked kBytes TT out:   92
leaked kBytes A:        -4
leaked kBytes TT out:   128
leaked kBytes TT out:   132
leaked kBytes A:        8
leaked kBytes C:        4
leaked kBytes A:        540
leaked kBytes A:        2024
leaked kBytes A:        16
leaked kBytes A:        -264


Negative values indicate that memory was released. So it seems that there are leaks before points "A" and "TT out". Not very clear, though, and that is why MemState has two more functions (available since version 4 Oct 2017 (http://masm32.com/board/index.php?topic=94.0)):

MemState(dump) lists total and average losses for all points:
sum of MemState results (total  per call):
leaked kBytes A:        1544    0.0240
leaked kBytes A0:       544     0.0416
leaked kB SetWin$ A:    212     0.0162
leaked kBytes B:        24      3.73e-04
leaked kBytes C:        4       3.06e-04
leaked kBytes SD 1:     44      0.00177
leaked kB SetWin$ B:    336     0.0135
leaked kBytes SD 3:     8       3.22e-04
leaked kB wRec out:     64      0.00258
leaked kBytes TT out:   6464    0.260
leaked kBytes SD 5:     12      4.84e-04


MemState(reset) sets sums and counters to zero. This is useful for applications that use a lot of memory at the beginning. So after the losses are somewhat stable, reset and watch for the small losses that follow over time. For example, after a reset, the dump results look different after a while:sum of MemState results (total  per call):
leaked kBytes A:        252     0.00907
leaked kBytes A0:       4       7.10e-04
leaked kB SetWin$ A:    0       0.0
leaked kBytes B:        16      5.76e-04
leaked kBytes C:        4       7.09e-04
leaked kBytes SD 1:     856     0.0360
leaked kB SetWin$ B:    256     0.0108
leaked kBytes SD 3:     0       0.0
leaked kB wRec out:     108     0.00454
leaked kBytes TT out:   6168    0.259


And now it is crystal clear that TT out is the culprit ;)

P.S.: If there is msUse=0 on top of the file, no code will be generated. So once the testing is finished, you can leave the MemState calls in your source without bloating the exe.