systeminformation icon indicating copy to clipboard operation
systeminformation copied to clipboard

100% CPU load on windows

Open 4n0nh4x0r opened this issue 2 years ago • 22 comments

Describe the bug Its not really a bug, more just something i noticed I m working on a monitoring tool using this library to get system data Therefore i need to get the data constantly On linux, this is no issue at all, but on windows, my cpu load goes up to 100%

To Reproduce

var data = {}
dataTransferInterval = setInterval(async () => {
        data.cpu  = await si.get({currentLoad:"currentLoad"})
        data.ram  = await si.get({mem:"total,used"})
        data.disk = await si.get({fsSize:"size,used"})
    }
}, 1000);

Current Output The output is fine, it just puts a LOT of load on the cpu on my windows system

Expected behavior Barely any noticable load on the cpu just like on my linux server

Environment (please complete the following information):

  • systeminformation package version: 5.9.13
  • Windows
  • Asus ROG Gaming Laptop

Additional context Add any other context about the problem here.

4n0nh4x0r avatar Nov 17 '21 00:11 4n0nh4x0r

@4n0nh4x0r thank you, yes this is something, we are doing an investigation. See also issue #616 ... hope to get a solution ready this weekend.

sebhildebrandt avatar Nov 17 '21 05:11 sebhildebrandt

Hopefully something can be done, othetrwise my monitoring system will take 100% of the cpu power for itself alone on windows servers lol

4n0nh4x0r avatar Nov 17 '21 11:11 4n0nh4x0r

Hello! I come to collaborate with this incident. I am developing a package of monitoring tools for servers and when testing version 5.9.7 it already had incidents with windows, I saw that I launch 5.9.14 and it persists. In my case, just checking any status flag (Timeup, CPU, OS) triggers the use of Powershell to 50% or more.

PS> Whoever wants to know my monitoring utility is called Healty https://github.com/gusgeek/Healty https://github.com/gusgeek/HealtyCenter For now in Spanish, soon in English.

aaferna avatar Nov 19 '21 09:11 aaferna

@4n0nh4x0r ... I made detailed research and it turns out that spinning up PowerShell Process is responsible for consuming resource and time. I now created a possibility to create a persistent PowerShell which then should reduce resources by 90% ...

It would be great if you can test the latest code here on GitHub: What you have to do to enable the persistent power shell:

  • on startup (if you are running in windows): si.powerShellStart();
  • and then again when shutting down the app: si.powerShellRelease();

All commands In between will then use this already pinned up powerShell.

The implementation ist still a little hacky ... but if your results are fine and this is what you would like to use, I will make a clean up! Any comments are welcome!

sebhildebrandt avatar Nov 21 '21 14:11 sebhildebrandt

Okay, that sounds fantastic actually For releasing the powershell again, does shutting down the app itself work aswell? or do i have to call powerShellRelease()?

I will test this once i m back at my dev pc

4n0nh4x0r avatar Nov 21 '21 15:11 4n0nh4x0r

@4n0nh4x0r closing the app should also work but it would be cleaner to call powerShellRelease() ... the main thing for now is of course if this goes now into the right direction and solves the problem also on your side. If yes, I can make a cleanup and a new release. At least on my machines this was quite fast. So I hope this works also on your side ;-)

sebhildebrandt avatar Nov 21 '21 15:11 sebhildebrandt

@sebhildebrandt getStaticData doesnt work with the powerShellStart i can see from process explorer that node starts a single powershell and never opens more than one process, HOWEVER the command also never finishes? ALSO i can see the memory size of the powershell increases very dramatically and gets out of control 200MB!?

const si = require('systeminformation');
(async () => {
    si.powerShellStart();
    let abc = await si.getStaticData();
    console.log(abc);
    si.powerShellRelease();
})();

si458 avatar Nov 21 '21 17:11 si458

Awesome, it works for me No more cpu load shooting to 100%

As for the latest comment here on this thread, in my case this doesnt happen.
The powershell instance is being opened, and being worked with, and it stays constant on 40MB memory size

4n0nh4x0r avatar Nov 21 '21 19:11 4n0nh4x0r

@4n0nh4x0r have you tried my simple script above? i see the single powershell open, then use about 60% cpu, usage and 200MB memory for about 20secs (as expected as its using a single powershell rather than loads) but then it doesnt output the response and the powershell also isnt released and node never quits?

si458 avatar Nov 21 '21 19:11 si458

