dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.98k stars 4.66k forks source link

How to get Common System Hardware information across platforms #22948

Open AlexGhiondea opened 7 years ago

AlexGhiondea commented 7 years ago

@heartlocker commented on Mon Jul 24 2017

As the .NET Core project could run on Windows, Linux and Mac OS. Is it possible for me to get the PhysicalMemory size of the running machine on these three OS. Just like in .NET framework, by using Microsoft.VisualBasic.Devices.ComputerInfo to get the Physical Memory information of the windows machine


@Petermarcu commented on Wed Jul 26 2017

@AlexGhiondea , should this issue be moved to CoreFX to make an API proposal?


@AlexGhiondea commented on Wed Jul 26 2017

@Petermarcu yes -- I will move it.

Petermarcu commented 7 years ago

This is probably a larger discussion and API design we should have about how to get common system information in general across platforms.

mellinoe commented 7 years ago

Do we want "RuntimeInformation" to encompass this sort of thing? Although there's a real risk of it becoming a "grab bag of stuff" (and it sort of already is...), I'm not sure there's a better place for it.

Petermarcu commented 7 years ago

That was the first thing I thought of. Yes, it may become a grab bag of stuff but hopefully its common information people want / need to know about the hardware, OS, etc. Kinda wish it wasn't under InteropServices but that ship has sailed :(

Petermarcu commented 7 years ago

@mellinoe , do you want to carry this forward and make an API proposal with a basic set of capabilities?

danmoseley commented 7 years ago

