mIRC Home    About    Download    Register    News    Help

Print Thread
COM memory leak #259745 20/01/17 03:13 PM
Joined: Apr 2010
Posts: 910
F
FroggieDaFrog Offline OP
Hoopy frood
OP Offline
Hoopy frood
F
Joined: Apr 2010
Posts: 910
I'm not sure if its mIRC or the interface I'm using, but something is leaking memory. The following code creates an instance of window's jscript engine, executes a function and dispatches the returned object.

When the identifying mIRC com is closed, the memory consumed by the dispatch com isn't freed:


Code:
;; /LeakTest <amount_of_times_to_call_function>
alias leaktest {
  var %x = $$1, %error
  var %js = (function(){return{"test":Array(3001).join("a")}})()

  .comopen LeakTest MSScriptControl.ScriptControl

  if (!$com(LeakTest)) || ($comerr) {
    %Error = SCRIPTCONTROL_INIT_FAIL
  }
  elseif (!$com(LeakTest, language, 4, bstr, jscript)) || ($comerr) {
    %Error = LANGUAGE_SET_FAIL
  }
  else {
    while (%x) {
      dec %x
      
      ;; Dispatches the result of the js function, but then immediately closes it
      ;; yet the memory isn't freed even if given time
      if (!$com(LeakTest, eval, 1, bstr, %Js, dispatch* LeakTest/Test) || $comerr || !$com(LeakTest/Test)) {
        %Error = failed to dispatch LeakTest/Test
        goto error
      }
      .comclose LeakTest/Test
      
    }
  }
  :error
  if ($com(LeakTest/Test)) .comclose $v1
  if ($com(LeakTest)) .comclose $v1

  if ($error || %error) {
    Echo -a ERROR: $v1 
  }
  else {
    echo -a Done!
  }
}



More Info:
Code:
//echo -a $os $version $beta $md5($mircexe,2) $file($mircexe).sig $alias(0) $script(0) $dll(0) $com(0)
10 7.47 7fbdee41d89a9a06431e08d52aa7f49b ok 1 1 0 0

Last edited by FroggieDaFrog; 20/01/17 03:41 PM.

I am SReject
My Stuff
Re: COM memory leak [Re: FroggieDaFrog] #259747 20/01/17 10:39 PM
Joined: Apr 2010
Posts: 910
F
FroggieDaFrog Offline OP
Hoopy frood
OP Offline
Hoopy frood
F
Joined: Apr 2010
Posts: 910
After further testing, I'm fairly sure this is an mIRC leak.

Using the following executed under cscript, the memory increases during the while loop but as soon as the loop finishes, the memory is freed; this doesn't appear to be the case with the previous posts' mIRC example

Save as leaktest.js
Open taskmanager (or any other tool to view memory usage)
Open cmd.exe
Navigate to the directory you saved leaktest.js
Input: cscript.exe leaktest.js
In the prompt, enter: 10000
Code:
WScript.StdOut.Write("How many times to iterate? ");
WScript.StdIn.Read(0);

var x = WScript.StdIn.ReadLine();
var js = '(function(){return{"test":Array(3001).join("a")}})()';

var leakTest, leakTestRes;

// creates an instance of scriptcontrol and sets the language
leakTest = new ActiveXObject("MSScriptControl.ScriptControl");
leakTest.language = "jscript";

while (x) {
 x -= 1;

 // calls the js function as the mSL code did
 leakTestRes = leakTest.eval(js);
}


WScript.StdOut.Write("Press enter to exit...");
WScript.StdIn.Read(0);
WScript.StdIn.ReadLine();




Edit
And another test that doesn't use jscript at all pulled from the helpfile -- (but modified to show issue). Though not as significant as my previous two examples this one also shows an increase in memory without freeing once the com is closed (My guess is something to do with dispatching):

Code:
;; /cpu 10000
alias cpu {
  var %Error, %x = $$1
  comopen Locator WbemScripting.SWbemLocator
  if ($comerr) {
    %Error = Failed to open instance
  }
  else {
    while (%x) {
      dec %x
      if ($com(Locator, ConnectServer, 3, dispatch* Services) && !$comerr && $com(Services)) {
        .comclose services
      }
      else {
        %Error = Failed to dispatch services
        break
      }
    }
  }
  :error
  if ($com(Services)) comclose Services
  if ($com(Locator)) comclose Locator
  if (%Error) {
    echo -a $v1
  }
  elseif (!$error) {
    echo -a Done!
  }
}

Last edited by FroggieDaFrog; 20/01/17 11:19 PM.

I am SReject
My Stuff
Re: COM memory leak [Re: FroggieDaFrog] #259750 22/01/17 12:36 PM
Joined: Dec 2002
Posts: 4,484
Khaled Offline
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 4,484
Thanks, I was able to reproduce this issue. It was due to memory not being freed by a function that retrieves the $com().progid value from the dispatch pointer. This issue has been fixed for the next version.