For me it stays at stable low cpu usage, and the memory allocated to the powershell is at about 114MB in this case But other than that nothing special

4n0nh4x0r avatar Nov 21 '21 19:11 4n0nh4x0r

For me it stays at stable low cpu usage, and the memory allocated to the powershell is at about 114MB in this case But other than that nothing special

do you get a reply though in the console and node quits?

si458 avatar Nov 21 '21 19:11 si458

I didnt get an output there, the script just continued doing work and never quits

4n0nh4x0r avatar Nov 21 '21 19:11 4n0nh4x0r

I didnt get an output there, the script just continued doing work and never quits

so its not just me then, so something isnt right with the getStaticData then and the powershell

also using your test script, the memory usage of powershell just keeps climbing for me? started at 34MB then starting climbing to 125MB after about 1 min running EDIT: running for 2 mins and its now at 130MB, defo memory leak

const si = require('systeminformation');
var data = {}
si.powerShellStart();
dataTransferInterval = setInterval(async () => {
    data.cpu = await si.get({ currentLoad: "currentLoad" })
    data.ram = await si.get({ mem: "total,used" })
    data.disk = await si.get({ fsSize: "size,used" })
}, 1000);

si458 avatar Nov 21 '21 19:11 si458

@si458 Which version of nodejs are you using? I m using node 16(.11.1)

4n0nh4x0r avatar Nov 21 '21 20:11 4n0nh4x0r

@si458 Which version of nodejs are you using? I m using node 16(.11.1)

Latest 14 LTS But I'm AFK at mo but will try updating to latest 16 LTS tomorrow and see what happens

si458 avatar Nov 21 '21 20:11 si458

Would be interesting to see if this is maybe a node version issue rather than a library issue itself

4n0nh4x0r avatar Nov 21 '21 23:11 4n0nh4x0r

@si458 , @4n0nh4x0r thank you for your feedback! My feeling here is, that the issues you describes are not related to a specific node version. To sum up:

  • my implementation is just a first step and I know that this is much to hacky!
  • the intension was, that I can see that this might be an approach to get the resources under control.
  • of course I need to have a look on it why we have a potential memory leak here.
  • and I need to see why getStaticData() does not finish. Even if using just one Powershell instance it should complete...
  • then the next step is to extend this concept to use a (auto growing) shell pool for those who run a lot of commands
  • I am thinking to implement something comparable to a connection pool for DBs with configurable time to live (in idle) and max pool size

All this is unfortunately needed because we HAVE to move to powershell and spinning up a powershell shell is so costly. I wish all of this would be as easy as with linux or Mac ;-)

sebhildebrandt avatar Nov 22 '21 05:11 sebhildebrandt

@sebhildebrandt something to note Cmd.exe isn't going away! For example I can still run query user to get all users, Yet it was replaced with powershell? Also I found no reference anywhere in the latest Windows 11 build that the wmic command line has indeed vanished?

si458 avatar Nov 22 '21 07:11 si458

@si458

USER: yes, you are right. The reason why it was replaced it due to international character problems. See also #610. The problem here is that windows keeps the original functionality and there is no way to get the stdout as UTF8 when executing query user ...