There is some stuff already on Environment of course (and proposals for more -- https://github.com/dotnet/corefx/issues/16767)

mellinoe commented 7 years ago

Unless we've pivoted in this regard, then we don't want to be adding new stuff into legacy types like Environment... we certainly could have changed our minds about that, though. That was kind of the impetus for developing RuntimeInformation in the first place.

mellinoe commented 7 years ago

@mellinoe , do you want to carry this forward and make an API proposal with a basic set of capabilities?

Sure, I can come up with something. I'll try to come up with some reasonable basic set and see how different OS's expose that info.

4creators commented 7 years ago

It would be very helpful to add new class to System.Diagnostics used solely for the purpose of gathering all important information on the physical system even if this information would be available in other parts of framework. The reason is that besides of running on different operating systems .NET Core may run on different hardware (including IoT and mobile devices).

public class System.Diagnostic.Hardware/Machine

// with some of the properties
public static long PhysicalMemory;   // (in bytes)
public static int ProcessorSockets;
public static int ProcessorCount;
public static int ProcessorCoreCount;    // alternatively ThreadsPerCore (for SMT processors)
public static int USB2Ports;
public static int USB3Ports;
public static int GraphicsAccelerators;
public static int GraphicsMemory;
public static int DisplayCount ;    // additional benefit - ability to detect headless hardware
public static int DisplayResolution (ppi)
public static int NetworkInterfaceCount;
public static long NetworkInterfaceSpeed;
public static int Gyroscope;
public static int GyroscopeAxisCount;
public static bool Gps;
public static bool Galileo;
public static bool Compass;
public static int Accelerometer;
public static int AccelerometerAxisCount;
public static int SpeakersCount;
public static int MicrophoneCount;
public static int CameraCount;
public static int CameraResolution;    // pixels
public static int HardDriveCount;
public static int HardDriveCapacity;
public static int HardDriveType;

The code above is an oversimplified sketch since it would be necessary to provide details for every 'complex' hardware with breakdown of features for every individual device i.e. display, network interface, graphics accelerator, hdd etc.

In addition it would be possible to list here selectively only the device types which are currently supported by .NET Framework/Core or list all and throw PNSE for not supported devices i.e. Serial ports but not Jtag port (or PNSE exception for Jtag). This would provide additional level of information on framework for user communicating hardware support matrix of framework.

Petermarcu commented 7 years ago

Another use case here is getting the identifier of various pieces of the hardware to know if you are on the same machine or a different one or if the hardware in the system has changed. Things like Processor Id, BIOS information, and Motherboard information.

4creators commented 7 years ago

There are some new capabilities in Linux kernel which could be useful for collecting hardware and performance data: Windows Management Instrumentation Now A Formal Bus With Linux 4.13. Kernel changes allow for fast feature uptake and can be safely used for forward looking feature implementation. I do not know very much about details of Linux WMI implementation so it would helpful to ask Linux experts for their opinion.

mellinoe commented 7 years ago

One thing that needs to be reflected in the API is that on some systems, or some configurations of systems, certain pieces of information will not be available, at all. If information is not available or permitted to be accessed by the system, that result should be expressible through the API itself, not as an exception. We can choose a "least common denominator" set of things that we KNOW will be available everywhere, but that isn't useful, especially not for a query API like this.

The NetworkInformation types, which I ported a while back to Linux and macOS, are a very good example of what NOT to do in this kind of API. They throw PlatformNotSupportedException's all over the place because many pieces of information are either inaccessible, represented differently, or just plain don't make sense for non-Windows platforms. These API's were never designed to be used outside of Windows, so it's not a surprise that they don't work well on Linux or Mac.

4creators commented 7 years ago

The NetworkInformation types, which I ported a while back to Linux and macOS, are a very good example of what NOT to do in this kind of API

Agree that PSNE exception is not very useful type of information from hardware query. But in principle what has to be presented to user this is the following matrix of capabilities:

  1. NET Core has no available hw info - reason for lack of info is unknown
  2. Platform does not have hw info which NET Core could query
  3. Net Core has partial hw info
  4. NET Core has full info Eventually one more:
  5. Platform has info but we cannot access it

This could be encapsulated in simple enum and property plus capabilities query i.e.:


public enum HardwareInfo
{
    Unknown = 0,
    NoInfo,
    NoInfoOnPlatform,
    NoAccess,
    Partial,
    Full
}

public abstract class DeviceInfo 
{
    public HardwareInfo HardwareInfo { get; protected set; }

    // Capabilities query similar to RuntimeInformation
    public abstract HardwareInfo IsSupported(string queryCapability);
}

[Flags]
public enum ProcessorConfiguration
{
    Unknown = 0,
    SymmetricMultiProcessing = 1,
    BigLittle = 2,
    // APU means that processor contains general purpose CPU core(s) and some accelerators
    ApplicationProccessingUnit = 4,
    GraphicsAccelerator = 8,
    FpgaAccelerator = 16,
    AiAccelerator = 32
}

public class ProcessorInfo : DeviceInfo
{
    public int SocketsCount { get; protected set; }
    public int ProcessorCount { get; protected set; }
    public enum ProcessorConfiguration { get; protected set; } 

    // Override abstract IsSupported
    public override HardwareInfo IsSupported(string queryCapability)
    {
        switch(queryCapability)
        {
            case namof(SocketsCount):
                return QuerySocketsCountCapabilities();
            case nameof(ProcessorCount):
                return QueryProcessorCountCapabilities();
            case nameof(ProcessorConfiguration):
                return QueryProcessorConfigurationCapabilities();
            default:
                throw new ArgumentException(nameof(queryCapability));
        }
    }
}

public class SmpProcessorInfo : ProcessorInfo
{
    public int CoreCount {get; protected set; }
    public int ThreadsPerCore { get; protected set; } 
    public InstructionSet InstructionSet { get; protected set; } 

    // Override abstract IsSupported
    public override HardwareInfo IsSupported(string queryCapability)
    {
        // implementation
    } 
}

// First look at this problem
public class BigLittleProcessorInfo : ProcessorInfo
{
    public CoreType[] CoreTypes { get; protected set; }
    public IReadOnlyDictionary<CoreType, int>CoreCount { get; protected set; }
    public IReadOnlyDictionary<CoreType, int>ThreadsPerCore { get; protected set; } 
    public IReadOnlyDictionary<CoreType, InstructionSet> InstructionSet { get; protected set; } 

    // Override abstract IsSupported
    public override HardwareInfo IsSupported(string queryCapability)
    {
        // implementation
    } 
}

// Most important values i.e. for SIMD intrinsics
[Flags]
public enum CpuidX86 : ulong
{
    Unknwown = 0,
    // No values set at this stage
    Sse,
    Sse2,
    Sse3,
    Ssse3,
    Sse41,
    Sse42,
    Avx,
    Avx2,
    Avx512,  // This one has to be more complex
    Aes,
    Fma,
    Tsx,
    .....
}

public struct CpuidValue
{
    public uint EAX;
    public uint EBX;
    public uint ECX;
    public uint EDX;
}

public class X86ProcessorInfo : SmpProcessorInfo
{
    public CpuidX86 Cpuid { get; protected set; }
    public int FirstLevelCache { get; protected set; }
    public int SecondLevelCache { get; protected set; }
    public bool IsSecondLevelCacheShared { get; protected set; }
    public int ThirdLevelCache { get; protected set; }
    public bool IsThirdLevelCacheShared { get; protected set; }
    // other items ......

    // Override abstract IsSupported
    public override HardwareInfo IsSupported(string queryCapability)
    {
        // implementation
    }

    public static CpuidValue GetCpuid(uint eaxQueryValue) { }  
}

public class PowerProcessorInfo : SmpProcessorInfo
{
....
}

public class ArmSmpProcessorInfo : SmpProcessorInfo
{
....
}

public class ArmBlProcessorInfo : BigLittleProcessorInfo
{
....
}

public class Hardware
{
    public static HardwareInfo IsSupported(string queryCapability)
    {
        switch(queryCapability)
        {
            case namof(ProcessorInfo):
                return QueryProcessorInfoCapabilities();
            // ......
    }
    // could be null - in this case check what is the reason
    public static ProcessorInfo ProcessorInfo { get; private set; }

    // all other devices
}

// usage

var processor = Hardware.ProcessorInfo;
if (processor is ArmSmpProcessorInfo armProc)
{
    // use armProc in code
}
else if (processor is X86ProcessorInfo x86Proc)
{
    // use x86Proc
}
else
{
    var support = Hardware.IsSupported(nameof(Hardware.ProcessorInfo));
    // use support info
}

This is loose outline how it can be done for processor. I think that this kind of pattern which tries to get only hardware features and not OS hardware abstractions would be most meaningful one and would escape problem of being not present in given OS. If info is not available one can query support.

Above example could be very useful for determining SIMD support on given platform - see issue dotnet/corefx#22843

Petermarcu commented 7 years ago

@mellinoe , should we start a prototype or two in corefxlab and start filling in some of these ideas?

4creators commented 7 years ago

@mellinoe @Petermarcu I would volunteer to do some work in such project if team would be interested

Petermarcu commented 7 years ago

@ericstj as well. Let's see if we can get a proposal together and get a project rolling.

ericstj commented 7 years ago

I've been thinking about this and wanted to share a few principles that we should remember from past API.

  1. Focus on hardware that is ubiquitous and unlikely to be completely obsolete in the future.
  2. Don't strive for completeness in a strongly typed API. That will just create an API that's always lagging the new tech that comes out.
  3. Don't take dependencies on new stacks (EG: bluetooth, GPU capabilities, etc) just to provide status / capability of that stack. Instead that capability should live with a library that exposes that stack.
  4. Don't expose things just because we can. We need to have a scenario for how an application/library might use that information. If that scenario involves loading up a different library then we might just defer to that library for the capability.

Things like CPU cores|clock|cache and memory all make sense for potentially making CPU vs memory tradeoffs. Some set of hardware identifiers that are exceptionally stable may make sense for machine identification.

I'm not sure about the others (eg: camera, display, gps, etc): how would someone make use of those without some sort of abstraction for actually interacting with those devices?

If folks can enumerate some more scenarios that would help.

danmoseley commented 7 years ago

Are there relevant de facto standards for this info - such that libraries exist on both Unix and Windows? Perhaps then our API could be relatively thin?

ericstj commented 7 years ago

For device enumeration Windows has Setup API and WMI. Setup API was never something we had inbox in .NET (though many folks have written wrappers) but we did have System.Management for WMI. I believe the equivalent is on linux is libudev. I haven't looked at Linux's WMI implementation @4creators mentioned above but that'd be something to examine as part of the System.Management issue.

I still question how useful device enumeration is without having some common way of interacting with the devices. For instance: what good is it to know the machine has a GPU unless I can give it work to do? What good is it to know that the machine has a GPS unless I can read its coordinates?

I think we need to fully describe the scenarios we're trying to enable to make sure we're scoping this correctly. "Wrap device enumeration" while interesting is probably not something we want to lump together with "expose the amount of memory and CPUs available on the machine". I think we should scope this issue down to some key scenarios then design the API that fits those.

joperezr commented 7 years ago

@mellinoe are you still planning on working on a proposal for this? @ericstj brings up very valid points that should be considered in that proposal, so if you do please consider scenarios of why would it be useful to expose some of this info.

mellinoe commented 7 years ago

I don't really have more time at the moment for this, maybe in the coming weeks.

joperezr commented 7 years ago

ok, flagging it as up-for-grabs then so that if anybody else is interested on this they can throw a proposal.

baruchiro commented 5 years ago

@joperezr Hi, is this issue for starters? I need this feature, and I see it marked as 'up-for-grabs'. How long do you think it will take to develop the feature for Linux and Apple?

KamranShahid commented 5 years ago

I need to grasb Linux harware info such as following

some java code is as following

SystemInfo si = new SystemInfo(); HardwareAbstractionLayer hal = si.getHardware(); ComputerSystem computerSystem = hal.getComputerSystem(); CentralProcessor processor = hal.getProcessor(); NetworkIF[] networkIFs = hal.getNetworkIFs(); final Firmware firmware = computerSystem.getFirmware();

1 Model via computerSystem.getModel()) 2 ProcessorId via processor.getProcessorID())
3 firmware version via firmware.getVersion()) 4 network mac address via if(networkIFs.length > 0) { networkIFs[0].getMacaddr()) }
5 linux machineId

