Detecting when any variable in a large JS program is set to NaN

I have a large, messy JS codebase. Sometimes, when the application is in use, the variable is set to NaN. Since it x = 2 + NaN

causes it to be x

set to NaN, NaN it is spread by the virus. At some point, after it has spread quite far, the user notices that there are NaNs all over the place, and the crap doesn't work at all. From this state it is very difficult for me to step back and determine the source of NaN (and there may well be many sources).

The NaN error is also not easily reproducible. Although hundreds of people are watching and reporting it to me, no one can tell me about the few steps that lead to NAS. Maybe it's a rare racial condition or something like that. But this is definitely a rare and uncertain origin.

How can I fix this error? Any ideas?

Two silly thoughts I thought might not be feasible:

  • Write some kind of pre-processor that inserts isNaN

    checks before each use of any variable and registers the first occurrence of NaN. I don't think this has been done before, and I don't know how difficult it would be. Any advice would be appreciated.

  • Run my code in a JS engine that has the ability to set a breakpoint anytime any variable is set to NaN. I don't think there is anything that does it out of the box, but how hard would it be to add it to Firefox or Chrome?

I feel like I shouldn't be the first person to have this kind of problem, but I can't seem to find anyone else talking about it.

+3


source to share


7 replies


If you are good at doing things outside of the global namespace and injecting things into objects, this might help. And I will predetermine that by saying that this is far from a complete solution, but at least it should help you find it.

function deepNaNWatch(objectToWatch) {
  'use strict';

  // Setting this to true will check object literals for NaN
  // For example: obj.example = { myVar : NaN };
  // This will, however, cost even more performance
  var configCheckObjectLiterals = true;

  var observeAllChildren = function observeAllChildren(parentObject) {

    for (var key in parentObject) {
      if (parentObject.hasOwnProperty(key)) {
        var childObject = parentObject[key];

        examineObject(childObject);
      }
    }
  };

  var examineObject = function examineObject(obj) {
    var objectType = typeof obj;

    if (objectType === 'object' || objectType === 'function') {
      Object.observe(obj, recursiveWatcher);
      if (configCheckObjectLiterals) {
        observeAllChildren(obj);
      }
    } if (objectType === 'number' && isNaN(obj)) {
      console.log('A wild NaN appears!');
    }
  };

  var recursiveWatcher = function recursiveWatcher(changes) {
    var changeInfo = changes[0];
    var changedObject = changeInfo.object[changeInfo.name];

    examineObject(changedObject);
  };

  Object.observe(objectToWatch, recursiveWatcher);
}

      

Call deepNaNWatch(parentObject)

for every top-level object / function you use to nest objects immediately after they are created. Every time an object or function is instantiated in an observable / function, it will itself be observed as well. Every time a number

is created or modified under the scanned object, remember that typeof NaN == 'number'

- it checks to see if it is NaN and if so will run the code in console.log('A wild NaN appears!');

. Be sure to change this to whatever kind of debug output you think will help.



This function would be more useful if someone could find a way to force it to a global object, but every attempt I made to do this just told me that I should sit back in time and think about what I did.

Oh, and if it's not obvious from the above, in a large-scale project, this feature will inevitably make mocking features like "speed" and "efficiency" a thing of the past.

+1


source


There is probably no solution for your problem: aka: break, whenever any variable is set to NaN. Instead, you can try watching your variables like this:

  • It was previously stated that the Chrome debugger offers conditional breakpoints. But it also supports viewing expressions. In the Watch-Expressions menu, you can set a break condition whenever a variable has a specific value.

  • Object.observe is a method that tracks changes to an object. You can listen for all changes on the object and call debug

    when any variable is set to NaN. For example, you can observe all changes in a window object. Whenever any variable on the window object is set to NaN, you call debug

    . Note that this Object.observe

    is quite advanced and not supported by all browsers (check the polyfill in this case ).

  • Take this opportunity to write a test case for each function in your code. Do some random testing and find the line of code that can generate NaN values.



Another problem is probably how to reproduce this error. Reloading your webpage over and over again doesn't make much sense. You can check the so-called mute browser: it launches a browser instance without displaying it. It can be used to run automated tests on a website, click buttons, perform some actions. Perhaps you can script it in such a way that it finally reproduces your error. This has the advantage that you don't have to reload the web page hundreds of times. There are several implementations for mute browsers. PhantomJS is really nice in my opinion. You can also launch the Chrome Debug console (you need a plugin: remote debugger ).

Also, note that NaN never matches NaN . It would be a shame if you can finally reproduce the bug, but your breakpoints are not working.

+2


source


Is your code compatible with your server side, or is it only client side? You mention that this is a rare problem because it can only happen in some browsers (or version of browsers) or in any situation that might be difficult to reproduce. If we assume that any occurrence of nan is a problem, and that when this happens to a user notification error ("there are NaNs all over the place"), then display an error popup instead, the error should contain the first occurrence of nan (then users can raport it " Despite the fact that hundreds of people are watching and reporting it to me "). Or do not show it, but send it to the server. To do this, write a simple function that takes only one variable as a variable and checks if the variable is NaN.Place it in your code in vulnerabilities (sensitive variables). And it may contain problematic code. I know this is very messy, but it might help.

+1


source


One of your math functions is failing. I've used Number (variable) to fix this issue before. Here's an example:

test3 = Number (test2 + test1) even though test1 and test2 appear to be numbers

+1


source


Yes, racing conditions can be a pain, it sounds like it can be.

Debugging to source will definitely be the way to go with this.

My suggestion was to set up some functional tests with a focus on where they were played, set some test conditions with different timeouts or such, and just rerun it until it catches it. Set up some logging process to see if possible backtrace.

What does your stack look like? I can't give too much analysis without looking at your code, but with its javascript, can you use the browser dev tools I'm guessing?

+1


source


If you know the locations in which it is distributed NaN

, you can try using program slicing to narrow down other program statements that affect this value (via control and dependent data). However, these tools are usually non-trivial to customize, so I would try the Object.observe

-types answers others give first.

You can try WALA from IBM. It is written in Java but has a Javascript interface . You can find information about slicer on the wiki .

Basically, if the tool works, you give it a program point (operator) and it will give you a set of statements that the starting point is (transiently) driven and / or data dependent. If you know several "infected" points and suspect the same source, you can use the intersection of their slices to narrow the list (a program point slice can often be a very large set of statements).

+1


source


(too long for a comment)

During testing, you can overwrite ALL functions Math

to check if a NaN

.

It won't catch

a = 'string' + 1;

      

but will catch things like

a = Math.cos('string');
a = Math.cos(Infinity);
a = Math.sqrt(-1);
a = Math.max(NaN, 1);
...

      

Example:

for(var n Object.getOwnPropertyNames(Math)){
    if (typeof Math[n] === 'function') Math[n] = wrap(Math[n]);
}
function wrap(fn){
    return function(){
        var res = fn.apply(this, arguments);
        if (isNaN(res)) throw new Error('NaN found!')/*or debugger*/;
        return res;
    };
}

      

I haven't tested, maybe an explicit list of "wrap" ed methods is better.

By the way, you shouldn't be putting this in production code.

+1


source







All Articles