Add support for ARM counters

Adding support for ARM counters via a third-party lib.
The main target platform is Android.
This commit is contained in:
tabi.katalin
2020-03-02 19:17:20 +01:00
committed by Baldur Karlsson
parent 6a2415f334
commit f1bd727dff
57 changed files with 10086 additions and 1 deletions
@@ -51,6 +51,7 @@ enum class CounterFamily
Intel,
NVIDIA,
VulkanExtended,
ARM,
};
CounterFamily GetCounterFamily(GPUCounter counter)
@@ -71,6 +72,10 @@ CounterFamily GetCounterFamily(GPUCounter counter)
{
return CounterFamily::VulkanExtended;
}
else if(IsARMCounter(counter))
{
return CounterFamily::ARM;
}
return CounterFamily::Generic;
}
@@ -84,6 +89,7 @@ QString ToString(CounterFamily family)
case CounterFamily::Intel: return lit("Intel");
case CounterFamily::NVIDIA: return lit("NVIDIA");
case CounterFamily::VulkanExtended: return lit("Vulkan Extended");
case CounterFamily::ARM: return lit("ARM");
case CounterFamily::Unknown: return lit("Unknown");
}
@@ -177,6 +177,10 @@ QTableWidgetItem *PerformanceCounterViewer::MakeCounterResultItem(const CounterR
case CounterUnit::Absolute:
case CounterUnit::Ratio: break;
case CounterUnit::Hertz: returnValue += lit(" Hz"); break;
case CounterUnit::Volt: returnValue += lit(" V"); break;
case CounterUnit::Celsius: returnValue += lit(" °C"); break;
}
return new CustomSortedTableItem(returnValue, SortValue(result, description));
+11
View File
@@ -83,6 +83,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NV", "renderdoc\driver\ihv\
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Intel", "renderdoc\driver\ihv\intel\Intel.vcxproj", "{7FCB5FC5-1DBD-4DA6-83A0-6BA4E945BDA5}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ARM", "renderdoc\driver\ihv\arm\ARM.vcxproj", "{F9CCE6CA-0CA3-4A22-9C7B-881369955E62}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Development|x64 = Development|x64
@@ -283,6 +285,14 @@ Global
{7FCB5FC5-1DBD-4DA6-83A0-6BA4E945BDA5}.Release|x64.Build.0 = Release|x64
{7FCB5FC5-1DBD-4DA6-83A0-6BA4E945BDA5}.Release|x86.ActiveCfg = Release|Win32
{7FCB5FC5-1DBD-4DA6-83A0-6BA4E945BDA5}.Release|x86.Build.0 = Release|Win32
{F9CCE6CA-0CA3-4A22-9C7B-881369955E62}.Development|x64.ActiveCfg = Development|x64
{F9CCE6CA-0CA3-4A22-9C7B-881369955E62}.Development|x64.Build.0 = Development|x64
{F9CCE6CA-0CA3-4A22-9C7B-881369955E62}.Development|x86.ActiveCfg = Development|Win32
{F9CCE6CA-0CA3-4A22-9C7B-881369955E62}.Development|x86.Build.0 = Development|Win32
{F9CCE6CA-0CA3-4A22-9C7B-881369955E62}.Release|x64.ActiveCfg = Release|x64
{F9CCE6CA-0CA3-4A22-9C7B-881369955E62}.Release|x64.Build.0 = Release|x64
{F9CCE6CA-0CA3-4A22-9C7B-881369955E62}.Release|x86.ActiveCfg = Release|Win32
{F9CCE6CA-0CA3-4A22-9C7B-881369955E62}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -318,5 +328,6 @@ Global
{37955C79-D91D-423F-8C6C-8F5BCF4F28D4} = {B5A783D9-AEB9-420D-8E77-D4D930F8D88C}
{40349AD9-5558-4DF4-84E2-11934DE90A11} = {4DA2F3E3-9A65-45DD-A69B-82C7757D4904}
{7FCB5FC5-1DBD-4DA6-83A0-6BA4E945BDA5} = {4DA2F3E3-9A65-45DD-A69B-82C7757D4904}
{F9CCE6CA-0CA3-4A22-9C7B-881369955E62} = {4DA2F3E3-9A65-45DD-A69B-82C7757D4904}
EndGlobalSection
EndGlobal
+6
View File
@@ -502,6 +502,12 @@ if(ENABLE_GL OR ENABLE_GLES)
list(APPEND renderdoc_objects $<TARGET_OBJECTS:rdoc_intel>)
endif()
# pull in the ARM folder for perf query
if(ENABLE_GL OR ENABLE_GLES)
add_subdirectory(driver/ihv/arm)
list(APPEND renderdoc_objects $<TARGET_OBJECTS:rdoc_arm>)
endif()
add_library(rdoc OBJECT ${sources})
target_compile_definitions(rdoc ${RDOC_DEFINITIONS})
target_include_directories(rdoc ${RDOC_INCLUDES})
+11
View File
@@ -558,11 +558,22 @@ struct AndroidRemoteServer : public RemoteServer
{
ResetAndroidSettings();
// enable profiling to measure hardware counters
Android::adbExecCommand(m_deviceID, "shell setprop security.perf_harden 0");
LazilyStartLogcatThread();
return RemoteServer::OpenCapture(proxyid, filename, opts, progress);
}
virtual void CloseCapture(IReplayController *rend) override
{
// disable profiling
Android::adbExecCommand(m_deviceID, "shell setprop security.perf_harden 1");
RemoteServer::CloseCapture(rend);
}
virtual rdcstr GetHomeFolder() override { return ""; }
virtual rdcarray<PathEntry> ListFolder(const char *path) override
{
+18 -1
View File
@@ -3229,7 +3229,10 @@ enum class GPUCounter : uint32_t
FirstVulkanExtended = 4000000,
LastNvidia = FirstVulkanExtended - 1,
LastVulkanExtended = 5000000,
FirstARM = 5000000,
LastVulkanExtended = FirstARM - 1,
LastARM = 6000000,
};
ITERABLE_OPERATORS(GPUCounter);
@@ -3290,6 +3293,17 @@ inline constexpr bool IsVulkanExtendedCounter(GPUCounter c)
return c >= GPUCounter::FirstVulkanExtended && c <= GPUCounter::LastVulkanExtended;
}
DOCUMENT(R"(Check whether or not this is an ARM private counter.
:param GPUCounter c: The counter.
:return: ``True`` if it is an ARM private counter, ``False`` if it's not.
:rtype: ``bool``
)");
inline constexpr bool IsARMCounter(GPUCounter c)
{
return c >= GPUCounter::FirstARM && c <= GPUCounter::LastARM;
}
DOCUMENT(R"(The unit that GPU counter data is returned in.
.. data:: Absolute
@@ -3324,6 +3338,9 @@ enum class CounterUnit : uint32_t
Ratio,
Bytes,
Cycles,
Hertz,
Volt,
Celsius
};
DECLARE_REFLECTION_ENUM(CounterUnit);
+99
View File
@@ -25,6 +25,7 @@
#include <algorithm>
#include <iterator>
#include "driver/ihv/amd/amd_counters.h"
#include "driver/ihv/arm/arm_counters.h"
#include "driver/ihv/intel/intel_gl_counters.h"
#include "gl_driver.h"
#include "gl_replay.h"
@@ -65,6 +66,11 @@ rdcarray<GPUCounter> GLReplay::EnumerateCounters()
ret.append(m_pIntelCounters->GetPublicCounterIds());
}
if(m_pARMCounters)
{
ret.append(m_pARMCounters->GetPublicCounterIds());
}
return ret;
}
@@ -96,6 +102,11 @@ CounterDescription GLReplay::DescribeCounter(GPUCounter counterID)
}
}
if(IsARMCounter(counterID) && m_pARMCounters)
{
return m_pARMCounters->GetCounterDescription(counterID);
}
// FFBA5548-FBF8-405D-BA18-F0329DA370A0
desc.uuid.words[0] = 0xFFBA5548;
desc.uuid.words[1] = 0xFBF8405D;
@@ -461,6 +472,84 @@ rdcarray<CounterResult> GLReplay::FetchCountersIntel(const rdcarray<GPUCounter>
return ret;
}
void GLReplay::FillTimersARM(uint32_t *eventStartID, uint32_t *sampleIndex,
rdcarray<uint32_t> *eventIDs, const DrawcallDescription &drawnode)
{
if(drawnode.children.empty())
return;
for(size_t i = 0; i < drawnode.children.size(); i++)
{
const DrawcallDescription &d = drawnode.children[i];
FillTimersARM(eventStartID, sampleIndex, eventIDs, drawnode.children[i]);
if(d.events.empty())
continue;
eventIDs->push_back(d.eventId);
m_pDriver->ReplayLog(*eventStartID, d.eventId, eReplay_WithoutDraw);
m_pARMCounters->BeginSample(d.eventId);
m_pDriver->ReplayLog(*eventStartID, d.eventId, eReplay_OnlyDraw);
// wait for the GPU to process all commands
GLsync sync = GL.glFenceSync(eGL_SYNC_GPU_COMMANDS_COMPLETE, 0);
GL.glClientWaitSync(sync, eGL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
m_pARMCounters->EndSample();
GL.glDeleteSync(sync);
*eventStartID = d.eventId + 1;
++*sampleIndex;
}
}
rdcarray<CounterResult> GLReplay::FetchCountersARM(const rdcarray<GPUCounter> &counters)
{
m_pARMCounters->DisableAllCounters();
// enable counters it needs
for(size_t i = 0; i < counters.size(); i++)
{
// This function is only called internally, and violating this assertion means our
// caller has invoked this method incorrectly
RDCASSERT(IsARMCounter(counters[i]));
m_pARMCounters->EnableCounter(counters[i]);
}
uint32_t passCount = m_pARMCounters->GetPassCount();
uint32_t sampleIndex = 0;
rdcarray<uint32_t> eventIDs;
m_pDriver->ReplayMarkers(false);
for(uint32_t p = 0; p < passCount; p++)
{
m_pARMCounters->BeginPass(p);
uint32_t eventStartID = 0;
sampleIndex = 0;
eventIDs.clear();
FillTimersARM(&eventStartID, &sampleIndex, &eventIDs, m_pDriver->GetRootDraw());
m_pARMCounters->EndPass();
}
m_pDriver->ReplayMarkers(true);
rdcarray<CounterResult> ret = m_pARMCounters->GetCounterData(eventIDs, counters);
return ret;
}
rdcarray<CounterResult> GLReplay::FetchCounters(const rdcarray<GPUCounter> &allCounters)
{
rdcarray<CounterResult> ret;
@@ -503,6 +592,16 @@ rdcarray<CounterResult> GLReplay::FetchCounters(const rdcarray<GPUCounter> &allC
}
}
if(m_pARMCounters)
{
rdcarray<GPUCounter> armCounters;
std::copy_if(allCounters.begin(), allCounters.end(), std::back_inserter(armCounters),
[](const GPUCounter &c) { return IsARMCounter(c); });
if(!armCounters.empty())
ret = FetchCountersARM(armCounters);
}
if(counters.empty())
{
return ret;
+18
View File
@@ -26,6 +26,7 @@
#include "gl_replay.h"
#include "core/settings.h"
#include "driver/ihv/amd/amd_counters.h"
#include "driver/ihv/arm/arm_counters.h"
#include "driver/ihv/intel/intel_gl_counters.h"
#include "maths/matrix.h"
#include "serialise/rdcfile.h"
@@ -67,6 +68,7 @@ void GLReplay::Shutdown()
{
SAFE_DELETE(m_pAMDCounters);
SAFE_DELETE(m_pIntelCounters);
SAFE_DELETE(m_pARMCounters);
DeleteDebugData();
@@ -235,6 +237,7 @@ void GLReplay::SetReplayData(GLWindowingData data)
{
AMDCounters *countersAMD = NULL;
IntelGlCounters *countersIntel = NULL;
ARMCounters *countersARM = NULL;
bool isMesa = false;
@@ -283,6 +286,11 @@ void GLReplay::SetReplayData(GLWindowingData data)
RDCLOG("AMD GPU detected - trying to initialise AMD counters");
countersAMD = new AMDCounters();
}
else if(m_DriverInfo.vendor == GPUVendor::ARM)
{
RDCLOG("ARM Mali GPU detected - trying to initialise ARM counters");
countersARM = new ARMCounters();
}
else
{
RDCLOG("%s GPU detected - no counters available", ToStr(m_DriverInfo.vendor).c_str());
@@ -308,6 +316,16 @@ void GLReplay::SetReplayData(GLWindowingData data)
delete countersIntel;
m_pIntelCounters = NULL;
}
if(countersARM && countersARM->Init())
{
m_pARMCounters = countersARM;
}
else
{
delete countersARM;
m_pARMCounters = NULL;
}
}
}
+9
View File
@@ -31,6 +31,7 @@
#include "gl_common.h"
class AMDCounters;
class ARMCounters;
class IntelGlCounters;
class WrappedOpenGL;
struct GLCounterContext;
@@ -465,4 +466,12 @@ private:
const DrawcallDescription &drawnode);
rdcarray<CounterResult> FetchCountersIntel(const rdcarray<GPUCounter> &counters);
// ARM counter instance
ARMCounters *m_pARMCounters = NULL;
void FillTimersARM(uint32_t *eventStartID, uint32_t *sampleIndex, rdcarray<uint32_t> *eventIDs,
const DrawcallDescription &drawnode);
rdcarray<CounterResult> FetchCountersARM(const rdcarray<GPUCounter> &counters);
};
+220
View File
@@ -0,0 +1,220 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Development|Win32">
<Configuration>Development</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Development|x64">
<Configuration>Development</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{F9CCE6CA-0CA3-4A22-9C7B-881369955E62}</ProjectGuid>
<RootNamespace>AMD</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
<ExecutablePath>$(ExecutablePath)</ExecutablePath>
<IncludePath>$(SolutionDir)\breakpad;$(IncludePath)</IncludePath>
<LibraryPath>$(LibraryPath)</LibraryPath>
<ExcludePath>$(ExcludePath)</ExcludePath>
<TargetName>driver_$(ProjectName)</TargetName>
</PropertyGroup>
<PropertyGroup>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Platform)'=='x64'">
<ClCompile>
<PreprocessorDefinitions>WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<PreprocessorDefinitions>RELEASE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(SolutionDir)renderdoc\;$(SolutionDir)renderdoc\3rdparty\</AdditionalIncludeDirectories>
<PreprocessorDefinitions>RENDERDOC_EXPORTS;RENDERDOC_PLATFORM_WIN32;WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<WarningLevel>Level4</WarningLevel>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<TreatWarningAsError>true</TreatWarningAsError>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<MinimalRebuild>false</MinimalRebuild>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<DisableSpecificWarnings>4100</DisableSpecificWarnings>
<AdditionalOptions>/w44062 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Development'">
<ClCompile>
<Optimization>Disabled</Optimization>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<BufferSecurityCheck>false</BufferSecurityCheck>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="arm_counters.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="arm_counters_stub.cpp" />
<ClCompile Include="official\lizard\gatord_xml_reader.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="official\lizard\gator_api.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="official\lizard\gator_message.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="official\lizard\hwcpipe_api.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="official\lizard\hwcpipe_communication.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="official\lizard\lizard.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="official\lizard\lizard_api.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="official\lizard\lizard_communication.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="official\lizard\lizard_counter.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="official\lizard\message_util.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="official\lizard\socket.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="official\lizard\thirdparty\hwcpipe\hwcpipe.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="official\lizard\thirdparty\hwcpipe\vendor\arm\mali\mali_profiler.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="official\lizard\thirdparty\hwcpipe\vendor\arm\pmu\pmu_counter.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="official\lizard\thirdparty\hwcpipe\vendor\arm\pmu\pmu_profiler.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="arm_counters.h" />
<ClInclude Include="official\lizard\gatord_xml_reader.hpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="official\lizard\gator_api.hpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="official\lizard\gator_constants.hpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="official\lizard\gator_message.hpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="official\lizard\hwcpipe_api.hpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="official\lizard\hwcpipe_communication.hpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="official\lizard\include\lizard\lizard.hpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="official\lizard\include\lizard\lizard_api.h">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="official\lizard\include\lizard\lizard_counter.hpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="official\lizard\lizard_communication.hpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="official\lizard\message_util.hpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="official\lizard\socket.hpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="official\lizard\thirdparty\hwcpipe\cpu_profiler.h">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="official\lizard\thirdparty\hwcpipe\gpu_profiler.h">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="official\lizard\thirdparty\hwcpipe\hwcpipe.h">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="official\lizard\thirdparty\hwcpipe\hwcpipe_log.h">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="official\lizard\thirdparty\hwcpipe\value.h">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="official\lizard\thirdparty\hwcpipe\vendor\arm\mali\hwc.hpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="official\lizard\thirdparty\hwcpipe\vendor\arm\mali\hwc_names.hpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="official\lizard\thirdparty\hwcpipe\vendor\arm\mali\mali_profiler.h">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="official\lizard\thirdparty\hwcpipe\vendor\arm\pmu\pmu_counter.h">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="official\lizard\thirdparty\hwcpipe\vendor\arm\pmu\pmu_profiler.h">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -0,0 +1,150 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Counters">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
</Filter>
<Filter Include="official">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
</Filter>
<Filter Include="official\lizard">
<UniqueIdentifier>{7e5aff88-5de6-4985-ad92-c69fd47fb6ce}</UniqueIdentifier>
</Filter>
<Filter Include="official\lizard\thirdparty">
<UniqueIdentifier>{b1fd2e34-8140-4eed-a65c-db2c592e00e9}</UniqueIdentifier>
</Filter>
<Filter Include="official\lizard\thirdparty\hwcpipe">
<UniqueIdentifier>{45866afa-f87c-4bfd-8f1b-720bebf4e82e}</UniqueIdentifier>
</Filter>
<Filter Include="official\lizard\thirdparty\hwcpipe\mali">
<UniqueIdentifier>{d25e3ccb-84a3-4a4a-8bf6-24795ba1bf47}</UniqueIdentifier>
</Filter>
<Filter Include="official\lizard\thirdparty\hwcpipe\pmu">
<UniqueIdentifier>{7f040482-c5df-453f-869b-a1d6cf2c537e}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="arm_counters.cpp">
<Filter>Counters</Filter>
</ClCompile>
<ClCompile Include="arm_counters_stub.cpp">
<Filter>Counters</Filter>
</ClCompile>
<ClCompile Include="official\lizard\gator_api.cpp">
<Filter>official\lizard</Filter>
</ClCompile>
<ClCompile Include="official\lizard\gator_message.cpp">
<Filter>official\lizard</Filter>
</ClCompile>
<ClCompile Include="official\lizard\gatord_xml_reader.cpp">
<Filter>official\lizard</Filter>
</ClCompile>
<ClCompile Include="official\lizard\hwcpipe_api.cpp">
<Filter>official\lizard</Filter>
</ClCompile>
<ClCompile Include="official\lizard\hwcpipe_communication.cpp">
<Filter>official\lizard</Filter>
</ClCompile>
<ClCompile Include="official\lizard\lizard.cpp">
<Filter>official\lizard</Filter>
</ClCompile>
<ClCompile Include="official\lizard\lizard_api.cpp">
<Filter>official\lizard</Filter>
</ClCompile>
<ClCompile Include="official\lizard\lizard_communication.cpp">
<Filter>official\lizard</Filter>
</ClCompile>
<ClCompile Include="official\lizard\lizard_counter.cpp">
<Filter>official\lizard</Filter>
</ClCompile>
<ClCompile Include="official\lizard\message_util.cpp">
<Filter>official\lizard</Filter>
</ClCompile>
<ClCompile Include="official\lizard\socket.cpp">
<Filter>official\lizard</Filter>
</ClCompile>
<ClCompile Include="official\lizard\thirdparty\hwcpipe\hwcpipe.cpp">
<Filter>official\lizard\thirdparty\hwcpipe</Filter>
</ClCompile>
<ClCompile Include="official\lizard\thirdparty\hwcpipe\vendor\arm\mali\mali_profiler.cpp">
<Filter>official\lizard\thirdparty\hwcpipe\mali</Filter>
</ClCompile>
<ClCompile Include="official\lizard\thirdparty\hwcpipe\vendor\arm\pmu\pmu_counter.cpp">
<Filter>official\lizard\thirdparty\hwcpipe\pmu</Filter>
</ClCompile>
<ClCompile Include="official\lizard\thirdparty\hwcpipe\vendor\arm\pmu\pmu_profiler.cpp">
<Filter>official\lizard\thirdparty\hwcpipe\pmu</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="arm_counters.h">
<Filter>Counters</Filter>
</ClInclude>
<ClInclude Include="official\lizard\gator_api.hpp">
<Filter>official\lizard</Filter>
</ClInclude>
<ClInclude Include="official\lizard\gator_constants.hpp">
<Filter>official\lizard</Filter>
</ClInclude>
<ClInclude Include="official\lizard\gator_message.hpp">
<Filter>official\lizard</Filter>
</ClInclude>
<ClInclude Include="official\lizard\gatord_xml_reader.hpp">
<Filter>official\lizard</Filter>
</ClInclude>
<ClInclude Include="official\lizard\hwcpipe_api.hpp">
<Filter>official\lizard</Filter>
</ClInclude>
<ClInclude Include="official\lizard\hwcpipe_communication.hpp">
<Filter>official\lizard</Filter>
</ClInclude>
<ClInclude Include="official\lizard\lizard_communication.hpp">
<Filter>official\lizard</Filter>
</ClInclude>
<ClInclude Include="official\lizard\message_util.hpp">
<Filter>official\lizard</Filter>
</ClInclude>
<ClInclude Include="official\lizard\socket.hpp">
<Filter>official\lizard</Filter>
</ClInclude>
<ClInclude Include="official\lizard\include\lizard\lizard.hpp">
<Filter>official\lizard</Filter>
</ClInclude>
<ClInclude Include="official\lizard\include\lizard\lizard_api.h">
<Filter>official\lizard</Filter>
</ClInclude>
<ClInclude Include="official\lizard\include\lizard\lizard_counter.hpp">
<Filter>official\lizard</Filter>
</ClInclude>
<ClInclude Include="official\lizard\thirdparty\hwcpipe\cpu_profiler.h">
<Filter>official\lizard\thirdparty\hwcpipe</Filter>
</ClInclude>
<ClInclude Include="official\lizard\thirdparty\hwcpipe\gpu_profiler.h">
<Filter>official\lizard\thirdparty\hwcpipe</Filter>
</ClInclude>
<ClInclude Include="official\lizard\thirdparty\hwcpipe\hwcpipe.h">
<Filter>official\lizard\thirdparty\hwcpipe</Filter>
</ClInclude>
<ClInclude Include="official\lizard\thirdparty\hwcpipe\hwcpipe_log.h">
<Filter>official\lizard\thirdparty\hwcpipe</Filter>
</ClInclude>
<ClInclude Include="official\lizard\thirdparty\hwcpipe\value.h">
<Filter>official\lizard\thirdparty\hwcpipe</Filter>
</ClInclude>
<ClInclude Include="official\lizard\thirdparty\hwcpipe\vendor\arm\mali\hwc.hpp">
<Filter>official\lizard\thirdparty\hwcpipe\mali</Filter>
</ClInclude>
<ClInclude Include="official\lizard\thirdparty\hwcpipe\vendor\arm\mali\hwc_names.hpp">
<Filter>official\lizard\thirdparty\hwcpipe\mali</Filter>
</ClInclude>
<ClInclude Include="official\lizard\thirdparty\hwcpipe\vendor\arm\mali\mali_profiler.h">
<Filter>official\lizard\thirdparty\hwcpipe\mali</Filter>
</ClInclude>
<ClInclude Include="official\lizard\thirdparty\hwcpipe\vendor\arm\pmu\pmu_counter.h">
<Filter>official\lizard\thirdparty\hwcpipe\pmu</Filter>
</ClInclude>
<ClInclude Include="official\lizard\thirdparty\hwcpipe\vendor\arm\pmu\pmu_profiler.h">
<Filter>official\lizard\thirdparty\hwcpipe\pmu</Filter>
</ClInclude>
</ItemGroup>
</Project>
+62
View File
@@ -0,0 +1,62 @@
set(sources
arm_counters.h
)
if(ANDROID)
list(APPEND sources
arm_counters.cpp
official/lizard/include/lizard/lizard_api.h
official/lizard/lizard_api.cpp
official/lizard/include/lizard/lizard.hpp
official/lizard/lizard.cpp
official/lizard/lizard_counter.cpp
official/lizard/lizard_communication.hpp
official/lizard/lizard_communication.cpp
official/lizard/hwcpipe_communication.hpp
official/lizard/hwcpipe_communication.cpp
official/lizard/hwcpipe_api.hpp
official/lizard/hwcpipe_api.cpp
official/lizard/gatord_xml_reader.cpp
official/lizard/gator_api.cpp
official/lizard/gator_constants.hpp
official/lizard/gator_message.cpp
official/lizard/gator_message.hpp
official/lizard/message_util.cpp
official/lizard/message_util.hpp
official/lizard/socket.cpp
official/lizard/thirdparty/hwcpipe/hwcpipe.h
official/lizard/thirdparty/hwcpipe/hwcpipe.cpp
official/lizard/thirdparty/hwcpipe/cpu_profiler.h
official/lizard/thirdparty/hwcpipe/gpu_profiler.h
official/lizard/thirdparty/hwcpipe/value.h
official/lizard/thirdparty/hwcpipe/vendor/arm/mali/hwc.hpp
official/lizard/thirdparty/hwcpipe/vendor/arm/mali/hwc_names.hpp
official/lizard/thirdparty/hwcpipe/vendor/arm/mali/mali_profiler.h
official/lizard/thirdparty/hwcpipe/vendor/arm/mali/mali_profiler.cpp
official/lizard/thirdparty/hwcpipe/vendor/arm/pmu/pmu_counter.h
official/lizard/thirdparty/hwcpipe/vendor/arm/pmu/pmu_counter.cpp
official/lizard/thirdparty/hwcpipe/vendor/arm/pmu/pmu_profiler.h
official/lizard/thirdparty/hwcpipe/vendor/arm/pmu/pmu_profiler.cpp
)
else()
list(APPEND sources
arm_counters_stub.cpp
)
endif()
add_library(rdoc_arm OBJECT ${sources})
set(include_dirs
${RDOC_INCLUDES}
official/lizard/
official/lizard/include/lizard
official/lizard/thirdparty/hwcpipe/
)
target_include_directories(rdoc_arm ${include_dirs})
target_compile_definitions(rdoc_arm ${RDOC_DEFINITIONS})
+227
View File
@@ -0,0 +1,227 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "arm_counters.h"
#include "android/android.h"
#include "common/common.h"
#include "common/formatting.h"
#include "core/plugins.h"
#include "os/os_specific.h"
#include <dlfcn.h>
#include "official/lizard/include/lizard/lizard_api.h"
static CounterDescription ARMCreateCounterDescription(GPUCounter index,
LizardCounterDescription &lzdDesc)
{
CounterDescription desc;
desc.name = StringFormat::Fmt("%s %s", lzdDesc.title, lzdDesc.name);
desc.counter = GPUCounter(index);
desc.category = lzdDesc.category;
if(strlen(lzdDesc.short_name) > 0)
desc.description = StringFormat::Fmt("%s (%s)", lzdDesc.description, lzdDesc.short_name);
else
desc.description = lzdDesc.description;
switch(lzdDesc.result_type)
{
case LZD_TYPE_INT: desc.resultType = CompType::UInt; break;
case LZD_TYPE_DOUBLE: desc.resultType = CompType::Double; break;
default: desc.resultType = CompType::UInt; break;
}
switch(lzdDesc.units)
{
case LZD_UNITS_BYTE: desc.unit = CounterUnit::Bytes; break;
case LZD_UNITS_CELSIUS: desc.unit = CounterUnit::Celsius; break;
case LZD_UNITS_HZ: desc.unit = CounterUnit::Hertz; break;
case LZD_UNITS_S: desc.unit = CounterUnit::Seconds; break;
case LZD_UNITS_V: desc.unit = CounterUnit::Volt; break;
default: desc.unit = CounterUnit::Absolute; break;
}
return desc;
}
ARMCounters::ARMCounters() : m_Api(NULL), m_Ctx(0), m_EventId(0), m_passIndex(0)
{
}
ARMCounters::~ARMCounters()
{
if(m_Ctx)
m_Api->Destroy(m_Ctx);
}
bool ARMCounters::Init()
{
if(LoadApi(&m_Api) != LZD_OK)
{
RDCLOG("Failed to load Lizard api.");
return false;
}
if(m_Api->version != LIZARD_VERSION_0_1)
{
RDCLOG("Lizard version is not supported.");
return false;
}
m_Ctx = m_Api->Init("127.0.0.1", 8080);
if(!m_Ctx)
{
RDCLOG("Failed to initialize Lizard.");
return false;
}
uint32_t count = m_Api->GetAvailableCountersCount(m_Ctx);
if(count == 0)
{
RDCLOG("Couldn't find available ARM counters.");
m_Api->Destroy(m_Ctx);
return false;
}
for(LizardCounterId idx = 1; idx <= count; idx++)
{
struct LizardCounterDescription lzdDesc;
LZD_Result result = m_Api->GetCounterDescription(m_Ctx, idx, &lzdDesc);
if(result == LZD_OK)
{
CounterDescription desc =
ARMCreateCounterDescription(GPUCounter((int)GPUCounter::FirstARM + idx), lzdDesc);
m_CounterDescriptions.push_back(desc);
m_CounterIds.push_back(desc.counter);
}
else
{
RDCLOG("Failed to get ARM counter information.");
m_Api->Destroy(m_Ctx);
return false;
}
}
return true;
}
rdcarray<GPUCounter> ARMCounters::GetPublicCounterIds()
{
return m_CounterIds;
}
CounterDescription ARMCounters::GetCounterDescription(GPUCounter index)
{
return m_CounterDescriptions[(int)index - (int)GPUCounter::FirstARM - 1];
}
void ARMCounters::EnableCounter(GPUCounter counter)
{
uint32_t id = (uint32_t)counter - (uint32_t)GPUCounter::FirstARM;
m_EnabledCounters.push_back(id);
}
void ARMCounters::DisableAllCounters()
{
m_EnabledCounters.clear();
}
uint32_t ARMCounters::GetPassCount()
{
return 1;
}
void ARMCounters::BeginPass(uint32_t passID)
{
m_passIndex = passID;
for(size_t i = 0; i < m_EnabledCounters.size(); i++)
{
m_Api->EnableCounter(m_Ctx, m_EnabledCounters[i]);
}
}
void ARMCounters::EndPass()
{
for(size_t i = 0; i < m_EnabledCounters.size(); i++)
{
m_Api->DisableCounter(m_Ctx, m_EnabledCounters[i]);
}
}
void ARMCounters::BeginSample(uint32_t eventId)
{
m_EventId = eventId;
m_Api->StartCapture(m_Ctx);
}
void ARMCounters::EndSample()
{
m_Api->StopCapture(m_Ctx);
for(uint32_t counterId : m_EnabledCounters)
{
const CounterDescription &desc = m_CounterDescriptions[counterId - 1];
CounterValue data;
data.u64 = 0;
if(desc.resultType == CompType::UInt)
{
data.u64 = m_Api->ReadCounterInt(m_Ctx, counterId);
}
else if(desc.resultType == CompType::Double)
{
data.d = m_Api->ReadCounterDouble(m_Ctx, counterId);
}
m_CounterData[m_EventId][counterId] = data;
}
}
rdcarray<CounterResult> ARMCounters::GetCounterData(const rdcarray<uint32_t> &eventIDs,
const rdcarray<GPUCounter> &counters)
{
rdcarray<CounterResult> result;
for(size_t i = 0; i < eventIDs.size(); i++)
{
uint32_t eventId = eventIDs[i];
for(size_t j = 0; j < counters.size(); j++)
{
GPUCounter counter = counters[j];
uint32_t counterId = (uint32_t)counter - (uint32_t)GPUCounter::FirstARM;
const CounterDescription &desc = GetCounterDescription(counter);
const CounterValue &data = m_CounterData[eventId][counterId];
if(desc.resultType == CompType::UInt)
{
result.push_back(CounterResult(eventId, counter, data.u64));
}
else if(desc.resultType == CompType::Double)
{
result.push_back(CounterResult(eventId, counter, data.d));
}
}
}
return result;
}
+73
View File
@@ -0,0 +1,73 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#pragma once
#include <map>
#include "api/replay/data_types.h"
#include "api/replay/renderdoc_replay.h"
#include "api/replay/replay_enums.h"
#include "common/common.h"
struct LizardApi;
typedef void *LizardInstance;
class ARMCounters
{
public:
ARMCounters();
~ARMCounters();
bool Init();
rdcarray<GPUCounter> GetPublicCounterIds();
CounterDescription GetCounterDescription(GPUCounter index);
void EnableCounter(GPUCounter counter);
void DisableAllCounters();
uint32_t GetPassCount();
void BeginPass(uint32_t passID);
void EndPass();
void BeginSample(uint32_t eventId);
void EndSample();
rdcarray<CounterResult> GetCounterData(const rdcarray<uint32_t> &eventIDs,
const rdcarray<GPUCounter> &counters);
private:
#if ENABLED(RDOC_ANDROID)
struct LizardApi *m_Api;
LizardInstance m_Ctx;
uint32_t m_EventId;
uint32_t m_passIndex;
rdcarray<uint32_t> m_EnabledCounters;
rdcarray<CounterDescription> m_CounterDescriptions;
rdcarray<GPUCounter> m_CounterIds;
std::map<uint32_t, std::map<uint32_t, CounterValue>> m_CounterData;
#endif
};
@@ -0,0 +1,83 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "arm_counters.h"
ARMCounters::ARMCounters()
{
}
ARMCounters::~ARMCounters()
{
}
bool ARMCounters::Init()
{
return false;
}
rdcarray<GPUCounter> ARMCounters::GetPublicCounterIds()
{
return {};
}
CounterDescription ARMCounters::GetCounterDescription(GPUCounter index)
{
return {};
}
void ARMCounters::EnableCounter(GPUCounter counter)
{
}
void ARMCounters::DisableAllCounters()
{
}
uint32_t ARMCounters::GetPassCount()
{
return 0;
}
void ARMCounters::BeginPass(uint32_t passID)
{
}
void ARMCounters::EndPass()
{
}
void ARMCounters::BeginSample(uint32_t eventId)
{
}
void ARMCounters::EndSample()
{
}
rdcarray<CounterResult> ARMCounters::GetCounterData(const rdcarray<uint32_t> &eventIDs,
const rdcarray<GPUCounter> &counters)
{
return {};
}
@@ -0,0 +1,19 @@
Copyright (c) 2020 Samsung Electronics (UK) Limited
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,21 @@
## Lizard
Lizard is a library to capture ARM hardware counters on Android devices.
### Using the library with gatord
This step is optional, the library can query a few hardware counters directly.
1. Build `gatord` for Android.
2. Copy `gatord` to a directory which is publicly readable, writeable and executable on the Android device (e.g. /data/local/tmp)
```sh
$ adb push gatord /data/local/tmp/gatord
```
3. Start gatord on the Android device ( -M: Mali device type e.g. G71, G76, etc.)
```sh
$ adb shell ./data/local/tmp/gatord -M G71
```
@@ -0,0 +1,461 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Samsung Electronics (UK) Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "gator_api.hpp"
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <cstring>
#include <vector>
#include "gator_constants.hpp"
#include "gator_message.hpp"
#include "gatord_xml_reader.hpp"
#include "message_util.hpp"
namespace lizard
{
static const std::string ATTR_TYPE = "type";
static const std::string TAG_REQUEST = "request";
static const std::string VALUE_CAPTURED = "captured";
static const std::string VALUE_CONFIGURATION = "configuration";
static const std::string VALUE_COUNTERS = "counters";
static const std::string VALUE_DEFAULTS = "defaults";
static const std::string VALUE_EVENTS = "events";
GatorApi::GatorApi(char *hostname, const uint32_t port,
std::vector<LizardCounter> &availableCounters, LizardCounterDataStore &dataStore)
: m_host(hostname),
m_port(port),
m_connection(NULL),
m_availableCounters(availableCounters),
m_data(dataStore)
{
}
GatorApi::~GatorApi()
{
if(m_host)
{
free(m_host);
}
if(m_connection)
{
delete m_connection;
m_connection = NULL;
}
}
bool GatorApi::createConnection()
{
m_connection = lizard::Socket::createConnection(m_host, m_port);
if(m_connection == NULL)
{
return false;
}
return true;
}
void GatorApi::destroyConnection()
{
Socket::destroyConnection(m_connection);
m_connection = NULL;
}
bool GatorApi::init(uint32_t &counterId)
{
std::string eventsXml = requestEvents();
std::string countersXml = requestCounters();
// Store all events supported by gatord
std::vector<GatordXML::EventCategory> gatordEvents =
GatordXML::parseEvents(eventsXml.c_str(), eventsXml.size());
// Store available counters provided by gatord
std::vector<std::string> gatordAvailableCounters =
GatordXML::parseCounters(countersXml.c_str(), countersXml.size());
std::vector<std::string>::const_iterator countersStart = gatordAvailableCounters.begin();
std::vector<std::string>::const_iterator countersEnd = gatordAvailableCounters.end();
uint32_t counterNum = counterId;
for(GatordXML::EventCategory &category : gatordEvents)
{
for(GatordXML::Event &event : category.events)
{
if(std::find(countersStart, countersEnd, event.counter) != countersEnd)
{
// found the counter
LizardCounter::ClassType classType = event.eventClass == CLASS_ABSOLUTE
? LizardCounter::CLASS_ABSOLUTE
: LizardCounter::CLASS_DELTA;
m_availableCounters.emplace_back(++counterId, event.counter.c_str(), event.name.c_str(),
event.title.c_str(), event.description.c_str(),
category.name.c_str(), event.multiplier, event.units,
classType, LizardCounter::SOURCE_GATORD);
}
}
}
return counterId > counterNum;
}
bool GatorApi::setupCapturedCounters()
{
std::string capturedXml = requestCaptured();
std::vector<GatordXML::CapturedCounter> capturedCounters =
GatordXML::parseCapturedCounters(capturedXml.c_str(), capturedXml.size());
if(capturedCounters.empty())
{
return false;
}
for(GatordXML::CapturedCounter captured : capturedCounters)
{
for(LizardCounter &counter : m_availableCounters)
{
if(captured.type == counter.key())
{
counter.setInternalKey(captured.key);
break;
}
}
}
return true;
}
bool GatorApi::sendConfiguration(const std::vector<LizardCounter> &enabledCounters)
{
std::string xml = std::string("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
xml += "<configurations revision=\"3\">\n";
for(uint32_t idx = 0; idx < enabledCounters.size(); idx++)
{
const lizard::LizardCounter &cnt = enabledCounters[idx];
xml += "<configuration counter=\"" + std::string(cnt.key()) + "\" />\n";
}
xml += "</configurations>";
sendXml(xml);
uint8_t response = getResponse();
if(response != RESPONSE_ACK)
{
return false;
}
return true;
}
bool GatorApi::sendSession()
{
std::string xml = std::string(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<session call_stack_unwinding=\"no\" "
"parse_debug_info=\"no\" version=\"1\" high_resolution=\"no\" buffer_mode=\"streaming\" "
"sample_rate=\"normal\" duration=\"0\" target_address=\"localhost\" live_rate=\"100\" "
"stop_gator=\"no\">\n<energy_capture version=\"1\" type=\"none\">\n<channel id=\"0\" "
"resistance=\"20\" power=\"yes\"/>\n</energy_capture></session>\n");
sendXml(xml);
uint8_t response = getResponse();
if(response != RESPONSE_ACK)
{
return false;
}
return true;
}
void GatorApi::sendDisconnect()
{
sendCommand(COMMAND_DISCONNECT);
getResponse();
}
std::string GatorApi::requestCounters()
{
requestXml(VALUE_COUNTERS);
return getXmlResponse();
}
std::string GatorApi::requestEvents()
{
requestXml(VALUE_EVENTS);
return getXmlResponse();
}
std::string GatorApi::requestConfiguration()
{
requestXml(VALUE_CONFIGURATION);
return getXmlResponse();
}
std::string GatorApi::requestDefaults()
{
requestXml(VALUE_DEFAULTS);
return getXmlResponse();
}
std::string GatorApi::requestCaptured()
{
requestXml(VALUE_CAPTURED);
return getXmlResponse();
}
void GatorApi::requestXml(const std::string &attributeValue)
{
std::string xml = std::string("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
xml += "<" + TAG_REQUEST + " " + ATTR_TYPE + "=" + attributeValue + "/>";
uint8_t header[5];
header[0] = COMMAND_REQUEST_XML;
writeLEInt(header + 1, xml.size());
size_t byteSent;
m_connection->send(header, sizeof(header), &byteSent);
m_connection->send(xml.c_str(), xml.size(), &byteSent);
}
void GatorApi::sendXml(const std::string &xml)
{
uint8_t header[5];
header[0] = COMMAND_DELIVER_XML;
writeLEInt(header + 1, xml.size());
size_t byteSent;
m_connection->send(header, sizeof(header), &byteSent);
m_connection->send(xml.c_str(), xml.size(), &byteSent);
}
uint8_t GatorApi::getResponse()
{
uint8_t responseType;
uint32_t responseSize;
getResponseHeader(&responseType, &responseSize);
return responseType;
}
std::string GatorApi::getXmlResponse()
{
uint8_t responseType;
uint32_t responseSize;
getResponseHeader(&responseType, &responseSize);
if(responseType != RESPONSE_XML)
return "";
std::vector<char> responseXml(responseSize);
size_t bytesRecv;
m_connection->receiveAll(responseXml.data(), responseSize, &bytesRecv);
std::string xml(responseXml.begin(), responseXml.end());
return xml;
}
void GatorApi::sendCommand(uint8_t command)
{
uint8_t message[5];
message[0] = command;
writeLEInt(message + 1, 0);
size_t byteSent;
m_connection->send(message, sizeof(message), &byteSent);
}
Socket::Result GatorApi::getResponseHeader(uint8_t *responseType, uint32_t *responseSize)
{
uint8_t responseHeader[5];
size_t bytesRecv;
Socket::Result result = m_connection->receive(responseHeader, sizeof(responseHeader), &bytesRecv);
if(result == Socket::Result::SUCCESS)
{
*responseType = responseHeader[0];
*responseSize = readLEInt(responseHeader + 1);
}
return result;
}
bool GatorApi::sendVersion()
{
char *msg_version = (char *)GATOR_PROTOCOL_VERSION;
char *msg_streamline = (char *)STREAMLINE;
size_t bytesSent;
m_connection->send(msg_version, strlen(msg_version), &bytesSent);
m_connection->send(msg_streamline, strlen(msg_streamline), &bytesSent);
// "GATOR 670\n"
size_t bytesRecv = 0;
size_t size = 10;
char buffer[size];
m_connection->receiveAll(buffer, sizeof(buffer), &bytesRecv);
std::string gator_pattern = "GATOR ";
if(bytesRecv != size || std::strncmp(buffer, gator_pattern.c_str(), gator_pattern.length()) != 0)
{
return false;
}
sendCommand(COMMAND_PING);
uint8_t response = getResponse();
if(response != RESPONSE_ACK)
{
return false;
}
return true;
}
bool GatorApi::resendConfiguration(const std::vector<LizardCounter> &enabledGatorCounters)
{
if(enabledGatorCounters.empty())
{
return false;
}
if(!createConnection())
{
return false;
}
if(!sendVersion())
{
destroyConnection();
return false;
}
if(!sendConfiguration(enabledGatorCounters))
{
sendDisconnect();
destroyConnection();
return false;
}
sendDisconnect();
destroyConnection();
return true;
}
bool GatorApi::startSession()
{
if(!createConnection())
{
return false;
}
if(!sendVersion())
{
destroyConnection();
return false;
}
if(!sendSession())
{
sendDisconnect();
destroyConnection();
return false;
}
if(!setupCapturedCounters())
{
sendDisconnect();
destroyConnection();
}
return true;
}
void GatorApi::startCapture()
{
sendCommand(COMMAND_APC_START);
}
void GatorApi::stopCapture()
{
sendCommand(COMMAND_APC_STOP);
}
GatorApi::MessageResult GatorApi::readMessage(GatorMessage &message)
{
uint8_t responseType;
uint32_t responseSize;
Socket::Result result = getResponseHeader(&responseType, &responseSize);
if(result == Socket::Result::SUCCESS)
{
message.setType(responseType);
if(responseSize > 0)
{
std::vector<uint8_t> response(responseSize);
size_t bytesRecv;
result = m_connection->receiveAll(response.data(), responseSize, &bytesRecv);
if(result == Socket::Result::SUCCESS)
{
message.setData(response);
return GatorApi::MessageResult::SUCCESS;
}
}
}
return GatorApi::MessageResult::ERROR;
}
void GatorApi::processMessage(GatorMessage &message)
{
if(!message.getData().empty() && message.getType() == RESPONSE_APC_DATA &&
message.getPackedInt() == FRAME_BLOCK_COUNTER)
{
processBlockCounter(message);
}
}
void GatorApi::processBlockCounter(GatorMessage &message)
{
(void) message.getPackedInt(); // skip first item in message
while(message.hasRemaining())
{
int64_t key = message.getPackedInt();
int64_t value = message.getPackedInt();
if(isValidKey(key))
{
Value v;
v.as_int = value;
for(LizardCounter counter : m_availableCounters)
{
if(counter.internalKey() == (uint64_t)key)
{
m_data.addValue(counter.id(), v);
}
}
}
}
}
bool GatorApi::isValidKey(int64_t key)
{
return key > 2;
}
} /* namespace lizard */
@@ -0,0 +1,90 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Samsung Electronics (UK) Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#ifndef GATOR_API_H
#define GATOR_API_H
#include <vector>
#include "../../gator_message.hpp"
#include "lizard_counter.hpp"
#include "socket.hpp"
namespace lizard
{
#define GATOR_PROTOCOL_VERSION "VERSION 671\n";
#define STREAMLINE "STREAMLINE\n";
class GatorApi
{
public:
enum MessageResult
{
SUCCESS,
ERROR,
};
GatorApi(char *host, const uint32_t port, std::vector<LizardCounter> &availableCounters,
LizardCounterDataStore &dataStore);
~GatorApi();
bool createConnection();
void destroyConnection();
bool init(uint32_t &counterId);
bool sendVersion();
void startCapture();
void stopCapture();
void sendDisconnect();
MessageResult readMessage(GatorMessage &message);
void processMessage(GatorMessage &message);
bool resendConfiguration(const std::vector<LizardCounter> &enabledGatorCounters);
bool startSession();
private:
bool setupCapturedCounters();
bool sendConfiguration(const std::vector<LizardCounter> &enabledCounters);
bool sendSession();
void requestXml(const std::string &attributeValue);
void sendXml(const std::string &xml);
uint8_t getResponse();
std::string getXmlResponse();
Socket::Result getResponseHeader(uint8_t *responseType, uint32_t *responseSize);
void sendCommand(uint8_t command);
std::string requestCounters();
std::string requestEvents();
std::string requestConfiguration();
std::string requestDefaults();
std::string requestCaptured();
void processBlockCounter(GatorMessage &message);
bool isValidKey(int64_t key);
char *m_host;
const uint32_t m_port;
Socket *m_connection;
std::vector<LizardCounter> &m_availableCounters;
LizardCounterDataStore &m_data;
};
} /* namespace lizard */
#endif // GATOR_API_H
@@ -0,0 +1,88 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Samsung Electronics (UK) Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#ifndef GATOR_CONSTANTS_H
#define GATOR_CONSTANTS_H
namespace lizard
{
enum
{
FRAME_UNKNOWN = 0,
FRAME_SUMMARY = 1,
FRAME_BACKTRACE = 2,
FRAME_NAME = 3,
FRAME_COUNTER = 4,
FRAME_BLOCK_COUNTER = 5,
FRAME_ANNOTATE = 6,
FRAME_SCHED_TRACE = 7,
FRAME_IDLE = 9,
FRAME_EXTERNAL = 10,
FRAME_PERF_ATTRS = 11,
FRAME_PROC = 11,
FRAME_PERF = 12,
FRAME_ACTIVITY_TRACE = 13,
};
enum
{
RESPONSE_XML = 1,
RESPONSE_APC_DATA = 3,
RESPONSE_ACK = 4,
RESPONSE_NAK = 5,
RESPONSE_ERROR = 0xFF
};
enum
{
COMMAND_REQUEST_XML = 0,
COMMAND_DELIVER_XML = 1,
COMMAND_APC_START = 2,
COMMAND_APC_STOP = 3,
COMMAND_DISCONNECT = 4,
COMMAND_PING = 5
};
enum
{
CLASS_UNKNOWN,
CLASS_ABSOLUTE,
CLASS_ACTIVITY,
CLASS_DELTA,
CLASS_INCIDENT
};
enum
{
DISPLAY_UNKNOWN,
DISPLAY_ACCUMULATE,
DISPLAY_AVERAGE,
DISPLAY_MAXIMUM,
DISPLAY_MINIMUM,
DISPLAY_HERTZ
};
} /* namespace lizard */
#endif // GATOR_CONSTANTS_H
@@ -0,0 +1,91 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Samsung Electronics (UK) Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "gator_message.hpp"
#include "message_util.hpp"
namespace lizard
{
GatorMessage::GatorMessage() : m_type(0), m_pos(0)
{
}
GatorMessage::GatorMessage(const std::vector<uint8_t> &in, uint8_t type)
: m_in(in), m_type(type), m_pos(0)
{
}
GatorMessage::~GatorMessage()
{
}
void GatorMessage::setType(uint8_t type)
{
m_type = type;
}
uint8_t GatorMessage::getType()
{
return m_type;
}
void GatorMessage::setData(const std::vector<uint8_t> &data)
{
m_in = data;
}
const std::vector<uint8_t> &GatorMessage::getData()
{
return m_in;
}
bool GatorMessage::hasData()
{
return !m_in.empty();
}
bool GatorMessage::hasRemaining()
{
return m_pos < (m_in.size() - 1);
}
int64_t GatorMessage::getPackedInt()
{
unsigned int sizeRead;
int64_t value = decodeSLEB128(&m_in[m_pos], &sizeRead);
m_pos += sizeRead;
return value;
}
std::string GatorMessage::getGatorString()
{
unsigned int sizeRead;
int64_t stringLength = decodeSLEB128(&m_in[m_pos], &sizeRead);
m_pos += sizeRead;
std::string result(m_in.begin() + m_pos, m_in.begin() + m_pos + stringLength);
m_pos += stringLength;
return result;
}
} /* namespace lizard */
@@ -0,0 +1,57 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Samsung Electronics (UK) Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#ifndef GATOR_MESSAGE_H
#define GATOR_MESSAGE_H
#include <string>
#include <vector>
#include "stdint.h"
namespace lizard
{
class GatorMessage
{
public:
GatorMessage();
GatorMessage(const std::vector<uint8_t> &in, uint8_t type);
~GatorMessage();
int64_t getPackedInt();
std::string getGatorString();
bool hasRemaining();
bool hasData();
void setType(uint8_t type);
uint8_t getType();
void setData(const std::vector<uint8_t> &data);
const std::vector<uint8_t> &getData();
private:
std::vector<uint8_t> m_in;
uint8_t m_type;
unsigned int m_pos;
};
} /* namespace lizard */
#endif // GATOR_MESSAGE_H
@@ -0,0 +1,266 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Samsung Electronics (UK) Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "gatord_xml_reader.hpp"
#include <cassert>
#include <cstdlib>
#include <iostream>
#include "gator_constants.hpp"
#include <pugixml/pugixml.hpp>
namespace lizard
{
static void reportXmlError(pugi::xml_parse_result result, const void *xmlData, size_t xmlSize)
{
std::cout << "XML [] parsed with errors: ";
std::cout << "Error description: " << result.description() << "\n";
std::cout << "Error offset: " << result.offset << " (error at [..."
<< (static_cast<const char *>(xmlData) + result.offset) << "]\n\n";
}
static uint8_t strToEventClass(const std::string &eventClass)
{
if(eventClass == "absolute")
{
return CLASS_ABSOLUTE;
}
else if(eventClass == "activity")
{
return CLASS_ACTIVITY;
}
else if(eventClass == "delta")
{
return CLASS_DELTA;
}
else if(eventClass == "incident")
{
return CLASS_INCIDENT;
}
else
{
return CLASS_UNKNOWN;
}
}
static uint8_t strToEventDisplay(const std::string &eventDisplay)
{
if(eventDisplay == "accumulate")
{
return DISPLAY_ACCUMULATE;
}
else if(eventDisplay == "average")
{
return DISPLAY_AVERAGE;
}
else if(eventDisplay == "maximum")
{
return DISPLAY_MAXIMUM;
}
else if(eventDisplay == "minimum")
{
return DISPLAY_MINIMUM;
}
else if(eventDisplay == "hertz")
{
return DISPLAY_HERTZ;
}
else
{
return DISPLAY_UNKNOWN;
}
}
static LizardCounter::UnitType strToEventUnits(const std::string &eventUnits)
{
if(eventUnits == "B")
{
return LizardCounter::UNITS_BYTE;
}
else if(eventUnits == "Hz")
{
return LizardCounter::UNITS_HZ;
}
else if(eventUnits == "MHz")
{
return LizardCounter::UNITS_MHZ;
}
else if(eventUnits == "pages")
{
return LizardCounter::UNITS_PAGES;
}
else if(eventUnits == "s")
{
return LizardCounter::UNITS_S;
}
else if(eventUnits == "V")
{
return LizardCounter::UNITS_V;
}
else if(eventUnits == "mV")
{
return LizardCounter::UNITS_MV;
}
else if(eventUnits == "°C")
{
return LizardCounter::UNITS_CELSIUS;
}
else if(eventUnits == "RPM")
{
return LizardCounter::UNITS_RPM;
}
else
{
return LizardCounter::UNITS_UNKNOWN;
}
}
std::vector<GatordXML::Configuration> GatordXML::parseConfiguration(const void *xmlData,
size_t xmlSize)
{
std::vector<GatordXML::Configuration> configurations;
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_buffer(xmlData, xmlSize);
if(result)
{
for(pugi::xml_node &cfg : doc.child("configurations").children("configuration"))
{
std::string name = cfg.attribute("counter").as_string();
uint32_t event = cfg.attribute("event").as_uint();
uint32_t cores = cfg.attribute("cores").as_uint();
assert(name.size() > 0);
configurations.push_back({name, event, cores});
}
}
else
{
reportXmlError(result, xmlData, xmlSize);
}
return configurations;
}
std::vector<std::string> GatordXML::parseCounters(const void *xmlData, size_t xmlSize)
{
std::vector<std::string> counters;
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_buffer(xmlData, xmlSize);
if(result)
{
for(pugi::xml_node &cfg : doc.child("counters").children("counter"))
{
std::string name = cfg.attribute("name").as_string();
assert(name.size() > 0);
counters.push_back(name);
}
}
else
{
reportXmlError(result, xmlData, xmlSize);
}
return counters;
}
std::vector<GatordXML::EventCategory> GatordXML::parseEvents(const void *xmlData, size_t xmlSize)
{
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_buffer(xmlData, xmlSize);
if(!result)
{
reportXmlError(result, xmlData, xmlSize);
return {};
}
std::vector<GatordXML::EventCategory> eventCategories;
for(pugi::xml_node &categoryNode : doc.child("events").children("category"))
{
GatordXML::EventCategory category;
category.name = categoryNode.attribute("name").as_string();
assert(category.name.size() > 0);
for(pugi::xml_node &eventNode : categoryNode.children("event"))
{
GatordXML::Event event;
event.name = eventNode.attribute("name").as_string();
event.title = eventNode.attribute("title").as_string();
event.counter = eventNode.attribute("counter").as_string();
event.description = eventNode.attribute("description").as_string();
event.event = eventNode.attribute("event").as_uint();
event.eventClass = strToEventClass(eventNode.attribute("class").as_string());
event.display = strToEventDisplay(eventNode.attribute("display").as_string());
event.units = strToEventUnits(eventNode.attribute("units").as_string());
event.multiplier = eventNode.attribute("multiplier").as_double();
if(event.multiplier == 0)
{
event.multiplier = 1;
}
assert(event.name.size() > 0);
category.events.push_back(event);
}
eventCategories.push_back(category);
}
return eventCategories;
}
std::vector<GatordXML::CapturedCounter> GatordXML::parseCapturedCounters(const void *xmlData,
size_t xmlSize)
{
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_buffer(xmlData, xmlSize);
if(!result)
{
reportXmlError(result, xmlData, xmlSize);
return {};
}
std::vector<GatordXML::CapturedCounter> capturedCounters;
for(pugi::xml_node &counterNode : doc.child("captured").child("counters"))
{
GatordXML::CapturedCounter counter;
counter.key = counterNode.attribute("key").as_int();
counter.type = counterNode.attribute("type").as_string();
counter.event = counterNode.attribute("event").as_int();
capturedCounters.push_back(counter);
}
return capturedCounters;
}
} /* namespace lizard */
@@ -0,0 +1,79 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Samsung Electronics (UK) Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#ifndef LIB_GATORD_XML_READER_HPP
#define LIB_GATORD_XML_READER_HPP
#include <string>
#include <vector>
#include "lizard_counter.hpp"
namespace lizard
{
namespace GatordXML
{
struct Configuration
{
std::string name;
uint32_t event;
uint32_t cores;
};
std::vector<Configuration> parseConfiguration(const void *xmlData, size_t xmlSize);
std::vector<std::string> parseCounters(const void *xmlData, size_t xmlSize);
struct Event
{
std::string title;
std::string name;
std::string description;
uint32_t event;
std::string counter;
uint8_t eventClass;
uint8_t display;
LizardCounter::UnitType units;
double multiplier;
};
struct EventCategory
{
std::string name;
std::vector<Event> events;
};
std::vector<EventCategory> parseEvents(const void *xmlData, size_t xmlSize);
struct CapturedCounter
{
uint32_t key;
std::string type;
uint32_t event;
};
std::vector<CapturedCounter> parseCapturedCounters(const void *xmlData, size_t xmlSize);
}
} /* namespace lizard */
#endif /* LIB_GATORD_XML_READER_HPP */
@@ -0,0 +1,172 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Samsung Electronics (UK) Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "hwcpipe_api.hpp"
namespace lizard
{
HwcPipeApi::HwcPipeApi(std::vector<LizardCounter> &availableCounters,
LizardCounterDataStore &dataStore)
: m_availableCounters(availableCounters), m_data(dataStore), m_HwcPipe(NULL)
{
}
HwcPipeApi::~HwcPipeApi()
{
if(m_HwcPipe)
{
delete m_HwcPipe;
}
}
template <typename TID, typename TINFO, typename THASH>
LizardCounter createCounter(uint64_t counterId, TID hwcpipeId,
const std::unordered_map<std::string, TID> &names,
const std::unordered_map<TID, TINFO, THASH> &info,
const std::string &category, const LizardCounter::SourceType sourceType)
{
std::string title;
for(auto &i : names)
{
if(i.second == hwcpipeId)
{
title = i.first;
break;
}
}
std::string desc = (info.at(hwcpipeId)).desc;
std::string unit = (info.at(hwcpipeId)).unit;
std::string key = "";
std::string name = "";
LizardCounter cnt = LizardCounter(
++counterId, key.c_str(), name.c_str(), title.c_str(), desc.c_str(), category.c_str(), 1,
unit.compare("B") == 0 ? LizardCounter::UNITS_BYTE : LizardCounter::UNITS_UNKNOWN,
LizardCounter::CLASS_ABSOLUTE, sourceType);
cnt.setInternalKey((uint64_t)hwcpipeId);
return cnt;
}
bool HwcPipeApi::init(uint32_t &counterId)
{
m_HwcPipe = new hwcpipe::HWCPipe();
uint64_t counterNum = counterId;
if(m_HwcPipe->cpu_profiler())
{
for(hwcpipe::CpuCounter hwcpipeId : m_HwcPipe->cpu_profiler()->supported_counters())
{
m_availableCounters.push_back(createCounter(++counterId, hwcpipeId, hwcpipe::cpu_counter_names,
hwcpipe::cpu_counter_info, "HWCPipe CPU Counter",
LizardCounter::SOURCE_HWCPIPE_CPU));
}
}
if(m_HwcPipe->gpu_profiler())
{
for(hwcpipe::GpuCounter hwcpipeId : m_HwcPipe->gpu_profiler()->supported_counters())
{
m_availableCounters.push_back(createCounter(++counterId, hwcpipeId, hwcpipe::gpu_counter_names,
hwcpipe::gpu_counter_info, "HWCPipe GPU Counter",
LizardCounter::SOURCE_HWCPIPE_GPU));
}
}
return counterId > counterNum;
}
void HwcPipeApi::enableCounters(const std::vector<LizardCounter> &counters)
{
hwcpipe::CpuCounterSet cpuCounterSet;
hwcpipe::GpuCounterSet gpuCounterSet;
for(LizardCounter counter : counters)
{
switch(counter.sourceType())
{
case LizardCounter::SourceType::SOURCE_HWCPIPE_CPU:
cpuCounterSet.insert((hwcpipe::CpuCounter)counter.internalKey());
break;
case LizardCounter::SourceType::SOURCE_HWCPIPE_GPU:
gpuCounterSet.insert((hwcpipe::GpuCounter)counter.internalKey());
break;
default: break;
}
}
m_HwcPipe->set_enabled_cpu_counters(cpuCounterSet);
m_HwcPipe->set_enabled_gpu_counters(gpuCounterSet);
}
void HwcPipeApi::startCapture()
{
m_HwcPipe->run();
m_HwcPipe->sample();
}
void HwcPipeApi::stopCapture()
{
m_HwcPipe->stop();
}
void HwcPipeApi::readMessage()
{
hwcpipe::Measurements measurements = m_HwcPipe->sample();
if(measurements.cpu)
{
for(const std::pair<hwcpipe::CpuCounter, hwcpipe::Value> &data : *measurements.cpu)
{
for(LizardCounter &counter : m_availableCounters)
{
if(counter.sourceType() == LizardCounter::SourceType::SOURCE_HWCPIPE_CPU &&
counter.internalKey() == (uint64_t)data.first)
{
Value value;
value.as_double = data.second.get<long long>();
m_data.addValue(counter.id(), value);
break;
}
}
}
}
if(measurements.gpu)
{
for(const std::pair<hwcpipe::GpuCounter, hwcpipe::Value> &data : *measurements.gpu)
{
for(LizardCounter &counter : m_availableCounters)
{
if(counter.sourceType() == LizardCounter::SourceType::SOURCE_HWCPIPE_GPU &&
counter.internalKey() == (uint64_t)data.first)
{
Value value;
value.as_double = data.second.get<double>();
m_data.addValue(counter.id(), value);
break;
}
}
}
}
}
}
@@ -0,0 +1,51 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Samsung Electronics (UK) Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#ifndef LIB_HWCPIPE_API_H
#define LIB_HWCPIPE_API_H
#include "thirdparty/hwcpipe/hwcpipe.h"
#include "lizard_counter.hpp"
namespace lizard
{
class HwcPipeApi
{
public:
HwcPipeApi(std::vector<LizardCounter> &availableCounters, LizardCounterDataStore &dataStore);
~HwcPipeApi();
bool init(uint32_t &counterNum);
void enableCounters(const std::vector<LizardCounter> &counters);
void startCapture();
void stopCapture();
void readMessage();
private:
std::vector<LizardCounter> &m_availableCounters;
LizardCounterDataStore &m_data;
hwcpipe::HWCPipe *m_HwcPipe;
};
}
#endif /* LIB_HWCPIPE_API_H */
@@ -0,0 +1,63 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Samsung Electronics (UK) Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include <hwcpipe_communication.hpp>
#include <chrono>
namespace lizard
{
HwcPipeThread::HwcPipeThread(HwcPipeApi &hwcPipeApi)
: m_HwcPipeApi(hwcPipeApi), m_thread()
{
}
HwcPipeThread::~HwcPipeThread()
{
}
void HwcPipeThread::start(void)
{
m_shouldWork = true;
m_HwcPipeApi.startCapture();
m_thread = std::thread(&HwcPipeThread::worker, this);
}
void HwcPipeThread::stop(void)
{
m_shouldWork = false;
m_thread.join();
m_HwcPipeApi.stopCapture();
}
void HwcPipeThread::worker(void)
{
while(m_shouldWork != false)
{
m_HwcPipeApi.readMessage();
}
m_HwcPipeApi.readMessage();
}
}
@@ -0,0 +1,54 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Samsung Electronics (UK) Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#ifndef LIB_HWCPIPE_COMMUNICATION_HPP
#define LIB_HWCPIPE_COMMUNICATION_HPP
#include <atomic>
#include <thread>
#include "hwcpipe_api.hpp"
namespace lizard
{
class HwcPipeThread
{
public:
HwcPipeThread(HwcPipeApi &hwcPipeApi);
~HwcPipeThread(void);
void start(void);
void stop(void);
private:
void worker(void);
std::atomic<bool> m_shouldWork;
HwcPipeApi& m_HwcPipeApi;
std::thread m_thread;
};
} /* namespace lizard */
#endif /* LIB_HWCPIPE_COMMUNICATION_HPP */
@@ -0,0 +1,176 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Samsung Electronics (UK) Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#ifndef LIB_LIZARD_HPP
#define LIB_LIZARD_HPP
#include <cstdint>
#include <vector>
#include <lizard_counter.hpp>
namespace lizard
{
class GatorApi;
class CommunicationThread;
class HwcPipeApi;
class HwcPipeThread;
class Lizard
{
public:
Lizard(void);
~Lizard(void);
/**
* Set the gatord hostname and port number to connect to.
*
* :param hostname: should be an ipv4 compatible address or hostname.
* :param port: port number.
*/
bool configure(const char *hostname, uint32_t port);
/**
* Query the list of available counters into an array.
*
* Usage:
* {
* Lizard lzd; // lzd should be configured after initialization!
*
* int count = lzd.availableCountersCount();
* std::vector<LizardCounter> counters;
* counters.reserve(count);
*
* int copied = lzd.availableCounters(&counters[0], count));
* }
*
* :param outCounter: output array of available counters.
* :param arraySize: size (element count) of the `outCounter` array.
* :returns: the number of `LizardCounter` elements copied into the `outCounter` array.
*/
uint32_t availableCounters(LizardCounter *outCounters, uint32_t arraySize);
/**
* Query the list of available counters (non-copy).
*
* Usage:
* {
* Lizard lzd; // lzd should be configured after initialization!
*
* int count = lzd.availableCountersCount();
* LizardCounter *counterslzd.availableCounters();
* }
*
* :returns: a pointer to a `LizardCounter` array containing maximum `availableCountersCount()`
* elements.
*/
const LizardCounter *availableCounters(void) const { return &m_availableCounters[0]; }
/**
* Query the number of available counters;
*
* Usage:
* See `availableCounters`.
*
* :returns: number of `LizardCounter` elements available.
*/
uint32_t availableCountersCount(void) const;
/**
* Enable a set of counters based on Id values.
*
* Usage:
* {
* Lizard lzd; // lzd should be configured after initialization!
*
* std::vector<LizardCounter> counters;
* // fill counters with required counters.
* // ...
* // Enable the first three counters.
* std::vector<LizardCounterId> ids;
* ids.reserve(3);
* lizard::countersToIds(&counters[0], &ids[0], 3);
*
* lzd.enableCounters(&ids[0], 3);
* }
*
* :param counterIds: array of `LizardCounterId` values to enable.
* :param arraySize: size (element count) of the `counterIds` array.
*/
void enableCounters(const LizardCounterId *counterIds, uint32_t arraySize);
void disableCounters(const LizardCounterId *counterIds, uint32_t arraySize);
/**
* Start the capture of the enabled counters.
*
* This operation does not block. Underlying communication is done in a different thread.
*
* :returns: true if the capture was started correctly, otherwise false.
*/
bool startCapture(void);
/**
* Stops the capture.
*
* Captured data can be accessed via another method.
*/
void endCapture(void);
LizardCounterData *readCounter(const LizardCounterId counterId) const;
size_t readCounterInt(const LizardCounterId counterId, int64_t *values) const;
size_t readCounterDouble(const LizardCounterId counterId, double *values) const;
const LizardCounter *getCounterInfo(const LizardCounterId counterId) const;
private:
bool configureGatord(const char *hostname, uint32_t port);
bool configureHwcPipe(void);
bool startGatord(void);
void stopGatord(void);
bool startHwcPipe(void);
void stopHwcPipe(void);
uint32_t m_idCounter;
std::vector<LizardCounter> m_availableCounters;
std::vector<bool> m_enabledCounters;
GatorApi *m_gatorApi;
CommunicationThread *m_comm;
HwcPipeApi *m_HwcPipeApi;
HwcPipeThread *m_HwcPipe_comm;
bool m_configuredGatord;
bool m_configuredHwcPipe;
LizardCounterDataStore m_dataStore;
};
} /* namepsace lizard */
#endif /* LIB_LIZARD_HPP */
@@ -0,0 +1,234 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Samsung Electronics (UK) Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#ifndef LIB_LIZARD_API_H
#define LIB_LIZARD_API_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif // End of __cplusplus
struct LizardApi;
typedef void *LizardInstance;
typedef uint32_t LizardCounterId;
enum LizardVersion
{
LIZARD_VERSION_0_1 = 1,
};
typedef enum {
LZD_OK = 0,
LZD_FAILURE,
} LZD_Result;
/**
* LizardInstance* LZD_Init(const char* host, int port);
*
* Initializes a Lizard Instance with the given host:port arguments.
* The Lizard Instance must be destroyed with the LZD_Destroy method.
*
* :param host: IP address of the target gatord.
* :param port: Port number of the target gatord.
*/
typedef LizardInstance (*LZD_Init_PFN)(const char *host, int port);
/**
* void LZD_Destroy(LizardInstance** ctx);
*
* Destroy the Lizard Instance and sets the `ctx` pointer's value to NULL.
*/
typedef void (*LZD_Destroy_PFN)(LizardInstance ctx);
/**
* Get the number of the available counters.
*
* The LizardIds are in the range of [1, MAX_UINT].
*
* :param ctx: LizardInstance created via LZD_Init.
* :returns: Number of available counters.
*/
typedef uint32_t (*LZD_GetAvailableCountersCount_PFN)(LizardInstance ctx);
struct LizardCounterDescription
{
LizardCounterId id;
const char *short_name;
const char *name;
const char *title;
const char *description;
const char *category;
double multiplier;
uint32_t units;
uint32_t class_type;
uint32_t result_type;
};
/**
* Get information about the counter
*
* :param ctx: LizardInstance created via LZD_Init.
* :param id: id of the counter, must be 1 or above.
* :param lzdDesc: LizardCounterDescription to be populated.
* :returns: LZD_OK if succeeded
*/
typedef LZD_Result (*LZD_GetCounterDescription_PFN)(LizardInstance ctx, LizardCounterId id,
LizardCounterDescription *lzdDesc);
/**
* Enable the counter for capture.
*
* :param ctx: Lizard Instance created via LZD_Init.
* :param id: Id of the counter to enable.
*/
typedef void (*LZD_EnableCounter_PFN)(LizardInstance ctx, LizardCounterId id);
/**
* Disable the counter for capture.
*
* :param ctx: Lizard Instance created via LZD_Init
* :param id: Id of the counter to disable.
*/
typedef void (*LZD_DisableCounter_PFN)(LizardInstance ctx, LizardCounterId id);
/**
* Disable all counters for capture.
*
* By default all counters are disabled.
*
* :param ctx: Lizard Instance created via LZD_Init
*/
typedef void (*LZD_DisableAllCounters_PFN)(LizardInstance ctx);
/**
* Start capture.
*
* The actual capture is performed in a different thread.
*
* :param ctx: Lizard Instance created via LZD_Init
* :returns: LZD_OK if succeeded
*/
typedef LZD_Result (*LZD_StartCapture_PFN)(LizardInstance ctx);
/**
* Stop capture.
*
* :param ctx: Lizard Instance created via LZD_Init
* :returns: LZD_OK if succeeded
*/
typedef LZD_Result (*LZD_StopCapture_PFN)(LizardInstance ctx);
typedef enum {
LZD_ABSOLUTE = 1,
LZD_DELTA = 2,
} LZD_CounterClassType;
/**
* Get the measured counter value as an integer.
*
* :param ctx: Lizard Instance created via LZD_Init
* :param id: The id of the counter which value is measured.
* :returns: The measured value.
*/
typedef int64_t (*LZD_ReadCounterInt_PFN)(LizardInstance ctx, LizardCounterId id);
/**
* Get the measured counter value as a double.
*
* :param ctx: Lizard Instance created via LZD_Init
* :param id: The id of the counter which value is measured.
* :returns: The measured value.
*/
typedef double (*LZD_ReadCounterDouble_PFN)(LizardInstance ctx, LizardCounterId id);
typedef enum {
LZD_UNITS_UNKNOWN,
LZD_UNITS_BYTE,
LZD_UNITS_CELSIUS,
LZD_UNITS_HZ,
LZD_UNITS_PAGES,
LZD_UNITS_RPM,
LZD_UNITS_S,
LZD_UNITS_V,
LZD_TYPE_INT,
LZD_TYPE_DOUBLE,
} LZD_CounterAttribute;
struct LizardApi
{
int struct_size;
int version;
LZD_Init_PFN Init;
LZD_Destroy_PFN Destroy;
LZD_GetAvailableCountersCount_PFN GetAvailableCountersCount;
LZD_GetCounterDescription_PFN GetCounterDescription;
LZD_EnableCounter_PFN EnableCounter;
LZD_DisableCounter_PFN DisableCounter;
LZD_DisableAllCounters_PFN DisableAllCounters;
LZD_StartCapture_PFN StartCapture;
LZD_StopCapture_PFN StopCapture;
LZD_ReadCounterInt_PFN ReadCounterInt;
LZD_ReadCounterDouble_PFN ReadCounterDouble;
};
/**
* Entry point of the API.
*
* To load the api search for the "LoadApi" function symbol and
* invoke the method with a `LizardApi` pointer to get access to all
* API functions.
*
* Example usage:
*
* void* lib = dlopen("liblizard.so", RTLD_LAZY);
* LZD_LoadApi_PFN loadApi = (LZD_LoadApi_PFN)dlsym(lib, "LoadApi");
*
* struct LizardApi* api;
*
* if (loadApi(&api) != LZD_OK) {
* // report failure and return
* }
* if (api->version != LIZARD_VERSION_0_1) {
* // report version mismatch and return
* }
*
* LizardInstance* ctx = api->Init("127.0.0.1", 8080);
*
* :param api: LizardApi struct pointer to initialize the API pointers.
* :returns: LZD_OK if the counter initialization was ok.
*/
typedef LZD_Result (*LZD_LoadApi_PFN)(struct LizardApi **api);
LZD_Result LoadApi(struct LizardApi **api);
#ifdef __cplusplus
}
#endif // End of __cplusplus
#endif /* LIB_LIZARD_API_H */
@@ -0,0 +1,136 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Samsung Electronics (UK) Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#ifndef LIB_LIZARD_COUNTER_HPP
#define LIB_LIZARD_COUNTER_HPP
#include <map>
#include <string>
#include <vector>
namespace lizard
{
typedef uint32_t LizardCounterId;
typedef union
{
int64_t as_int;
double as_double;
} Value;
class LizardCounterData
{
public:
LizardCounterData(LizardCounterId id, const int64_t *values, size_t length);
LizardCounterData(LizardCounterId id, const double *values, size_t length);
~LizardCounterData(void);
const int64_t *getIntValues() const { return reinterpret_cast<int64_t *>(m_values); }
const double *getDoubleValues() const { return reinterpret_cast<double *>(m_values); }
const Value *getValues() const { return m_values; }
LizardCounterId getId() const { return m_id; }
size_t getLength() const { return m_length; }
bool isInt() const { return m_is_int; }
private:
LizardCounterId m_id;
Value *m_values;
size_t m_length;
bool m_is_int;
};
class LizardCounter
{
public:
enum ClassType
{
CLASS_ABSOLUTE,
CLASS_DELTA,
};
enum SourceType
{
SOURCE_GATORD,
SOURCE_HWCPIPE_CPU,
SOURCE_HWCPIPE_GPU,
};
enum UnitType
{
UNITS_UNKNOWN,
UNITS_BYTE,
UNITS_CELSIUS,
UNITS_HZ,
UNITS_MHZ,
UNITS_PAGES,
UNITS_RPM,
UNITS_S,
UNITS_V,
UNITS_MV
};
LizardCounter() : m_id(0), m_multiplier(1) {}
LizardCounter(LizardCounterId id, const char *key, const char *name, const char *title,
const char *description, const char *category, const double multiplier,
UnitType units, ClassType classType, SourceType sourceType);
~LizardCounter(void) {}
LizardCounterId id(void) const { return m_id; }
const char *key(void) const { return m_key.c_str(); }
const char *name(void) const { return m_name.c_str(); }
const char *title(void) const { return m_title.c_str(); }
const char *description(void) const { return m_description.c_str(); }
const char *category(void) const { return m_category.c_str(); }
double multiplier(void) const { return m_multiplier; }
UnitType units(void) const { return m_units; }
ClassType classType(void) const { return m_classType; }
SourceType sourceType(void) const { return m_sourceType; }
uint64_t internalKey(void) const { return m_internalKey; }
void setInternalKey(uint64_t key);
private:
const LizardCounterId m_id;
std::string m_key;
std::string m_name;
std::string m_title;
std::string m_description;
std::string m_category;
double m_multiplier;
UnitType m_units;
ClassType m_classType;
SourceType m_sourceType;
uint64_t m_internalKey;
};
class LizardCounterDataStore
{
public:
void addValue(int64_t key, Value value);
const std::vector<Value> &getValues(int64_t key) const;
void clear();
private:
std::map<int64_t, std::vector<Value>> m_values;
};
}; /* namespace lizard */
#endif /* LIB_LIZARD_COUNTER_HPP */
@@ -0,0 +1,341 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Samsung Electronics (UK) Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "lizard.hpp"
#include <unistd.h>
#include <atomic>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <functional>
#include "gator_api.hpp"
#include "hwcpipe_communication.hpp"
#include "lizard_communication.hpp"
#include "lizard_counter.hpp"
namespace lizard
{
enum
{
MAX_HOSTNAME_SIZE = 64,
};
Lizard::Lizard(void)
: m_idCounter(0),
m_availableCounters(),
m_enabledCounters(),
m_gatorApi(NULL),
m_comm(NULL),
m_HwcPipeApi(NULL),
m_HwcPipe_comm(NULL)
{
}
Lizard::~Lizard()
{
if(m_gatorApi)
{
delete m_gatorApi;
}
if(m_HwcPipeApi)
{
delete m_HwcPipeApi;
}
}
bool Lizard::configure(const char *hostname, uint32_t port)
{
m_availableCounters.clear();
m_configuredGatord = configureGatord(hostname, port);
m_configuredHwcPipe = configureHwcPipe();
if(m_configuredGatord || m_configuredHwcPipe)
{
m_availableCounters.shrink_to_fit();
m_enabledCounters.clear();
m_enabledCounters.resize(m_availableCounters.size() + 1); // +1 as 0 ID is invalid
return true;
}
return false;
}
bool Lizard::configureGatord(const char *hostname, uint32_t port)
{
m_gatorApi = new lizard::GatorApi(strndup(hostname, MAX_HOSTNAME_SIZE), port, m_availableCounters,
m_dataStore);
if(!m_gatorApi->createConnection())
{
return false;
}
if(!m_gatorApi->sendVersion())
{
m_gatorApi->destroyConnection();
return false;
}
bool success = m_gatorApi->init(m_idCounter);
m_gatorApi->sendDisconnect();
m_gatorApi->destroyConnection();
return success;
}
uint32_t Lizard::availableCountersCount(void) const
{
return m_availableCounters.size();
}
void Lizard::enableCounters(const LizardCounterId *counterIds, uint32_t arraySize)
{
for(uint32_t idx = 0; idx < arraySize; idx++)
{
m_enabledCounters[counterIds[idx]] = true;
}
}
void Lizard::disableCounters(const LizardCounterId *counterIds, uint32_t arraySize)
{
for(uint32_t idx = 0; idx < arraySize; idx++)
{
m_enabledCounters[counterIds[idx]] = false;
}
}
bool Lizard::startCapture(void)
{
m_dataStore.clear();
bool started = false;
if(m_configuredGatord)
{
started = startGatord() || started;
}
if(m_configuredHwcPipe)
{
started = startHwcPipe() || started;
}
return started;
}
bool Lizard::startGatord()
{
if(m_comm != NULL)
{
return false;
}
std::vector<LizardCounter> enabledGatorCounters;
for(size_t i = 0; i < m_availableCounters.size(); i++)
{
if(m_enabledCounters[m_availableCounters[i].id()] &&
m_availableCounters[i].sourceType() == LizardCounter::SOURCE_GATORD)
{
enabledGatorCounters.push_back(m_availableCounters[i]);
}
}
if(!m_gatorApi->resendConfiguration(enabledGatorCounters))
{
return false;
}
if(!m_gatorApi->startSession())
{
return false;
}
m_comm = new CommunicationThread(*m_gatorApi);
m_comm->start();
// Give gatord a bit of time to start up
usleep(1);
return true;
}
bool Lizard::startHwcPipe()
{
if(m_HwcPipe_comm != NULL)
{
return false;
}
std::vector<LizardCounter> enabledHwcPipeCounters;
for(size_t i = 0; i < m_availableCounters.size(); i++)
{
if(m_enabledCounters[m_availableCounters[i].id()] &&
(m_availableCounters[i].sourceType() == LizardCounter::SOURCE_HWCPIPE_CPU ||
m_availableCounters[i].sourceType() == LizardCounter::SOURCE_HWCPIPE_GPU))
{
enabledHwcPipeCounters.push_back(m_availableCounters[i]);
}
}
if(enabledHwcPipeCounters.empty())
{
return false;
}
m_HwcPipeApi->enableCounters(enabledHwcPipeCounters);
m_HwcPipe_comm = new HwcPipeThread(*m_HwcPipeApi);
m_HwcPipe_comm->start();
return true;
}
void Lizard::endCapture(void)
{
stopGatord();
stopHwcPipe();
}
void Lizard::stopGatord(void)
{
if(m_comm != NULL)
{
usleep(1);
m_comm->stop();
delete m_comm;
m_comm = NULL;
}
}
void Lizard::stopHwcPipe(void)
{
if(m_HwcPipe_comm != NULL)
{
m_HwcPipe_comm->stop();
delete m_HwcPipe_comm;
m_HwcPipe_comm = NULL;
}
}
LizardCounterData *Lizard::readCounter(const LizardCounterId counterId) const
{
if(counterId < 1 || counterId > m_availableCounters.size())
{
return NULL;
}
const std::vector<Value> &values = m_dataStore.getValues(counterId);
switch(getCounterInfo(counterId)->sourceType())
{
case LizardCounter::SOURCE_GATORD:
{
std::vector<int64_t> vec(values.size());
for(size_t i = 0; i < values.size(); i++)
{
vec[i] = values[i].as_int;
}
return new LizardCounterData(counterId, (int64_t *)&vec[0], vec.size());
}
case LizardCounter::SOURCE_HWCPIPE_CPU:
case LizardCounter::SOURCE_HWCPIPE_GPU:
{
std::vector<double> vec(values.size());
for(size_t i = 0; i < values.size(); i++)
{
vec[i] = values[i].as_double;
}
return new LizardCounterData(counterId, vec.data(), vec.size());
}
}
return NULL;
}
size_t Lizard::readCounterInt(const LizardCounterId counterId, int64_t *values) const
{
if(counterId < 1 || counterId > m_availableCounters.size())
{
return 0;
}
const std::vector<Value> &vals = m_dataStore.getValues(counterId);
std::vector<int64_t> vec(vals.size());
for(size_t i = 0; i < vals.size(); i++)
{
vec[i] = vals[i].as_int;
}
if(values)
{
memcpy(values, vec.data(), vec.size() * sizeof(int64_t));
}
return vec.size();
}
size_t Lizard::readCounterDouble(const LizardCounterId counterId, double *values) const
{
if(counterId < 1 || counterId > m_availableCounters.size())
{
return 0;
}
const std::vector<Value> &vals = m_dataStore.getValues(counterId);
std::vector<double> vec(vals.size());
for(size_t i = 0; i < vals.size(); i++)
{
vec[i] = vals[i].as_double;
}
if(values)
{
memcpy(values, vec.data(), vec.size() * sizeof(double));
}
return vec.size();
}
const LizardCounter *Lizard::getCounterInfo(const LizardCounterId counterId) const
{
if(counterId < 1 || counterId > m_availableCounters.size())
{
return NULL;
}
return &m_availableCounters[counterId - 1];
}
bool Lizard::configureHwcPipe()
{
m_HwcPipeApi = new HwcPipeApi(m_availableCounters, m_dataStore);
return m_HwcPipeApi->init(m_idCounter);
}
} /* namespace lizard */
@@ -0,0 +1,280 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Samsung Electronics (UK) Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include <lizard_api.h>
#include <cstring>
#include <map>
#include <gator_constants.hpp>
#include <lizard.hpp>
static LizardInstance LZD_Init(const char *host, int port)
{
lizard::Lizard *lzd = new lizard::Lizard();
bool configured = lzd->configure(host, port);
if(!configured)
{
delete lzd;
return NULL;
}
return static_cast<LizardInstance>(lzd);
}
static void LZD_Destroy(LizardInstance ctx)
{
delete static_cast<lizard::Lizard *>(ctx);
}
static uint32_t LZD_GetAvailableCountersCount(LizardInstance ctx)
{
lizard::Lizard *lzd = static_cast<lizard::Lizard *>(ctx);
if(lzd == NULL)
{
return 0;
}
return lzd->availableCountersCount();
}
static LZD_Result LZD_GetCounterDescription(LizardInstance ctx, LizardCounterId id,
LizardCounterDescription *desc)
{
lizard::Lizard *lzd = static_cast<lizard::Lizard *>(ctx);
if(lzd == NULL || desc == NULL)
{
return LZD_FAILURE;
}
uint32_t count = lzd->availableCountersCount();
if(id == 0 || id > count)
{
return LZD_FAILURE;
}
const lizard::LizardCounter *lzdCounter = lzd->availableCounters() + (id - 1);
desc->id = lzdCounter->id();
desc->short_name = lzdCounter->key();
desc->title = lzdCounter->title();
desc->name = lzdCounter->name();
desc->category = lzdCounter->category();
desc->description = lzdCounter->description();
desc->multiplier = lzdCounter->multiplier();
switch(lzdCounter->classType())
{
case lizard::LizardCounter::CLASS_ABSOLUTE: desc->class_type = LZD_ABSOLUTE; break;
case lizard::LizardCounter::CLASS_DELTA: desc->class_type = LZD_DELTA; break;
default: break;
}
switch(lzdCounter->units())
{
case lizard::LizardCounter::UNITS_BYTE: desc->units = LZD_UNITS_BYTE; break;
case lizard::LizardCounter::UNITS_CELSIUS: desc->units = LZD_UNITS_CELSIUS; break;
case lizard::LizardCounter::UNITS_MHZ:
case lizard::LizardCounter::UNITS_HZ: desc->units = LZD_UNITS_HZ; break;
case lizard::LizardCounter::UNITS_PAGES: desc->units = LZD_UNITS_PAGES; break;
case lizard::LizardCounter::UNITS_RPM: desc->units = LZD_UNITS_RPM; break;
case lizard::LizardCounter::UNITS_S: desc->units = LZD_UNITS_S; break;
case lizard::LizardCounter::UNITS_V: desc->units = LZD_UNITS_V; break;
default: desc->units = LZD_UNITS_UNKNOWN; break;
}
if(lzdCounter->multiplier() == 1 && lzdCounter->sourceType() == lizard::LizardCounter::SOURCE_GATORD)
{
desc->result_type = LZD_TYPE_INT;
}
else
{
desc->result_type = LZD_TYPE_DOUBLE;
}
return LZD_OK;
}
static void LZD_EnableCounter(LizardInstance ctx, LizardCounterId id)
{
lizard::Lizard *lzd = static_cast<lizard::Lizard *>(ctx);
if(lzd != NULL)
{
lzd->enableCounters(&id, 1);
}
}
static void LZD_DisableCounter(LizardInstance ctx, LizardCounterId id)
{
lizard::Lizard *lzd = static_cast<lizard::Lizard *>(ctx);
if(lzd != NULL)
{
lzd->disableCounters(&id, 1);
}
}
static void LZD_DisableAllCounters(LizardInstance ctx)
{
lizard::Lizard *lzd = static_cast<lizard::Lizard *>(ctx);
if(lzd != NULL)
{
uint32_t count = lzd->availableCountersCount();
const lizard::LizardCounter *counters = lzd->availableCounters();
for(uint32_t idx = 0; idx < count; idx++)
{
lizard::LizardCounterId id = counters[idx].id();
lzd->disableCounters(&id, 1);
}
}
}
static LZD_Result LZD_StartCapture(LizardInstance ctx)
{
lizard::Lizard *lzd = static_cast<lizard::Lizard *>(ctx);
if(lzd == NULL)
{
return LZD_FAILURE;
}
bool result = lzd->startCapture();
return result ? LZD_OK : LZD_FAILURE;
}
static LZD_Result LZD_StopCapture(LizardInstance ctx)
{
lizard::Lizard *lzd = static_cast<lizard::Lizard *>(ctx);
if(lzd == NULL)
{
return LZD_FAILURE;
}
lzd->endCapture();
return LZD_OK;
}
static int64_t LZD_ReadCounterInt(LizardInstance ctx, LizardCounterId id)
{
lizard::Lizard *lzd = static_cast<lizard::Lizard *>(ctx);
if(lzd == NULL)
{
return 0;
}
size_t size = lzd->readCounterInt(id, nullptr);
std::vector<int64_t> values(size);
size = lzd->readCounterInt(id, values.data());
int64_t result = 0;
for(size_t idx = 0; idx < size; idx++)
{
result += values[idx];
}
if(lzd->getCounterInfo(id)->classType() == lizard::LizardCounter::CLASS_ABSOLUTE && size != 0)
{
result /= size;
}
if(lzd->getCounterInfo(id)->units() == lizard::LizardCounter::UNITS_MHZ)
{
result *= 1000000;
}
return result * lzd->getCounterInfo(id)->multiplier();
}
static double LZD_ReadCounterDouble(LizardInstance ctx, LizardCounterId id)
{
lizard::Lizard *lzd = static_cast<lizard::Lizard *>(ctx);
if(lzd == NULL)
{
return 0;
}
size_t size = lzd->readCounterDouble(id, nullptr);
std::vector<double> values(size);
size = lzd->readCounterDouble(id, values.data());
double result = 0;
for(size_t idx = 0; idx < size; idx++)
{
result += values[idx];
}
if(lzd->getCounterInfo(id)->classType() == lizard::LizardCounter::CLASS_ABSOLUTE && size != 0)
{
result /= size;
}
return result * lzd->getCounterInfo(id)->multiplier();
}
static struct LizardApi s_ApiInstance;
static void InitApi(void)
{
s_ApiInstance.struct_size = sizeof(struct LizardApi);
s_ApiInstance.version = LIZARD_VERSION_0_1;
s_ApiInstance.Init = &LZD_Init;
s_ApiInstance.Destroy = &LZD_Destroy;
s_ApiInstance.GetAvailableCountersCount = &LZD_GetAvailableCountersCount;
s_ApiInstance.GetCounterDescription = &LZD_GetCounterDescription;
s_ApiInstance.EnableCounter = &LZD_EnableCounter;
s_ApiInstance.DisableCounter = &LZD_DisableCounter;
s_ApiInstance.DisableAllCounters = &LZD_DisableAllCounters;
s_ApiInstance.StartCapture = &LZD_StartCapture;
s_ApiInstance.StopCapture = &LZD_StopCapture;
s_ApiInstance.ReadCounterInt = &LZD_ReadCounterInt;
s_ApiInstance.ReadCounterDouble = &LZD_ReadCounterDouble;
}
extern "C" LZD_Result LoadApi(struct LizardApi **api)
{
static bool initialized = false;
if(!initialized)
{
InitApi();
initialized = true;
}
if(api == NULL)
{
return LZD_FAILURE;
}
*api = &s_ApiInstance;
return LZD_OK;
}
@@ -0,0 +1,69 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Samsung Electronics (UK) Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "lizard_communication.hpp"
#include "gator_api.hpp"
#include "gator_message.hpp"
namespace lizard
{
CommunicationThread::CommunicationThread(GatorApi &gatorApi) : m_gatorApi(gatorApi), m_thread()
{
}
CommunicationThread::~CommunicationThread()
{
}
void CommunicationThread::start(void)
{
m_shouldWork = true;
m_gatorApi.startCapture();
m_thread = std::thread(&CommunicationThread::worker, this);
}
void CommunicationThread::stop(void)
{
m_shouldWork = false;
m_gatorApi.stopCapture();
m_thread.join();
}
void CommunicationThread::worker(void)
{
bool hasData = false;
while(m_shouldWork != false || hasData)
{
GatorMessage message;
GatorApi::MessageResult result = m_gatorApi.readMessage(message);
hasData = result == GatorApi::MessageResult::SUCCESS;
if(hasData)
{
m_gatorApi.processMessage(message);
}
}
}
}
@@ -0,0 +1,57 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Samsung Electronics (UK) Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#ifndef LIB_LIZARD_COMMUNICATION_HPP
#define LIB_LIZARD_COMMUNICATION_HPP
#include <atomic>
#include <functional>
#include <map>
#include <thread>
#include <vector>
#include "gator_api.hpp"
namespace lizard
{
class CommunicationThread
{
public:
CommunicationThread(GatorApi &gatorApi);
~CommunicationThread(void);
void start(void);
void stop(void);
private:
void worker(void);
std::atomic<bool> m_shouldWork;
GatorApi &m_gatorApi;
std::thread m_thread;
};
} /* namespace lizard */
#endif /* LIB_LIZARD_COMMUNICATION_HPP */
@@ -0,0 +1,92 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Samsung Electronics (UK) Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "lizard_counter.hpp"
#include <cstring>
namespace lizard
{
static std::vector<Value> s_empty;
LizardCounterData::LizardCounterData(LizardCounterId id, const int64_t *values, size_t length)
: m_id(id), m_length(length), m_is_int(true)
{
m_values = new Value[length];
memcpy(m_values, (const void *)values, sizeof(int64_t) * length);
}
LizardCounterData::LizardCounterData(LizardCounterId id, const double *values, size_t length)
: m_id(id), m_length(length), m_is_int(false)
{
m_values = new Value[length];
memcpy(m_values, (const void *)values, sizeof(double) * length);
}
LizardCounterData::~LizardCounterData()
{
delete[] m_values;
}
LizardCounter::LizardCounter(LizardCounterId id, const char *key, const char *name, const char *title,
const char *description, const char *category, const double multiplier,
UnitType units, ClassType classType, SourceType sourceType)
: m_id(id),
m_key(key),
m_name(name),
m_title(title),
m_description(description),
m_category(category),
m_multiplier(multiplier),
m_units(units),
m_classType(classType),
m_sourceType(sourceType)
{
}
void LizardCounter::setInternalKey(uint64_t key)
{
m_internalKey = key;
}
void LizardCounterDataStore::addValue(int64_t key, Value value)
{
m_values[key].push_back(value);
}
const std::vector<Value> &LizardCounterDataStore::getValues(int64_t key) const
{
const std::map<int64_t, std::vector<Value> >::const_iterator it = m_values.find(key);
if(it != m_values.end())
return it->second;
else
return s_empty;
}
void LizardCounterDataStore::clear()
{
m_values.clear();
}
} /* namespace lizard */
@@ -0,0 +1,77 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Samsung Electronics (UK) Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include <stdint.h>
namespace lizard
{
void writeLEInt(uint8_t *buf, uint32_t v)
{
buf[0] = (v >> 0) & 0xFF;
buf[1] = (v >> 8) & 0xFF;
buf[2] = (v >> 16) & 0xFF;
buf[3] = (v >> 24) & 0xFF;
}
uint32_t readLEInt(uint8_t *buffer)
{
return (buffer[0] << 0) | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
}
/**
* Utility function to decode a SLEB128 value.
* Source: https://llvm.org/doxygen/LEB128_8h_source.html
*/
int64_t decodeSLEB128(const uint8_t *p, unsigned *n = nullptr, const uint8_t *end = nullptr,
const char **error = nullptr)
{
const uint8_t *orig_p = p;
int64_t Value = 0;
unsigned Shift = 0;
uint8_t Byte;
if(error)
*error = nullptr;
do
{
if(end && p == end)
{
if(error)
*error = "malformed sleb128, extends past end";
if(n)
*n = (unsigned)(p - orig_p);
return 0;
}
Byte = *p++;
Value |= (uint64_t(Byte & 0x7f) << Shift);
Shift += 7;
} while(Byte >= 128);
// Sign extend negative numbers if needed.
if(Shift < 64 && (Byte & 0x40))
Value |= (-1ULL) << Shift;
if(n)
*n = (unsigned)(p - orig_p);
return Value;
}
} /* namespace lizard */
@@ -0,0 +1,40 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Samsung Electronics (UK) Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#ifndef GATOR_MESSAGE_UTIL_H
#define GATOR_MESSAGE_UTIL_H
namespace lizard
{
void writeLEInt(uint8_t *buf, uint32_t v);
uint32_t readLEInt(uint8_t *buffer);
/// Utility function to decode a SLEB128 value.
int64_t decodeSLEB128(const uint8_t *p, unsigned *n = nullptr, const uint8_t *end = nullptr,
const char **error = nullptr);
} /* namespace lizard */
#endif // GATOR_MESSAGE_UTIL_H
@@ -0,0 +1,156 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Samsung Electronics (UK) Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "socket.hpp"
#include <sys/socket.h>
#include <unistd.h>
#include <cerrno>
#include <arpa/inet.h>
#include <sys/socket.h>
namespace lizard
{
using namespace std;
Socket *Socket::createConnection(const char *host, uint32_t port)
{
struct sockaddr_in serv_addr;
int sock;
if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
return NULL;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
if(inet_pton(AF_INET, host, &serv_addr.sin_addr) <= 0)
{
return NULL;
}
int connectResult = connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
if(connectResult < 0)
{
return NULL;
}
return new lizard::Socket(sock);
}
void Socket::destroyConnection(Socket *connection)
{
delete connection;
}
static Socket::Result toSocketResult(ssize_t value, int lastErrno)
{
if(value > 0)
return Socket::SUCCESS;
if(value == 0)
return Socket::CONNECTION_CLOSED;
switch(lastErrno)
{
case EAGAIN: return Socket::WOULD_BLOCK;
case ECONNABORTED:
case ECONNRESET: return Socket::CONNECTION_TERMINATED;
default: return Socket::ERROR;
}
}
Socket::Socket(int fd) : m_fd(fd)
{
}
Socket::~Socket()
{
close();
}
Socket::Result Socket::send(const void *buffer, size_t bufferSize, size_t *bytesSent)
{
ssize_t bytesWritten = ::send(m_fd, (const char *)buffer, bufferSize, 0);
Socket::Result result = toSocketResult(bytesWritten, errno);
if(bytesSent != NULL)
*bytesSent = bytesWritten > 0 ? (size_t)bytesWritten : 0;
return result;
}
Socket::Result Socket::receive(void *buffer, size_t bufferSize, size_t *bytesRecv)
{
ssize_t bytesRead = ::recv(m_fd, (char *)buffer, bufferSize, 0);
Result result = toSocketResult(bytesRead, errno);
if(bytesRecv != NULL)
*bytesRecv = bytesRead > 0 ? (size_t)bytesRead : 0;
return result;
}
Socket::Result Socket::receiveAll(void *buffer, size_t bufferSize, size_t *bytesRecv)
{
size_t bytesRead = 0;
Result result;
while(bytesRead < bufferSize)
{
bytesRead += ::recv(m_fd, (char *)buffer + bytesRead, bufferSize - bytesRead, 0);
result = toSocketResult(bytesRead, errno);
if(result != SUCCESS)
{
return result;
}
}
if(bytesRecv != NULL)
*bytesRecv = bytesRead > 0 ? (size_t)bytesRead : 0;
return result;
}
Socket::Result Socket::shutdown()
{
int shutdownResult = ::shutdown(m_fd, SHUT_RDWR);
return shutdownResult == 0 ? SUCCESS : ERROR;
}
Socket::Result Socket::close()
{
int closeResult = ::close(m_fd);
return closeResult == 0 ? SUCCESS : ERROR;
}
string Socket::resultstr(Socket::Result result)
{
switch(result)
{
case SUCCESS: return "SUCCESS";
case WOULD_BLOCK: return "WOULD_BLOCK";
case CONNECTION_TERMINATED: return "CONNECTION_TERMINATED";
case CONNECTION_CLOSED: return "CONNECTION_CLOSED";
case ERROR: return "ERROR";
default: return "UNSPECIFIED RESULT";
}
}
} /* namespace lizard */
@@ -0,0 +1,64 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Samsung Electronics (UK) Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#ifndef SOCKET_HPP
#define SOCKET_HPP
#include <cstddef>
#include <string>
namespace lizard
{
class Socket
{
int m_fd;
public:
enum Result
{
SUCCESS,
WOULD_BLOCK,
CONNECTION_TERMINATED,
CONNECTION_CLOSED,
ERROR,
}; // enum Result
Socket(int fd);
~Socket();
Result send(const void *buffer, size_t bufferSize, size_t *bytesSent);
Result receive(void *buffer, size_t bufferSize, size_t *bytesRead);
Result receiveAll(void *buffer, size_t bufferSize, size_t *bytesRead);
Result close();
Result shutdown();
static Socket *createConnection(const char *host, uint32_t port);
static void destroyConnection(Socket *connection);
static std::string resultstr(Result result);
}; // class Socket
} /* namespace lizard */
#endif // SOCKET_HPP
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Arm Software
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,156 @@
<!--
- Copyright (c) 2019, Arm Limited and Contributors
-
- SPDX-License-Identifier: MIT
-
- Permission is hereby granted, free of charge,
- to any person obtaining a copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation the rights to
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
- and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-->
# HWCPipe
## Introduction
HWCPipe is a simple and extensible interface for reading CPU and GPU hardware counters.
## License
The software is provided under an MIT license.
This project has a third-party dependency, which may have independent licensing:
- [nlohmann/json](https://github.com/nlohmann/json): A JSON library for modern C++
## Contributions
All contributions are accepted under the same [LICENSE](LICENSE).
## Building
To use HWCPipe, build it as a shared library in your project.
If your project uses CMake, you can add the following to your `CMakeLists.txt`:
```
add_subdirectory(hwcpipe)
```
## Usage
### Using HWCPipe
Basic usage for HWCPipe is simple:
```
// HWCPipe performs automated platform detection for CPU/GPU counters
hwcpipe::HWCPipe h;
// Start HWCPipe once at the beginning of the profiling session
h.run();
while (main_loop) {
// Call sample() to sample counters with the frequency you need
auto measurements = h.sample();
[...]
}
// At the end of the profiling session, stop HWCPipe
h.stop();
```
The `sample` function returns a `Measurements` struct, which can be accessed like this:
```
// Check if CPU measurements are available
if (measurements.cpu)
{
// Look for a counter in the map
const auto &counter = measurements.cpu->find(CpuCounter::Cycles);
if (counter != measurements.cpu->end())
{
// Get the data stored in the counter, casted to the type you need
auto value = counter->second.get<float>();
}
}
```
### Enabling counters
The available counters are specified in the `CpuCounter` and `GpuCounter` enums (`cpu_profiler.h` and `gpu_profiler.h` respectively).
Platforms will support a subset of these counters, which can be queried via:
```
auto cpu_counters = h.cpu_profiler()->supported_counters();
auto gpu_counters = h.gpu_profiler()->supported_counters()
```
You can specify the counters to be enabled in the following ways:
```
// Enable a default set of counters
auto h = hwcpipe::HWCPipe();
// Pass sets of CPU and GPU counters to be enabled
auto h = hwcpipe::HWCPipe({CpuCounter::Cycles, CpuCounter::Instructions}, {GpuCounter::GpuCycles});
// Pass a JSON string
auto h = hwcpipe::HWCPipe(json);
```
The JSON string should be formatted like this:
```
{
"cpu": ["Cycles", "Instructions"],
"gpu": ["GpuCycles"]
}
```
Available counter names can be found in `cpu_counter_names` (`cpu_profiler.h`) and `gpu_counter_names` (`gpu_profiler.h`).
For more information regarding Mali counters, see [Mali Performance Counters](https://community.arm.com/graphics/b/blog/posts/mali-bifrost-family-performance-counters).
### Enabling profiling on Android
In order for performance data to be displayed, profiling needs to be enabled on the device.
Some devices may disable it by default.
Profiling can be enabled via `adb`:
```
adb shell setprop security.perf_harden 0
```
## Adding support for a new platform
If the counters provided in `CpuCounter` and `GpuCounter` are enough for the new platform,
the process is simple:
* Add an implementation of either `CpuProfiler` of `GpuProfiler` (you can use `PmuProfiler` and `MaliProfiler` as references).
* Add your platform to the automated platform detection in `hwcpipe.cpp`. For consistency in platform detection, the constructor for your platform should throw if the platform is not available.
* Add your platform to the build system.
### Adding new counters
If you need to add new counters to the existing ones, you should update the following variables:
* Add the counter to the `CpuCounter`/`GpuCounter` enum.
* Add the counter name to the `cpu_counter_names`/`gpu_counter_names` map (necessary for JSON initialization).
* Add a description and the unit for your counter to the `cpu_counter_info`/`gpu_counter_info` map.
The `CpuCounter` and `GpuCounter` enums are meant to be expanded. Platforms must not break if new counters are added.
@@ -0,0 +1,115 @@
/*
* Copyright (c) 2019 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "value.h"
#include <string>
#include <unordered_map>
#include <unordered_set>
namespace hwcpipe
{
// The available CPU counters. Profiler implementations will support a subset of them.
enum class CpuCounter
{
Cycles,
Instructions,
CacheReferences,
CacheMisses,
BranchInstructions,
BranchMisses,
MaxValue
};
// Mapping from CPU counter names to enum values. Used for JSON initialization.
const std::unordered_map<std::string, CpuCounter> cpu_counter_names{
{"Cycles", CpuCounter::Cycles},
{"Instructions", CpuCounter::Instructions},
{"CacheReferences", CpuCounter::CacheReferences},
{"CacheMisses", CpuCounter::CacheMisses},
{"BranchInstructions", CpuCounter::BranchInstructions},
{"BranchMisses", CpuCounter::BranchMisses},
};
// A hash function for CpuCounter values
struct CpuCounterHash
{
template <typename T>
std::size_t operator()(T t) const
{
return static_cast<std::size_t>(t);
}
};
struct CpuCounterInfo
{
std::string desc;
std::string unit;
};
// Mapping from each counter to its corresponding information (description and unit)
const std::unordered_map<CpuCounter, CpuCounterInfo, CpuCounterHash> cpu_counter_info{
{CpuCounter::Cycles, {"Number of CPU cycles", "cycles"}},
{CpuCounter::Instructions, {"Number of CPU instructions", "instructions"}},
{CpuCounter::CacheReferences, {"Number of cache references", "references"}},
{CpuCounter::CacheMisses, {"Number of cache misses", "misses"}},
{CpuCounter::BranchInstructions, {"Number of branch instructions", "instructions"}},
{CpuCounter::BranchMisses, {"Number of branch misses", "misses"}},
};
typedef std::unordered_set<CpuCounter, CpuCounterHash> CpuCounterSet;
typedef std::unordered_map<CpuCounter, Value, CpuCounterHash>
CpuMeasurements;
/** An interface for classes that collect CPU performance data. */
class CpuProfiler
{
public:
virtual ~CpuProfiler() = default;
// Returns the enabled counters
virtual const CpuCounterSet &enabled_counters() const = 0;
// Returns the counters that the platform supports
virtual const CpuCounterSet &supported_counters() const = 0;
// Sets the enabled counters after initialization
virtual void set_enabled_counters(CpuCounterSet counters) = 0;
// Starts a profiling session
virtual void run() = 0;
// Sample the counters. Returns a map of measurements for the counters
// that are both available and enabled.
// A profiling session must be running when sampling the counters.
virtual const CpuMeasurements &sample() = 0;
// Stops the active profiling session
virtual void stop() = 0;
};
} // namespace hwcpipe
@@ -0,0 +1,192 @@
/*
* Copyright (c) 2019 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "value.h"
#include <string>
#include <unordered_map>
#include <unordered_set>
namespace hwcpipe
{
// The available GPU counters. Profiler implementations will support a subset of them.
enum class GpuCounter
{
GpuCycles,
VertexComputeCycles,
FragmentCycles,
TilerCycles,
VertexComputeJobs,
FragmentJobs,
Pixels,
Tiles,
TransactionEliminations,
EarlyZTests,
EarlyZKilled,
LateZTests,
LateZKilled,
Instructions,
DivergedInstructions,
ShaderCycles,
ShaderArithmeticCycles,
ShaderLoadStoreCycles,
ShaderTextureCycles,
CacheReadLookups,
CacheWriteLookups,
ExternalMemoryReadAccesses,
ExternalMemoryWriteAccesses,
ExternalMemoryReadStalls,
ExternalMemoryWriteStalls,
ExternalMemoryReadBytes,
ExternalMemoryWriteBytes,
MaxValue
};
// Mapping from GPU counter names to enum values. Used for JSON initialization.
const std::unordered_map<std::string, GpuCounter> gpu_counter_names{
{"GpuCycles", GpuCounter::GpuCycles},
{"VertexComputeCycles", GpuCounter::VertexComputeCycles},
{"FragmentCycles", GpuCounter::FragmentCycles},
{"TilerCycles", GpuCounter::TilerCycles},
{"VertexComputeJobs", GpuCounter::VertexComputeJobs},
{"Tiles", GpuCounter::Tiles},
{"TransactionEliminations", GpuCounter::TransactionEliminations},
{"FragmentJobs", GpuCounter::FragmentJobs},
{"Pixels", GpuCounter::Pixels},
{"EarlyZTests", GpuCounter::EarlyZTests},
{"EarlyZKilled", GpuCounter::EarlyZKilled},
{"LateZTests", GpuCounter::LateZTests},
{"LateZKilled", GpuCounter::LateZKilled},
{"Instructions", GpuCounter::Instructions},
{"DivergedInstructions", GpuCounter::DivergedInstructions},
{"ShaderCycles", GpuCounter::ShaderCycles},
{"ShaderArithmeticCycles", GpuCounter::ShaderArithmeticCycles},
{"ShaderLoadStoreCycles", GpuCounter::ShaderLoadStoreCycles},
{"ShaderTextureCycles", GpuCounter::ShaderTextureCycles},
{"CacheReadLookups", GpuCounter::CacheReadLookups},
{"CacheWriteLookups", GpuCounter::CacheWriteLookups},
{"ExternalMemoryReadAccesses", GpuCounter::ExternalMemoryReadAccesses},
{"ExternalMemoryWriteAccesses", GpuCounter::ExternalMemoryWriteAccesses},
{"ExternalMemoryReadStalls", GpuCounter::ExternalMemoryReadStalls},
{"ExternalMemoryWriteStalls", GpuCounter::ExternalMemoryWriteStalls},
{"ExternalMemoryReadBytes", GpuCounter::ExternalMemoryReadBytes},
{"ExternalMemoryWriteBytes", GpuCounter::ExternalMemoryWriteBytes},
};
// A hash function for GpuCounter values
struct GpuCounterHash
{
template <typename T>
std::size_t operator()(T t) const
{
return static_cast<std::size_t>(t);
}
};
struct GpuCounterInfo
{
std::string desc;
std::string unit;
};
// Mapping from each counter to its corresponding information (description and unit)
const std::unordered_map<GpuCounter, GpuCounterInfo, GpuCounterHash> gpu_counter_info{
{GpuCounter::GpuCycles, {"Number of GPU cycles", "cycles"}},
{GpuCounter::VertexComputeCycles, {"Number of vertex/compute cycles", "cycles"}},
{GpuCounter::FragmentCycles, {"Number of fragment cycles", "cycles"}},
{GpuCounter::TilerCycles, {"Number of tiler cycles", "cycles"}},
{GpuCounter::VertexComputeJobs, {"Number of vertex/compute jobs", "jobs"}},
{GpuCounter::Tiles, {"Number of physical tiles written", "tiles"}},
{GpuCounter::TransactionEliminations, {"Number of transaction eliminations", "tiles"}},
{GpuCounter::FragmentJobs, {"Number of fragment jobs", "jobs"}},
{GpuCounter::Pixels, {"Number of pixels shaded", "cycles"}},
{GpuCounter::EarlyZTests, {"Early-Z tests performed", "tests"}},
{GpuCounter::EarlyZKilled, {"Early-Z tests resulting in a kill", "tests"}},
{GpuCounter::LateZTests, {"Late-Z tests performed", "tests"}},
{GpuCounter::LateZKilled, {"Late-Z tests resulting in a kill", "tests"}},
{GpuCounter::Instructions, {"Number of shader instructions", "instructions"}},
{GpuCounter::DivergedInstructions, {"Number of diverged shader instructions", "instructions"}},
{GpuCounter::ShaderCycles, {"Shader total cycles", "cycles"}},
{GpuCounter::ShaderArithmeticCycles, {"Shader arithmetic cycles", "cycles"}},
{GpuCounter::ShaderLoadStoreCycles, {"Shader load/store cycles", "cycles"}},
{GpuCounter::ShaderTextureCycles, {"Shader texture cycles", "cycles"}},
{GpuCounter::CacheReadLookups, {"Cache read lookups", "lookups"}},
{GpuCounter::CacheWriteLookups, {"Cache write lookups", "lookups"}},
{GpuCounter::ExternalMemoryReadAccesses, {"Reads from external memory", "accesses"}},
{GpuCounter::ExternalMemoryWriteAccesses, {"Writes to external memory", "accesses"}},
{GpuCounter::ExternalMemoryReadStalls, {"Stalls when reading from external memory", "stalls"}},
{GpuCounter::ExternalMemoryWriteStalls, {"Stalls when writing to external memory", "stalls"}},
{GpuCounter::ExternalMemoryReadBytes, {"Bytes read to external memory", "B"}},
{GpuCounter::ExternalMemoryWriteBytes, {"Bytes written to external memory", "B"}},
};
typedef std::unordered_set<GpuCounter, GpuCounterHash> GpuCounterSet;
typedef std::unordered_map<GpuCounter, Value, GpuCounterHash> GpuMeasurements;
/** An interface for classes that collect GPU performance data. */
class GpuProfiler
{
public:
virtual ~GpuProfiler() = default;
// Returns the enabled counters
virtual const GpuCounterSet &enabled_counters() const = 0;
// Returns the counters that the platform supports
virtual const GpuCounterSet &supported_counters() const = 0;
// Sets the enabled counters after initialization
virtual void set_enabled_counters(GpuCounterSet counters) = 0;
// Starts a profiling session
virtual void run() = 0;
// Sample the counters. Returns a map of measurements for the counters
// that are both available and enabled.
// A profiling session must be running when sampling the counters.
virtual const GpuMeasurements &sample() = 0;
// Stops the active profiling session
virtual void stop() = 0;
};
} // namespace hwcpipe
@@ -0,0 +1,197 @@
/*
* Copyright (c) 2019 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "hwcpipe.h"
#include "hwcpipe_log.h"
#ifdef __linux__
# include "vendor/arm/pmu/pmu_profiler.h"
# include "vendor/arm/mali/mali_profiler.h"
#endif
#ifndef HWCPIPE_NO_JSON
#include <json.hpp>
using json = nlohmann::json;
#endif
#include <memory>
namespace hwcpipe
{
#ifndef HWCPIPE_NO_JSON
HWCPipe::HWCPipe(const char *json_string)
{
auto json = json::parse(json_string);
CpuCounterSet enabled_cpu_counters{};
auto cpu = json.find("cpu");
if (cpu != json.end())
{
for (auto &counter_name : cpu->items())
{
auto counter = cpu_counter_names.find(counter_name.value().get<std::string>());
if (counter != cpu_counter_names.end())
{
enabled_cpu_counters.insert(counter->second);
}
else
{
HWCPIPE_LOG("CPU counter \"%s\" not found.", counter_name.value().get<std::string>().c_str());
}
}
}
GpuCounterSet enabled_gpu_counters{};
auto gpu = json.find("gpu");
if (gpu != json.end())
{
for (auto &counter_name : gpu->items())
{
auto counter = gpu_counter_names.find(counter_name.value().get<std::string>());
if (counter != gpu_counter_names.end())
{
enabled_gpu_counters.insert(counter->second);
}
else
{
HWCPIPE_LOG("GPU counter \"%s\" not found.", counter_name.value().get<std::string>().c_str());
}
}
}
create_profilers(std::move(enabled_cpu_counters), std::move(enabled_gpu_counters));
}
#endif
HWCPipe::HWCPipe(CpuCounterSet enabled_cpu_counters, GpuCounterSet enabled_gpu_counters)
{
create_profilers(std::move(enabled_cpu_counters), std::move(enabled_gpu_counters));
}
HWCPipe::HWCPipe()
{
CpuCounterSet enabled_cpu_counters{CpuCounter::Cycles,
CpuCounter::Instructions,
CpuCounter::CacheReferences,
CpuCounter::CacheMisses,
CpuCounter::BranchInstructions,
CpuCounter::BranchMisses};
GpuCounterSet enabled_gpu_counters{GpuCounter::GpuCycles,
GpuCounter::VertexComputeCycles,
GpuCounter::FragmentCycles,
GpuCounter::TilerCycles,
GpuCounter::CacheReadLookups,
GpuCounter::CacheWriteLookups,
GpuCounter::ExternalMemoryReadAccesses,
GpuCounter::ExternalMemoryWriteAccesses,
GpuCounter::ExternalMemoryReadStalls,
GpuCounter::ExternalMemoryWriteStalls,
GpuCounter::ExternalMemoryReadBytes,
GpuCounter::ExternalMemoryWriteBytes};
create_profilers(std::move(enabled_cpu_counters), std::move(enabled_gpu_counters));
}
void HWCPipe::set_enabled_cpu_counters(CpuCounterSet counters)
{
if (cpu_profiler_)
{
cpu_profiler_->set_enabled_counters(std::move(counters));
}
}
void HWCPipe::set_enabled_gpu_counters(GpuCounterSet counters)
{
if (gpu_profiler_)
{
gpu_profiler_->set_enabled_counters(std::move(counters));
}
}
void HWCPipe::run()
{
if (cpu_profiler_)
{
cpu_profiler_->run();
}
if (gpu_profiler_)
{
gpu_profiler_->run();
}
}
Measurements HWCPipe::sample()
{
Measurements m;
if (cpu_profiler_)
{
m.cpu = &cpu_profiler_->sample();
}
if (gpu_profiler_)
{
m.gpu = &gpu_profiler_->sample();
}
return m;
}
void HWCPipe::stop()
{
if (cpu_profiler_)
{
cpu_profiler_->stop();
}
if (gpu_profiler_)
{
gpu_profiler_->stop();
}
}
void HWCPipe::create_profilers(CpuCounterSet enabled_cpu_counters, GpuCounterSet enabled_gpu_counters)
{
// Automated platform detection
#ifdef __linux__
try
{
cpu_profiler_ = std::unique_ptr<PmuProfiler>(new PmuProfiler(enabled_cpu_counters));
}
catch (const std::runtime_error &e)
{
HWCPIPE_LOG("PMU profiler initialization failed: %s", e.what());
}
try
{
gpu_profiler_ = std::unique_ptr<MaliProfiler>(new MaliProfiler(enabled_gpu_counters));
}
catch (const std::runtime_error &e)
{
HWCPIPE_LOG("Mali profiler initialization failed: %s", e.what());
}
#else
HWCPIPE_LOG("No counters available for this platform.");
#endif
}
} // namespace hwcpipe
@@ -0,0 +1,93 @@
/*
* Copyright (c) 2019 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
/* Force disable JSON in HWCPIPE */
#define HWCPIPE_NO_JSON
#include "cpu_profiler.h"
#include "gpu_profiler.h"
#include <functional>
#include <memory>
namespace hwcpipe
{
struct Measurements
{
const CpuMeasurements *cpu{nullptr};
const GpuMeasurements *gpu{nullptr};
};
/** A class that collects CPU/GPU performance data. */
class HWCPipe
{
public:
#ifndef HWCPIPE_NO_JSON
// Initializes HWCPipe via a JSON configuration string
explicit HWCPipe(const char *json_string);
#endif
// Initializes HWCPipe with the specified counters
HWCPipe(CpuCounterSet enabled_cpu_counters, GpuCounterSet enabled_gpu_counters);
// Initializes HWCPipe with a default set of counters
HWCPipe();
// Sets the enabled counters for the CPU profiler
void set_enabled_cpu_counters(CpuCounterSet counters);
// Sets the enabled counters for the GPU profiler
void set_enabled_gpu_counters(GpuCounterSet counters);
// Starts a profiling session
void run();
// Sample the counters. The function returns pointers to the CPU and GPU
// measurements maps, if the corresponding profiler is enabled.
// The entries in the maps are the counters that are both available and enabled.
// A profiling session must be running when sampling the counters.
Measurements sample();
// Stops the active profiling session
void stop();
const CpuProfiler *cpu_profiler()
{
return cpu_profiler_.get();
}
const GpuProfiler *gpu_profiler()
{
return gpu_profiler_.get();
}
private:
std::unique_ptr<CpuProfiler> cpu_profiler_{};
std::unique_ptr<GpuProfiler> gpu_profiler_{};
void create_profilers(CpuCounterSet enabled_cpu_counters, GpuCounterSet enabled_gpu_counters);
};
} // namespace hwcpipe
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2019 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#define HWCPIPE_TAG "HWCPipe"
#if defined(__ANDROID__)
# include <android/log.h>
# define HWCPIPE_LOG(...) //__android_log_print(ANDROID_LOG_VERBOSE, HWCPIPE_TAG, __VA_ARGS__)
#else
# define HWCPIPE_LOG(...) \
{ \
fprintf(stdout, "%s [INFO] : ", HWCPIPE_TAG); \
fprintf(stdout, __VA_ARGS__); \
fprintf(stdout, "\n"); \
}
#endif
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2019 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
namespace hwcpipe
{
class Value
{
public:
Value() :
is_int_(true),
int_(0),
double_(0.0f)
{}
Value(long long value) :
is_int_(true),
int_(value)
{}
Value(double value) :
is_int_(false),
double_(value)
{}
template <typename T>
T get() const
{
return is_int_ ? static_cast<T>(int_) : static_cast<T>(double_);
}
void set(long long value)
{
int_ = value;
is_int_ = true;
}
void set(double value)
{
double_ = value;
is_int_ = false;
}
private:
bool is_int_;
long long int_{0};
double double_{0.0};
};
} // namespace hwcpipe
@@ -0,0 +1,413 @@
/*
* Copyright (c) 2017-2019 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
#include "hwc_names.hpp"
#ifndef DOXYGEN_SKIP_THIS
# if defined(ANDROID) || defined(__ANDROID__)
/* We use _IOR_BAD/_IOW_BAD rather than _IOR/_IOW otherwise fails to compile with NDK-BUILD because of _IOC_TYPECHECK is defined, not because the paramter is invalid */
# define MALI_IOR(a, b, c) _IOR_BAD(a, b, c)
# define MALI_IOW(a, b, c) _IOW_BAD(a, b, c)
# else
# define MALI_IOR(a, b, c) _IOR(a, b, c)
# define MALI_IOW(a, b, c) _IOW(a, b, c)
# endif
namespace mali_userspace
{
union uk_header
{
uint32_t id;
uint32_t ret;
uint64_t sizer;
};
# define BASE_GPU_NUM_TEXTURE_FEATURES_REGISTERS 3
# define BASE_MAX_COHERENT_GROUPS 16
struct mali_base_gpu_core_props
{
uint32_t product_id;
uint16_t version_status;
uint16_t minor_revision;
uint16_t major_revision;
uint16_t padding;
uint32_t gpu_speed_mhz;
uint32_t gpu_freq_khz_max;
uint32_t gpu_freq_khz_min;
uint32_t log2_program_counter_size;
uint32_t texture_features[BASE_GPU_NUM_TEXTURE_FEATURES_REGISTERS];
uint64_t gpu_available_memory_size;
};
struct mali_base_gpu_l2_cache_props
{
uint8_t log2_line_size;
uint8_t log2_cache_size;
uint8_t num_l2_slices;
uint8_t padding[5];
};
struct mali_base_gpu_tiler_props
{
uint32_t bin_size_bytes;
uint32_t max_active_levels;
};
struct mali_base_gpu_thread_props
{
uint32_t max_threads;
uint32_t max_workgroup_size;
uint32_t max_barrier_size;
uint16_t max_registers;
uint8_t max_task_queue;
uint8_t max_thread_group_split;
uint8_t impl_tech;
uint8_t padding[7];
};
struct mali_base_gpu_coherent_group
{
uint64_t core_mask;
uint16_t num_cores;
uint16_t padding[3];
};
struct mali_base_gpu_coherent_group_info
{
uint32_t num_groups;
uint32_t num_core_groups;
uint32_t coherency;
uint32_t padding;
mali_base_gpu_coherent_group group[BASE_MAX_COHERENT_GROUPS];
};
# define GPU_MAX_JOB_SLOTS 16
struct gpu_raw_gpu_props
{
uint64_t shader_present;
uint64_t tiler_present;
uint64_t l2_present;
uint64_t unused_1;
uint32_t l2_features;
uint32_t suspend_size;
uint32_t mem_features;
uint32_t mmu_features;
uint32_t as_present;
uint32_t js_present;
uint32_t js_features[GPU_MAX_JOB_SLOTS];
uint32_t tiler_features;
uint32_t texture_features[3];
uint32_t gpu_id;
uint32_t thread_max_threads;
uint32_t thread_max_workgroup_size;
uint32_t thread_max_barrier_size;
uint32_t thread_features;
uint32_t coherency_mode;
};
struct mali_base_gpu_props
{
mali_base_gpu_core_props core_props;
mali_base_gpu_l2_cache_props l2_props;
uint64_t unused;
mali_base_gpu_tiler_props tiler_props;
mali_base_gpu_thread_props thread_props;
gpu_raw_gpu_props raw_props;
mali_base_gpu_coherent_group_info coherency_info;
};
struct kbase_uk_gpuprops
{
uk_header header;
mali_base_gpu_props props;
};
# define KBASE_GPUPROP_VALUE_SIZE_U8 (0x0)
# define KBASE_GPUPROP_VALUE_SIZE_U16 (0x1)
# define KBASE_GPUPROP_VALUE_SIZE_U32 (0x2)
# define KBASE_GPUPROP_VALUE_SIZE_U64 (0x3)
# define KBASE_GPUPROP_PRODUCT_ID 1
# define KBASE_GPUPROP_MINOR_REVISION 3
# define KBASE_GPUPROP_MAJOR_REVISION 4
# define KBASE_GPUPROP_COHERENCY_NUM_GROUPS 61
# define KBASE_GPUPROP_COHERENCY_NUM_CORE_GROUPS 62
# define KBASE_GPUPROP_COHERENCY_GROUP_0 64
# define KBASE_GPUPROP_COHERENCY_GROUP_1 65
# define KBASE_GPUPROP_COHERENCY_GROUP_2 66
# define KBASE_GPUPROP_COHERENCY_GROUP_3 67
# define KBASE_GPUPROP_COHERENCY_GROUP_4 68
# define KBASE_GPUPROP_COHERENCY_GROUP_5 69
# define KBASE_GPUPROP_COHERENCY_GROUP_6 70
# define KBASE_GPUPROP_COHERENCY_GROUP_7 71
# define KBASE_GPUPROP_COHERENCY_GROUP_8 72
# define KBASE_GPUPROP_COHERENCY_GROUP_9 73
# define KBASE_GPUPROP_COHERENCY_GROUP_10 74
# define KBASE_GPUPROP_COHERENCY_GROUP_11 75
# define KBASE_GPUPROP_COHERENCY_GROUP_12 76
# define KBASE_GPUPROP_COHERENCY_GROUP_13 77
# define KBASE_GPUPROP_COHERENCY_GROUP_14 78
# define KBASE_GPUPROP_COHERENCY_GROUP_15 79
# define KBASE_GPUPROP_L2_NUM_L2_SLICES 15
struct gpu_props
{
uint32_t product_id;
uint16_t minor_revision;
uint16_t major_revision;
uint32_t num_groups;
uint32_t num_core_groups;
uint64_t core_mask[16];
uint32_t l2_slices;
};
static const struct
{
uint32_t type;
size_t offset;
int size;
} gpu_property_mapping[] = {
# define PROP(name, member) \
{ \
KBASE_GPUPROP_##name, offsetof(struct gpu_props, member), \
sizeof(((struct gpu_props *) 0)->member) \
}
PROP(PRODUCT_ID, product_id),
PROP(MINOR_REVISION, minor_revision),
PROP(MAJOR_REVISION, major_revision),
PROP(COHERENCY_NUM_GROUPS, num_groups),
PROP(COHERENCY_NUM_CORE_GROUPS, num_core_groups),
PROP(COHERENCY_GROUP_0, core_mask[0]),
PROP(COHERENCY_GROUP_1, core_mask[1]),
PROP(COHERENCY_GROUP_2, core_mask[2]),
PROP(COHERENCY_GROUP_3, core_mask[3]),
PROP(COHERENCY_GROUP_4, core_mask[4]),
PROP(COHERENCY_GROUP_5, core_mask[5]),
PROP(COHERENCY_GROUP_6, core_mask[6]),
PROP(COHERENCY_GROUP_7, core_mask[7]),
PROP(COHERENCY_GROUP_8, core_mask[8]),
PROP(COHERENCY_GROUP_9, core_mask[9]),
PROP(COHERENCY_GROUP_10, core_mask[10]),
PROP(COHERENCY_GROUP_11, core_mask[11]),
PROP(COHERENCY_GROUP_12, core_mask[12]),
PROP(COHERENCY_GROUP_13, core_mask[13]),
PROP(COHERENCY_GROUP_14, core_mask[14]),
PROP(COHERENCY_GROUP_15, core_mask[15]),
PROP(L2_NUM_L2_SLICES, l2_slices),
# undef PROP
{0, 0, 0}};
struct kbase_hwcnt_reader_metadata
{
uint64_t timestamp = 0;
uint32_t event_id = 0;
uint32_t buffer_idx = 0;
};
namespace
{
/** Message header */
union kbase_uk_hwcnt_header
{
/* 32-bit number identifying the UK function to be called. */
uint32_t id;
/* The int return code returned by the called UK function. */
uint32_t ret;
/* Used to ensure 64-bit alignment of this union. Do not remove. */
uint64_t sizer;
};
/** IOCTL parameters to check version */
struct kbase_uk_hwcnt_reader_version_check_args
{
union kbase_uk_hwcnt_header header;
uint16_t major;
uint16_t minor;
uint8_t padding[4];
};
union kbase_pointer
{
void * value;
uint32_t compat_value;
uint64_t sizer;
};
struct kbase_ioctl_get_gpuprops
{
kbase_pointer buffer;
uint32_t size;
uint32_t flags;
};
struct kbase_ioctl_version_check
{
uint16_t major;
uint16_t minor;
};
struct kbase_ioctl_set_flags
{
uint32_t create_flags;
};
struct kbase_ioctl_hwcnt_reader_setup
{
uint32_t buffer_count;
uint32_t jm_bm;
uint32_t shader_bm;
uint32_t tiler_bm;
uint32_t mmu_l2_bm;
};
# define KBASE_IOCTL_TYPE 0x80
# define KBASE_IOCTL_GET_GPUPROPS MALI_IOW(KBASE_IOCTL_TYPE, 3, struct mali_userspace::kbase_ioctl_get_gpuprops)
# define KBASE_IOCTL_VERSION_CHECK _IOWR(KBASE_IOCTL_TYPE, 0, struct mali_userspace::kbase_ioctl_version_check)
# define KBASE_IOCTL_SET_FLAGS _IOW(KBASE_IOCTL_TYPE, 1, struct mali_userspace::kbase_ioctl_set_flags)
# define KBASE_IOCTL_HWCNT_READER_SETUP _IOW(KBASE_IOCTL_TYPE, 8, struct mali_userspace::kbase_ioctl_hwcnt_reader_setup)
/** IOCTL parameters to set flags */
struct kbase_uk_hwcnt_reader_set_flags
{
union kbase_uk_hwcnt_header header;
uint32_t create_flags;
uint32_t padding;
};
/** IOCTL parameters to configure reader */
struct kbase_uk_hwcnt_reader_setup
{
union kbase_uk_hwcnt_header header;
/* IN */
uint32_t buffer_count;
uint32_t jm_bm;
uint32_t shader_bm;
uint32_t tiler_bm;
uint32_t mmu_l2_bm;
/* OUT */
int32_t fd;
};
static const uint32_t HWCNT_READER_API = 1;
struct uku_version_check_args
{
uk_header header;
uint16_t major;
uint16_t minor;
uint8_t padding[4];
};
enum
{
UKP_FUNC_ID_CHECK_VERSION = 0,
/* Related to mali0 ioctl interface */
LINUX_UK_BASE_MAGIC = 0x80,
BASE_CONTEXT_CREATE_KERNEL_FLAGS = 0x2,
KBASE_FUNC_HWCNT_UK_FUNC_ID = 512,
KBASE_FUNC_GPU_PROPS_REG_DUMP = KBASE_FUNC_HWCNT_UK_FUNC_ID + 14,
KBASE_FUNC_HWCNT_READER_SETUP = KBASE_FUNC_HWCNT_UK_FUNC_ID + 36,
KBASE_FUNC_HWCNT_DUMP = KBASE_FUNC_HWCNT_UK_FUNC_ID + 11,
KBASE_FUNC_HWCNT_CLEAR = KBASE_FUNC_HWCNT_UK_FUNC_ID + 12,
KBASE_FUNC_SET_FLAGS = KBASE_FUNC_HWCNT_UK_FUNC_ID + 18,
/* The ids of ioctl commands for the reader interface */
KBASE_HWCNT_READER = 0xBE,
KBASE_HWCNT_READER_GET_HWVER = MALI_IOR(KBASE_HWCNT_READER, 0x00, uint32_t),
KBASE_HWCNT_READER_GET_BUFFER_SIZE = MALI_IOR(KBASE_HWCNT_READER, 0x01, uint32_t),
KBASE_HWCNT_READER_DUMP = MALI_IOW(KBASE_HWCNT_READER, 0x10, uint32_t),
KBASE_HWCNT_READER_CLEAR = MALI_IOW(KBASE_HWCNT_READER, 0x11, uint32_t),
KBASE_HWCNT_READER_GET_BUFFER = MALI_IOR(KBASE_HWCNT_READER, 0x20, struct kbase_hwcnt_reader_metadata),
KBASE_HWCNT_READER_PUT_BUFFER = MALI_IOW(KBASE_HWCNT_READER, 0x21, struct kbase_hwcnt_reader_metadata),
KBASE_HWCNT_READER_SET_INTERVAL = MALI_IOW(KBASE_HWCNT_READER, 0x30, uint32_t),
KBASE_HWCNT_READER_ENABLE_EVENT = MALI_IOW(KBASE_HWCNT_READER, 0x40, uint32_t),
KBASE_HWCNT_READER_DISABLE_EVENT = MALI_IOW(KBASE_HWCNT_READER, 0x41, uint32_t),
KBASE_HWCNT_READER_GET_API_VERSION = MALI_IOW(KBASE_HWCNT_READER, 0xFF, uint32_t)
};
enum
{
PIPE_DESCRIPTOR_IN, /**< The index of a pipe's input descriptor. */
PIPE_DESCRIPTOR_OUT, /**< The index of a pipe's output descriptor. */
PIPE_DESCRIPTOR_COUNT /**< The number of descriptors forming a pipe. */
};
enum
{
POLL_DESCRIPTOR_SIGNAL, /**< The index of the signal descriptor in poll fds array. */
POLL_DESCRIPTOR_HWCNT_READER, /**< The index of the hwcnt reader descriptor in poll fds array. */
POLL_DESCRIPTOR_COUNT /**< The number of descriptors poll is waiting for. */
};
/** Write a single byte into the pipe to interrupt the reader thread */
typedef char poll_data_t;
} // namespace
template <typename T>
static inline int mali_ioctl(int fd, T &arg)
{
auto * hdr = &arg.header;
const int cmd = _IOC(_IOC_READ | _IOC_WRITE, LINUX_UK_BASE_MAGIC, hdr->id, sizeof(T));
if (ioctl(fd, cmd, &arg))
return -1;
if (hdr->ret)
return -1;
return 0;
}
} // namespace mali_userspace
#endif /* DOXYGEN_SKIP_THIS */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,614 @@
/*
* Copyright (c) 2017-2019 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "mali_profiler.h"
#include "hwcpipe_log.h"
#include <algorithm>
using mali_userspace::MALI_NAME_BLOCK_JM;
using mali_userspace::MALI_NAME_BLOCK_MMU;
using mali_userspace::MALI_NAME_BLOCK_SHADER;
using mali_userspace::MALI_NAME_BLOCK_TILER;
namespace hwcpipe
{
namespace
{
struct MaliHWInfo
{
unsigned mp_count;
unsigned gpu_id;
unsigned r_value;
unsigned p_value;
unsigned core_mask;
unsigned l2_slices;
};
MaliHWInfo get_mali_hw_info(const char *path)
{
int fd = open(path, O_RDWR); // NOLINT
if (fd < 0)
{
throw std::runtime_error("Failed to get HW info.");
}
{
mali_userspace::kbase_uk_hwcnt_reader_version_check_args version_check_args;
version_check_args.header.id = mali_userspace::UKP_FUNC_ID_CHECK_VERSION; // NOLINT
version_check_args.major = 10;
version_check_args.minor = 2;
if (mali_userspace::mali_ioctl(fd, version_check_args) != 0)
{
mali_userspace::kbase_ioctl_version_check _version_check_args = {0, 0};
if (ioctl(fd, KBASE_IOCTL_VERSION_CHECK, &_version_check_args) < 0)
{
close(fd);
throw std::runtime_error("Failed to check version.");
}
}
}
{
mali_userspace::kbase_uk_hwcnt_reader_set_flags flags; // NOLINT
memset(&flags, 0, sizeof(flags));
flags.header.id = mali_userspace::KBASE_FUNC_SET_FLAGS; // NOLINT
flags.create_flags = mali_userspace::BASE_CONTEXT_CREATE_KERNEL_FLAGS;
if (mali_userspace::mali_ioctl(fd, flags) != 0)
{
mali_userspace::kbase_ioctl_set_flags _flags = {1u << 1};
if (ioctl(fd, KBASE_IOCTL_SET_FLAGS, &_flags) < 0)
{
close(fd);
throw std::runtime_error("Failed settings flags ioctl.");
}
}
}
{
MaliHWInfo hw_info; // NOLINT
memset(&hw_info, 0, sizeof(hw_info));
mali_userspace::kbase_uk_gpuprops props = {};
props.header.id = mali_userspace::KBASE_FUNC_GPU_PROPS_REG_DUMP;
if (mali_ioctl(fd, props) == 0)
{
hw_info.gpu_id = props.props.core_props.product_id;
hw_info.r_value = props.props.core_props.major_revision;
hw_info.p_value = props.props.core_props.minor_revision;
for (uint32_t i = 0; i < props.props.coherency_info.num_core_groups; i++)
hw_info.core_mask |= props.props.coherency_info.group[i].core_mask;
hw_info.mp_count = __builtin_popcountll(hw_info.core_mask);
hw_info.l2_slices = props.props.l2_props.num_l2_slices;
close(fd);
}
else
{
mali_userspace::kbase_ioctl_get_gpuprops get_props = {};
int ret;
if ((ret = ioctl(fd, KBASE_IOCTL_GET_GPUPROPS, &get_props)) < 0)
{
throw std::runtime_error("Failed getting GPU properties.");
close(fd);
}
get_props.size = ret;
std::vector<uint8_t> buffer(ret);
get_props.buffer.value = buffer.data();
ret = ioctl(fd, KBASE_IOCTL_GET_GPUPROPS, &get_props);
if (ret < 0)
{
throw std::runtime_error("Failed getting GPU properties.");
close(fd);
}
#define READ_U8(p) ((p)[0])
#define READ_U16(p) (READ_U8((p)) | (uint16_t(READ_U8((p) + 1)) << 8))
#define READ_U32(p) (READ_U16((p)) | (uint32_t(READ_U16((p) + 2)) << 16))
#define READ_U64(p) (READ_U32((p)) | (uint64_t(READ_U32((p) + 4)) << 32))
mali_userspace::gpu_props props = {};
const auto *ptr = buffer.data();
int size = ret;
while (size > 0)
{
uint32_t type = READ_U32(ptr);
uint32_t value_type = type & 3;
uint64_t value;
ptr += 4;
size -= 4;
switch (value_type)
{
case KBASE_GPUPROP_VALUE_SIZE_U8:
value = READ_U8(ptr);
ptr++;
size--;
break;
case KBASE_GPUPROP_VALUE_SIZE_U16:
value = READ_U16(ptr);
ptr += 2;
size -= 2;
break;
case KBASE_GPUPROP_VALUE_SIZE_U32:
value = READ_U32(ptr);
ptr += 4;
size -= 4;
break;
case KBASE_GPUPROP_VALUE_SIZE_U64:
value = READ_U64(ptr);
ptr += 8;
size -= 8;
break;
}
for (unsigned i = 0; mali_userspace::gpu_property_mapping[i].type; i++)
{
if (mali_userspace::gpu_property_mapping[i].type == (type >> 2))
{
auto offset = mali_userspace::gpu_property_mapping[i].offset;
void *p = reinterpret_cast<uint8_t *>(&props) + offset;
switch (mali_userspace::gpu_property_mapping[i].size)
{
case 1:
*reinterpret_cast<uint8_t *>(p) = value;
break;
case 2:
*reinterpret_cast<uint16_t *>(p) = value;
break;
case 4:
*reinterpret_cast<uint32_t *>(p) = value;
break;
case 8:
*reinterpret_cast<uint64_t *>(p) = value;
break;
default:
throw std::runtime_error("Invalid property size.");
close(fd);
}
break;
}
}
}
hw_info.gpu_id = props.product_id;
hw_info.r_value = props.major_revision;
hw_info.p_value = props.minor_revision;
for (uint32_t i = 0; i < props.num_core_groups; i++)
hw_info.core_mask |= props.core_mask[i];
hw_info.mp_count = __builtin_popcountll(hw_info.core_mask);
hw_info.l2_slices = props.l2_slices;
close(fd);
}
return hw_info;
}
}
} // namespace
typedef std::function<uint64_t(void)> MaliValueGetter;
MaliProfiler::MaliProfiler(const GpuCounterSet &enabled_counters) :
enabled_counters_(enabled_counters)
{
// Throws if setup fails
init();
const std::unordered_map<GpuCounter, MaliValueGetter, GpuCounterHash> bifrost_mappings = {
{GpuCounter::GpuCycles, [this] { return get_counter_value(MALI_NAME_BLOCK_JM, "GPU_ACTIVE"); }},
{GpuCounter::VertexComputeCycles, [this] { return get_counter_value(MALI_NAME_BLOCK_JM, "JS1_ACTIVE"); }},
{GpuCounter::FragmentCycles, [this] { return get_counter_value(MALI_NAME_BLOCK_JM, "JS0_ACTIVE"); }},
{GpuCounter::TilerCycles, [this] { return get_counter_value(MALI_NAME_BLOCK_TILER, "TILER_ACTIVE"); }},
{GpuCounter::VertexComputeJobs, [this] { return get_counter_value(MALI_NAME_BLOCK_JM, "JS1_JOBS"); }},
{GpuCounter::FragmentJobs, [this] { return get_counter_value(MALI_NAME_BLOCK_JM, "JS0_JOBS"); }},
{GpuCounter::Pixels, [this] { return get_counter_value(MALI_NAME_BLOCK_JM, "JS0_TASKS") * 1024; }},
{GpuCounter::Tiles, [this] { return get_counter_value(MALI_NAME_BLOCK_SHADER, "FRAG_PTILES"); }},
{GpuCounter::TransactionEliminations, [this] { return get_counter_value(MALI_NAME_BLOCK_SHADER, "FRAG_TRANS_ELIM"); }},
{GpuCounter::EarlyZTests, [this] { return get_counter_value(MALI_NAME_BLOCK_SHADER, "FRAG_QUADS_EZS_TEST"); }},
{GpuCounter::EarlyZKilled, [this] { return get_counter_value(MALI_NAME_BLOCK_SHADER, "FRAG_QUADS_EZS_KILL"); }},
{GpuCounter::LateZTests, [this] { return get_counter_value(MALI_NAME_BLOCK_SHADER, "FRAG_LZS_TEST"); }},
{GpuCounter::LateZKilled, [this] { return get_counter_value(MALI_NAME_BLOCK_SHADER, "FRAG_LZS_KILL"); }},
{GpuCounter::Instructions, [this] { return get_counter_value(MALI_NAME_BLOCK_SHADER, "EXEC_INSTR_COUNT"); }},
{GpuCounter::DivergedInstructions, [this] { return get_counter_value(MALI_NAME_BLOCK_SHADER, "EXEC_INSTR_DIVERGED"); }},
{GpuCounter::ShaderCycles, [this] { return get_counter_value(MALI_NAME_BLOCK_SHADER, "EXEC_CORE_ACTIVE"); }},
{GpuCounter::ShaderArithmeticCycles, [this] { return get_counter_value(MALI_NAME_BLOCK_SHADER, "EXEC_INSTR_COUNT"); }},
{GpuCounter::ShaderLoadStoreCycles, [this] { return get_counter_value(MALI_NAME_BLOCK_SHADER, "LS_MEM_READ_FULL") + get_counter_value(MALI_NAME_BLOCK_SHADER, "LS_MEM_WRITE_FULL") + get_counter_value(MALI_NAME_BLOCK_SHADER, "LS_MEM_READ_SHORT") + get_counter_value(MALI_NAME_BLOCK_SHADER, "LS_MEM_WRITE_SHORT") + get_counter_value(MALI_NAME_BLOCK_SHADER, "LS_MEM_ATOMIC"); }},
{GpuCounter::ShaderTextureCycles, [this] { return get_counter_value(MALI_NAME_BLOCK_SHADER, "TEX_FILT_NUM_OPERATIONS"); }},
{GpuCounter::CacheReadLookups, [this] { return get_counter_value(MALI_NAME_BLOCK_MMU, "L2_READ_LOOKUP"); }},
{GpuCounter::CacheWriteLookups, [this] { return get_counter_value(MALI_NAME_BLOCK_MMU, "L2_WRITE_LOOKUP"); }},
{GpuCounter::ExternalMemoryReadAccesses, [this] { return get_counter_value(MALI_NAME_BLOCK_MMU, "L2_EXT_READ"); }},
{GpuCounter::ExternalMemoryWriteAccesses, [this] { return get_counter_value(MALI_NAME_BLOCK_MMU, "L2_EXT_WRITE"); }},
{GpuCounter::ExternalMemoryReadStalls, [this] { return get_counter_value(MALI_NAME_BLOCK_MMU, "L2_EXT_AR_STALL"); }},
{GpuCounter::ExternalMemoryWriteStalls, [this] { return get_counter_value(MALI_NAME_BLOCK_MMU, "L2_EXT_W_STALL"); }},
{GpuCounter::ExternalMemoryReadBytes, [this] { return get_counter_value(MALI_NAME_BLOCK_MMU, "L2_EXT_READ_BEATS") * 16; }},
{GpuCounter::ExternalMemoryWriteBytes, [this] { return get_counter_value(MALI_NAME_BLOCK_MMU, "L2_EXT_WRITE_BEATS") * 16; }},
};
const std::unordered_map<GpuCounter, MaliValueGetter, GpuCounterHash> midgard_mappings = {
{GpuCounter::GpuCycles, [this] { return get_counter_value(MALI_NAME_BLOCK_JM, "GPU_ACTIVE"); }},
{GpuCounter::VertexComputeCycles, [this] { return get_counter_value(MALI_NAME_BLOCK_JM, "JS1_ACTIVE"); }},
{GpuCounter::FragmentCycles, [this] { return get_counter_value(MALI_NAME_BLOCK_JM, "JS0_ACTIVE"); }},
{GpuCounter::VertexComputeJobs, [this] { return get_counter_value(MALI_NAME_BLOCK_JM, "JS1_JOBS"); }},
{GpuCounter::FragmentJobs, [this] { return get_counter_value(MALI_NAME_BLOCK_JM, "JS0_JOBS"); }},
{GpuCounter::Pixels, [this] { return get_counter_value(MALI_NAME_BLOCK_JM, "JS0_TASKS") * 1024; }},
{GpuCounter::Tiles, [this] { return get_counter_value(MALI_NAME_BLOCK_SHADER, "FRAG_PTILES"); }},
{GpuCounter::TransactionEliminations, [this] { return get_counter_value(MALI_NAME_BLOCK_SHADER, "FRAG_TRANS_ELIM"); }},
{GpuCounter::EarlyZTests, [this] { return get_counter_value(MALI_NAME_BLOCK_SHADER, "FRAG_QUADS_EZS_TEST"); }},
{GpuCounter::EarlyZKilled, [this] { return get_counter_value(MALI_NAME_BLOCK_SHADER, "FRAG_QUADS_EZS_KILLED"); }},
{GpuCounter::LateZTests, [this] { return get_counter_value(MALI_NAME_BLOCK_SHADER, "FRAG_THREADS_LZS_TEST"); }},
{GpuCounter::LateZKilled, [this] { return get_counter_value(MALI_NAME_BLOCK_SHADER, "FRAG_THREADS_LZS_KILLED"); }},
{GpuCounter::CacheReadLookups, [this] { return get_counter_value(MALI_NAME_BLOCK_MMU, "L2_READ_LOOKUP"); }},
{GpuCounter::CacheWriteLookups, [this] { return get_counter_value(MALI_NAME_BLOCK_MMU, "L2_WRITE_LOOKUP"); }},
{GpuCounter::ExternalMemoryReadAccesses, [this] { return get_counter_value(MALI_NAME_BLOCK_MMU, "L2_EXT_READ"); }},
{GpuCounter::ExternalMemoryWriteAccesses, [this] { return get_counter_value(MALI_NAME_BLOCK_MMU, "L2_EXT_WRITE"); }},
{GpuCounter::ExternalMemoryReadStalls, [this] { return get_counter_value(MALI_NAME_BLOCK_MMU, "L2_EXT_AR_STALL"); }},
{GpuCounter::ExternalMemoryWriteStalls, [this] { return get_counter_value(MALI_NAME_BLOCK_MMU, "L2_EXT_W_STALL"); }},
{GpuCounter::ExternalMemoryReadBytes, [this] { return get_counter_value(MALI_NAME_BLOCK_MMU, "L2_EXT_READ_BEATS") * 16; }},
{GpuCounter::ExternalMemoryWriteBytes, [this] { return get_counter_value(MALI_NAME_BLOCK_MMU, "L2_EXT_WRITE_BEATS") * 16; }},
};
auto product = std::find_if(std::begin(mali_userspace::products), std::end(mali_userspace::products), [&](const mali_userspace::CounterMapping &cm) {
return (cm.product_mask & gpu_id_) == cm.product_id;
});
if (product != std::end(mali_userspace::products))
{
switch (product->product_id)
{
case mali_userspace::PRODUCT_ID_T60X:
case mali_userspace::PRODUCT_ID_T62X:
case mali_userspace::PRODUCT_ID_T72X:
mappings_ = midgard_mappings;
mappings_[GpuCounter::Pixels] = [this]() { return get_counter_value(MALI_NAME_BLOCK_JM, "JS0_TASKS") * 256; };
break;
case mali_userspace::PRODUCT_ID_T76X:
case mali_userspace::PRODUCT_ID_T82X:
case mali_userspace::PRODUCT_ID_T83X:
case mali_userspace::PRODUCT_ID_T86X:
case mali_userspace::PRODUCT_ID_TFRX:
mappings_ = midgard_mappings;
break;
case mali_userspace::PRODUCT_ID_TMIX:
case mali_userspace::PRODUCT_ID_THEX:
mappings_ = bifrost_mappings;
mappings_[GpuCounter::ShaderTextureCycles] = [this] { return get_counter_value(MALI_NAME_BLOCK_SHADER, "TEX_COORD_ISSUE"); };
break;
case mali_userspace::PRODUCT_ID_TSIX:
case mali_userspace::PRODUCT_ID_TNOX:
default:
mappings_ = bifrost_mappings;
break;
}
}
else
{
HWCPIPE_LOG("Mali counters initialization failed: Failed to identify GPU");
}
}
void MaliProfiler::init()
{
MaliHWInfo hw_info = get_mali_hw_info(device_);
num_cores_ = hw_info.mp_count;
num_l2_slices_ = hw_info.l2_slices;
gpu_id_ = hw_info.gpu_id;
fd_ = open(device_, O_RDWR | O_CLOEXEC | O_NONBLOCK); // NOLINT
if (fd_ < 0)
{
throw std::runtime_error("Failed to open /dev/mali0.");
}
{
mali_userspace::kbase_uk_hwcnt_reader_version_check_args check; // NOLINT
memset(&check, 0, sizeof(check));
if (mali_userspace::mali_ioctl(fd_, check) != 0)
{
mali_userspace::kbase_ioctl_version_check _check = {0, 0};
if (ioctl(fd_, KBASE_IOCTL_VERSION_CHECK, &_check) < 0)
{
throw std::runtime_error("Failed to get ABI version.");
}
}
else if (check.major < 10)
{
throw std::runtime_error("Unsupported ABI version 10.");
}
}
{
mali_userspace::kbase_uk_hwcnt_reader_set_flags flags; // NOLINT
memset(&flags, 0, sizeof(flags));
flags.header.id = mali_userspace::KBASE_FUNC_SET_FLAGS; // NOLINT
flags.create_flags = mali_userspace::BASE_CONTEXT_CREATE_KERNEL_FLAGS;
if (mali_userspace::mali_ioctl(fd_, flags) != 0)
{
mali_userspace::kbase_ioctl_set_flags _flags = {1u << 1};
if (ioctl(fd_, KBASE_IOCTL_SET_FLAGS, &_flags) < 0)
{
throw std::runtime_error("Failed settings flags ioctl.");
}
}
}
{
mali_userspace::kbase_uk_hwcnt_reader_setup setup; // NOLINT
memset(&setup, 0, sizeof(setup));
setup.header.id = mali_userspace::KBASE_FUNC_HWCNT_READER_SETUP; // NOLINT
setup.buffer_count = buffer_count_;
setup.jm_bm = -1;
setup.shader_bm = -1;
setup.tiler_bm = -1;
setup.mmu_l2_bm = -1;
setup.fd = -1;
if (mali_userspace::mali_ioctl(fd_, setup) != 0)
{
mali_userspace::kbase_ioctl_hwcnt_reader_setup _setup = {};
_setup.buffer_count = buffer_count_;
_setup.jm_bm = -1;
_setup.shader_bm = -1;
_setup.tiler_bm = -1;
_setup.mmu_l2_bm = -1;
int ret;
if ((ret = ioctl(fd_, KBASE_IOCTL_HWCNT_READER_SETUP, &_setup)) < 0)
{
throw std::runtime_error("Failed setting hwcnt reader ioctl.");
}
hwc_fd_ = ret;
}
else
{
hwc_fd_ = setup.fd;
}
}
{
uint32_t api_version = ~mali_userspace::HWCNT_READER_API;
if (ioctl(hwc_fd_, mali_userspace::KBASE_HWCNT_READER_GET_API_VERSION, &api_version) != 0) // NOLINT
{
throw std::runtime_error("Could not determine hwcnt reader API.");
}
else if (api_version != mali_userspace::HWCNT_READER_API)
{
throw std::runtime_error("Invalid API version.");
}
}
if (ioctl(hwc_fd_, static_cast<int>(mali_userspace::KBASE_HWCNT_READER_GET_BUFFER_SIZE), &buffer_size_) != 0) // NOLINT
{
throw std::runtime_error("Failed to get buffer size.");
}
if (ioctl(hwc_fd_, static_cast<int>(mali_userspace::KBASE_HWCNT_READER_GET_HWVER), &hw_ver_) != 0) // NOLINT
{
throw std::runtime_error("Could not determine HW version.");
}
if (hw_ver_ < 5)
{
throw std::runtime_error("Unsupported HW version.");
}
sample_data_ = static_cast<uint8_t *>(mmap(nullptr, buffer_count_ * buffer_size_, PROT_READ, MAP_PRIVATE, hwc_fd_, 0));
if (sample_data_ == MAP_FAILED) // NOLINT
{
throw std::runtime_error("Failed to map sample data.");
}
auto product = std::find_if(std::begin(mali_userspace::products), std::end(mali_userspace::products), [&](const mali_userspace::CounterMapping &cm) {
return (cm.product_mask & hw_info.gpu_id) == cm.product_id;
});
if (product != std::end(mali_userspace::products))
{
names_lut_ = product->names_lut;
}
else
{
throw std::runtime_error("Could not identify GPU.");
}
raw_counter_buffer_.resize(buffer_size_ / sizeof(uint32_t));
// Build core remap table.
core_index_remap_.clear();
core_index_remap_.reserve(hw_info.mp_count);
unsigned int mask = hw_info.core_mask;
while (mask != 0)
{
unsigned int bit = __builtin_ctz(mask);
core_index_remap_.push_back(bit);
mask &= ~(1u << bit);
}
}
void MaliProfiler::run()
{
sample_counters();
wait_next_event();
}
void MaliProfiler::stop()
{
// We don't need to do anything on stop()
}
const GpuMeasurements &MaliProfiler::sample()
{
sample_counters();
wait_next_event();
for (const auto &counter : enabled_counters_)
{
auto mapping = mappings_.find(counter);
if (mapping == mappings_.end())
{
continue;
}
measurements_[mapping->first] = mapping->second();
}
return measurements_;
}
void MaliProfiler::sample_counters()
{
if (ioctl(hwc_fd_, mali_userspace::KBASE_HWCNT_READER_DUMP, 0) != 0)
{
throw std::runtime_error("Could not sample hardware counters.");
}
}
void MaliProfiler::wait_next_event()
{
pollfd poll_fd; // NOLINT
poll_fd.fd = hwc_fd_;
poll_fd.events = POLLIN;
const int count = poll(&poll_fd, 1, -1);
if (count < 0)
{
throw std::runtime_error("poll() failed.");
}
if ((poll_fd.revents & POLLIN) != 0)
{
mali_userspace::kbase_hwcnt_reader_metadata meta; // NOLINT
if (ioctl(hwc_fd_, static_cast<int>(mali_userspace::KBASE_HWCNT_READER_GET_BUFFER), &meta) != 0) // NOLINT
{
throw std::runtime_error("Failed READER_GET_BUFFER.");
}
memcpy(raw_counter_buffer_.data(), sample_data_ + buffer_size_ * meta.buffer_idx, buffer_size_);
timestamp_ = meta.timestamp;
if (ioctl(hwc_fd_, mali_userspace::KBASE_HWCNT_READER_PUT_BUFFER, &meta) != 0) // NOLINT
{
throw std::runtime_error("Failed READER_PUT_BUFFER.");
}
}
else if ((poll_fd.revents & POLLHUP) != 0)
{
throw std::runtime_error("HWC hung up.");
}
}
uint64_t MaliProfiler::get_counter_value(mali_userspace::MaliCounterBlockName block, const char *name) const
{
uint64_t sum = 0;
switch (block)
{
case mali_userspace::MALI_NAME_BLOCK_MMU:
// If an MMU counter is selected, sum the values over MMU slices
for (int i = 0; i < num_l2_slices_; i++)
{
sum += get_counters(block, i)[find_counter_index_by_name(block, name)];
}
return sum;
case mali_userspace::MALI_NAME_BLOCK_SHADER:
// If a shader core counter is selected, sum the values over shader cores
for (int i = 0; i < num_cores_; i++)
{
sum += get_counters(block, i)[find_counter_index_by_name(block, name)];
}
return sum;
case mali_userspace::MALI_NAME_BLOCK_JM:
case mali_userspace::MALI_NAME_BLOCK_TILER:
default:
return static_cast<uint64_t>(get_counters(block)[find_counter_index_by_name(block, name)]);
}
}
const uint32_t *MaliProfiler::get_counters(mali_userspace::MaliCounterBlockName block, int index) const
{
switch (block)
{
case mali_userspace::MALI_NAME_BLOCK_JM:
return raw_counter_buffer_.data() + mali_userspace::MALI_NAME_BLOCK_SIZE * 0;
case mali_userspace::MALI_NAME_BLOCK_MMU:
if (index < 0 || index >= num_l2_slices_)
{
throw std::runtime_error("Invalid slice number.");
}
// If an MMU counter is selected, index refers to the MMU slice
return raw_counter_buffer_.data() + mali_userspace::MALI_NAME_BLOCK_SIZE * (2 + index);
case mali_userspace::MALI_NAME_BLOCK_TILER:
return raw_counter_buffer_.data() + mali_userspace::MALI_NAME_BLOCK_SIZE * 1;
default:
if (index < 0 || index >= num_cores_)
{
throw std::runtime_error("Invalid core number.");
}
// If a shader core counter is selected, index refers to the core index
return raw_counter_buffer_.data() + mali_userspace::MALI_NAME_BLOCK_SIZE * (2 + num_l2_slices_ + core_index_remap_[index]);
}
}
int MaliProfiler::find_counter_index_by_name(mali_userspace::MaliCounterBlockName block, const char *name) const
{
const char *const *names = &names_lut_[mali_userspace::MALI_NAME_BLOCK_SIZE * block];
for (int i = 0; i < mali_userspace::MALI_NAME_BLOCK_SIZE; ++i)
{
if (strstr(names[i], name) != nullptr)
{
return i;
}
}
return -1;
}
} // namespace hwcpipe
@@ -0,0 +1,124 @@
/*
* Copyright (c) 2019 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "gpu_profiler.h"
#include "hwc.hpp"
#include <functional>
#include <vector>
namespace hwcpipe
{
/** A Gpu profiler that uses Mali counter data. */
class MaliProfiler : public GpuProfiler
{
public:
explicit MaliProfiler(const GpuCounterSet &enabled_counters);
virtual ~MaliProfiler() = default;
virtual const GpuCounterSet &enabled_counters() const override
{
return enabled_counters_;
}
virtual const GpuCounterSet &supported_counters() const override
{
return supported_counters_;
};
virtual void set_enabled_counters(GpuCounterSet counters) override
{
enabled_counters_ = std::move(counters);
};
virtual void run() override;
virtual const GpuMeasurements &sample() override;
virtual void stop() override;
private:
GpuCounterSet enabled_counters_{};
const GpuCounterSet supported_counters_{
GpuCounter::GpuCycles,
GpuCounter::VertexComputeCycles,
GpuCounter::FragmentCycles,
GpuCounter::TilerCycles,
GpuCounter::VertexComputeJobs,
GpuCounter::Tiles,
GpuCounter::TransactionEliminations,
GpuCounter::FragmentJobs,
GpuCounter::Pixels,
GpuCounter::EarlyZTests,
GpuCounter::EarlyZKilled,
GpuCounter::LateZTests,
GpuCounter::LateZKilled,
GpuCounter::Instructions,
GpuCounter::DivergedInstructions,
GpuCounter::ShaderCycles,
GpuCounter::ShaderArithmeticCycles,
GpuCounter::ShaderLoadStoreCycles,
GpuCounter::ShaderTextureCycles,
GpuCounter::CacheReadLookups,
GpuCounter::CacheWriteLookups,
GpuCounter::ExternalMemoryReadAccesses,
GpuCounter::ExternalMemoryWriteAccesses,
GpuCounter::ExternalMemoryReadStalls,
GpuCounter::ExternalMemoryWriteStalls,
GpuCounter::ExternalMemoryReadBytes,
GpuCounter::ExternalMemoryWriteBytes,
};
typedef std::function<double(void)> MaliValueGetter;
std::unordered_map<GpuCounter, MaliValueGetter, GpuCounterHash> mappings_{};
const char *const device_{"/dev/mali0"};
int num_cores_{0};
int num_l2_slices_{0};
int gpu_id_{0};
uint32_t hw_ver_{0};
int buffer_count_{16};
size_t buffer_size_{0};
uint8_t * sample_data_{nullptr};
uint64_t timestamp_{0};
const char *const *names_lut_{
nullptr};
std::vector<uint32_t> raw_counter_buffer_{};
std::vector<unsigned int> core_index_remap_{};
int fd_{-1};
int hwc_fd_{-1};
GpuMeasurements measurements_{};
void init();
void sample_counters();
void wait_next_event();
const uint32_t *get_counters(mali_userspace::MaliCounterBlockName block, int index = 0) const;
uint64_t get_counter_value(mali_userspace::MaliCounterBlockName block, const char *name) const;
int find_counter_index_by_name(mali_userspace::MaliCounterBlockName block, const char *name) const;
};
} // namespace hwcpipe
@@ -0,0 +1,175 @@
/*
* Copyright (c) 2017-2019 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "pmu_counter.h"
#include <asm/unistd.h>
#include <cstring>
#include <stdexcept>
#include <sys/ioctl.h>
/* Add std_to_string implementation as it is possible that Android does not provide it */
#include <string>
#include <sstream>
template <typename T>
std::string std_to_string(T value)
{
std::ostringstream os ;
os << value ;
return os.str() ;
}
PmuCounter::PmuCounter() :
_perf_config()
{
_perf_config.type = PERF_TYPE_HARDWARE;
_perf_config.size = sizeof(perf_event_attr);
// Start disabled
_perf_config.disabled = 1;
// The inherit bit specifies that this counter should count events of child
// tasks as well as the task specified
_perf_config.inherit = 1;
// Enables saving of event counts on context switch for inherited tasks
_perf_config.inherit_stat = 1;
}
PmuCounter::PmuCounter(uint64_t config) :
PmuCounter()
{
open(config);
}
PmuCounter::~PmuCounter()
{
close();
}
void PmuCounter::open(uint64_t config)
{
_perf_config.config = config;
open(_perf_config);
}
void PmuCounter::open(const perf_event_attr &perf_config)
{
// Measure this process/thread (+ children) on any CPU
_fd = syscall(__NR_perf_event_open, &perf_config, 0, -1, -1, 0);
if (_fd < 0)
{
throw std::runtime_error("perf_event_open failed. Counter ID: " + config_to_str(_perf_config));
}
const int result = ioctl(_fd, PERF_EVENT_IOC_ENABLE, 0);
if (result == -1)
{
throw std::runtime_error("Failed to enable PMU counter: " + std::string(strerror(errno)));
}
}
void PmuCounter::close()
{
if (_fd != -1)
{
::close(_fd);
_fd = -1;
}
}
bool PmuCounter::reset()
{
const int result = ioctl(_fd, PERF_EVENT_IOC_RESET, 0);
if (result == -1)
{
throw std::runtime_error("Failed to reset PMU counter: " + std::string(std::strerror(errno)));
}
return result != -1;
}
std::string PmuCounter::config_to_str(const perf_event_attr &perf_config)
{
switch (perf_config.type)
{
case PERF_TYPE_HARDWARE:
switch (perf_config.config)
{
case PERF_COUNT_HW_CPU_CYCLES:
return "PERF_COUNT_HW_CPU_CYCLES";
case PERF_COUNT_HW_INSTRUCTIONS:
return "PERF_COUNT_HW_INSTRUCTIONS";
case PERF_COUNT_HW_CACHE_REFERENCES:
return "PERF_COUNT_HW_CACHE_REFERENCES";
case PERF_COUNT_HW_CACHE_MISSES:
return "PERF_COUNT_HW_CACHE_MISSES";
case PERF_COUNT_HW_BRANCH_INSTRUCTIONS:
return "PERF_COUNT_HW_BRANCH_INSTRUCTIONS";
case PERF_COUNT_HW_BRANCH_MISSES:
return "PERF_COUNT_HW_BRANCH_MISSES";
case PERF_COUNT_HW_BUS_CYCLES:
return "PERF_COUNT_HW_BUS_CYCLES";
case PERF_COUNT_HW_STALLED_CYCLES_FRONTEND:
return "PERF_COUNT_HW_STALLED_CYCLES_FRONTEND";
case PERF_COUNT_HW_STALLED_CYCLES_BACKEND:
return "PERF_COUNT_HW_STALLED_CYCLES_BACKEND";
case PERF_COUNT_HW_REF_CPU_CYCLES:
return "PERF_COUNT_HW_REF_CPU_CYCLES";
default:
return "UNKNOWN HARDWARE COUNTER";
}
case PERF_TYPE_SOFTWARE:
switch (perf_config.config)
{
case PERF_COUNT_SW_CPU_CLOCK:
return "PERF_COUNT_SW_CPU_CLOCK";
case PERF_COUNT_SW_TASK_CLOCK:
return "PERF_COUNT_SW_TASK_CLOCK";
case PERF_COUNT_SW_PAGE_FAULTS:
return "PERF_COUNT_SW_PAGE_FAULTS";
case PERF_COUNT_SW_CONTEXT_SWITCHES:
return "PERF_COUNT_SW_CONTEXT_SWITCHES";
case PERF_COUNT_SW_CPU_MIGRATIONS:
return "PERF_COUNT_SW_CPU_MIGRATIONS";
case PERF_COUNT_SW_PAGE_FAULTS_MIN:
return "PERF_COUNT_SW_PAGE_FAULTS_MIN";
case PERF_COUNT_SW_PAGE_FAULTS_MAJ:
return "PERF_COUNT_SW_PAGE_FAULTS_MAJ";
case PERF_COUNT_SW_ALIGNMENT_FAULTS:
return "PERF_COUNT_SW_ALIGNMENT_FAULTS";
case PERF_COUNT_SW_EMULATION_FAULTS:
return "PERF_COUNT_SW_EMULATION_FAULTS";
case PERF_COUNT_SW_DUMMY:
return "PERF_COUNT_SW_DUMMY";
default:
return "UNKNOWN SOFTWARE COUNTER";
}
default:
return std_to_string(perf_config.config);
}
}
@@ -0,0 +1,103 @@
/*
* Copyright (c) 2017-2019 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <cstdint>
#include <cstring>
#include <errno.h>
#include <linux/perf_event.h>
#include <stdexcept>
#include <string>
#include <sys/syscall.h>
#include <unistd.h>
#include "hwcpipe_log.h"
/** Class provides access to CPU hardware counters. */
class PmuCounter
{
public:
/** Default constructor. */
PmuCounter();
/** Create PMU counter with specified config.
*
* This constructor automatically calls @ref open with the default
* configuration.
*
* @param[in] config Counter identifier.
*/
PmuCounter(uint64_t config);
/** Default destructor. */
~PmuCounter();
/** Get the counter value.
*
* @return Counter value casted to the specified type. */
template <typename T>
T get_value() const;
/** Open the specified counter based on the default configuration.
*
* @param[in] config The default configuration.
*/
void open(uint64_t config);
/** Open the specified configuration.
*
* @param[in] perf_config The specified configuration.
*/
void open(const perf_event_attr &perf_config);
/** Close the currently open counter. */
void close();
/** Reset counter.
*
* @return false if reset fails. */
bool reset();
/** Print counter config ID. */
std::string config_to_str(const perf_event_attr &perf_config);
private:
perf_event_attr _perf_config;
long _fd{-1};
};
template <typename T>
T PmuCounter::get_value() const
{
long long value{};
const ssize_t result = read(_fd, &value, sizeof(long long));
if (result == -1)
{
throw std::runtime_error("Can't get PMU counter value: " + std::string(std::strerror(errno)));
}
return static_cast<T>(value);
}
@@ -0,0 +1,117 @@
/*
* Copyright (c) 2017-2019 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "pmu_profiler.h"
#include "hwcpipe_log.h"
namespace hwcpipe
{
const std::unordered_map<CpuCounter, uint64_t, CpuCounterHash> pmu_mappings{
{CpuCounter::Cycles, PERF_COUNT_HW_CPU_CYCLES},
{CpuCounter::Instructions, PERF_COUNT_HW_INSTRUCTIONS},
{CpuCounter::CacheReferences, PERF_COUNT_HW_CACHE_REFERENCES},
{CpuCounter::CacheMisses, PERF_COUNT_HW_CACHE_MISSES},
{CpuCounter::BranchInstructions, PERF_COUNT_HW_BRANCH_INSTRUCTIONS},
{CpuCounter::BranchMisses, PERF_COUNT_HW_BRANCH_MISSES},
};
PmuProfiler::PmuProfiler(const CpuCounterSet &enabled_counters) :
enabled_counters_(enabled_counters)
{
// Set up PMU counters
for (const auto &counter : enabled_counters)
{
const auto &pmu_config = pmu_mappings.find(counter);
if (pmu_config != pmu_mappings.end())
{
try
{
// Create a PMU counter with the specified configuration
auto pmu_counter_res = pmu_counters_.emplace(counter, pmu_config->second);
// Try reading a value from the counter to check that it opened correctly
auto &pmu_counter = pmu_counter_res.first->second;
pmu_counter.get_value<long long>();
// PMU counter is created and can retrieve values
available_counters_.insert(counter);
}
catch (const std::runtime_error &e)
{
// PMU counter initialization failed
HWCPIPE_LOG("%s", e.what());
}
}
}
if (available_counters_.size() == 0)
{
throw std::runtime_error("PMU counters not available.");
}
}
void PmuProfiler::run()
{
for (auto &pmu_counter : pmu_counters_)
{
pmu_counter.second.reset();
prev_measurements_[pmu_counter.first] = Value{};
}
}
const CpuMeasurements &PmuProfiler::sample()
{
for (const auto &counter : enabled_counters_)
{
const auto &pmu_counter = pmu_counters_.find(counter);
if (pmu_counter == pmu_counters_.end())
{
continue;
}
try
{
auto value = pmu_counter->second.get_value<long long>();
// Resetting the PMU counter every frame seems to alter the data,
// so we make a differential reading.
measurements_[pmu_counter->first] = value - prev_measurements_[pmu_counter->first].get<long long>();
prev_measurements_[pmu_counter->first] = value;
}
catch (const std::runtime_error &e)
{
HWCPIPE_LOG("Failed to get value from PMU: %s.", e.what());
}
}
return measurements_;
}
void PmuProfiler::stop()
{
// We don't need to do anything on stop()
}
} // namespace hwcpipe
@@ -0,0 +1,77 @@
/*
* Copyright (c) 2019 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "cpu_profiler.h"
#include "pmu_counter.h"
namespace hwcpipe
{
/** A CPU profiler that uses PMU counter data. */
class PmuProfiler : public CpuProfiler
{
public:
explicit PmuProfiler(const CpuCounterSet &enabled_counters);
virtual ~PmuProfiler() = default;
virtual const CpuCounterSet &enabled_counters() const override
{
return enabled_counters_;
}
virtual const CpuCounterSet &supported_counters() const override
{
return supported_counters_;
};
virtual void set_enabled_counters(CpuCounterSet counters) override
{
enabled_counters_ = std::move(counters);
};
virtual void run() override;
virtual const CpuMeasurements &sample() override;
virtual void stop() override;
private:
CpuCounterSet enabled_counters_{};
CpuCounterSet available_counters_{};
const CpuCounterSet supported_counters_{
CpuCounter::Cycles,
CpuCounter::Instructions,
CpuCounter::CacheReferences,
CpuCounter::CacheMisses,
CpuCounter::BranchInstructions,
CpuCounter::BranchMisses};
CpuMeasurements measurements_{};
CpuMeasurements prev_measurements_{};
std::unordered_map<CpuCounter, PmuCounter, CpuCounterHash> pmu_counters_{};
};
} // namespace hwcpipe
+3
View File
@@ -677,6 +677,9 @@
<ProjectReference Include="driver\ihv\amd\AMD.vcxproj">
<Project>{5de5a561-548a-4dd7-90f0-06a2b39eae9a}</Project>
</ProjectReference>
<ProjectReference Include="driver\ihv\arm\ARM.vcxproj">
<Project>{F9CCE6CA-0CA3-4A22-9C7B-881369955E62}</Project>
</ProjectReference>
<ProjectReference Include="driver\ihv\intel\Intel.vcxproj">
<Project>{7fcb5fc5-1dbd-4da6-83a0-6ba4e945bda5}</Project>
</ProjectReference>