Can anybody help me in it?

Even if somebody point me to third party nuget package then it will also be helpful

joperezr commented 5 years ago

@baruchiro I'm not super familiar with RuntimeInformation but it seems to be that we mostly just get all the current information from Pinvoking into native functions like the following:

https://github.com/dotnet/corefx/blob/a10890f4ffe0fadf090c922578ba0e606ebdd16c/src/Native/Unix/System.Native/pal_runtimeinformation.c#L12-L23

I'm not sure if all the requested information will be easy to get on all of our supported Unix distros, but I think that is the point of this issue. The idea is to find if there is extra runtime information that we can consistently get across all of our supported OSes without introducing new dependencies (like the need to install a new native dependency on the system). Once that is found, we will need to find an Api proposal that will have to pass through a review to see if this is something we want to add to the framework, and if it is, then we will add it. Here is a list of the next steps to do here:

  1. Find if there is a consistent way of getting extra hardware/runtime information across all of the supported Unix distros and Windows.
  2. Modify this issue with a formal Api Proposal(This is a good example of a formal Api Proposal) of the Apis that we should add, along with the info gathered from step 1 on what would be the way to implement it.
  3. We will review the proposal, and if approved, then we will flag this issue as Api-approved and we will be ready to get a PR that will add these Apis to the framework.
