aboutsummaryrefslogblamecommitdiffstats
path: root/util/system/mem_info.cpp
blob: 90ed353a59d9b18ce28dba123eacca5924194fcb (plain) (tree)
1
2
3
4
5
6
7
8
9
10

                                
                             

                                
                   







                                                                   
                    
                                 
 
                      
                                                  




                                   
                           

                         
                           
                                           










                                                         
                                            







































                                                           



                    
                                    

                   

                                                                   
 
                                             




                                                                                           
                                                                   











                                                                         
 
                               
 
                  
                                                                   
                            
                                                      
                                                                                                                                                                     
 

                                                                   

                           
                                                                                          
 
                                    
                                                      



                                                                                                                                                                               
                      









                                                                                                                                                                                            
                        
                      
                                                      
                    

                                        
 

                                  
 

















                                                                                                                                         
 






                                                                                               
 
                                             
      
                      
 
#include "mem_info.h"

#include <util/generic/strbuf.h>
#include <util/stream/file.h>
#include <util/string/cast.h>
#include <util/string/builder.h>
#include "error.h"
#include "info.h"

#if defined(_unix_)
    #if defined(_freebsd_)
        #include <sys/sysctl.h>
        #include <sys/types.h>
        #include <sys/user.h>
    #elif defined(_darwin_) && !defined(_arm_) && !defined(__IOS__)
        #include <libproc.h>
    #elif defined(__MACH__) && defined(__APPLE__)
        #include <mach/mach.h>
    #endif
#elif defined(_win_)
    #include <Windows.h>
    #include <util/generic/ptr.h>

using NTSTATUS = LONG;
    #define STATUS_INFO_LENGTH_MISMATCH 0xC0000004
    #define STATUS_BUFFER_TOO_SMALL 0xC0000023

typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
typedef struct _CLIENT_ID {
    HANDLE UniqueProcess;
    HANDLE UniqueThread;
} CLIENT_ID, *PCLIENT_ID;
using KWAIT_REASON = ULONG;
typedef struct _SYSTEM_THREAD_INFORMATION {
    LARGE_INTEGER KernelTime;
    LARGE_INTEGER UserTime;
    LARGE_INTEGER CreateTime;
    ULONG WaitTime;
    PVOID StartAddress;
    CLIENT_ID ClientId;
    LONG Priority;
    LONG BasePriority;
    ULONG ContextSwitches;
    ULONG ThreadState;
    KWAIT_REASON WaitReason;
} SYSTEM_THREAD_INFORMATION, *PSYSTEM_THREAD_INFORMATION;
typedef struct _SYSTEM_PROCESS_INFORMATION {
    ULONG NextEntryOffset;
    ULONG NumberOfThreads;
    LARGE_INTEGER SpareLi1;
    LARGE_INTEGER SpareLi2;
    LARGE_INTEGER SpareLi3;
    LARGE_INTEGER CreateTime;
    LARGE_INTEGER UserTime;
    LARGE_INTEGER KernelTime;
    UNICODE_STRING ImageName;
    LONG BasePriority;
    HANDLE UniqueProcessId;
    HANDLE InheritedFromUniqueProcessId;
    ULONG HandleCount;
    ULONG SessionId;
    ULONG_PTR PageDirectoryBase;
    SIZE_T PeakVirtualSize;
    SIZE_T VirtualSize;
    DWORD PageFaultCount;
    SIZE_T PeakWorkingSetSize;
    SIZE_T WorkingSetSize;
    SIZE_T QuotaPeakPagedPoolUsage;
    SIZE_T QuotaPagedPoolUsage;
    SIZE_T QuotaPeakNonPagedPoolUsage;
    SIZE_T QuotaNonPagedPoolUsage;
    SIZE_T PagefileUsage;
    SIZE_T PeakPagefileUsage;
    SIZE_T PrivatePageCount;
    LARGE_INTEGER ReadOperationCount;
    LARGE_INTEGER WriteOperationCount;
    LARGE_INTEGER OtherOperationCount;
    LARGE_INTEGER ReadTransferCount;
    LARGE_INTEGER WriteTransferCount;
    LARGE_INTEGER OtherTransferCount;
    SYSTEM_THREAD_INFORMATION Threads[1];
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;

typedef enum _SYSTEM_INFORMATION_CLASS {
    SystemBasicInformation = 0,
    SystemProcessInformation = 5,
} SYSTEM_INFORMATION_CLASS;

#else

#endif

namespace NMemInfo {
    TMemInfo GetMemInfo(pid_t pid) {
        TMemInfo result;

#if defined(_unix_)

    #if defined(_linux_) || defined(_freebsd_) || defined(_cygwin_)
        const ui32 pagesize = NSystemInfo::GetPageSize();
    #endif

    #if defined(_linux_) || defined(_cygwin_)
        TString path;
        if (!pid) {
            path = "/proc/self/statm";
        } else {
            path = TStringBuilder() << TStringBuf("/proc/") << pid << TStringBuf("/statm");
        }
        const TString stats = TUnbufferedFileInput(path).ReadAll();

        TStringBuf statsiter(stats);

        result.VMS = FromString<ui64>(statsiter.NextTok(' ')) * pagesize;
        result.RSS = FromString<ui64>(statsiter.NextTok(' ')) * pagesize;

        #if defined(_cygwin_)
        //cygwin not very accurate
        result.VMS = Max(result.VMS, result.RSS);
        #endif
    #elif defined(_freebsd_)
        int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
        size_t size = sizeof(struct kinfo_proc);

        struct kinfo_proc proc;
        Zero(proc);

        errno = 0;
        if (sysctl((int*)mib, 4, &proc, &size, nullptr, 0) == -1) {
            int err = errno;
            TString errtxt = LastSystemErrorText(err);
            ythrow yexception() << "sysctl({CTL_KERN,KERN_PROC,KERN_PROC_PID,pid},4,proc,&size,NULL,0) returned -1, errno: " << err << " (" << errtxt << ")" << Endl;
        }

        result.VMS = proc.ki_size;
        result.RSS = proc.ki_rssize * pagesize;
    #elif defined(_darwin_) && !defined(_arm_) && !defined(__IOS__)
        if (!pid) {
            pid = getpid();
        }
        struct proc_taskinfo taskInfo;
        const int r = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &taskInfo, sizeof(taskInfo));

        if (r != sizeof(taskInfo)) {
            int err = errno;
            TString errtxt = LastSystemErrorText(err);
            ythrow yexception() << "proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &taskInfo, sizeof(taskInfo)) returned " << r << ", errno: " << err << " (" << errtxt << ")" << Endl;
        }
        result.VMS = taskInfo.pti_virtual_size;
        result.RSS = taskInfo.pti_resident_size;
    #elif defined(__MACH__) && defined(__APPLE__)
        Y_UNUSED(pid);
        struct mach_task_basic_info taskInfo;
        mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT;

        const int r = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&taskInfo, &infoCount);
        if (r != KERN_SUCCESS) {
            int err = errno;
            TString errtxt = LastSystemErrorText(err);
            ythrow yexception() << "task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &infoCount) returned" << r << ", errno: " << err << " (" << errtxt << ")" << Endl;
        }
        result.VMS = taskInfo.virtual_size;
        result.RSS = taskInfo.resident_size;
    #elif defined(_arm_)
        Y_UNUSED(pid);
        ythrow yexception() << "arm is not supported";
    #endif
#elif defined(_win_)
        if (!pid) {
            pid = GetCurrentProcessId();
        }

        NTSTATUS status;
        TArrayHolder<char> buffer;
        ULONG bufferSize;

        // Query data for all processes and threads in the system.
        // This is probably an overkill if the target process is normal not-privileged one,
        // but allows to obtain information even about system processes that are not open-able directly.
        typedef NTSTATUS(_stdcall * NTQSI_PROC)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
        NTQSI_PROC NtQuerySystemInformation = (NTQSI_PROC)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "NtQuerySystemInformation");
        bufferSize = 0x4000;

        for (;;) {
            buffer.Reset(new char[bufferSize]);
            status = NtQuerySystemInformation(SystemProcessInformation, buffer.Get(), bufferSize, &bufferSize);

            if (!status) {
                break;
            }

            if (status != STATUS_BUFFER_TOO_SMALL && status != STATUS_INFO_LENGTH_MISMATCH) {
                ythrow yexception() << "NtQuerySystemInformation failed with status code " << status;
            }
        }

        SYSTEM_PROCESS_INFORMATION* process = (SYSTEM_PROCESS_INFORMATION*)buffer.Get();
        while (process->UniqueProcessId != (HANDLE)(size_t)(pid)) {
            if (!process->NextEntryOffset) {
                ythrow yexception() << "GetMemInfo: invalid PID";
            }

            process = (SYSTEM_PROCESS_INFORMATION*)((char*)process + process->NextEntryOffset);
        }

        result.VMS = process->VirtualSize;
        result.RSS = process->WorkingSetSize;
#endif
        return result;
    }
}