From 2bd9613e2aa7ec38e0320eb767f2c145e9c9a298 Mon Sep 17 00:00:00 2001 From: BitesPotatoBacks <83843298+BitesPotatoBacks@users.noreply.github.com> Date: Wed, 31 May 2023 13:57:26 -0700 Subject: [PATCH] Fix #8 and #9, refactoring --- iorepdump/Makefile | 6 + iorepdump/iorepdump.m | 82 + socpwrbud.xcodeproj/project.pbxproj | 397 +++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../UserInterfaceState.xcuserstate | Bin 0 -> 28762 bytes socpwrbud/Makefile | 14 + socpwrbud/socpwrbud.h | 44 + socpwrbud/socpwrbud.m | 1371 +++++++++++++++++ 9 files changed, 1929 insertions(+) create mode 100644 iorepdump/Makefile create mode 100644 iorepdump/iorepdump.m create mode 100644 socpwrbud.xcodeproj/project.pbxproj create mode 100644 socpwrbud.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 socpwrbud.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 socpwrbud.xcodeproj/project.xcworkspace/xcuserdata/taevonturner.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 socpwrbud/Makefile create mode 100644 socpwrbud/socpwrbud.h create mode 100644 socpwrbud/socpwrbud.m diff --git a/iorepdump/Makefile b/iorepdump/Makefile new file mode 100644 index 0000000..65bd7c3 --- /dev/null +++ b/iorepdump/Makefile @@ -0,0 +1,6 @@ + +CC=clang +CFLAGS=-fobjc-arc -funroll-loops -arch arm64 -lIOReport -framework Foundation + +iorepdump: + $(CC) $(CFLAGS) -o iorepdump iorepdump.m diff --git a/iorepdump/iorepdump.m b/iorepdump/iorepdump.m new file mode 100644 index 0000000..48a6c07 --- /dev/null +++ b/iorepdump/iorepdump.m @@ -0,0 +1,82 @@ +// +// iorepdump.m +// socpwrbud (iorepdump) +// +// Copyright (c) 2023 dehydratedpotato. +// + +#include "../socpwrbud/socpwrbud.h" + +extern CFMutableDictionaryRef IOReportCopyAllChannels(uint64_t, uint64_t); +extern int IOReportChannelGetFormat(CFDictionaryRef samples); + +enum { + kIOReportInvalidFormat = 0, + kIOReportFormatSimple = 1, + kIOReportFormatState = 2, + kIOReportFormatSimpleArray = 4 +}; + +NSString* getcpu(void) +{ + size_t len = 32; + char* cpubrand = malloc(len); + sysctlbyname("machdep.cpu.brand_string", cpubrand, &len, NULL, 0); + + NSString* ret = [NSString stringWithFormat:@"%s", cpubrand, nil]; + + free(cpubrand); + + return ret; +} + +int main(int argc, char* argv[]) +{ + @autoreleasepool + { + NSString* cpu = getcpu(); + int clusters = 2; + + if (([cpu rangeOfString:@"Pro"].location != NSNotFound) || + ([cpu rangeOfString:@"Max"].location != NSNotFound)) + clusters = 3; + else if ([cpu rangeOfString:@"Ultra"].location != NSNotFound) + clusters = 6; + + CFMutableDictionaryRef subchn = NULL; + CFMutableDictionaryRef chn = IOReportCopyAllChannels(0, 0); + IOReportSubscriptionRef sub = IOReportCreateSubscription(NULL, chn, &subchn, 0, 0); + + IOReportIterate(IOReportCreateSamples(sub, subchn, NULL), ^(IOReportSampleRef sample) { + NSString* group = IOReportChannelGetGroup(sample); + NSString* subgroup = IOReportChannelGetSubGroup(sample); + NSString* chann_name = IOReportChannelGetChannelName(sample); + + if ([group isEqual:@"CPU Stats"] || + [group isEqual:@"GPU Stats"] || + [group isEqual:@"AMC Stats"] || + [group isEqual:@"CLPC Stats"] || + [group isEqual:@"PMP"] || + [group isEqual:@"Energy Model"]) { + + switch (IOReportChannelGetFormat(sample)) { + case kIOReportFormatSimple: + printf("Grp: %s Subgrp: %s Chn: %s Int: %ld\n", [group UTF8String] , [subgroup UTF8String], [chann_name UTF8String], IOReportSimpleGetIntegerValue(sample, 0)); + break; + case kIOReportFormatState: + for (int i = 0; i < IOReportStateGetCount(sample); i++) + printf("Grp: %s Subgrp: %s Chn: %s State: %s Res: %lld\n", [group UTF8String] , [subgroup UTF8String], [chann_name UTF8String], [IOReportStateGetNameForIndex(sample, i) UTF8String], IOReportStateGetResidency(sample, i)); + break; + case kIOReportFormatSimpleArray: + for (int i = 0; i < clusters; i++) + printf("Grp: %s Subgrp: %s Chn: %s Arr: %lld\n", [group UTF8String] , [subgroup UTF8String], [chann_name UTF8String], IOReportArrayGetValueAtIndex(sample, i)); + break; + } + + } + return kIOReportIterOk; + }); + + return 0; + } +} diff --git a/socpwrbud.xcodeproj/project.pbxproj b/socpwrbud.xcodeproj/project.pbxproj new file mode 100644 index 0000000..be2d8e7 --- /dev/null +++ b/socpwrbud.xcodeproj/project.pbxproj @@ -0,0 +1,397 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 3F06F0632A27E9D400AF43E7 /* socpwrbud.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F06F0622A27E9D400AF43E7 /* socpwrbud.m */; }; + 3F06F06A2A27E9EB00AF43E7 /* Makefile in Sources */ = {isa = PBXBuildFile; fileRef = 3F06F0692A27E9EB00AF43E7 /* Makefile */; }; + 3F06F06D2A27E9FE00AF43E7 /* libIOReport.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F06F06C2A27E9FE00AF43E7 /* libIOReport.tbd */; }; + 3F06F0752A27EA0E00AF43E7 /* iorepdump.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F06F0742A27EA0E00AF43E7 /* iorepdump.m */; }; + 3F06F07B2A27EAA300AF43E7 /* Makefile in Sources */ = {isa = PBXBuildFile; fileRef = 3F06F07A2A27EAA300AF43E7 /* Makefile */; }; + 3F06F07C2A27EAE800AF43E7 /* libIOReport.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F06F06C2A27E9FE00AF43E7 /* libIOReport.tbd */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 3F06F05D2A27E9D400AF43E7 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 3F06F0702A27EA0E00AF43E7 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 3F06F05F2A27E9D400AF43E7 /* socpwrbud */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = socpwrbud; sourceTree = BUILT_PRODUCTS_DIR; }; + 3F06F0622A27E9D400AF43E7 /* socpwrbud.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = socpwrbud.m; sourceTree = ""; }; + 3F06F0692A27E9EB00AF43E7 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; + 3F06F06C2A27E9FE00AF43E7 /* libIOReport.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libIOReport.tbd; path = usr/lib/libIOReport.tbd; sourceTree = SDKROOT; }; + 3F06F0722A27EA0E00AF43E7 /* iorepdump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = iorepdump; sourceTree = BUILT_PRODUCTS_DIR; }; + 3F06F0742A27EA0E00AF43E7 /* iorepdump.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = iorepdump.m; sourceTree = ""; }; + 3F06F0792A27EA3100AF43E7 /* socpwrbud.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = socpwrbud.h; sourceTree = ""; }; + 3F06F07A2A27EAA300AF43E7 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 3F06F05C2A27E9D400AF43E7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3F06F06D2A27E9FE00AF43E7 /* libIOReport.tbd in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3F06F06F2A27EA0E00AF43E7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3F06F07C2A27EAE800AF43E7 /* libIOReport.tbd in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 3F06F0562A27E9D300AF43E7 = { + isa = PBXGroup; + children = ( + 3F06F0612A27E9D400AF43E7 /* socpwrbud */, + 3F06F0732A27EA0E00AF43E7 /* iorepdump */, + 3F06F0602A27E9D400AF43E7 /* Products */, + 3F06F06B2A27E9FE00AF43E7 /* Frameworks */, + ); + sourceTree = ""; + }; + 3F06F0602A27E9D400AF43E7 /* Products */ = { + isa = PBXGroup; + children = ( + 3F06F05F2A27E9D400AF43E7 /* socpwrbud */, + 3F06F0722A27EA0E00AF43E7 /* iorepdump */, + ); + name = Products; + sourceTree = ""; + }; + 3F06F0612A27E9D400AF43E7 /* socpwrbud */ = { + isa = PBXGroup; + children = ( + 3F06F0622A27E9D400AF43E7 /* socpwrbud.m */, + 3F06F0792A27EA3100AF43E7 /* socpwrbud.h */, + 3F06F0692A27E9EB00AF43E7 /* Makefile */, + ); + path = socpwrbud; + sourceTree = ""; + }; + 3F06F06B2A27E9FE00AF43E7 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 3F06F06C2A27E9FE00AF43E7 /* libIOReport.tbd */, + ); + name = Frameworks; + sourceTree = ""; + }; + 3F06F0732A27EA0E00AF43E7 /* iorepdump */ = { + isa = PBXGroup; + children = ( + 3F06F0742A27EA0E00AF43E7 /* iorepdump.m */, + 3F06F07A2A27EAA300AF43E7 /* Makefile */, + ); + path = iorepdump; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 3F06F05E2A27E9D400AF43E7 /* socpwrbud */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3F06F0662A27E9D400AF43E7 /* Build configuration list for PBXNativeTarget "socpwrbud" */; + buildPhases = ( + 3F06F05B2A27E9D400AF43E7 /* Sources */, + 3F06F05C2A27E9D400AF43E7 /* Frameworks */, + 3F06F05D2A27E9D400AF43E7 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = socpwrbud; + productName = socpwrbud; + productReference = 3F06F05F2A27E9D400AF43E7 /* socpwrbud */; + productType = "com.apple.product-type.tool"; + }; + 3F06F0712A27EA0E00AF43E7 /* iorepdump */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3F06F0762A27EA0E00AF43E7 /* Build configuration list for PBXNativeTarget "iorepdump" */; + buildPhases = ( + 3F06F06E2A27EA0E00AF43E7 /* Sources */, + 3F06F06F2A27EA0E00AF43E7 /* Frameworks */, + 3F06F0702A27EA0E00AF43E7 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = iorepdump; + productName = iorepdump; + productReference = 3F06F0722A27EA0E00AF43E7 /* iorepdump */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 3F06F0572A27E9D400AF43E7 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1420; + TargetAttributes = { + 3F06F05E2A27E9D400AF43E7 = { + CreatedOnToolsVersion = 14.2; + }; + 3F06F0712A27EA0E00AF43E7 = { + CreatedOnToolsVersion = 14.2; + }; + }; + }; + buildConfigurationList = 3F06F05A2A27E9D400AF43E7 /* Build configuration list for PBXProject "socpwrbud" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 3F06F0562A27E9D300AF43E7; + productRefGroup = 3F06F0602A27E9D400AF43E7 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 3F06F05E2A27E9D400AF43E7 /* socpwrbud */, + 3F06F0712A27EA0E00AF43E7 /* iorepdump */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 3F06F05B2A27E9D400AF43E7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3F06F06A2A27E9EB00AF43E7 /* Makefile in Sources */, + 3F06F0632A27E9D400AF43E7 /* socpwrbud.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3F06F06E2A27EA0E00AF43E7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3F06F07B2A27EAA300AF43E7 /* Makefile in Sources */, + 3F06F0752A27EA0E00AF43E7 /* iorepdump.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 3F06F0642A27E9D400AF43E7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 3F06F0652A27E9D400AF43E7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + }; + name = Release; + }; + 3F06F0672A27E9D400AF43E7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 9BWZQ52AFK; + ENABLE_HARDENED_RUNTIME = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.dehydratedpotato.socpwrbud; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 3F06F0682A27E9D400AF43E7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 9BWZQ52AFK; + ENABLE_HARDENED_RUNTIME = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.dehydratedpotato.socpwrbud; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 3F06F0772A27EA0E00AF43E7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 9BWZQ52AFK; + ENABLE_HARDENED_RUNTIME = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.dehydratedpotato.iorepdump; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 3F06F0782A27EA0E00AF43E7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 9BWZQ52AFK; + ENABLE_HARDENED_RUNTIME = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.dehydratedpotato.iorepdump; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 3F06F05A2A27E9D400AF43E7 /* Build configuration list for PBXProject "socpwrbud" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3F06F0642A27E9D400AF43E7 /* Debug */, + 3F06F0652A27E9D400AF43E7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3F06F0662A27E9D400AF43E7 /* Build configuration list for PBXNativeTarget "socpwrbud" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3F06F0672A27E9D400AF43E7 /* Debug */, + 3F06F0682A27E9D400AF43E7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3F06F0762A27EA0E00AF43E7 /* Build configuration list for PBXNativeTarget "iorepdump" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3F06F0772A27EA0E00AF43E7 /* Debug */, + 3F06F0782A27EA0E00AF43E7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 3F06F0572A27E9D400AF43E7 /* Project object */; +} diff --git a/socpwrbud.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/socpwrbud.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/socpwrbud.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/socpwrbud.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/socpwrbud.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/socpwrbud.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/socpwrbud.xcodeproj/project.xcworkspace/xcuserdata/taevonturner.xcuserdatad/UserInterfaceState.xcuserstate b/socpwrbud.xcodeproj/project.xcworkspace/xcuserdata/taevonturner.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..589f541dafefed2ed6f55d3e119e40a7c19d3a5b GIT binary patch literal 28762 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 +#import + +/* IOReport symbols (linked with libIOReport.dylib) + */ +enum { + kIOReportIterOk, + kIOReportIterFailed, + kIOReportIterSkipped +}; +typedef struct IOReportSubscriptionRef* IOReportSubscriptionRef; +typedef CFDictionaryRef IOReportSampleRef; + +extern IOReportSubscriptionRef IOReportCreateSubscription(void* a, CFMutableDictionaryRef desiredChannels, CFMutableDictionaryRef* subbedChannels, uint64_t channel_id, CFTypeRef b); +extern CFDictionaryRef IOReportCreateSamples(IOReportSubscriptionRef iorsub, CFMutableDictionaryRef subbedChannels, CFTypeRef a); +extern CFDictionaryRef IOReportCreateSamplesDelta(CFDictionaryRef prev, CFDictionaryRef current, CFTypeRef a); + +extern CFMutableDictionaryRef IOReportCopyChannelsInGroup(NSString*, NSString*, uint64_t, uint64_t, uint64_t); + +typedef int (^ioreportiterateblock)(IOReportSampleRef ch); +extern void IOReportIterate(CFDictionaryRef samples, ioreportiterateblock); + +extern int IOReportStateGetCount(CFDictionaryRef); +extern uint64_t IOReportStateGetResidency(CFDictionaryRef, int); +extern uint64_t IOReportArrayGetValueAtIndex(CFDictionaryRef, int); +extern long IOReportSimpleGetIntegerValue(CFDictionaryRef, int); +extern NSString* IOReportChannelGetChannelName(CFDictionaryRef); +extern NSString* IOReportChannelGetSubGroup(CFDictionaryRef); +extern NSString* IOReportStateGetNameForIndex(CFDictionaryRef, int); + +extern void IOReportMergeChannels(CFMutableDictionaryRef, CFMutableDictionaryRef, CFTypeRef); +extern NSString* IOReportChannelGetGroup(CFDictionaryRef); + +#endif /* socpwrbud_h */ diff --git a/socpwrbud/socpwrbud.m b/socpwrbud/socpwrbud.m new file mode 100644 index 0000000..580352f --- /dev/null +++ b/socpwrbud/socpwrbud.m @@ -0,0 +1,1371 @@ +// +// socpwrbud.m +// socpwrbud +// +// Copyright (c) 2023 dehydratedpotato. +// + +#import "socpwrbud.h" +#import +#import + +#define TOOL_VERSION "v0.4.1" + +#define METRIC_ACTIVE "%active" +#define METRIC_IDLE "%idle" +#define METRIC_FREQ "freq" +#define METRIC_DVFS "dvfs" +#define METRIC_DVFSMS "dvfs_ms" +#define METRIC_DVFSVOLTS "dvfs_volts" +#define METRIC_POWER "power" +#define METRIC_VOLTS "volts" +#define METRIC_INSNS "insns" +#define METRIC_CYCLES "cycles" +#define METRIC_CORES "cores" + +#define UNIT_ECPU "ecpu" +#define UNIT_PCPU "pcpu" +#define UNIT_GPU "gpu" + +#define METRIC_COUNT 11 +#define UNITS_COUNT 3 +#define OPT_COUNT 11 + +typedef struct param_set { + const char* name; + const char* description; +} param_set; + +static const struct param_set metrics_set[METRIC_COUNT] = { + { METRIC_ACTIVE, "show active residencies" }, + { METRIC_IDLE, "show idle residencies" }, + { METRIC_FREQ, "show active frequencies" }, + { METRIC_DVFS, "show dvfs state distributions" }, + { METRIC_DVFSVOLTS,"show (milli)volts of dvfs states" }, + { METRIC_DVFSMS, "show time spent in ms of dvfs states" }, + { METRIC_POWER, "show power consumption of units" }, + { METRIC_VOLTS, "show voltage of units" }, + { METRIC_INSNS, "show instructions retired / per-clock metrics of supporting units" }, + { METRIC_CYCLES, "show the supposed cycles spent during sample of supporting units" }, + { METRIC_CORES, "show per-core stats for selected metrics on supporting units" } +}; + +static const struct param_set units_set[UNITS_COUNT] = { + { UNIT_ECPU, "efficiency cluster(s) statistics" }, + { UNIT_PCPU, "performance cluster(s) statistics" }, + { UNIT_GPU, "integrated graphics statistics" }, +}; + +static const struct option long_opts[OPT_COUNT] = { + { "help", no_argument, 0, 'h' }, + { "version", no_argument, 0, 'v' }, + { "interval", required_argument, 0, 'i' }, + { "samples", required_argument, 0, 's' }, + { "output", required_argument, 0, 'o' }, + { "hide-unit", required_argument, 0, 'H' }, + { "metrics", required_argument, 0, 'm' }, + { "all-metrics", no_argument, 0, 'a' }, + { "set-watts", no_argument, 0, 'w' }, + { "set-ghz", no_argument, 0, 'g' }, + { "property-list", no_argument, 0, 'p' }, +}; + +static const char* long_opts_description[OPT_COUNT] = { + " print this message and exit\n", + " print tool version number and exit\n\n", + " perform samples between N ms [default: 175ms]\n", + " collect and display N samples (0=inf) [default: 1]\n", + " set a file for metric stdout\n\n", + " comma separated list of unit statistics to hide\n", + " comma separated list of metrics to report\n", + " report all available metrics for the visible units\n\n", + " set power measurement to watts (default is mW)\n", + " set frequency measurement to GHz (default is MHz)\n", + " output as property list rather than plain text\n\n", +}; + +#define VOLTAGE_STATES_ECPU CFSTR("voltage-states1-sram") +#define VOLTAGE_STATES_PCPU CFSTR("voltage-states5-sram") +#define VOLTAGE_STATES_GPU CFSTR("voltage-states9") + +#define PWR_UNIT_MWATT 1 +#define PWR_UNIT_WATT 1e-3 +#define FREQ_UNIT_MHZ 1 +#define FREQ_UNIT_GHZ 1e-3 + +#define PWR_UNIT_MWATT_LABEL "mW" +#define PWR_UNIT_WATT_LABEL "W" +#define FREQ_UNIT_MHZ_LABEL "MHz" +#define FREQ_UNIT_GHZ_LABEL "GHz" + +static NSArray* pleb_complex_freq_chankeys = @[@"ECPU", @"PCPU", @"GPUPH"]; +static NSArray* pleb_core_freq_chankeys = @[@"ECPU", @"PCPU"]; +static NSArray* pleb_complex_pwr_chankeys = @[@"ECPU", @"PCPU", @"GPU"]; +static NSArray* pleb_core_pwr_chankeys = @[@"ECPU", @"PCPU"]; + +static NSArray* promax_complex_freq_chankeys = @[@"ECPU", @"PCPU", @"PCPU1", @"GPUPH"]; +static NSArray* promax_core_freq_chankeys = @[@"ECPU0", @"PCPU0", @"PCPU1"]; +static NSArray* promax_complex_pwr_chankeys = @[@"EACC_CPU", @"PACC0_CPU", @"PACC1_CPU", @"GPU0"]; +static NSArray* promax_core_pwr_chankeys = @[@"EACC_CPU", @"PACC0_CPU", @"PACC1_CPU"]; + +static NSArray* ultra_complex_freq_chankeys = @[@"DIE_0_ECPU", @"DIE_1_ECPU", @"DIE_0_PCPU", @"DIE_0_PCPU1", @"DIE_1_PCPU", @"DIE_1_PCPU1", @"GPUPH"]; +static NSArray* ultra_core_freq_chankeys = @[@"DIE_0_ECPU_CPU", @"DIE_1_ECPU_CPU", @"DIE_0_PCPU_CPU", @"DIE_0_PCPU1_CPU", @"DIE_1_PCPU_CPU", @"DIE_1_PCPU1_CPU"]; +static NSArray* ultra_complex_pwr_chankeys = @[@"DIE_0_EACC_CPU", @"DIE_1_EACC_CPU", @"DIE_0_PACC0_CPU", @"DIE_0_PACC1_CPU", @"DIE_1_PACC0_CPU", @"DIE_1_PACC1_CPU", @"GPU0_0"]; +static NSArray* ultra_core_pwr_chankeys = @[@"DIE_0_EACC_CPU", @"DIE_1_EACC_CPU", @"DIE_0_PACC0_CPU", @"DIE_0_PACC1_CPU", @"DIE_1_PACC0_CPU", @"DIE_1_PACC1_CPU"]; + +static NSString* ptype_state = @"P"; +static NSString* vtype_state = @"V"; +static NSString* idletype_state = @"IDLE"; +static NSString* downtype_state = @"DOWN"; +static NSString* offtype_state = @"OFF"; + +static const char* power_consumption_string = "\t\tPower Consumption: %.2f %s\n"; +static const char* active_voltage_string = "\n\t\tActive Voltage: %.2f mV\n"; +static const char* active_frequency_string = "\t\tActive Frequency: %g %s\n"; +static const char* active_residency_string = "\t\tActive Residency: %.1f%%\n"; +static const char* idle_residency_string = "\t\tIdle Residency: %.1f%%\n"; +static const char* dvfs_distribution_string = "\t\tDvfs Distribution: "; + +static const char* core_power_consumption_string = "\t\t\tPower Consumption: %.2f %s\n"; +static const char* core_active_voltage_string = "\t\t\tActive Voltage: %.2f mV\n"; +static const char* core_active_frequency_string = "\t\t\tActive Frequency: %g %s\n"; +static const char* core_active_residency_string = "\t\t\tActive Residency: %.1f%%\n"; +static const char* core_idle_residency_string = "\t\t\tIdle Residency: %.1f%%\n"; +static const char* core_dvfs_distribution_string = "\t\t\tDvfs Distribution: "; + +/* data structs + */ +typedef struct pwr_samples_perf_data { + NSMutableArray* cluster_pwr; /* power of each cluster */ + NSMutableArray* core_pwr; /* core_pwr[CLUSTER][CORE]: power of each core */ +} pwr_samples_perf_data; + +typedef struct cpu_samples_perf_data { + NSMutableArray* sums; /* sum of state distribution ticks per-cluster */ + NSMutableArray* distribution; /* distribution[CLUSTER][STATE]: distribution of individual states */ + NSMutableArray* freqs; /* calculated "active" frequency per-cluster */ + NSMutableArray* volts; /* calculated "active" voltage per-cluster */ + NSMutableArray* residency; /* calculated "active" usage/residency per-cluster */ +} cpu_samples_perf_data; + +typedef struct clpc_samples_perf_data { + NSMutableArray* insn_retired; /* (cluster) instructions retired during sample */ + NSMutableArray* insn_perclk; /* (cluster) instructions per clock during sample */ +} clpc_samples_perf_data; +// +//typedef struct core_samples_perf_data { +// NSMutableArray* sums; /* sums[CLUSTER][CORE]: sum of state distribution ticks per-core in cluster */ +// NSMutableArray* distribution; /* distribution[CLUSTER][CORE][STATE]: distribution of individual states */ +// NSMutableArray* freqs; /* freqs[CLUSTER][CORE]: calculated "active" frequency per-core */ +// NSMutableArray* volts; /* volts[CLUSTER][CORE]: calculated "active" voltage per-core */ +// NSMutableArray* residency; /* residency[CLUSTER][CORE]: calculated "active" usage/residency per-core */ +//} core_samples_perf_data; + +typedef struct unit_data { + NSString* silicon_code; /* codename, i.e. T6001 (Apple M1 Max) */ + NSString* silicon_name; /* lowercased name, i.e. apple m1 max*/ + NSString* pcpu_microarch; /* such as Firestorm */ + NSString* ecpu_microarch; /* such as Icestorm */ + + NSMutableArray* percluster_ncores; + int gpu_ncores; + + struct { + IOReportSubscriptionRef pwr_sub; + CFMutableDictionaryRef pwr_sub_chann; + CFMutableDictionaryRef energy_chann; + CFMutableDictionaryRef pmp_chann; + + pwr_samples_perf_data perf_data; + + NSArray* complex_channkeys; /* list of key stubs for cluster channels */ + NSArray* core_channkeys; /* list of key stubs for core channels */ + } pwr_samples; + + struct { + IOReportSubscriptionRef cpu_sub; + CFMutableDictionaryRef cpu_sub_chann; + CFMutableDictionaryRef cpu_chann; + CFMutableDictionaryRef gpu_chann; + + cpu_samples_perf_data cluster_perf_data; + cpu_samples_perf_data core_perf_data; + + NSArray* complex_channkeys; /* list of key stubs for cluster channels */ + NSArray* core_channkeys; /* list of key stubs for core channels */ + + NSArray* dvfs_freq_states; /* ...[CLUSTER][STATE]: list of dvfs freq states */ + NSArray* dvfs_voltage_states; /* ...[CLUSTER][STATE]: list of dvfs voltage states */ + } soc_samples; + + struct { + IOReportSubscriptionRef clpc_sub; + CFMutableDictionaryRef clpc_sub_chann; + CFMutableDictionaryRef clpc_chann; + + clpc_samples_perf_data perf_data; + } clpc_samples; +} unit_data; + +typedef struct cmd_data { + float pwr_unit; /* unit of measurement, mW (default) or W */ + const char* pwr_unit_label; + float freq_unit; /* unit of measurement, mhz (default) or ghz */ + const char* freq_unit_label; + + int interval; /* sleep time between samples */ + int samples; /* target samples to make */ + + bool plist; + + FILE* file_out; + + struct { + bool hide_ecpu; + bool hide_pcpu; + bool hide_gpu; + bool show_active; + bool show_idle; + bool show_freq; + bool show_volts; + bool show_percore; + bool show_dvfs; + bool show_dvfs_ms; + bool show_dvfs_volts; + bool show_pwr; + bool show_insns; + bool show_cycles; + } flags; +} cmd_data; + +/* defs + */ +static void error(int, const char*, ...); +static void usage(cmd_data*cmd); + +static void sample(unit_data* unit_data, cmd_data* cmd_data); +static void format(unit_data* unit_data); + +static inline void output_text(unit_data* unit_data, cmd_data* cmd_data, int* current_loop); +static inline void output_plist(unit_data* unit_data, cmd_data* cmd_data, int* current_loop); + +static inline void init_cmd_data(cmd_data* data); +static inline void init_unit_data(unit_data* data); + +int main(int argc, char * argv[]) { + cmd_data* cmd = malloc(sizeof(cmd_data)); + unit_data* unit = malloc(sizeof(unit_data)); + + init_cmd_data(cmd); + + NSString* active_metrics_str = nil; + NSArray* active_metrics_list = nil; + NSString* hide_units_str = nil; + NSArray* hide_units_list = nil; + + int opt = 0; + int optindex = 0; + + while((opt = getopt_long(argc, argv, "hvi:s:po:m:H:wga", long_opts, &optindex)) != -1) { + switch(opt) { + case '?': + case 'h': + usage(cmd); + case 'v': + printf("%s %s (build %s %s)\n", getprogname(), TOOL_VERSION, __DATE__, __TIME__); + return 0; + case 'i': + cmd->interval = atoi(optarg); + if (cmd->interval < 1) cmd->interval = 1; + break; + case 's': + cmd->samples = atoi(optarg); + if (cmd->samples <= 0) cmd->samples = -1; + break; + case 'm': + active_metrics_str = [NSString stringWithFormat:@"%s", strdup(optarg)]; + active_metrics_list = [active_metrics_str componentsSeparatedByString:@","]; + active_metrics_str = nil; + + memset(&cmd->flags, 0, sizeof(cmd->flags)); + + for (int i = 0; i < [active_metrics_list count]; i++) { + NSString* string = [active_metrics_list[i] lowercaseString]; + + // ew, chained ifs + if ([string isEqual:[NSString stringWithUTF8String:METRIC_ACTIVE]]) + cmd->flags.show_active = true; + else if ([string isEqual:[NSString stringWithUTF8String:METRIC_IDLE]]) + cmd->flags.show_idle = true; + else if ([string isEqual:[NSString stringWithUTF8String:METRIC_FREQ]]) + cmd->flags.show_freq = true; + else if ([string isEqual:[NSString stringWithUTF8String:METRIC_VOLTS]]) + cmd->flags.show_volts = true; + else if ([string isEqual:[NSString stringWithUTF8String:METRIC_CORES]]) + cmd->flags.show_percore = true; + else if ([string isEqual:[NSString stringWithUTF8String:METRIC_DVFS]]) + cmd->flags.show_dvfs = true; + else if ([string isEqual:[NSString stringWithUTF8String:METRIC_DVFSMS]]) + cmd->flags.show_dvfs_ms = true; + else if ([string isEqual:[NSString stringWithUTF8String:METRIC_DVFSVOLTS]]) + cmd->flags.show_dvfs_volts = true; + else if ([string isEqual:[NSString stringWithUTF8String:METRIC_POWER]]) + cmd->flags.show_pwr = true; + else if ([string isEqual:[NSString stringWithUTF8String:METRIC_INSNS]]) + cmd->flags.show_insns = true; + else if ([string isEqual:[NSString stringWithUTF8String:METRIC_CYCLES]]) + cmd->flags.show_cycles = true; + else + error(1, "Incorrect metric option \"%s\" in list", [string UTF8String]); + } + + active_metrics_list = nil; + break; + case 'H': + hide_units_str = [NSString stringWithFormat:@"%s", strdup(optarg)]; + hide_units_list = [hide_units_str componentsSeparatedByString:@","]; + hide_units_str = nil; + + for (int i = 0; i < [hide_units_list count]; i++) { + NSString* string = [hide_units_list[i] lowercaseString]; + + if ([string isEqual:@"ecpu"]) { + cmd->flags.hide_ecpu = true; + } else if ([string isEqual:@"pcpu"]){ + cmd->flags.hide_pcpu = true; + } else if ([string isEqual:@"gpu"]) { + cmd->flags.hide_gpu = true; + } else + error(1, "Incorrect unit option \"%s\" in list", [string UTF8String]); + } + + hide_units_list = nil; + break; + case 'w': + cmd->pwr_unit = PWR_UNIT_WATT; + cmd->pwr_unit_label = PWR_UNIT_WATT_LABEL; + break; + case 'g': + cmd->freq_unit = FREQ_UNIT_GHZ; + cmd->freq_unit_label = FREQ_UNIT_GHZ_LABEL; + break; + case 'p': + cmd->plist = true; + break; + case 'o': + { + char* name = strdup(optarg); + cmd->file_out = fopen(strdup(optarg), "w"); + + if (cmd->file_out == NULL) error(1, "Bad file provided: \"%s\"", name); + } + break; + case 'a': + cmd->flags.show_active = true; + cmd->flags.show_idle = true; + cmd->flags.show_freq = true; + cmd->flags.show_volts = true; + cmd->flags.show_dvfs = true; + cmd->flags.show_dvfs_volts = true; + cmd->flags.show_dvfs_ms = true; + cmd->flags.show_percore = true; + cmd->flags.show_pwr = true; + cmd->flags.show_insns = true; + cmd->flags.show_cycles = true; + break; + } + } + + init_unit_data(unit); /* do this after so no slowdowns when printing version or help */ + + int current_loop = 0; + + for(; cmd->samples--;) { + sample(unit, cmd); + format(unit); + + if (cmd->plist == false) { + output_text(unit, cmd, ¤t_loop); + } else { + fprintf(cmd->file_out, "\n\n\n"); + + fprintf(cmd->file_out, "\n"); + output_plist(unit, cmd, ¤t_loop); + fprintf(cmd->file_out, "\n\n"); + } + + for (int i = 0; i < [unit->percluster_ncores count] + 1; i++) { + + /* cluster */ + [unit->soc_samples.cluster_perf_data.distribution[i] removeAllObjects]; + unit->soc_samples.cluster_perf_data.freqs[i] = @0; + unit->soc_samples.cluster_perf_data.volts[i] = @0; + unit->soc_samples.cluster_perf_data.residency[i] = @0; + unit->soc_samples.cluster_perf_data.sums[i] = @0; + unit->pwr_samples.perf_data.cluster_pwr[i] = @0; + + /* clpc */ + unit->clpc_samples.perf_data.insn_perclk[i] = @0; + unit->clpc_samples.perf_data.insn_retired[i] = @0; + + /* core */ + if (i < [unit->percluster_ncores count]) { + + for (int ii = 0; ii < [unit->percluster_ncores[i] intValue]; ii++) { + [unit->soc_samples.core_perf_data.distribution[i][ii] removeAllObjects]; + unit->soc_samples.core_perf_data.sums[i][ii] = @0; + unit->soc_samples.core_perf_data.freqs[i][ii] = @0; + unit->soc_samples.core_perf_data.volts[i][ii] = @0; + unit->soc_samples.core_perf_data.residency[i][ii] = @0; + unit->pwr_samples.perf_data.core_pwr[i][ii] = @0; + } + } + } + + current_loop++; + } + return 0; +} + +static void error(int exitcode, const char* format, ...) { + va_list args; + fprintf(stderr, "\e[1m%s:\033[0;31m error:\033[0m\e[0m ", getprogname()); + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); + exit(exitcode); +} + +static void usage(cmd_data* cmd_data) { + fprintf(cmd_data->file_out, "\e[1m\nUsage: %s [-wgap] [-i interval] [-s samples]\n\n\e[0m", getprogname()); + fprintf(cmd_data->file_out, " A sudoless tool to profile your Apple M-Series CPU+GPU active core\n and cluster frequencies, residencies, power consumption, and other metrics.\n Inspired by Powermetrics. Thrown together by dehydratedpotato.\n\n\e[1mThe following command-line options are supported:\e[0m\n\n"); + + for (int i = 0; i < OPT_COUNT; i++) { + fprintf(cmd_data->file_out, " -%c, --%s%s", long_opts[i].val, long_opts[i].name, long_opts_description[i]); + } + + fprintf(cmd_data->file_out, "\e[1mThe following are metrics supported by --metrics:\e[0m\n\n"); + + for (int i = 0; i < METRIC_COUNT; i++) { + fprintf(cmd_data->file_out, " %-15s%s\n", metrics_set[i].name, metrics_set[i].description); + } + + fprintf(cmd_data->file_out, "\n default: %s,%s,%s,%s,%s\n\n\e[1mThe following are units supported by --hide-units:\e[0m\n\n", METRIC_ACTIVE, METRIC_FREQ, METRIC_DVFS, METRIC_INSNS, METRIC_CORES); + + for (int i = 0; i < UNITS_COUNT; i++) { + fprintf(cmd_data->file_out, " %-15s%s\n", units_set[i].name, units_set[i].description); + } + + exit(0); +} + +static void sample(unit_data* unit_data, cmd_data* cmd_data) { + + CFDictionaryRef cpusamp_a = IOReportCreateSamples(unit_data->soc_samples.cpu_sub, unit_data->soc_samples.cpu_sub_chann, NULL); + CFDictionaryRef pwrsamp_a = IOReportCreateSamples(unit_data->pwr_samples.pwr_sub, unit_data->pwr_samples.pwr_sub_chann, NULL); + CFDictionaryRef clpcsamp_a = IOReportCreateSamples(unit_data->clpc_samples.clpc_sub, unit_data->clpc_samples.clpc_sub_chann, NULL); + + [NSThread sleepForTimeInterval: (cmd_data->interval * 1e-3)]; + + CFDictionaryRef cpusamp_b = IOReportCreateSamples(unit_data->soc_samples.cpu_sub, unit_data->soc_samples.cpu_sub_chann, NULL); + CFDictionaryRef pwrsamp_b = IOReportCreateSamples(unit_data->pwr_samples.pwr_sub, unit_data->pwr_samples.pwr_sub_chann, NULL); + CFDictionaryRef clpcsamp_b = IOReportCreateSamples(unit_data->clpc_samples.clpc_sub, unit_data->clpc_samples.clpc_sub_chann, NULL); + + // deltas + CFDictionaryRef cpu_delta = IOReportCreateSamplesDelta(cpusamp_a, cpusamp_b, NULL); + CFDictionaryRef pwr_delta = IOReportCreateSamplesDelta(pwrsamp_a, pwrsamp_b, NULL); + CFDictionaryRef clpc_delta = IOReportCreateSamplesDelta(clpcsamp_a, clpcsamp_b, NULL); + + CFRelease(cpusamp_a); + CFRelease(cpusamp_b); + CFRelease(pwrsamp_a); + CFRelease(pwrsamp_b); + CFRelease(clpcsamp_a); + CFRelease(clpcsamp_b); + + IOReportIterate(cpu_delta, ^int(IOReportSampleRef sample) { + for (int i = 0; i < IOReportStateGetCount(sample); i++) { + NSString* subgroup = IOReportChannelGetSubGroup(sample); + NSString* idx_name = IOReportStateGetNameForIndex(sample, i); + NSString* chann_name = IOReportChannelGetChannelName(sample); + uint64_t residency = IOReportStateGetResidency(sample, i); + + for (int ii = 0; ii < [unit_data->percluster_ncores count] + 1; ii++) { + if ([subgroup isEqual: @"CPU Complex Performance States"] || [subgroup isEqual: @"GPU Performance States"]) { + + if (![chann_name isEqual:unit_data->soc_samples.complex_channkeys[ii]]) continue; + + /* active residency*/ + if ([idx_name rangeOfString:ptype_state].location != NSNotFound || + [idx_name rangeOfString:vtype_state].location != NSNotFound) + { + NSNumber* sum = [[NSNumber alloc] initWithUnsignedLongLong:([unit_data->soc_samples.cluster_perf_data.sums[ii] unsignedLongLongValue] + residency)]; + NSNumber* distribution = [[NSNumber alloc] initWithUnsignedLongLong:residency]; + + unit_data->soc_samples.cluster_perf_data.sums[ii] = sum; + [unit_data->soc_samples.cluster_perf_data.distribution[ii] addObject:distribution]; + + sum = nil; + distribution = nil; + + /* idle residency */ + } else if ([idx_name rangeOfString:idletype_state].location != NSNotFound || + [idx_name rangeOfString:offtype_state].location != NSNotFound) + { + NSNumber* res = [[NSNumber alloc] initWithUnsignedLongLong:residency]; + + unit_data->soc_samples.cluster_perf_data.residency[ii] = res; + + res = nil; + } + } + else if ([subgroup isEqual:@"CPU Core Performance States"] && ii < [unit_data->percluster_ncores count]) { + for (int iii = 0; iii < [unit_data->percluster_ncores[ii] intValue]; iii++) { + @autoreleasepool { + NSString* key = [NSString stringWithFormat:@"%@%d",unit_data->soc_samples.core_channkeys[ii], iii, nil]; + + if (![chann_name isEqual:key]) { + key = nil; + continue; + } + + /* active residency */ + if ([idx_name rangeOfString:ptype_state].location != NSNotFound || + [idx_name rangeOfString:vtype_state].location != NSNotFound) + { + NSNumber* sum = [[NSNumber alloc] initWithUnsignedLongLong:([unit_data->soc_samples.core_perf_data.sums[ii][iii] unsignedLongLongValue] + residency)]; + NSNumber* distribution = [[NSNumber alloc] initWithUnsignedLongLong:residency]; + + unit_data->soc_samples.core_perf_data.sums[ii][iii] = sum; + [unit_data->soc_samples.core_perf_data.distribution[ii][iii] addObject: distribution]; + + sum = nil; + distribution = nil; + + /* idle residency */ + } else if ([idx_name rangeOfString:idletype_state].location != NSNotFound || + [idx_name rangeOfString:offtype_state].location != NSNotFound) + { + NSNumber* res = [[NSNumber alloc] initWithUnsignedLongLong:residency]; + + unit_data->soc_samples.core_perf_data.residency[ii][iii] = res; + + res = nil; + } + + key = nil; + } + } + } + } + + chann_name = nil; + subgroup = nil; + idx_name = nil; + } + + return kIOReportIterOk; + }); + + IOReportIterate(pwr_delta, ^int(IOReportSampleRef sample) { + NSString* chann_name = IOReportChannelGetChannelName(sample); + NSString* group = IOReportChannelGetGroup(sample); + long value = IOReportSimpleGetIntegerValue(sample, 0); + + if ([group isEqual:@"Energy Model"]) { + for (int ii = 0; ii < [unit_data->percluster_ncores count] + 1; ii++) { + if ([chann_name isEqual:unit_data->pwr_samples.complex_channkeys[ii]]) { + NSNumber* pwr = [[NSNumber alloc] initWithFloat:(float)value / (cmd_data->interval * 1e-3)]; + + unit_data->pwr_samples.perf_data.cluster_pwr[ii] = pwr; + pwr = nil; + } + + if (ii < [unit_data->percluster_ncores count]) { + for (int iii = 0; iii < [unit_data->percluster_ncores[ii] intValue]; iii++) { + @autoreleasepool { + NSString* key = [[NSString alloc] initWithFormat:@"%@%d",unit_data->pwr_samples.core_channkeys[ii], iii, nil]; + + if ([chann_name isEqual:key]) { + NSNumber* pwr = [[NSNumber alloc] initWithFloat:(float)value / (cmd_data->interval * 1e-3)]; + + unit_data->pwr_samples.perf_data.core_pwr[ii][iii] = pwr; + + pwr = nil; + } + + key = nil; + } + } + } + } + /* the gpu entry doen't seem to exist on pleb silicon so we have to read from PMP */ + } else if ([group isEqual:@"PMP"] && [chann_name isEqual:@"GPU"] && + [unit_data->silicon_name length] == 8 /* 8 == length of "Apple Mx" */) + { + NSNumber* pwr = [[NSNumber alloc] initWithFloat:(float)value / (cmd_data->interval * 1e-3)]; + + unit_data->pwr_samples.perf_data.cluster_pwr[2] = pwr; /* constant 2 is always true when this logic executes */ + pwr = nil; + } + + chann_name = nil; + group = nil; + + return kIOReportIterOk; + }); + + IOReportIterate(clpc_delta, ^int(IOReportSampleRef sample) { + NSString* chann_name = IOReportChannelGetChannelName(sample); + + for (int i = 0; i < [unit_data->percluster_ncores count]; i++) { + long value = IOReportArrayGetValueAtIndex(sample, i); + + NSNumber* insn = [[NSNumber alloc] initWithLong:value]; + + if ([chann_name isEqual:@"CPU cycles, by cluster"]) + unit_data->clpc_samples.perf_data.insn_perclk[i] = insn; + if ([chann_name isEqual:@"CPU instructions, by cluster"]) + unit_data->clpc_samples.perf_data.insn_retired[i] = insn; + + insn = nil; + } + + chann_name = nil; + + return kIOReportIterOk; + }); + + CFRelease(cpu_delta); + CFRelease(pwr_delta); + CFRelease(clpc_delta); +} + +static void format(unit_data* unit_data) { + uint64_t res = 0; + uint64_t core_res = 0; + + for (int i = 0; i < [unit_data->percluster_ncores count] + 1; i++) { + /* formatting cpu freqs */ + for (int ii = 0; ii < [unit_data->soc_samples.cluster_perf_data.distribution[i] count]; ii++) { + @autoreleasepool { + res = [unit_data->soc_samples.cluster_perf_data.distribution[i][ii] unsignedLongLongValue]; + + if (res != 0) { + float perc = (res / [unit_data->soc_samples.cluster_perf_data.sums[i] floatValue]);\ + + NSNumber* distribution = [[NSNumber alloc] initWithFloat: perc]; + NSNumber* freq = [[NSNumber alloc] initWithFloat: ([unit_data->soc_samples.cluster_perf_data.freqs[i] floatValue] + ([unit_data->soc_samples.dvfs_freq_states[i][ii] floatValue]*perc))]; + NSNumber* volt = [[NSNumber alloc] initWithFloat: ([unit_data->soc_samples.cluster_perf_data.volts[i] floatValue] + ([unit_data->soc_samples.dvfs_voltage_states[i][ii] floatValue]*perc))]; + + unit_data->soc_samples.cluster_perf_data.freqs[i] = freq; + unit_data->soc_samples.cluster_perf_data.volts[i] = volt; + [unit_data->soc_samples.cluster_perf_data.distribution[i] replaceObjectAtIndex:ii withObject:distribution]; + + distribution = nil; + freq = nil; + volt = nil; + } + + if (i < [unit_data->percluster_ncores count]) { + for (int iii = 0; iii < [unit_data->percluster_ncores[i] intValue]; iii++) { + + /* sometimes a state is missing for cores on M2 so break when the index is too big */ + if (ii > [unit_data->soc_samples.core_perf_data.distribution[i][iii] count] - 1) break; + + core_res = [unit_data->soc_samples.core_perf_data.distribution[i][iii][ii] unsignedLongLongValue]; + + if (core_res != 0) { + float core_perc = (core_res / [unit_data->soc_samples.core_perf_data.sums[i][iii] floatValue]); + + NSNumber* distribution = [[NSNumber alloc] initWithFloat:core_perc]; + NSNumber* freq = [[NSNumber alloc] initWithFloat: ([unit_data->soc_samples.core_perf_data.freqs[i][iii] floatValue] + + ([unit_data->soc_samples.dvfs_freq_states[i][ii] floatValue] * core_perc))]; + NSNumber* volt = [[NSNumber alloc] initWithFloat: ([unit_data->soc_samples.core_perf_data.volts[i][iii] floatValue] + + ([unit_data->soc_samples.dvfs_voltage_states[i][ii] floatValue] * core_perc))]; + + unit_data->soc_samples.core_perf_data.freqs[i][iii] = freq; + unit_data->soc_samples.core_perf_data.volts[i][iii] = volt; + [unit_data->soc_samples.core_perf_data.distribution[i][iii] replaceObjectAtIndex:ii withObject:distribution]; + + distribution = nil; + freq = nil; + volt = nil; + } + } + } + } + } + + /* formatting idle residency */ + float res = [unit_data->soc_samples.cluster_perf_data.residency[i] floatValue]; + + if (res != 0) { + unit_data->soc_samples.cluster_perf_data.residency[i] = [[NSNumber alloc] initWithFloat:((res / ([unit_data->soc_samples.cluster_perf_data.sums[i] floatValue] + res)) * 100)]; + } + + if (i < [unit_data->percluster_ncores count]) { + for (int iii = 0; iii < [unit_data->percluster_ncores[i] intValue]; iii++) { + float core_res = [unit_data->soc_samples.core_perf_data.residency[i][iii] floatValue]; + + if (core_res != 0) { + NSNumber* a = [[NSNumber alloc] initWithFloat:(core_res / ([unit_data->soc_samples.core_perf_data.sums[i][iii] floatValue] + core_res)) * 100]; + unit_data->soc_samples.core_perf_data.residency[i][iii] = a; + + a = nil; + } + } + } + } +} + +static inline void output_text(unit_data* unit_data, cmd_data* cmd_data, int* current_loop) { + int current_core = 0; + + cpu_samples_perf_data* core_perf_data = &unit_data->soc_samples.core_perf_data; + cpu_samples_perf_data* cluster_perf_data = &unit_data->soc_samples.cluster_perf_data; + pwr_samples_perf_data* pwr_perf_data = &unit_data->pwr_samples.perf_data; + + fprintf(cmd_data->file_out, "%s %s (Sample %d):\n\n", [[unit_data->silicon_name capitalizedString] UTF8String], [unit_data->silicon_code UTF8String], *current_loop + 1); + + for (int i = 0; i < [unit_data->percluster_ncores count] + 1; i++) { + if (([unit_data->soc_samples.complex_channkeys[i] rangeOfString:@"ECPU"].location != NSNotFound && cmd_data->flags.hide_ecpu) || + ([unit_data->soc_samples.complex_channkeys[i] rangeOfString:@"PCPU"].location != NSNotFound && cmd_data->flags.hide_pcpu) || + ([unit_data->soc_samples.complex_channkeys[i] rangeOfString:@"GPU"].location != NSNotFound && cmd_data->flags.hide_gpu)) continue; /* continue when hidden */ + + char* microarch = "Unknown"; + + /* opening metrics + */ + if ([unit_data->soc_samples.complex_channkeys[i] rangeOfString:@"ECPU"].location != NSNotFound) + microarch = (char *) [unit_data->ecpu_microarch UTF8String]; + else if ([unit_data->soc_samples.complex_channkeys[i] rangeOfString:@"PCPU"].location != NSNotFound) + microarch = (char *) [unit_data->pcpu_microarch UTF8String]; + + if ([unit_data->soc_samples.complex_channkeys[i] rangeOfString:@"CPU"].location != NSNotFound) { + fprintf(cmd_data->file_out, "\t%d-Core %s %s:\n\n", [unit_data->percluster_ncores[i] intValue], microarch, [unit_data->soc_samples.complex_channkeys[i] UTF8String]); + + if (cmd_data->flags.show_cycles) fprintf(cmd_data->file_out, "\t\tSupposed Cycles Spent: %ld\n", [unit_data->clpc_samples.perf_data.insn_perclk[i] longValue]); + + /* instructions metrics are only available on CPU clusters */ + if (cmd_data->flags.show_insns) { + float retired = [unit_data->clpc_samples.perf_data.insn_retired[i] floatValue]; + + if (retired > 0) { + fprintf(cmd_data->file_out, "\t\tInstructions Retired: %.5e\n", retired); + fprintf(cmd_data->file_out, "\t\tInstructions Per-Clock: %.5f\n\n", retired / [unit_data->clpc_samples.perf_data.insn_perclk[i] floatValue]); + } else { + fprintf(cmd_data->file_out, "\t\tInstructions Retired: 0\n"); + fprintf(cmd_data->file_out, "\t\tInstructions Per-Clock: 0\n\n"); + } + } + } else + fprintf(cmd_data->file_out, "\t%d-Core Integrated Graphics:\n\n", unit_data->gpu_ncores); + + /* clusters + */ + if (cmd_data->flags.show_pwr) fprintf(cmd_data->file_out, power_consumption_string, (float)([pwr_perf_data->cluster_pwr[i] floatValue] * cmd_data->pwr_unit), cmd_data->pwr_unit_label); + if (cmd_data->flags.show_volts) fprintf(cmd_data->file_out, active_voltage_string, [cluster_perf_data->volts[i] floatValue]); + if (cmd_data->flags.show_freq) fprintf(cmd_data->file_out, active_frequency_string, (float)(fabs([cluster_perf_data->freqs[i] floatValue] * cmd_data->freq_unit)), cmd_data->freq_unit_label); + if (cmd_data->flags.show_idle) fprintf(cmd_data->file_out, "\n"); + if (cmd_data->flags.show_active)fprintf(cmd_data->file_out, active_residency_string, fabs(100-[cluster_perf_data->residency[i] floatValue])); + if (cmd_data->flags.show_idle) fprintf(cmd_data->file_out, idle_residency_string, fabs([cluster_perf_data->residency[i] floatValue])); + + /* cluster dvfs + */ + if (cmd_data->flags.show_dvfs) { + if ([cluster_perf_data->freqs[i] floatValue] > 0) { + fprintf(cmd_data->file_out, "%s", dvfs_distribution_string); + + for (int iii = 0; iii < [unit_data->soc_samples.dvfs_freq_states[i] count]; iii++) { + float res = [cluster_perf_data->distribution[i][iii] floatValue]; + + if (res > 0) { + if (!cmd_data->flags.show_dvfs_volts) { + fprintf(cmd_data->file_out, "%.fMHz: %.2f%%",[unit_data->soc_samples.dvfs_freq_states[i][iii] floatValue], res*100); + } else { + fprintf(cmd_data->file_out, "%.fMHz, %.fmV: %.2f%%",[unit_data->soc_samples.dvfs_freq_states[i][iii] floatValue], [unit_data->soc_samples.dvfs_voltage_states[i][iii] floatValue], res*100); + } + + if (cmd_data->flags.show_dvfs_ms) fprintf(cmd_data->file_out, " (%.fms)", res * cmd_data->interval); + fprintf(cmd_data->file_out, " "); + } + } + fprintf(cmd_data->file_out, "\n"); + } + } + fprintf(cmd_data->file_out, "\n"); + + /* cores + */ + if (i < [unit_data->percluster_ncores count]) { + for (int ii = 0; ii < [unit_data->percluster_ncores[i] intValue]; ii++) { + fprintf(cmd_data->file_out, "\t\tCore %d:\n", current_core); + + if (cmd_data->flags.show_pwr) fprintf(cmd_data->file_out, core_power_consumption_string, (float)([pwr_perf_data->core_pwr[i][ii] floatValue] * cmd_data->pwr_unit), cmd_data->pwr_unit_label); + if (cmd_data->flags.show_volts) fprintf(cmd_data->file_out, core_active_voltage_string, [core_perf_data->volts[i][ii] floatValue]); + if (cmd_data->flags.show_freq) fprintf(cmd_data->file_out, core_active_frequency_string, (float)(fabs([core_perf_data->freqs[i][ii] floatValue] * cmd_data->freq_unit)), cmd_data->freq_unit_label); + if (cmd_data->flags.show_active) fprintf(cmd_data->file_out, core_active_residency_string, (float)(fabs(100-[core_perf_data->residency[i][ii] floatValue]))); + if (cmd_data->flags.show_idle) fprintf(cmd_data->file_out, core_idle_residency_string, (float)(fabs([core_perf_data->residency[i][ii] floatValue]))); + + /* core dvfs + */ + if (cmd_data->flags.show_dvfs) { + if ([core_perf_data->freqs[i][ii] floatValue] > 0) { + fprintf(cmd_data->file_out, "%s", core_dvfs_distribution_string); + + for (int iii = 0; iii < [unit_data->soc_samples.dvfs_freq_states[i] count]; iii++) { + float res = [core_perf_data->distribution[i][ii][iii] floatValue]; + + if (res > 0) { + if (!cmd_data->flags.show_dvfs_volts) { + fprintf(cmd_data->file_out, "%.fMHz: %.2f%%",[unit_data->soc_samples.dvfs_freq_states[i][iii] floatValue], res*100); + } else { + fprintf(cmd_data->file_out, "%.fMHz, %.fmV: %.2f%%",[unit_data->soc_samples.dvfs_freq_states[i][iii] floatValue], [unit_data->soc_samples.dvfs_voltage_states[i][iii] floatValue], res*100); + } + if (cmd_data->flags.show_dvfs_ms) fprintf(cmd_data->file_out, " (%.fms)", res * cmd_data->interval); + fprintf(cmd_data->file_out, " "); + } + } + fprintf(cmd_data->file_out, "\n"); + } else { + fprintf(cmd_data->file_out, "\t\t\tDvfm Distribution: None\n"); + } + } + + current_core++; + } + fprintf(cmd_data->file_out, "\n"); + } + } +} + +static inline void output_plist(unit_data* unit_data, cmd_data* cmd_data, int* current_loop) { + int current_core = 0; + + cpu_samples_perf_data* core_perf_data = &unit_data->soc_samples.core_perf_data; + cpu_samples_perf_data* cluster_perf_data = &unit_data->soc_samples.cluster_perf_data; + pwr_samples_perf_data* pwr_perf_data = &unit_data->pwr_samples.perf_data; + + fprintf(cmd_data->file_out, "processor%s\nsoc_id%s\nsample%d\n", + [[unit_data->silicon_name capitalizedString] UTF8String], + [unit_data->silicon_code UTF8String], + *current_loop + 1); + + for (int i = 0; i < [unit_data->percluster_ncores count] + 1; i++) { + if (([unit_data->soc_samples.complex_channkeys[i] rangeOfString:@"ECPU"].location != NSNotFound && cmd_data->flags.hide_ecpu) || + ([unit_data->soc_samples.complex_channkeys[i] rangeOfString:@"PCPU"].location != NSNotFound && cmd_data->flags.hide_pcpu) || + ([unit_data->soc_samples.complex_channkeys[i] rangeOfString:@"GPU"].location != NSNotFound && cmd_data->flags.hide_gpu)) continue; /* continue when hidden */ + + char* microarch = "Unknown"; + + /* opening metrics + */ + if ([unit_data->soc_samples.complex_channkeys[i] rangeOfString:@"ECPU"].location != NSNotFound) + microarch = (char *) [unit_data->ecpu_microarch UTF8String]; + else if ([unit_data->soc_samples.complex_channkeys[i] rangeOfString:@"PCPU"].location != NSNotFound) + microarch = (char *) [unit_data->pcpu_microarch UTF8String]; + + if ([unit_data->soc_samples.complex_channkeys[i] rangeOfString:@"CPU"].location != NSNotFound) { + fprintf(cmd_data->file_out, "%s\n\n\tmicroarch%s\n\tcore_cnt%d\n", [unit_data->soc_samples.complex_channkeys[i] UTF8String], microarch, [unit_data->percluster_ncores[i] intValue]); + + if (cmd_data->flags.show_cycles) + fprintf(cmd_data->file_out, "\tcycles_spent%ld\n", [unit_data->clpc_samples.perf_data.insn_perclk[i] longValue]); + + /* instructions metrics are only available on CPU clusters */ + if (cmd_data->flags.show_insns) { + float retired = [unit_data->clpc_samples.perf_data.insn_retired[i] floatValue]; + + if (retired > 0) { + fprintf(cmd_data->file_out, "\tinstrcts_ret%.f\n", retired); + fprintf(cmd_data->file_out, "\tinstrcts_per_clk%.5f\n", retired / [unit_data->clpc_samples.perf_data.insn_perclk[i] floatValue]); + } else { + fprintf(cmd_data->file_out, "\tinstrcts_ret0\n"); + fprintf(cmd_data->file_out, "\tinstrcts_per_clk0\n"); + } + } + } else + fprintf(cmd_data->file_out, "GPU\n\n\tcore_cnt%d\n", unit_data->gpu_ncores); + + /* clusters + */ + if (cmd_data->flags.show_pwr) fprintf(cmd_data->file_out, "\tpower%.2f\n", (float)([pwr_perf_data->cluster_pwr[i] floatValue] * cmd_data->pwr_unit)); + if (cmd_data->flags.show_volts) fprintf(cmd_data->file_out, "\tmvolts%.f\n", [cluster_perf_data->volts[i] floatValue]); + if (cmd_data->flags.show_freq) fprintf(cmd_data->file_out, "\tfreq%.2f\n", (float)(fabs([cluster_perf_data->freqs[i] floatValue] * cmd_data->freq_unit))); + if (cmd_data->flags.show_active)fprintf(cmd_data->file_out, "\tfreq%.2f\n", fabs(100-[cluster_perf_data->residency[i] floatValue])); + if (cmd_data->flags.show_idle) fprintf(cmd_data->file_out, "\tidle_res%.2f\n", fabs([cluster_perf_data->residency[i] floatValue])); + + /* cluster dvfs + */ + if (cmd_data->flags.show_dvfs) { + if ([cluster_perf_data->freqs[i] floatValue] > 0) { + fprintf(cmd_data->file_out, "\tdvfs_distrib\n\t\n"); + + for (int iii = 0; iii < [unit_data->soc_samples.dvfs_freq_states[i] count]; iii++) { + float res = [cluster_perf_data->distribution[i][iii] floatValue]; + + if (res > 0) { + fprintf(cmd_data->file_out, "\t\t%ld\n\t\t\n",[unit_data->soc_samples.dvfs_freq_states[i][iii] longValue]); + + if (cmd_data->flags.show_dvfs_volts) fprintf(cmd_data->file_out, "\t\t\tstate_mvolts%.f\n", [unit_data->soc_samples.dvfs_voltage_states[i][iii] floatValue]); + + fprintf(cmd_data->file_out, "\t\t\tresidency%.2f\n",res * 100); + + if (cmd_data->flags.show_dvfs_ms) fprintf(cmd_data->file_out, "\t\t\ttime_ms%.f\n", res * cmd_data->interval); + fprintf(cmd_data->file_out, "\t\t\n"); + } + } + fprintf(cmd_data->file_out, "\t\n"); + } + } + + + /* cores + */ + if (i < [unit_data->percluster_ncores count]) { + for (int ii = 0; ii < [unit_data->percluster_ncores[i] intValue]; ii++) { + fprintf(cmd_data->file_out, "\tcore_%d\n\t\n", current_core); + + if (cmd_data->flags.show_pwr) fprintf(cmd_data->file_out, "\t\tpower%.2f\n", (float)([pwr_perf_data->core_pwr[i][ii] floatValue] * cmd_data->pwr_unit)); + if (cmd_data->flags.show_volts) fprintf(cmd_data->file_out, "\t\tmvolts%.f\n", [core_perf_data->volts[i][ii] floatValue]); + if (cmd_data->flags.show_freq) fprintf(cmd_data->file_out, "\t\tfreq%.2f\n", (float)(fabs([core_perf_data->freqs[i][ii] floatValue] * cmd_data->freq_unit))); + if (cmd_data->flags.show_active) fprintf(cmd_data->file_out, "\t\tactive_res%.2f\n", (float)(fabs(100-[core_perf_data->residency[i][ii] floatValue]))); + if (cmd_data->flags.show_idle) fprintf(cmd_data->file_out, "\t\tidle_res%.2f\n", (float)(fabs([core_perf_data->residency[i][ii] floatValue]))); + + /* core dvfs + */ + if (cmd_data->flags.show_dvfs) { + fprintf(cmd_data->file_out, "\t\tdvfs_distrib\n\t\t\n"); + + for (int iii = 0; iii < [unit_data->soc_samples.dvfs_freq_states[i] count]; iii++) { + float res = [core_perf_data->distribution[i][ii][iii] floatValue]; + + if (res > 0) { + fprintf(cmd_data->file_out, "\t\t\t%ld\n\t\t\t\n",[unit_data->soc_samples.dvfs_freq_states[i][iii] longValue]); + + if (cmd_data->flags.show_dvfs_volts) + fprintf(cmd_data->file_out, "\t\t\t\tstate_mvolts%.f\n",[unit_data->soc_samples.dvfs_voltage_states[i][iii] floatValue]); + + fprintf(cmd_data->file_out, "\t\t\t\tresidency%.2f\n",res * 100); + + if (cmd_data->flags.show_dvfs_ms) fprintf(cmd_data->file_out, "\t\t\t\ttime_ms%.f\n", res * cmd_data->interval); + fprintf(cmd_data->file_out, "\t\t\t\n"); + } + } + fprintf(cmd_data->file_out, "\t\t\n"); + } + + fprintf(cmd_data->file_out, "\t\n"); + + current_core++; + } + } + + fprintf(cmd_data->file_out, "\n"); + } +} + +static inline void make_dvfs_table(NSMutableArray* freq_states, NSMutableArray* volt_states) { + NSData* frmt_data; + + const unsigned char* databytes; + + io_registry_entry_t entry; + io_iterator_t iter; + CFMutableDictionaryRef service; + + mach_port_t port; + if (@available(macOS 12, *)) + port = kIOMainPortDefault; + else + port = kIOMasterPortDefault; + + if (!(service = IOServiceMatching("AppleARMIODevice"))) + error(1, "Failed to find AppleARMIODevice service in IORegistry"); + if (!(IOServiceGetMatchingServices(port, service, &iter) == kIOReturnSuccess)) + error(1, "Failed to access AppleARMIODevice service in IORegistry"); + + while ((entry = IOIteratorNext(iter)) != IO_OBJECT_NULL) { + + /* check if the first voltage state is here. if so, the others should be... kinda gross, should probably fix this later + */ + if (IORegistryEntryCreateCFProperty(entry, VOLTAGE_STATES_ECPU, kCFAllocatorDefault, 0) != nil) { + + for (int i = 3; i--;) { + const void* data = nil; + + /* maybe i'll add voltage-states-13-sram in the future for higher end silicon, but it's not all that necessary anyway... */ + switch(i) { + case 2: data = IORegistryEntryCreateCFProperty(entry, CFSTR("voltage-states1-sram"), kCFAllocatorDefault, 0); break; + case 1: data = IORegistryEntryCreateCFProperty(entry, CFSTR("voltage-states5-sram"), kCFAllocatorDefault, 0); break; + case 0: data = IORegistryEntryCreateCFProperty(entry, CFSTR("voltage-states9"), kCFAllocatorDefault, 0); break; + } + + if (data != nil) { + freq_states[2-i] = [[NSMutableArray alloc] init]; + volt_states[2-i] = [[NSMutableArray alloc] init]; + + frmt_data = (NSData*)CFBridgingRelease(data); + databytes = [(NSData*)CFBridgingRelease(data) bytes]; + + for (int ii = 0; ii < [frmt_data length] - 4; ii += 8) { + uint32_t freq_dword = *(uint32_t*)(databytes + ii) * 1e-6; + uint32_t volt_dword = *(uint32_t*)(databytes + ii + 4); + + if (freq_dword != 0) [freq_states[2-i] addObject:[NSNumber numberWithUnsignedInt:freq_dword]]; + [volt_states[2-i] addObject:[NSNumber numberWithUnsignedInt:volt_dword]]; + } + } + } + + break; + } + } + + IOObjectRelease(entry); + IOObjectRelease(iter); +} + +static inline void get_core_counts(unit_data* unit_data) { + io_registry_entry_t entry; + io_iterator_t iter; + + CFMutableDictionaryRef service; + + mach_port_t port; + if (@available(macOS 12, *)) + port = kIOMainPortDefault; + else + port = kIOMasterPortDefault; + + if (!(service = IOServiceMatching("AppleARMIODevice"))) + error(1, "Failed to find AppleARMIODevice service in IORegistry"); + if (!(IOServiceGetMatchingServices(port, service, &iter) == kIOReturnSuccess)) + error(1, "Failed to access AppleARMIODevice service in IORegistry"); + + while ((entry = IOIteratorNext(iter)) != IO_OBJECT_NULL) { + const void* data = IORegistryEntryCreateCFProperty(entry, CFSTR("clusters"), kCFAllocatorDefault, 0); + + if (data != nil) { + NSData* frmt_data = (NSData*)CFBridgingRelease(data); + const unsigned char* databytes = [frmt_data bytes]; + + /* every 4th index after the first is our byte data for the cluster core count */ + for (int ii = 0; ii < [frmt_data length]; ii += 4) { + NSString* datastrng = [[NSString alloc] initWithFormat:@"%02x", databytes[ii]]; + + /* Ultra support */ + if ([unit_data->silicon_name rangeOfString:@"ultra"].location != NSNotFound) { + for (int i = 2; i--;) + [unit_data->percluster_ncores addObject:[NSNumber numberWithInt: (int) atof([datastrng UTF8String])]]; + } else + [unit_data->percluster_ncores addObject:[NSNumber numberWithInt: (int) atof([datastrng UTF8String])]]; + + datastrng = nil; + } + + break; + } + } + + /* pull gpu core counts from a different spot + */ + if (!(service = IOServiceMatching("AGXAccelerator"))) + error(1, "Failed to find AGXAccelerator service in IORegistry"); + if (!(IOServiceGetMatchingServices(port, service, &iter) == kIOReturnSuccess)) + error(1, "Failed to access AGXAccelerator service in IORegistry"); + + while ((entry = IOIteratorNext(iter)) != IO_OBJECT_NULL) { + + CFTypeRef a = IORegistryEntryCreateCFProperty(entry, CFSTR("gpu-core-count"), kCFAllocatorDefault, 0); + + if (a != nil) unit_data->gpu_ncores = [(__bridge NSNumber *)a intValue]; + + break; + } + + IOObjectRelease(entry); + IOObjectRelease(iter); +} + +static inline void get_silicon_code(unit_data* unit_data) { + if ([unit_data->silicon_name rangeOfString:@"m1"].location != NSNotFound) { + if ([unit_data->silicon_name rangeOfString:@"m1 pro"].location != NSNotFound) { + unit_data->silicon_code = @"T6000"; + return; + } else if ([unit_data->silicon_name rangeOfString:@"m1 max"].location != NSNotFound) { + unit_data->silicon_code = @"T6001"; + return; + } else if ([unit_data->silicon_name rangeOfString:@"m1 ultra"].location != NSNotFound) { + unit_data->silicon_code = @"T6002"; + return; + } else { + unit_data->silicon_code = @"T8103"; + return; + } + } else if ([unit_data->silicon_name rangeOfString:@"m2"].location != NSNotFound) { + if ([unit_data->silicon_name rangeOfString:@"m2 pro"].location != NSNotFound) { + unit_data->silicon_code = @"T6020"; + return; + } else if ([unit_data->silicon_name rangeOfString:@"m2 max"].location != NSNotFound) { + unit_data->silicon_code = @"T6021"; + return; + } else if ([unit_data->silicon_name rangeOfString:@"m2 ultra"].location != NSNotFound) { + unit_data->silicon_code = @"T6022"; + return; + } else { + unit_data->silicon_code = @"T8112"; + return; + } + } else { + unit_data->silicon_code = @"T****"; + } + + io_registry_entry_t entry; + io_iterator_t iter; + + CFMutableDictionaryRef servicedict; + CFMutableDictionaryRef service; + + mach_port_t port; + if (@available(macOS 12, *)) + port = kIOMainPortDefault; + else + port = kIOMasterPortDefault; + + if (!(service = IOServiceMatching("IOPlatformExpertDevice"))) + return; + if (!(IOServiceGetMatchingServices(port, service, &iter) == kIOReturnSuccess)) + return; + + while ((entry = IOIteratorNext(iter)) != IO_OBJECT_NULL) { + if ((IORegistryEntryCreateCFProperties(entry, &servicedict, kCFAllocatorDefault, 0) != kIOReturnSuccess)) return; + + const void* data = CFDictionaryGetValue(servicedict, @"platform-name");; + + if (data != nil) { + NSData* frmt_data = (NSData*)CFBridgingRelease(data); + + const unsigned char* databytes = [frmt_data bytes]; + + unit_data->silicon_code = [[NSString stringWithFormat:@"%s", databytes] capitalizedString]; + + frmt_data = nil; + } + } + + IOObjectRelease(entry); + IOObjectRelease(iter); +// CFRelease(service); + + return; +} + +static inline void get_microarches(unit_data* unit_data) { + if ([unit_data->silicon_name rangeOfString:@"m1"].location != NSNotFound) { + unit_data->ecpu_microarch = @"Icestorm"; + unit_data->pcpu_microarch = @"Firestorm"; + return; + } else if ([unit_data->silicon_name rangeOfString:@"m2"].location != NSNotFound) { + unit_data->ecpu_microarch = @"Blizzard"; + unit_data->pcpu_microarch = @"Avalanche"; + return; + } else { + unit_data->ecpu_microarch = @"Unknown"; + unit_data->pcpu_microarch = @"Unknown"; + } + + io_registry_entry_t service; + NSData * data; + + const unsigned char * ecpu_microarch = NULL; + const unsigned char * pcpu_microarch = NULL; + + mach_port_t port; + if (@available(macOS 12, *)) + port = kIOMainPortDefault; + else + port = kIOMasterPortDefault; + + int cores = [unit_data->percluster_ncores[0] intValue] + [unit_data->percluster_ncores[1] intValue] - 1; + + if ((service = IORegistryEntryFromPath(port, "IOService:/AppleARMPE/cpu0"))) { + if ((data = (__bridge NSData *)(CFDataRef)IORegistryEntryCreateCFProperty(service, CFSTR("compatible"), kCFAllocatorDefault, 0))) + ecpu_microarch = [data bytes]; + } + + if ((service = IORegistryEntryFromPath(port, [[NSString stringWithFormat:@"IOService:/AppleARMPE/cpu%d", cores] UTF8String]))) { + if ((data = (__bridge NSData *)(CFDataRef)IORegistryEntryCreateCFProperty(service, CFSTR("compatible"), kCFAllocatorDefault, 0))) + pcpu_microarch = [data bytes]; + } + + unit_data->ecpu_microarch = [[[NSString stringWithFormat:@"%s", ecpu_microarch] componentsSeparatedByString:@","][1] capitalizedString]; + unit_data->pcpu_microarch = [[[NSString stringWithFormat:@"%s", pcpu_microarch] componentsSeparatedByString:@","][1] capitalizedString]; +} + +static inline void init_unit_data(unit_data* data) { + memset((void*)data, 0, sizeof(unit_data)); + + cpu_samples_perf_data* core_perf_data = &data->soc_samples.core_perf_data; + cpu_samples_perf_data* cluster_perf_data = &data->soc_samples.cluster_perf_data; + pwr_samples_perf_data* pwr_perf_data = &data->pwr_samples.perf_data; + clpc_samples_perf_data* clpc_perf_data = &data->clpc_samples.perf_data; + + /* get soc name + */ + size_t len = 32; + char* cpubrand = malloc(len); + sysctlbyname("machdep.cpu.brand_string", cpubrand, &len, NULL, 0); + + if (cpubrand != NULL) + data->silicon_name = [[NSString stringWithFormat:@"%s", cpubrand, nil] lowercaseString]; + else + data->silicon_name = @"Unknown SoC"; + + free(cpubrand); + + /* get constant data + */ + NSMutableArray* freq_states = [NSMutableArray arrayWithCapacity:3]; + NSMutableArray* volt_states = [NSMutableArray arrayWithCapacity:3]; + + make_dvfs_table(freq_states, volt_states); + + data->percluster_ncores = [NSMutableArray arrayWithCapacity:3]; + + get_core_counts(data); + get_silicon_code(data); + get_microarches(data); + + /* input chann keys / dvfs stuff + */ + if ([data->silicon_name rangeOfString:@"virtual"].location != NSNotFound) { + error(1, "Running this tool in a Virtual Machine is not allowed"); + } + + if ([data->silicon_name rangeOfString:@"pro"].location != NSNotFound || + [data->silicon_name rangeOfString:@"max"].location != NSNotFound) { + + data->soc_samples.complex_channkeys = promax_complex_freq_chankeys; + data->soc_samples.core_channkeys = promax_core_freq_chankeys; + data->pwr_samples.complex_channkeys = promax_complex_pwr_chankeys; + data->pwr_samples.core_channkeys = promax_core_pwr_chankeys; + + data->soc_samples.dvfs_freq_states = [NSArray arrayWithObjects: freq_states[0], freq_states[1], freq_states[1], freq_states[2], nil]; + data->soc_samples.dvfs_voltage_states = [NSArray arrayWithObjects: volt_states[0], volt_states[1], volt_states[1], volt_states[2], nil]; + } else if ([data->silicon_name rangeOfString:@"ultra"].location != NSNotFound) { + data->soc_samples.complex_channkeys = ultra_complex_freq_chankeys; + data->soc_samples.core_channkeys = ultra_core_freq_chankeys; + data->pwr_samples.complex_channkeys = ultra_complex_pwr_chankeys; + data->pwr_samples.core_channkeys = ultra_core_pwr_chankeys; + + data->soc_samples.dvfs_freq_states = [NSArray arrayWithObjects: freq_states[0], freq_states[0], freq_states[1], freq_states[1], freq_states[1], freq_states[1], freq_states[2], nil]; + data->soc_samples.dvfs_voltage_states = [NSArray arrayWithObjects: volt_states[0], volt_states[0], volt_states[1], volt_states[1], volt_states[1], volt_states[1], volt_states[2], nil]; + } else { + data->soc_samples.complex_channkeys = pleb_complex_freq_chankeys; + data->soc_samples.core_channkeys = pleb_core_freq_chankeys; + data->pwr_samples.complex_channkeys = pleb_complex_pwr_chankeys; + data->pwr_samples.core_channkeys = pleb_core_pwr_chankeys; + + data->soc_samples.dvfs_freq_states = freq_states; + data->soc_samples.dvfs_voltage_states = volt_states; + } + + NSUInteger percluster_ncores = [data->percluster_ncores count]; + + /* cluster (+ 1 for GPU) */ + cluster_perf_data->distribution = [NSMutableArray arrayWithCapacity:percluster_ncores + 1]; + cluster_perf_data->freqs = [NSMutableArray arrayWithCapacity:percluster_ncores + 1]; + cluster_perf_data->volts = [NSMutableArray arrayWithCapacity:percluster_ncores + 1]; + cluster_perf_data->residency = [NSMutableArray arrayWithCapacity:percluster_ncores + 1]; + cluster_perf_data->sums = [NSMutableArray arrayWithCapacity:percluster_ncores + 1]; + pwr_perf_data->cluster_pwr = [NSMutableArray arrayWithCapacity:percluster_ncores + 1]; + + /* core */ + core_perf_data->distribution = [NSMutableArray arrayWithCapacity:percluster_ncores]; + core_perf_data->freqs = [NSMutableArray arrayWithCapacity:percluster_ncores]; + core_perf_data->volts = [NSMutableArray arrayWithCapacity:percluster_ncores]; + core_perf_data->residency = [NSMutableArray arrayWithCapacity:percluster_ncores]; + core_perf_data->sums = [NSMutableArray arrayWithCapacity:percluster_ncores]; + pwr_perf_data->core_pwr = [NSMutableArray arrayWithCapacity:percluster_ncores]; + + /* clpc */ + clpc_perf_data->insn_perclk = [NSMutableArray arrayWithCapacity:percluster_ncores]; + clpc_perf_data->insn_retired = [NSMutableArray arrayWithCapacity:percluster_ncores]; + + for (int i = 0; i < percluster_ncores + 1; i++) { + + /* cluster */ + cluster_perf_data->distribution[i] = [NSMutableArray array]; + cluster_perf_data->freqs[i] = @0; + cluster_perf_data->volts[i] = @0; + cluster_perf_data->residency[i] = @0; + cluster_perf_data->sums[i] = @0; + pwr_perf_data->cluster_pwr[i] = @0; + + if (i < percluster_ncores) { + /* clpc */ + clpc_perf_data->insn_perclk[i] = @0; + clpc_perf_data->insn_retired[i] = @0; + + int cluster_ncores = [data->percluster_ncores[i] intValue]; + + /* core */ + core_perf_data->distribution[i] = [NSMutableArray arrayWithCapacity:cluster_ncores]; + core_perf_data->freqs[i] = [NSMutableArray arrayWithCapacity:cluster_ncores]; + core_perf_data->volts[i] = [NSMutableArray arrayWithCapacity:cluster_ncores]; + core_perf_data->residency[i] = [NSMutableArray arrayWithCapacity:cluster_ncores]; + core_perf_data->sums[i] = [NSMutableArray arrayWithCapacity:cluster_ncores]; + pwr_perf_data->core_pwr[i] = [NSMutableArray arrayWithCapacity:cluster_ncores]; + + for (int ii = 0; ii < cluster_ncores; ii++) { + core_perf_data->distribution[i][ii] = [NSMutableArray array]; + core_perf_data->sums[i][ii] = @0; + core_perf_data->freqs[i][ii] = @0; + core_perf_data->volts[i][ii] = @0; + core_perf_data->residency[i][ii] = @0; + pwr_perf_data->core_pwr[i][ii] = @0; + } + } + } + + data->soc_samples.cpu_chann = IOReportCopyChannelsInGroup(@"CPU Stats", 0, 0, 0, 0); + data->soc_samples.gpu_chann = IOReportCopyChannelsInGroup(@"GPU Stats", 0, 0, 0, 0); + data->pwr_samples.energy_chann = IOReportCopyChannelsInGroup(@"Energy Model", 0, 0, 0, 0); + data->pwr_samples.pmp_chann = IOReportCopyChannelsInGroup(@"PMP", 0, 0, 0, 0); + data->clpc_samples.clpc_chann = IOReportCopyChannelsInGroup(@"CLPC Stats", 0, 0, 0, 0); + + IOReportMergeChannels(data->soc_samples.cpu_chann, data->soc_samples.gpu_chann, NULL); + IOReportMergeChannels(data->pwr_samples.energy_chann, data->pwr_samples.pmp_chann, NULL); + + data->soc_samples.cpu_sub = IOReportCreateSubscription(NULL, data->soc_samples.cpu_chann, &data->soc_samples.cpu_sub_chann, 0, 0); + data->pwr_samples.pwr_sub = IOReportCreateSubscription(NULL, data->pwr_samples.energy_chann, &data->pwr_samples.pwr_sub_chann, 0, 0); + data->clpc_samples.clpc_sub = IOReportCreateSubscription(NULL, data->clpc_samples.clpc_chann, &data->clpc_samples.clpc_sub_chann, 0, 0); + + CFRelease(data->soc_samples.cpu_chann); + CFRelease(data->soc_samples.gpu_chann); + CFRelease(data->pwr_samples.energy_chann); + CFRelease(data->pwr_samples.pmp_chann); + CFRelease(data->clpc_samples.clpc_chann); +} + +static inline void init_cmd_data(cmd_data* data) { + memset((void*)data, 0, sizeof(cmd_data)); + + data->interval = 275; + data->samples = 1; + + data->file_out = stdout; + data->pwr_unit = PWR_UNIT_MWATT; + data->pwr_unit_label = PWR_UNIT_MWATT_LABEL; + data->freq_unit = FREQ_UNIT_MHZ; + data->freq_unit_label = FREQ_UNIT_MHZ_LABEL; + + data->flags.show_active = true; + data->flags.show_pwr = true; + data->flags.show_freq = true; + data->flags.show_dvfs = true; + data->flags.show_insns = true; + data->flags.show_percore = true; +}