WYishai commented 5 years ago

Hi! Is there any estimate of when this should be implemented?

ZhangGaoxing commented 5 years ago

I think this should be implemented as soon as possible. .NET Core is about 3.0, this issue is not suitable for Future.😄

joperezr commented 5 years ago

We currently don't have good consensus on a API to detect runtimes, if you are concerned with something other than that feel free to open a new issue for that.

WYishai commented 5 years ago

@joperezr, there were several open issues that were merged into this issue (such as https://github.com/dotnet/standard/issues/327). I'm not sure I fully understand what exactly you mean by "detect runtime," but I miss the ability to check the hardware of the computer I'm running (more specifically CPU, memory, motherboard etc..) and the amount of computer resource that in use / free (CPU, memory, etc...)

lukeschlather commented 5 years ago

https://github.com/dotnet/standard/issues/327 definitely seems like a separate issue from this one (though it should clearly be an issue in dotnet core.)

WeihanLi commented 5 years ago

really need for this

jkotas commented 5 years ago

@WeihanLi What are the specific bits of information you need?

WeihanLi commented 5 years ago

Hi @jkotas , I wanna develop a monitor service, monitor the system cpu/memory/disk usage, if some usage above the level custom defined, send email or do something else to notify someone.

Currently, mainly for system memory & disk usage

jkotas commented 5 years ago

@WeihanLi None of the APIs listed in this issue return system cpu/memory/disk usage.

WeihanLi commented 5 years ago

I came here from the issue here https://github.com/dotnet/standard/issues/327 , should I open a new issue here? @jkotas

---------- Update -----------

open a new issue here https://github.com/dotnet/corefx/issues/38914

newpost commented 5 years ago

when support get hardware infomation across OS?

rustbomber commented 5 years ago

These apis are urgently needed.

d668 commented 4 years ago

yeah NodeJs has this since the inception

var os = require('os');
console.log(os.cpus());
console.log(os.totalmem());
console.log(os.freemem())
chriss2401 commented 4 years ago

So I guess there is no update on this ?

tomrus88 commented 4 years ago

Would be nice at lest get direct access to cpuid instruction without needing to create bytecodes for this manually...

KamranShahid commented 4 years ago

I have done a workaround to get hardware info as per Platform. For windows I have used old way of system Management classes, for Linux i have used different Bash commands to Get Processor Id, Model,model version,machine id. Following are some linux commands i am using

**1. "LinuxModel": "cat /sys/class/dmi/id/product_name"

  1. "LinuxModelVersion": "cat /sys/class/dmi/id/product_version"
  2. "LinuxProcessorId": "dmidecode -t processor | grep -E ID | sed 's/.*: //' | head -n 1"
  3. "LinuxFirmwareVersion": "cat /sys/class/dmi/id/bios_version",
  4. "LinuxMachineId": "cat /var/lib/dbus/machine-id"**

Waiting for some support in the .net core framework soon

baruchiro commented 4 years ago

@KamranShahid Thank you for the workaround. I think we need to offer workarounds here, since it doesn't seem the .NET team planning to solve it soon.

In my company, we also use 6. "LinuxTotalMemory": "awk '/MemTotal/ {print $2}' /proc/meminfo"

KamranShahid commented 4 years ago

@KamranShahid Thank you for the workaround. I think we need to offer workarounds here, since it doesn't seem the .NET team planning to solve it soon.

In my company, we also use 6. "LinuxTotalMemory": "awk '/MemTotal/ {print $2}' /proc/meminfo"

Agreed @baruchiro . I have same motive to help-out others till there comes something supported in the framework

ygoe commented 4 years ago

Since there is so much possible information about each subject to get, maybe there should be separate classes for memory, processor and all the other topics. There is already DriveInfo that can be used on Linux as well if you initialise it with the path you're interested in. All these classes could be grouped in some "system/hardware information" namespace.

But then again, does this have to be in the framework itself? Couldn't we just create a NuGet package for that?

danmoseley commented 4 years ago

The advantage of having your own nuget package is that you can update much more quickly, without going through the .NET team and waiting for their releases.

In my mind, the framework should contain the "enduring basics" especially ones that have a clear, efficient implementation. And for more elaborate things - it would be great to have a community package.

baruchiro commented 4 years ago

@danmosemsft I agree. I can't lead a package myself, but I'm ready to join one today.

damageboy commented 4 years ago

@danmosemsft I completely agree. Moreover, there is already proven solution in the form of hwloc which also has python and rust bindings and is supported on Linux/Windows/Mac.

It would make sense to provide cross platform bindings through hwloc and get the breadth of information it already provides from the ability to generate complete topologies including:

I think this screenshot of lstopo of an AMD ThreadRipper 3970x best summarizes what can be gleaned from it:

image

damageboy commented 4 years ago

I'd like to add another unfortunate data-point to this discussion.

I think that when possible, the relevant hardware information should be considered as JIT time constants to help select the proper code path.

One recent and very unfortunate case I've encountered is PDEP on AMD systems:

https://twitter.com/trav_downs/status/1203425075896225792

To summerize: While AMD processors officially support this opcode, it is really implemented in microcode via a loop and might execute anywhere between 18-289 cycles.

The only proper way to handle such erratic CPU behaviour (and it's really not the last, nor the first time this sort of thing happens and will continue to happen) is to provide the above mentioned HW information to developers while also making sure that the JIT can and will treat this information (CPU model, family, manufacturer, cache line sized, etc.) as a constant when it comes to code-generation.

If this feels like I'm just piling on to an existing issue, let me know, I will gladly open a separate issue for this.

ygoe commented 4 years ago

@damageboy That sounds like very specific data for a rare use case, far from simply gathering the most basic data about the system environment, like free RAM or system-wide CPU usage. But when I glance over this issue again, it seems to go in that direction altogether.

So maybe I should ask to include just free/total RAM information and some CPU usage counters into .NET, maybe the Environment class, and leave all those tiniest details (that only apply to certain environments) to a third-party package?

danmoseley commented 4 years ago

@damageboy could you open a new issue for that (interesting, though)?

danmoseley commented 4 years ago

So maybe I should ask to include just free/total RAM information and some CPU usage counters into .NET

RAM info should be already on the Process class (notwithstanding this recent fix)