Re: COM memory leak [Re: Khaled] #259751 22/01/17 06:11 PM
Joined: Apr 2010
Posts: 910
F
FroggieDaFrog Offline OP
Hoopy frood
OP Offline
Hoopy frood
F
Joined: Apr 2010
Posts: 910
Many thanks.


I am SReject
My Stuff
Re: COM memory leak [Re: Khaled] #259785 26/01/17 02:46 PM
Joined: Apr 2010
Posts: 910
F
FroggieDaFrog Offline OP
Hoopy frood
OP Offline
Hoopy frood
F
Joined: Apr 2010
Posts: 910
Via my own testing this has indeed been fixed with the 7.47.91 beta. Thank you


I am SReject
My Stuff
Re: COM memory leak [Re: FroggieDaFrog] #262317 20/01/18 06:24 PM
Joined: Apr 2010
Posts: 910
F
FroggieDaFrog Offline OP
Hoopy frood
OP Offline
Hoopy frood
F
Joined: Apr 2010
Posts: 910
I'm not sure when this crept back into the interface but mIRC isn't freeing memory again:

1. Open your favorite program-memory-usage monitor(taskmanager)
2. Load the following script into a fresh mIRC
3. Enter: /example

Code:
alias example {
  var %x = $iif($1 isnum, $1, 5000)
  var %com = example
  var %js = (function(){return{"test":Array(4000).join("a")}})()
  var %err

  echo -a Processing %x iterations

  .comopen example MSScriptControl.scriptControl
  if (!$com(example) || $comerr) {
    %err = Failed to create MSScriptControl.ScriptControl
  }
  elseif (!$com(example, language, 4, bstr, jscript) || $comerr) {
    %err = Failed to set language to jscript
  }
  else {
    while (%x) {
      dec %x
      if (!$com(example, Eval, 1, bstr, %js) || $comerr) {
        %err = Failed to execute jscript
      }
    }
  }

  echo -a Done!

  :error
  if ($com(example)) {
    .comclose example
  }
  if (%err) {
    echo -s %err
  }
}


For comparison, here is the same script to be ran under cscript.exe. You will notice after the initial memory spike of creating the MSScriptControl then the memory fluctuates but doesn't have a steady increase:
Code:
WScript.StdOut.write("How many times to iterate?");
WScript.StdIn.Read(0);

var x = Number(WScript.StdIn.readLine());
var js = '(function(){return{"test":Array(4000).join("a")}})()'

if (isNaN(x)) {
    x = 5000;
}

var leakTest, leakTestRes;

// create an instance of the ScriptControl and set language
leakTest = new ActiveXObject('MSScriptControl.ScriptControl');
leakTest.language = 'jscript';

while (x > 0) {
    x--;
    
    leakTestRes = leakTest.eval(js);
}

// remove reference.
leakTest = null;

WScript.StdOut.Write("Press enter to exit...");
WScript.StdIn.Read(0);
WScript.StdIn.ReadLine();




Echo info - this issue has been reported to me as early as mIRC v7.48 (the release that supposedly fixed it confused)
Code:
10 7.51 1561 ad5337d2a735eec9e6ffc9ffa5b097d3 ok 1 1 0 0

Last edited by FroggieDaFrog; 20/01/18 07:00 PM.

I am SReject
My Stuff
Re: COM memory leak [Re: FroggieDaFrog] #262318 20/01/18 09:08 PM
Joined: Dec 2002
Posts: 4,484
Khaled Offline
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 4,484
Are you able to reproduce this issue with your original bug report? I tested the "alias cpu" script you provided before and could not reproduce a memory leak. If so, this may be a different bug.

Re: COM memory leak [Re: Khaled] #262319 20/01/18 09:13 PM
Joined: Apr 2010
Posts: 910
F
FroggieDaFrog Offline OP
Hoopy frood
OP Offline
Hoopy frood
F
Joined: Apr 2010
Posts: 910
I do believe its a new bug. Before making this report I tested with the /CPU alias and other WMI objects and could not get a noticable leak.

To be clear, this is not related to dispatching; The latest example above doesn't use such yet mIRC's memory rises with each call and isn't freed once the com is closed.

Last edited by FroggieDaFrog; 20/01/18 09:19 PM.

I am SReject
My Stuff
Re: COM memory leak [Re: FroggieDaFrog] #262320 20/01/18 09:29 PM
Joined: Dec 2002
Posts: 4,484
Khaled Offline
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 4,484
Quote:
$com(example, Eval, 1, bstr, %js)

Right, what is this call meant to do?

Re: COM memory leak [Re: Khaled] #262321 20/01/18 09:31 PM
Joined: Apr 2010
Posts: 910
F
FroggieDaFrog Offline OP
Hoopy frood
OP Offline
Hoopy frood
F
Joined: Apr 2010
Posts: 910
It evaluates the input string(stored in %js) as javascript and returns the resulting value: an object containing a single key("test") with a string value of 4000 a's("aaaaaaaaaaaaaaa....")