WMIC: yes it is marked as deprecated (https://docs.microsoft.com/en-us/windows/win32/wmisdk/wmic) and I got reports that in some versions it was not longer available (I guess with Windows 11 22483 and higher it will be then completely removed) ... Generally wmic has the same problem with international characters and this is also a reason why I had to move to powerShell ... I wish that this would be different but unfortunately this all is a little bit more complicated in windows.

sebhildebrandt avatar Nov 22 '21 08:11 sebhildebrandt

@4n0nh4x0r i have just tried upgrading my node to 16.13.0 and its the same issue runs one instance of powershell as expected, but the memory usage of it keeps climbing, ok its very very slowly but it still climbs @sebhildebrandt maybe you need to clear the old data from the console window after so long? i also understand these things take time but baby steps in the right direction 👍

si458 avatar Nov 22 '21 12:11 si458

@4n0nh4x0r after me watching process explorer using only data.cpu = await si.get({ currentLoad: "currentLoad" }) doesnt see a memory increase? but if you use only data.ram = await si.get({ mem: "total,used" }) or data.disk = await si.get({ fsSize: "size,used" }) then suddenly the memory increases in the powershell instance? @sebhildebrandt might be helpful to debug maybe

si458 avatar Nov 22 '21 12:11 si458

in this way, with promise all I managed to obtain data from all sides without exploiting the use of cpu

let search = new Object() Promise.all(

        [
            "os",
            "disk",
            "cpuload",
            "networkstate",
            "networkconnections",
            "networkdevices",
            "ram",
            "uptime"
        ].map(async item => {
            
            switch (item) {

                case "os":
                    await si.osInfo().then(data => { search.os = data })
                    .catch((e)=>{ loggering('app', JSON.stringify(e) , path.join(deployPath, 'log/') ) })
                    break;

                case "disk":
                    await si.fsSize().then(data => { search.disk = data })
                    .catch((e)=>{ loggering('app', JSON.stringify(e) , path.join(deployPath, 'log/') ) })
                    break;

                case "cpuload":
                    await si.currentLoad().then(data => { search.cpuload = data })
                    .catch((e)=>{ loggering('app', JSON.stringify(e) , path.join(deployPath, 'log/') ) })
                    break;

                case "networkstate":
                    await si.networkConnections().then(data => { 
                        
                        let establecido = 0, escuchando = 0, esperando_cierre = 0, time_wait = 0, cerrado = 0, UNKNOWN = 0, SYN_SENT = 0, SYN_RECV = 0, LAST_ACK = 0, FIN_WAIT1 = 0, FIN_WAIT2 = 0

                        for (let index = 0; index < data.length; index++) {
                            const element = data[index].state;
                            switch (data[index].state) {
                                case "ESTABLISHED":
                                    establecido ++
                                    break;
                                case "CLOSE":
                                    cerrado ++
                                    break;
                                case "TIME_WAIT":
                                    time_wait ++
                                    break;
                                case "LISTEN":
                                    escuchando ++
                                    break;
                                case "CLOSE_WAIT":
                                    esperando_cierre ++
                                    break;
                                case "SYN_SENT":
                                    SYN_SENT ++
                                    break;
                                case "SYN_RECV":
                                    SYN_RECV ++
                                    break;
                                case "LAST_ACK":
                                    LAST_ACK ++
                                    break;
                                case "FIN_WAIT1":
                                    FIN_WAIT1 ++
                                    break;
                                case "FIN_WAIT2":
                                    FIN_WAIT2 ++
                                    break;
                                default:
                                    UNKNOWN ++
                                    break;
                            }
                            
                        }

                        search.networkstate = { 

                            ESTABLISHED: establecido,
                            CLOSE: cerrado,
                            TIME_WAIT: time_wait,
                            LISTEN: escuchando,
                            CLOSE_WAIT:esperando_cierre,
                            UNKNOWN: UNKNOWN,
                            SYN_SENT:SYN_SENT,
                            SYN_RECV:SYN_RECV,
                            LAST_ACK:LAST_ACK,
                            FIN_WAIT1:FIN_WAIT1,
                            FIN_WAIT2:FIN_WAIT2
            
                        }

                    })
                    .catch((e)=>{ loggering('app', JSON.stringify(e) , path.join(deployPath, 'log/') ) })
                    break;

                case "networkconnections":
                    await si.networkConnections().then(data => { search.networkconnections = data })
                    .catch((e)=>{ loggering('app', JSON.stringify(e) , path.join(deployPath, 'log/') ) })
                    break;

                case "networkdevices":
                    await si.networkStats().then(data => { search.networkdevices = data })
                    .catch((e)=>{ loggering('app', JSON.stringify(e) , path.join(deployPath, 'log/') ) })
                    break;

                case "processes":
                    await si.processes().then(data => { search.processes = data })
                    .catch((e)=>{ loggering('app', JSON.stringify(e) , path.join(deployPath, 'log/') ) })
                    break;
    
                case "ram":
                    await si.mem().then(data => { search.ram = data })
                    .catch((e)=>{ loggering('app', JSON.stringify(e) , path.join(deployPath, 'log/') ) })
                    break;
    
                case "uptime":

                    let totalSeconds = si.time().uptime;
                    let seconds = parseInt(totalSeconds, 10);
            
                    let days = Math.floor(seconds / (3600*24));
                    seconds  -= days*3600*24;
                    let hrs   = Math.floor(seconds / 3600);
                    seconds  -= hrs*3600;
                    let mnts = Math.floor(seconds / 60);
                    seconds  -= mnts*60;

                    search.uptime = {
                        "dias": days,
                        "horas": hrs,
                        "minutos": mnts,
                        "segundos": seconds
                    }

                    break;
            
            }}

        )

aaferna avatar Nov 26 '21 23:11 aaferna