From 596c1a8e70e3c8250b2ddf9acc74c5da8ac60112 Mon Sep 17 00:00:00 2001 From: Jitendra Sharma Date: Thu, 30 Apr 2020 12:18:11 +0530 Subject: [PATCH] Implement zetSysmanProcessesGetState API This change does the following: - Implement API zetSysmanProcessesGetState. - Implement ULT to validate zetSysmanProcessesGetState's implementation - Redesign ULTs for zetSysmanDeviceGetProperties to mock only OS specific classes, and validate zetSysmanDeviceGetProperties's implementation end to end. - Some bug fixes in code that are caught by these new ULTs Change-Id: I4a83789771d32978576ff62859628df2c0c795ad Signed-off-by: Jitendra Sharma --- .../sysman/engine/linux/os_engine_imp.h | 2 + .../tools/source/sysman/linux/fs_access.cpp | 5 + .../tools/source/sysman/linux/fs_access.h | 1 + .../sysman/scheduler/linux/os_scheduler_imp.h | 2 + .../sysman/sysman_device/linux/CMakeLists.txt | 1 + .../linux/os_sysman_device_imp.cpp | 158 +++++++++++++----- .../linux/os_sysman_device_imp.h | 44 +++++ .../sysman/sysman_device/os_sysman_device.h | 2 + .../sysman/sysman_device/sysman_device.h | 1 + .../sysman_device/sysman_device_imp.cpp | 23 +++ .../sysman/sysman_device/sysman_device_imp.h | 9 +- .../windows/os_sysman_device_imp.cpp | 5 + level_zero/tools/source/sysman/sysman_imp.cpp | 2 +- .../sysman/sysman_device/CMakeLists.txt | 3 +- .../sysman/sysman_device/linux/CMakeLists.txt | 14 ++ .../linux/mock_sysfs_sysman_device.h | 109 ++++++++++++ .../linux/test_sysman_device.cpp | 126 ++++++++++++++ .../sysman_device/windows/CMakeLists.txt | 14 ++ .../{ => windows}/mock_sysman_device.h | 1 + .../{ => windows}/test_sysman_device.cpp | 41 +++++ 20 files changed, 512 insertions(+), 51 deletions(-) create mode 100644 level_zero/tools/source/sysman/sysman_device/linux/os_sysman_device_imp.h create mode 100644 level_zero/tools/test/unit_tests/sources/sysman/sysman_device/linux/CMakeLists.txt create mode 100644 level_zero/tools/test/unit_tests/sources/sysman/sysman_device/linux/mock_sysfs_sysman_device.h create mode 100644 level_zero/tools/test/unit_tests/sources/sysman/sysman_device/linux/test_sysman_device.cpp create mode 100644 level_zero/tools/test/unit_tests/sources/sysman/sysman_device/windows/CMakeLists.txt rename level_zero/tools/test/unit_tests/sources/sysman/sysman_device/{ => windows}/mock_sysman_device.h (92%) rename level_zero/tools/test/unit_tests/sources/sysman/sysman_device/{ => windows}/test_sysman_device.cpp (64%) diff --git a/level_zero/tools/source/sysman/engine/linux/os_engine_imp.h b/level_zero/tools/source/sysman/engine/linux/os_engine_imp.h index 7b1da62298..a377816dbb 100644 --- a/level_zero/tools/source/sysman/engine/linux/os_engine_imp.h +++ b/level_zero/tools/source/sysman/engine/linux/os_engine_imp.h @@ -23,6 +23,8 @@ class LinuxEngineImp : public OsEngine, public NEO::NonCopyableClass { protected: SysfsAccess *pSysfsAccess = nullptr; + + private: static const std::string computeEngineGroupFile; static const std::string computeEngineGroupName; }; diff --git a/level_zero/tools/source/sysman/linux/fs_access.cpp b/level_zero/tools/source/sysman/linux/fs_access.cpp index 712261e99b..51fab819dd 100644 --- a/level_zero/tools/source/sysman/linux/fs_access.cpp +++ b/level_zero/tools/source/sysman/linux/fs_access.cpp @@ -435,6 +435,11 @@ ze_result_t SysfsAccess::write(const std::string file, const uint64_t val) { return FsAccess::write(fullPath(file), stream.str()); } +ze_result_t SysfsAccess::scanDirEntries(const std::string path, std::vector &list) { + list.clear(); + return FsAccess::listDirectory(fullPath(path).c_str(), list); +} + ze_result_t SysfsAccess::readSymLink(const std::string path, std::string &val) { // Prepend sysfs directory path and call the base readSymLink return FsAccess::readSymLink(fullPath(path).c_str(), val); diff --git a/level_zero/tools/source/sysman/linux/fs_access.h b/level_zero/tools/source/sysman/linux/fs_access.h index 8ddbd7dfe3..796194c337 100644 --- a/level_zero/tools/source/sysman/linux/fs_access.h +++ b/level_zero/tools/source/sysman/linux/fs_access.h @@ -90,6 +90,7 @@ class SysfsAccess : private FsAccess { MOCKABLE_VIRTUAL ze_result_t write(const std::string file, const uint64_t val); ze_result_t write(const std::string file, const double val); + MOCKABLE_VIRTUAL ze_result_t scanDirEntries(const std::string path, std::vector &list); ze_result_t readSymLink(const std::string path, std::string &buf); ze_result_t getRealPath(const std::string path, std::string &buf); ze_result_t bindDevice(const std::string device); diff --git a/level_zero/tools/source/sysman/scheduler/linux/os_scheduler_imp.h b/level_zero/tools/source/sysman/scheduler/linux/os_scheduler_imp.h index 3b72d6176b..cd848eb828 100644 --- a/level_zero/tools/source/sysman/scheduler/linux/os_scheduler_imp.h +++ b/level_zero/tools/source/sysman/scheduler/linux/os_scheduler_imp.h @@ -26,6 +26,8 @@ class LinuxSchedulerImp : public NEO::NonCopyableClass, public OsScheduler { LinuxSchedulerImp() = default; LinuxSchedulerImp(OsSysman *pOsSysman); ~LinuxSchedulerImp() override = default; + + protected: SysfsAccess *pSysfsAccess = nullptr; private: diff --git a/level_zero/tools/source/sysman/sysman_device/linux/CMakeLists.txt b/level_zero/tools/source/sysman/sysman_device/linux/CMakeLists.txt index 23b8dea9d2..89b348a802 100644 --- a/level_zero/tools/source/sysman/sysman_device/linux/CMakeLists.txt +++ b/level_zero/tools/source/sysman/sysman_device/linux/CMakeLists.txt @@ -7,6 +7,7 @@ set(L0_SRCS_TOOLS_SYSMAN_DEVICE_LINUX ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt ${CMAKE_CURRENT_SOURCE_DIR}/os_sysman_device_imp.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/os_sysman_device_imp.h ) if(UNIX) diff --git a/level_zero/tools/source/sysman/sysman_device/linux/os_sysman_device_imp.cpp b/level_zero/tools/source/sysman/sysman_device/linux/os_sysman_device_imp.cpp index bcab4b2aa7..4eb7ac0d02 100644 --- a/level_zero/tools/source/sysman/sysman_device/linux/os_sysman_device_imp.cpp +++ b/level_zero/tools/source/sysman/sysman_device/linux/os_sysman_device_imp.cpp @@ -5,46 +5,18 @@ * */ +#include "level_zero/tools/source/sysman/sysman_device/linux/os_sysman_device_imp.h" + #include "shared/source/os_interface/linux/drm_neo.h" #include "shared/source/os_interface/linux/os_interface.h" #include "level_zero/core/source/device/device.h" -#include "level_zero/tools/source/sysman/linux/fs_access.h" -#include "level_zero/tools/source/sysman/linux/os_sysman_imp.h" -#include "level_zero/tools/source/sysman/sysman_device/os_sysman_device.h" -#include "level_zero/tools/source/sysman/sysman_device/sysman_device_imp.h" +#include #include namespace L0 { -class LinuxSysmanDeviceImp : public OsSysmanDevice { - public: - void getSerialNumber(int8_t (&serialNumber)[ZET_STRING_PROPERTY_SIZE]) override; - void getBoardNumber(int8_t (&boardNumber)[ZET_STRING_PROPERTY_SIZE]) override; - void getBrandName(int8_t (&brandName)[ZET_STRING_PROPERTY_SIZE]) override; - void getModelName(int8_t (&modelName)[ZET_STRING_PROPERTY_SIZE]) override; - void getVendorName(int8_t (&vendorName)[ZET_STRING_PROPERTY_SIZE]) override; - void getDriverVersion(int8_t (&driverVersion)[ZET_STRING_PROPERTY_SIZE]) override; - ze_result_t reset() override; - LinuxSysmanDeviceImp(OsSysman *pOsSysman); - ~LinuxSysmanDeviceImp() override = default; - - // Don't allow copies of the LinuxSysmanDeviceImp object - LinuxSysmanDeviceImp(const LinuxSysmanDeviceImp &obj) = delete; - LinuxSysmanDeviceImp &operator=(const LinuxSysmanDeviceImp &obj) = delete; - - private: - SysfsAccess *pSysfsAccess; - LinuxSysmanImp *pLinuxSysmanImp; - static const std::string deviceDir; - static const std::string vendorFile; - static const std::string deviceFile; - static const std::string subsystemVendorFile; - static const std::string driverFile; - static const std::string functionLevelReset; -}; - const std::string vendorIntel("Intel(R) Corporation"); const std::string unknown("Unknown"); const std::string intelPciId("0x8086"); @@ -54,15 +26,27 @@ const std::string LinuxSysmanDeviceImp::deviceFile("device/device"); const std::string LinuxSysmanDeviceImp::subsystemVendorFile("device/subsystem_vendor"); const std::string LinuxSysmanDeviceImp::driverFile("device/driver"); const std::string LinuxSysmanDeviceImp::functionLevelReset("device/reset"); +const std::string LinuxSysmanDeviceImp::clientsDir("clients"); + +// Map engine entries(numeric values) present in /sys/class/drm/card/clients//busy, +// with engine enum defined in leve-zero spec +// Note that entries with int 2 and 3(represented by i915 as CLASS_VIDEO and CLASS_VIDEO_ENHANCE) +// are both mapped to MEDIA, as CLASS_VIDEO represents any media fixed-function hardware. +const std::map engineMap = { + {0, ZET_ENGINE_TYPE_3D}, + {1, ZET_ENGINE_TYPE_DMA}, + {2, ZET_ENGINE_TYPE_MEDIA}, + {3, ZET_ENGINE_TYPE_MEDIA}, + {4, ZET_ENGINE_TYPE_COMPUTE}}; void LinuxSysmanDeviceImp::getSerialNumber(int8_t (&serialNumber)[ZET_STRING_PROPERTY_SIZE]) { std::copy(unknown.begin(), unknown.end(), serialNumber); - serialNumber[unknown.size()] = '\0'; + serialNumber[unknown.size() - 1] = '\0'; } void LinuxSysmanDeviceImp::getBoardNumber(int8_t (&boardNumber)[ZET_STRING_PROPERTY_SIZE]) { std::copy(unknown.begin(), unknown.end(), boardNumber); - boardNumber[unknown.size()] = '\0'; + boardNumber[unknown.size() - 1] = '\0'; } void LinuxSysmanDeviceImp::getBrandName(int8_t (&brandName)[ZET_STRING_PROPERTY_SIZE]) { @@ -70,16 +54,16 @@ void LinuxSysmanDeviceImp::getBrandName(int8_t (&brandName)[ZET_STRING_PROPERTY_ ze_result_t result = pSysfsAccess->read(subsystemVendorFile, strVal); if (ZE_RESULT_SUCCESS != result) { std::copy(unknown.begin(), unknown.end(), brandName); - brandName[unknown.size()] = '\0'; + brandName[unknown.size() - 1] = '\0'; return; } if (strVal.compare(intelPciId) == 0) { std::copy(vendorIntel.begin(), vendorIntel.end(), brandName); - brandName[vendorIntel.size()] = '\0'; + brandName[vendorIntel.size() - 1] = '\0'; return; } std::copy(unknown.begin(), unknown.end(), brandName); - brandName[unknown.size()] = '\0'; + brandName[unknown.size() - 1] = '\0'; } void LinuxSysmanDeviceImp::getModelName(int8_t (&modelName)[ZET_STRING_PROPERTY_SIZE]) { @@ -87,12 +71,12 @@ void LinuxSysmanDeviceImp::getModelName(int8_t (&modelName)[ZET_STRING_PROPERTY_ ze_result_t result = pSysfsAccess->read(deviceFile, strVal); if (ZE_RESULT_SUCCESS != result) { std::copy(unknown.begin(), unknown.end(), modelName); - modelName[unknown.size()] = '\0'; + modelName[unknown.size() - 1] = '\0'; return; } std::copy(strVal.begin(), strVal.end(), modelName); - modelName[strVal.size()] = '\0'; + modelName[strVal.size() - 1] = '\0'; } void LinuxSysmanDeviceImp::getVendorName(int8_t (&vendorName)[ZET_STRING_PROPERTY_SIZE]) { @@ -100,21 +84,21 @@ void LinuxSysmanDeviceImp::getVendorName(int8_t (&vendorName)[ZET_STRING_PROPERT ze_result_t result = pSysfsAccess->read(vendorFile, strVal); if (ZE_RESULT_SUCCESS != result) { std::copy(unknown.begin(), unknown.end(), vendorName); - vendorName[unknown.size()] = '\0'; + vendorName[unknown.size() - 1] = '\0'; return; } if (strVal.compare(intelPciId) == 0) { std::copy(vendorIntel.begin(), vendorIntel.end(), vendorName); - vendorName[vendorIntel.size()] = '\0'; + vendorName[vendorIntel.size() - 1] = '\0'; return; } std::copy(unknown.begin(), unknown.end(), vendorName); - vendorName[unknown.size()] = '\0'; + vendorName[unknown.size() - 1] = '\0'; } void LinuxSysmanDeviceImp::getDriverVersion(int8_t (&driverVersion)[ZET_STRING_PROPERTY_SIZE]) { std::copy(unknown.begin(), unknown.end(), driverVersion); - driverVersion[unknown.size()] = '\0'; + driverVersion[unknown.size() - 1] = '\0'; } static void getPidFdsForOpenDevice(ProcfsAccess *pProcfsAccess, SysfsAccess *pSysfsAccess, const ::pid_t pid, std::vector &deviceFds) { @@ -228,6 +212,94 @@ ze_result_t LinuxSysmanDeviceImp::reset() { return ZE_RESULT_SUCCESS; } +// Processes in the form of clients are present in sysfs like this: +// # /sys/class/drm/card0/clients$ ls +// 4 5 +// # /sys/class/drm/card0/clients/4$ ls +// busy name pid +// # /sys/class/drm/card0/clients/4/busy$ ls +// 0 1 2 3 +// +// Number of processes(If one process opened drm device multiple times, then multiple entries will be +// present for same process in clients directory) will be the number of clients +// (For example from above example, processes dirs are 4,5) +// Thus total number of times drm connection opened with this device will be 2. +// process.pid = pid (from above example) +// process.engines -> For each client's busy dir, numbers 0,1,2,3 represent engines and they contain +// accumulated nanoseconds each client spent on engines. +// Thus we traverse each file in busy dir for non-zero time and if we find that file say 0,then we could say that +// this engine 0 is used by process. +ze_result_t LinuxSysmanDeviceImp::scanProcessesState(std::vector &pProcessList) { + std::vector clientIds; + ze_result_t result = pSysfsAccess->scanDirEntries(clientsDir, clientIds); + if (ZE_RESULT_SUCCESS != result) { + return result; + } + + // Create a map with unique pid as key and engineType as value + std::map pidClientMap; + for (auto clientId : clientIds) { + // realClientPidPath will be something like: clients//pid + std::string realClientPidPath = clientsDir + "/" + clientId + "/" + "pid"; + uint64_t pid; + result = pSysfsAccess->read(realClientPidPath, pid); + if (ZE_RESULT_SUCCESS != result) { + return result; + } + + // Traverse the clients//busy directory to get accelerator engines used by process + std::vector engineNums; + std::string busyDirForEngines = clientsDir + "/" + clientId + "/" + "busy"; + result = pSysfsAccess->scanDirEntries(busyDirForEngines, engineNums); + if (ZE_RESULT_SUCCESS != result) { + return result; + } + int64_t engineType = 0; + // Scan all engine files present in /sys/class/drm/card0/clients//busy and check + // whether that engine is used by process + for (auto engineNum : engineNums) { + uint64_t timeSpent = 0; + std::string engine = busyDirForEngines + "/" + engineNum; + result = pSysfsAccess->read(engine, timeSpent); + if (ZE_RESULT_SUCCESS != result) { + return result; + } + if (timeSpent > 0) { + int i915EnginNumber = stoi(engineNum); + auto i915MapToL0EngineType = engineMap.find(i915EnginNumber); + zet_engine_type_t val = ZET_ENGINE_TYPE_OTHER; + if (i915MapToL0EngineType != engineMap.end()) { + // Found a valid map + val = i915MapToL0EngineType->second; + } + // In this for loop we want to retrieve the overall engines used by process + engineType = engineType | (1 << val); + } + } + + auto ret = pidClientMap.insert(std::make_pair(pid, engineType)); + if (ret.second == false) { + // insertion failed as entry with same pid already exists in map + // Now update the engineType field of the existing pid entry + auto pidEntryFromMap = pidClientMap.find(pid); + auto existingEngineType = pidEntryFromMap->second; + engineType = existingEngineType | engineType; + pidClientMap[pid] = engineType; + } + } + + // iterate through all elements of pidClientMap + for (auto itr = pidClientMap.begin(); itr != pidClientMap.end(); ++itr) { + zet_process_state_t process; + process.processId = static_cast(itr->first); + process.memSize = 0; // Unsupported + process.engines = itr->second; + pProcessList.push_back(process); + } + + return result; +} + LinuxSysmanDeviceImp::LinuxSysmanDeviceImp(OsSysman *pOsSysman) { pLinuxSysmanImp = static_cast(pOsSysman); @@ -239,4 +311,4 @@ OsSysmanDevice *OsSysmanDevice::create(OsSysman *pOsSysman) { return static_cast(pLinuxSysmanDeviceImp); } -} // namespace L0 +} // namespace L0 \ No newline at end of file diff --git a/level_zero/tools/source/sysman/sysman_device/linux/os_sysman_device_imp.h b/level_zero/tools/source/sysman/sysman_device/linux/os_sysman_device_imp.h new file mode 100644 index 0000000000..bcc6537428 --- /dev/null +++ b/level_zero/tools/source/sysman/sysman_device/linux/os_sysman_device_imp.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2020 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#pragma once +#include "level_zero/tools/source/sysman/linux/fs_access.h" +#include "level_zero/tools/source/sysman/linux/os_sysman_imp.h" +#include "level_zero/tools/source/sysman/sysman_device/os_sysman_device.h" +#include "level_zero/tools/source/sysman/sysman_device/sysman_device_imp.h" + +namespace L0 { + +class LinuxSysmanDeviceImp : public OsSysmanDevice, public NEO::NonCopyableClass { + public: + void getSerialNumber(int8_t (&serialNumber)[ZET_STRING_PROPERTY_SIZE]) override; + void getBoardNumber(int8_t (&boardNumber)[ZET_STRING_PROPERTY_SIZE]) override; + void getBrandName(int8_t (&brandName)[ZET_STRING_PROPERTY_SIZE]) override; + void getModelName(int8_t (&modelName)[ZET_STRING_PROPERTY_SIZE]) override; + void getVendorName(int8_t (&vendorName)[ZET_STRING_PROPERTY_SIZE]) override; + void getDriverVersion(int8_t (&driverVersion)[ZET_STRING_PROPERTY_SIZE]) override; + ze_result_t reset() override; + ze_result_t scanProcessesState(std::vector &pProcessList) override; + LinuxSysmanDeviceImp() = default; + LinuxSysmanDeviceImp(OsSysman *pOsSysman); + ~LinuxSysmanDeviceImp() override = default; + + protected: + SysfsAccess *pSysfsAccess = nullptr; + LinuxSysmanImp *pLinuxSysmanImp = nullptr; + + private: + static const std::string deviceDir; + static const std::string vendorFile; + static const std::string deviceFile; + static const std::string subsystemVendorFile; + static const std::string driverFile; + static const std::string functionLevelReset; + static const std::string clientsDir; +}; + +} // namespace L0 \ No newline at end of file diff --git a/level_zero/tools/source/sysman/sysman_device/os_sysman_device.h b/level_zero/tools/source/sysman/sysman_device/os_sysman_device.h index 5bb1162bb1..d992143716 100644 --- a/level_zero/tools/source/sysman/sysman_device/os_sysman_device.h +++ b/level_zero/tools/source/sysman/sysman_device/os_sysman_device.h @@ -11,6 +11,7 @@ #include #include +#include namespace L0 { @@ -23,6 +24,7 @@ class OsSysmanDevice { virtual void getVendorName(int8_t (&vendorName)[ZET_STRING_PROPERTY_SIZE]) = 0; virtual void getDriverVersion(int8_t (&driverVersion)[ZET_STRING_PROPERTY_SIZE]) = 0; virtual ze_result_t reset() = 0; + virtual ze_result_t scanProcessesState(std::vector &pProcessList) = 0; static OsSysmanDevice *create(OsSysman *pOsSysman); virtual ~OsSysmanDevice() {} }; diff --git a/level_zero/tools/source/sysman/sysman_device/sysman_device.h b/level_zero/tools/source/sysman/sysman_device/sysman_device.h index ec155fcb65..c0388f7d4f 100644 --- a/level_zero/tools/source/sysman/sysman_device/sysman_device.h +++ b/level_zero/tools/source/sysman/sysman_device/sysman_device.h @@ -15,6 +15,7 @@ class SysmanDevice { virtual ~SysmanDevice(){}; virtual ze_result_t deviceGetProperties(zet_sysman_properties_t *pProperties) = 0; virtual ze_result_t reset() = 0; + virtual ze_result_t processesGetState(uint32_t *pCount, zet_process_state_t *pProcesses) = 0; virtual void init() = 0; }; diff --git a/level_zero/tools/source/sysman/sysman_device/sysman_device_imp.cpp b/level_zero/tools/source/sysman/sysman_device/sysman_device_imp.cpp index fee7406d4a..3a6bb8b299 100644 --- a/level_zero/tools/source/sysman/sysman_device/sysman_device_imp.cpp +++ b/level_zero/tools/source/sysman/sysman_device/sysman_device_imp.cpp @@ -14,6 +14,29 @@ namespace L0 { +ze_result_t SysmanDeviceImp::processesGetState(uint32_t *pCount, zet_process_state_t *pProcesses) { + std::vector pProcessList; + ze_result_t result = pOsSysmanDevice->scanProcessesState(pProcessList); + if (result != ZE_RESULT_SUCCESS) { + return result; + } + + if ((*pCount > 0) && (*pCount < pProcessList.size())) { + result = ZE_RESULT_ERROR_INVALID_SIZE; + } + if (pProcesses != nullptr) { + uint32_t limit = std::min(*pCount, static_cast(pProcessList.size())); + for (uint32_t i = 0; i < limit; i++) { + pProcesses[i].processId = pProcessList[i].processId; + pProcesses[i].engines = pProcessList[i].engines; + pProcesses[i].memSize = pProcessList[i].memSize; + } + } + *pCount = static_cast(pProcessList.size()); + + return result; +} + ze_result_t SysmanDeviceImp::deviceGetProperties(zet_sysman_properties_t *pProperties) { Device *device = L0::Device::fromHandle(hCoreDevice); ze_device_properties_t deviceProperties; diff --git a/level_zero/tools/source/sysman/sysman_device/sysman_device_imp.h b/level_zero/tools/source/sysman/sysman_device/sysman_device_imp.h index c4db89b4e6..83d02ab8e5 100644 --- a/level_zero/tools/source/sysman/sysman_device/sysman_device_imp.h +++ b/level_zero/tools/source/sysman/sysman_device/sysman_device_imp.h @@ -6,6 +6,8 @@ */ #pragma once +#include "shared/source/helpers/non_copyable_or_moveable.h" + #include #include "os_sysman_device.h" @@ -15,11 +17,12 @@ namespace L0 { -class SysmanDeviceImp : public SysmanDevice { +class SysmanDeviceImp : public SysmanDevice, public NEO::NonCopyableClass { public: void init() override; ze_result_t deviceGetProperties(zet_sysman_properties_t *pProperties) override; ze_result_t reset() override; + ze_result_t processesGetState(uint32_t *pCount, zet_process_state_t *pProcesses) override; OsSysmanDevice *pOsSysmanDevice = nullptr; ze_device_handle_t hCoreDevice = {}; @@ -29,10 +32,6 @@ class SysmanDeviceImp : public SysmanDevice { pOsSysman(pOsSysman){}; ~SysmanDeviceImp() override; - // Don't allow copies of the SysmanDeviceImp object - SysmanDeviceImp(const SysmanDeviceImp &obj) = delete; - SysmanDeviceImp &operator=(const SysmanDeviceImp &obj) = delete; - private: OsSysman *pOsSysman = nullptr; zet_sysman_properties_t sysmanProperties = {}; diff --git a/level_zero/tools/source/sysman/sysman_device/windows/os_sysman_device_imp.cpp b/level_zero/tools/source/sysman/sysman_device/windows/os_sysman_device_imp.cpp index 222992ebcd..f88b01732d 100644 --- a/level_zero/tools/source/sysman/sysman_device/windows/os_sysman_device_imp.cpp +++ b/level_zero/tools/source/sysman/sysman_device/windows/os_sysman_device_imp.cpp @@ -19,6 +19,7 @@ class WddmSysmanDeviceImp : public OsSysmanDevice { void getVendorName(int8_t (&vendorName)[ZET_STRING_PROPERTY_SIZE]) override; void getDriverVersion(int8_t (&driverVersion)[ZET_STRING_PROPERTY_SIZE]) override; ze_result_t reset() override; + ze_result_t scanProcessesState(std::vector &pProcessList) override; WddmSysmanDeviceImp(OsSysman *pOsSysman); ~WddmSysmanDeviceImp() = default; @@ -50,6 +51,10 @@ ze_result_t WddmSysmanDeviceImp::reset() { return ZE_RESULT_ERROR_UNSUPPORTED_FEATURE; } +ze_result_t WddmSysmanDeviceImp::scanProcessesState(std::vector &pProcessList) { + return ZE_RESULT_ERROR_UNSUPPORTED_FEATURE; +} + WddmSysmanDeviceImp::WddmSysmanDeviceImp(OsSysman *pOsSysman) { } diff --git a/level_zero/tools/source/sysman/sysman_imp.cpp b/level_zero/tools/source/sysman/sysman_imp.cpp index 2f78c818f7..0d6f5d518b 100644 --- a/level_zero/tools/source/sysman/sysman_imp.cpp +++ b/level_zero/tools/source/sysman/sysman_imp.cpp @@ -138,7 +138,7 @@ ze_result_t SysmanImp::schedulerSetComputeUnitDebugMode(ze_bool_t *pNeedReboot) } ze_result_t SysmanImp::processesGetState(uint32_t *pCount, zet_process_state_t *pProcesses) { - return ZE_RESULT_ERROR_UNSUPPORTED_FEATURE; + return pSysmanDevice->processesGetState(pCount, pProcesses); } ze_result_t SysmanImp::deviceReset() { diff --git a/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/CMakeLists.txt b/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/CMakeLists.txt index e2a5e9471b..78bf3d4ddd 100644 --- a/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/CMakeLists.txt +++ b/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/CMakeLists.txt @@ -6,6 +6,5 @@ target_sources(${TARGET_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt - ${CMAKE_CURRENT_SOURCE_DIR}/test_sysman_device.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/mock_sysman_device.h ) +add_subdirectories() diff --git a/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/linux/CMakeLists.txt b/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/linux/CMakeLists.txt new file mode 100644 index 0000000000..aa04189d78 --- /dev/null +++ b/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/linux/CMakeLists.txt @@ -0,0 +1,14 @@ +# +# Copyright (C) 2020 Intel Corporation +# +# SPDX-License-Identifier: MIT +# + +if(UNIX) + target_sources(${TARGET_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt + ${CMAKE_CURRENT_SOURCE_DIR}/test_sysman_device.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/mock_sysfs_sysman_device.h + ) +endif() \ No newline at end of file diff --git a/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/linux/mock_sysfs_sysman_device.h b/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/linux/mock_sysfs_sysman_device.h new file mode 100644 index 0000000000..fa4c136aa5 --- /dev/null +++ b/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/linux/mock_sysfs_sysman_device.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2020 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#pragma once +#include "level_zero/core/test/unit_tests/mock.h" +#include "level_zero/tools/source/sysman/sysman_device/linux/os_sysman_device_imp.h" + +#include "sysman/linux/fs_access.h" +#include "sysman/sysman.h" + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Winconsistent-missing-override" +#endif + +using ::testing::_; + +namespace L0 { +namespace ult { + +const std::string vendorIntel("Intel(R) Corporation"); +const std::string unknown("Unknown"); +const std::string intelPciId("0x8086"); +const std::string deviceDir("device"); +const std::string vendorFile("device/vendor"); +const std::string deviceFile("device/device"); +const std::string subsystemVendorFile("device/subsystem_vendor"); +const std::string driverFile("device/driver"); +const std::string functionLevelReset("device/reset"); +const std::string clientsDir("clients"); +constexpr uint64_t pid1 = 1711u; +constexpr uint64_t pid2 = 1722u; +constexpr uint64_t engineTimeSpent = 123456u; +const std::string clientId1("4"); +const std::string clientId2("5"); +const std::string clientId3("6"); +const std::string engine0("0"); +const std::string engine1("1"); +const std::string engine2("2"); +const std::string engine3("3"); + +class SysmanDeviceSysfsAccess : public SysfsAccess {}; + +template <> +struct Mock : public SysfsAccess { + ze_result_t getValString(const std::string file, std::string &val) { + if (file.compare(subsystemVendorFile) == 0) { + val = "0x8086"; + } + if (file.compare(deviceFile) == 0) { + val = "0x3ea5"; + } + if (file.compare(vendorFile) == 0) { + val = "0x8086"; + } + return ZE_RESULT_SUCCESS; + } + + ze_result_t getValUnsignedLong(const std::string file, uint64_t &val) { + if ((file.compare("clients/4/pid") == 0) || (file.compare("clients/5/pid") == 0)) { + val = pid1; + } + if (file.compare("clients/6/pid") == 0) { + val = pid2; + } + if ((file.compare("clients/4/busy/0") == 0) || (file.compare("clients/4/busy/3") == 0) || (file.compare("clients/5/busy/1") == 0) || (file.compare("clients/6/busy/0") == 0)) { + val = engineTimeSpent; + } + return ZE_RESULT_SUCCESS; + } + + ze_result_t getScannedDirEntries(const std::string path, std::vector &list) { + if (path.compare(clientsDir) == 0) { + list.push_back(clientId1); + list.push_back(clientId2); + list.push_back(clientId3); + } + if ((path.compare("clients/4/busy") == 0) || (path.compare("clients/5/busy") == 0) || (path.compare("clients/6/busy") == 0)) { + list.push_back(engine0); + list.push_back(engine1); + list.push_back(engine2); + list.push_back(engine3); + } + return ZE_RESULT_SUCCESS; + } + + Mock() = default; + + MOCK_METHOD2(read, ze_result_t(const std::string file, std::string &val)); + MOCK_METHOD2(read, ze_result_t(const std::string file, uint64_t &val)); + MOCK_METHOD2(scanDirEntries, ze_result_t(const std::string path, std::vector &list)); +}; + +class PublicLinuxSysmanDeviceImp : public L0::LinuxSysmanDeviceImp { + public: + using LinuxSysmanDeviceImp::pLinuxSysmanImp; + using LinuxSysmanDeviceImp::pSysfsAccess; +}; + +} // namespace ult +} // namespace L0 + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif \ No newline at end of file diff --git a/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/linux/test_sysman_device.cpp b/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/linux/test_sysman_device.cpp new file mode 100644 index 0000000000..9221113d6d --- /dev/null +++ b/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/linux/test_sysman_device.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2020 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "level_zero/core/test/unit_tests/fixtures/device_fixture.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "mock_sysfs_sysman_device.h" + +using ::testing::_; +using ::testing::DoAll; +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::Matcher; +using ::testing::NiceMock; + +namespace L0 { +namespace ult { + +constexpr int64_t memSize1 = 0; +constexpr int64_t memSize2 = 0; +// In mock function getValUnsignedLong, we have set the engines used as 0, 3 and 1. +// Hence, expecting 28 as engine field because 28 in binary would be 00011100 +// This indicates bit number 2, 3 and 4 are set, thus this indicates, this process +// used ZET_ENGINE_TYPE_3D, ZET_ENGINE_TYPE_MEDIA and ZET_ENGINE_TYPE_DMA +// Their corresponding mapping with i915 engine numbers are 0, 3 and 1 respectively. +constexpr int64_t engines1 = 28u; +// 4 in binary 0100, as 2nd bit is set, hence it indicates, process used ZET_ENGINE_TYPE_3D +// Corresponding i915 mapped value in mocked getValUnsignedLong() is 0. +constexpr int64_t engines2 = 4u; +constexpr uint32_t totalProcessStates = 2u; // Two process States for two pids +const std::string expectedModelName("0x3ea5"); +class SysmanSysmanDeviceFixture : public DeviceFixture, public ::testing::Test { + protected: + std::unique_ptr sysmanImp; + zet_sysman_handle_t hSysman; + + OsSysmanDevice *pOsSysmanDevice = nullptr; + Mock *pSysfsAccess = nullptr; + L0::SysmanDevice *pSysmanDevicePrev = nullptr; + L0::SysmanDeviceImp sysmanDeviceImp; + PublicLinuxSysmanDeviceImp linuxSysmanDeviceImp; + + void SetUp() override { + DeviceFixture::SetUp(); + sysmanImp = std::make_unique(device->toHandle()); + pSysfsAccess = new NiceMock>; + linuxSysmanDeviceImp.pSysfsAccess = pSysfsAccess; + pOsSysmanDevice = static_cast(&linuxSysmanDeviceImp); + + ON_CALL(*pSysfsAccess, read(_, Matcher(_))) + .WillByDefault(::testing::Invoke(pSysfsAccess, &Mock::getValString)); + ON_CALL(*pSysfsAccess, read(_, Matcher(_))) + .WillByDefault(::testing::Invoke(pSysfsAccess, &Mock::getValUnsignedLong)); + ON_CALL(*pSysfsAccess, scanDirEntries(_, _)) + .WillByDefault(::testing::Invoke(pSysfsAccess, &Mock::getScannedDirEntries)); + + pSysmanDevicePrev = sysmanImp->pSysmanDevice; + sysmanImp->pSysmanDevice = static_cast(&sysmanDeviceImp); + sysmanDeviceImp.pOsSysmanDevice = pOsSysmanDevice; + sysmanDeviceImp.hCoreDevice = sysmanImp->hCoreDevice; + sysmanDeviceImp.init(); + hSysman = sysmanImp->toHandle(); + } + + void TearDown() override { + // restore state + sysmanImp->pSysmanDevice = pSysmanDevicePrev; + sysmanDeviceImp.pOsSysmanDevice = nullptr; + + // cleanup + if (pSysfsAccess != nullptr) { + delete pSysfsAccess; + pSysfsAccess = nullptr; + } + DeviceFixture::TearDown(); + } + + uint32_t get_prop_length(int8_t prop[ZET_STRING_PROPERTY_SIZE]) { + uint32_t length = 0; + for (int i = 0; i < ZET_STRING_PROPERTY_SIZE; i++) { + if (prop[i] == '\0') { + break; + } else { + length += 1; + } + } + + return length; + } +}; + +TEST_F(SysmanSysmanDeviceFixture, GivenValidSysmanHandleWhenCallingzetSysmanDeviceGetPropertiesThenVerifyzetSysmanDeviceGetPropertiesCallSucceeds) { + zet_sysman_properties_t properties; + ze_result_t result = zetSysmanDeviceGetProperties(hSysman, &properties); + + EXPECT_EQ(ZE_RESULT_SUCCESS, result); + EXPECT_EQ(properties.numSubdevices, 0u); + EXPECT_TRUE(0 == std::memcmp(properties.boardNumber, unknown.c_str(), get_prop_length(properties.boardNumber))); + EXPECT_TRUE(0 == std::memcmp(properties.brandName, vendorIntel.c_str(), get_prop_length(properties.brandName))); + EXPECT_TRUE(0 == std::memcmp(properties.driverVersion, unknown.c_str(), get_prop_length(properties.driverVersion))); + EXPECT_TRUE(0 == std::memcmp(properties.modelName, expectedModelName.c_str(), get_prop_length(properties.modelName))); + EXPECT_TRUE(0 == std::memcmp(properties.serialNumber, unknown.c_str(), get_prop_length(properties.serialNumber))); + EXPECT_TRUE(0 == std::memcmp(properties.vendorName, vendorIntel.c_str(), get_prop_length(properties.vendorName))); +} + +TEST_F(SysmanSysmanDeviceFixture, GivenValidSysmanHandleWhileRetrievingInformationAboutHostProcessesUsingDeviceThenSuccessIsReturned) { + uint32_t count = 0; + ASSERT_EQ(ZE_RESULT_SUCCESS, zetSysmanProcessesGetState(hSysman, &count, nullptr)); + EXPECT_EQ(count, totalProcessStates); + std::vector processes(count); + ASSERT_EQ(ZE_RESULT_SUCCESS, zetSysmanProcessesGetState(hSysman, &count, processes.data())); + EXPECT_EQ(processes[0].processId, pid1); + EXPECT_EQ(processes[0].engines, engines1); + EXPECT_EQ(processes[0].memSize, memSize1); + EXPECT_EQ(processes[1].processId, pid2); + EXPECT_EQ(processes[1].engines, engines2); + EXPECT_EQ(processes[1].memSize, memSize2); +} + +} // namespace ult +} // namespace L0 \ No newline at end of file diff --git a/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/windows/CMakeLists.txt b/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/windows/CMakeLists.txt new file mode 100644 index 0000000000..b254b7cce5 --- /dev/null +++ b/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/windows/CMakeLists.txt @@ -0,0 +1,14 @@ +# +# Copyright (C) 2020 Intel Corporation +# +# SPDX-License-Identifier: MIT +# + +if(WIN32) + target_sources(${TARGET_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt + ${CMAKE_CURRENT_SOURCE_DIR}/test_sysman_device.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/mock_sysman_device.h + ) +endif() \ No newline at end of file diff --git a/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/mock_sysman_device.h b/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/windows/mock_sysman_device.h similarity index 92% rename from level_zero/tools/test/unit_tests/sources/sysman/sysman_device/mock_sysman_device.h rename to level_zero/tools/test/unit_tests/sources/sysman/sysman_device/windows/mock_sysman_device.h index 9b9614e79a..a42619b1d0 100644 --- a/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/mock_sysman_device.h +++ b/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/windows/mock_sysman_device.h @@ -33,6 +33,7 @@ struct Mock : public OsSysmanDevice { MOCK_METHOD1(getVendorName, void(int8_t (&vendorName)[ZET_STRING_PROPERTY_SIZE])); MOCK_METHOD1(getDriverVersion, void(int8_t (&driverVersion)[ZET_STRING_PROPERTY_SIZE])); MOCK_METHOD0(reset, ze_result_t()); + MOCK_METHOD1(scanProcessesState, ze_result_t(std::vector &pProcessList)); }; } // namespace ult diff --git a/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/test_sysman_device.cpp b/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/windows/test_sysman_device.cpp similarity index 64% rename from level_zero/tools/test/unit_tests/sources/sysman/sysman_device/test_sysman_device.cpp rename to level_zero/tools/test/unit_tests/sources/sysman/sysman_device/windows/test_sysman_device.cpp index 4f4334701f..b0af01fd68 100644 --- a/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/test_sysman_device.cpp +++ b/level_zero/tools/test/unit_tests/sources/sysman/sysman_device/windows/test_sysman_device.cpp @@ -27,6 +27,20 @@ ACTION_P(SetString, value) { } } +ACTION_P(SetProcessList, value) { + arg0 = value; +} + +constexpr uint32_t pid1 = 1711u; +constexpr uint32_t pid2 = 1722u; +constexpr uint32_t pid3 = 1733u; +constexpr int64_t memSize1 = 0; +constexpr int64_t memSize2 = 0; +constexpr int64_t memSize3 = 0; +constexpr int64_t engines1 = 12u; +constexpr int64_t engines2 = 10u; +constexpr int64_t engines3 = 4u; + class SysmanSysmanDeviceFixture : public DeviceFixture, public ::testing::Test { protected: @@ -38,6 +52,11 @@ class SysmanSysmanDeviceFixture : public DeviceFixture, public ::testing::Test { L0::SysmanDevice *pSysmanDevice_prev; ze_device_handle_t hCoreDevice; + zet_process_state_t mockProcessState1 = {pid1, memSize1, engines1}; + zet_process_state_t mockProcessState2 = {pid2, memSize2, engines2}; + zet_process_state_t mockProcessState3 = {pid3, memSize3, engines3}; + std::vector mockProcessStates{mockProcessState1, mockProcessState2, mockProcessState3}; + void SetUp() override { DeviceFixture::SetUp(); sysmanImp = new SysmanImp(device->toHandle()); @@ -74,6 +93,10 @@ class SysmanSysmanDeviceFixture : public DeviceFixture, public ::testing::Test { Return())); ON_CALL(*pOsSysmanDevice, reset()) .WillByDefault(Return(ZE_RESULT_SUCCESS)); + ON_CALL(*pOsSysmanDevice, scanProcessesState(_)) + .WillByDefault(DoAll( + SetProcessList(mockProcessStates), + Return(ZE_RESULT_SUCCESS))); pSysmanDevice_prev = sysmanImp->pSysmanDevice; sysmanImp->pSysmanDevice = static_cast(pSysmanDevice); @@ -104,5 +127,23 @@ TEST_F(SysmanSysmanDeviceFixture, GivenValidSysmanHandleWhenCallingzetSysmanDevi EXPECT_EQ(ZE_RESULT_SUCCESS, result); } +TEST_F(SysmanSysmanDeviceFixture, GivenValidSysmanHandleWhileRetrievingInformationAboutHostProcessesUsingDeviceThenSuccessIsReturned) { + uint32_t count = 0; + ASSERT_EQ(ZE_RESULT_SUCCESS, zetSysmanProcessesGetState(hSysman, &count, nullptr)); + EXPECT_EQ(count, 3u); + std::vector processes(count); + ASSERT_EQ(ZE_RESULT_SUCCESS, zetSysmanProcessesGetState(hSysman, &count, processes.data())); + EXPECT_EQ(count, 3u); + EXPECT_EQ(processes[0].processId, pid1); + EXPECT_EQ(processes[1].processId, pid2); + EXPECT_EQ(processes[2].processId, pid3); + EXPECT_EQ(processes[0].engines, engines1); + EXPECT_EQ(processes[1].engines, engines2); + EXPECT_EQ(processes[2].engines, engines3); + EXPECT_EQ(processes[0].memSize, memSize1); + EXPECT_EQ(processes[1].memSize, memSize2); + EXPECT_EQ(processes[2].memSize, memSize3); +} + } // namespace ult } // namespace L0