The documentation for MSScriptControl is scarce but here you go

Last edited by FroggieDaFrog; 20/01/18 09:36 PM.

I am SReject
My Stuff
Re: COM memory leak [Re: FroggieDaFrog] #262323 20/01/18 09:38 PM
Joined: Dec 2002
Posts: 4,484
Khaled Offline
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 4,484
Quote:
It evaluates the input string(stored in %js) as javascript and returns the resulting value: an object containing a single key("test") with a string value of 3000 a's

Okay, where in the $com() call is this array accessible by mIRC? The $com() identifier provides you with access to the variables returned by the routine that $com() calls. But I cannot see where this is in your call.

Re: COM memory leak [Re: Khaled] #262324 20/01/18 09:45 PM
Joined: Apr 2010
Posts: 910
F
FroggieDaFrog Offline OP
Hoopy frood
OP Offline
Hoopy frood
F
Joined: Apr 2010
Posts: 910
In the example, the result isn't used by the mSL. I stopped coding the example at the first point inwhich mIRC's memory consumption balloons.

The invocation of the Eval method begins the ballooning of mIRC's memory consumption even though the result of the call is discarded. Furthermore, closing the MSScriptControl com that the eval method was called against does NOT free the memory.

This is different from doing the same thing under cscript.exe which causes cscript.exe to max at 30-40mb regardless of number of calls against the MSScriptControl instance's Eval method.


If need be I can write a more fleshed-out script and provide the cscript.exe equivalent to help you further understand/trace the issue

Last edited by FroggieDaFrog; 20/01/18 09:49 PM.

I am SReject
My Stuff
Re: COM memory leak [Re: FroggieDaFrog] #262325 20/01/18 10:03 PM
Joined: Dec 2002
Posts: 4,484
Khaled Offline
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 4,484
I think I see what the issue is. The call you are making returns the result via a dispatch pointer. mIRC preserves this so that you can access it with $com().result. However, mIRC does not free it because in the past this caused mIRC to crash due to that pointer being later used by the scripter. There is currently no way for mIRC to know that this has happened. I can't remember all the ways scripters use the $com().result value. Have you used that in the past, if it was a dispatch pointer, to create a new COM object?

Re: COM memory leak [Re: Khaled] #262326 20/01/18 10:15 PM
Joined: Apr 2010
Posts: 910
F
FroggieDaFrog Offline OP
Hoopy frood
OP Offline
Hoopy frood
F
Joined: Apr 2010
Posts: 910
I could be wrong, but $com().result cannot be used short of its literal value; that is AFAIK, there's no way to take the value of $com().result to create a new com/dispatch instance.

So keeping the dispatched pointer alive doesn't make sense IF the $com() call does not provide a dispatch*/unknown* 'variable' for mIRC to fill with the result.

Using our example above:
$com(example, eval, 1, bstr, %js)
- Should NOT keep the returned dispatched pointer as the script has NOT requested it. Instead, $com().result should be filled with the pointer's converted UINT value, and the actual pointer be discarded

$com(example, eval, 1, bstr, %js, dispatch* result)
- Should keep the dispatched pointer(referencable as a new com 'result') as the script has requested the result be kept alive.

--

Also, if I close the root com, why isn't the memory being freed for these inaccessible pointers.


Last edited by FroggieDaFrog; 20/01/18 10:33 PM.

I am SReject
My Stuff
Re: COM memory leak [Re: FroggieDaFrog] #262331 21/01/18 08:44 AM
Joined: Dec 2002
Posts: 4,484
Khaled Offline
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 4,484
Quote:
Should NOT keep the returned dispatched pointer as the script has NOT requested it. Instead, $com().result should be filled with the pointer's converted UINT value, and the actual pointer be discarded

The result cannot be freed immediately because it needs to be used in $com().result. This could also be used, for example, by a script that passes the UINT of the dispatch pointer to a DLL that then uses the dispatch pointer. However, this is short-lived, so mIRC should be able to free it on the next $com()/comclose call without side-effects - unless the script has stored the value of the pointer and tries to use it again - but that would be a script issue. This change has been made to the next version, however COM support will need thorough testing. This change is essentially undoing changes due to previous gpf bug reports, although the COM code may have been less robust at the time.

Re: COM memory leak [Re: Khaled] #262335 21/01/18 04:01 PM
Joined: Apr 2010
Posts: 910
F
FroggieDaFrog Offline OP
Hoopy frood
OP Offline
Hoopy frood
F
Joined: Apr 2010
Posts: 910
Using my JSON For mIRC script, and its test suit.

The memory leak appears to be fixed in the .1800 beta without side affects. I have instructed the users of my script to monitor memory usage with the beta and report back. I will give you an update if something unexpected creeps up.


I am SReject
My Stuff