Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

100% CPU load on windows #626

Open
4n0nh4x0r opened this issue Nov 17, 2021 · 22 comments
Open

100% CPU load on windows #626

4n0nh4x0r opened this issue Nov 17, 2021 · 22 comments

Comments

@4n0nh4x0r
Copy link

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.

@sebhildebrandt
Copy link
Owner

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

@4n0nh4x0r
Copy link
Author

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

@aaferna
Copy link

aaferna commented Nov 19, 2021

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.

@sebhildebrandt
Copy link
Owner

@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!

@4n0nh4x0r
Copy link
Author

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

@sebhildebrandt
Copy link
Owner

@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 ;-)

@si458
Copy link
Contributor

si458 commented Nov 21, 2021

@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();
})();

@4n0nh4x0r
Copy link
Author

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

@si458
Copy link
Contributor

si458 commented Nov 21, 2021

@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?

@4n0nh4x0r
Copy link
Author

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

@si458
Copy link
Contributor

si458 commented Nov 21, 2021

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?

@4n0nh4x0r
Copy link
Author

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

@si458
Copy link
Contributor

si458 commented Nov 21, 2021

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);

@4n0nh4x0r
Copy link
Author

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

@si458
Copy link
Contributor

si458 commented Nov 21, 2021

@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

@4n0nh4x0r
Copy link
Author

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

@sebhildebrandt
Copy link
Owner

sebhildebrandt commented Nov 22, 2021

@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 ;-)

@si458
Copy link
Contributor

si458 commented Nov 22, 2021

@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?

@sebhildebrandt
Copy link
Owner

@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.

@si458
Copy link
Contributor

si458 commented Nov 22, 2021

@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
Copy link
Contributor

si458 commented Nov 22, 2021

@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

@aaferna
Copy link

aaferna commented Nov 26, 2021

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;
            
            }}

        )

zincnode added a commit to zincnode/H-Bar that referenced this issue Jun 18, 2023
Fix the problem that systeminformation keeps launching new powershells on
Windows platform, causing CPU usage to rise to 100%.
More details: sebhildebrandt/systeminformation#626
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants