1/* 2 * Copyright (c) 2013-2014 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <CoreFoundation/CoreFoundation.h> 25#include <asl.h> 26#include <mach/mach.h> 27#include <notify.h> 28 29#include "PMAssertions.h" 30#include "PrivateLib.h" 31 32#include <IOReport.h> 33 34#define DISPLAY_ON_ASSERTION_LOG_DELAY (60LL) 35#define DISPLAY_OFF_ASSERTION_LOG_DELAY (10LL) 36 37#ifdef TARGET_OS_EMBEDDED 38#define AA_MAX_ENTRIES 512 39#else 40#define AA_MAX_ENTRIES 64 41#endif 42 43 44extern assertionType_t gAssertionTypes[]; 45extern CFMutableDictionaryRef gProcessDict; 46extern uint32_t gDebugFlags; 47extern uint32_t gActivityAggCnt; 48 49 50typedef struct { 51 CFDateRef startTime; 52 CFMutableArrayRef types; 53} assertionAggregate_t; 54 55typedef struct { 56 uint32_t idx; 57 CFMutableArrayRef log; 58 uint32_t unreadCnt; // Number of entries logged since last read by 59 // entitled reader. There should be only one entitled 60 // reader in the system. 61} assertionActivity_t; 62 63assertionActivity_t activity; 64assertionAggregate_t aggregate; 65#if TARGET_OS_EMBEDDED 66static uint32_t gActivityLogCnt = 1; // Number of requests received to enable activity logging 67#else 68static uint32_t gActivityLogCnt = 0; // Has to be explicity enabled on OSX 69#endif 70 71 72__private_extern__ bool isDisplayAsleep( ); 73 74static void logAssertionActivity(assertLogAction action, 75 assertion_t *assertion) 76{ 77 78 bool logBT = false; 79 CFDateRef time = NULL; 80 CFStringRef actionStr = NULL; 81 CFNumberRef pid_cf = NULL, retain_cf = NULL, uniqueAID = NULL; 82 CFTypeRef type = NULL, name = NULL, btSymbols = NULL; 83 CFTypeRef onBehalfPid = NULL, onBehalfPidStr = NULL; 84 85 CFMutableDictionaryRef entry = NULL; 86 CFDictionaryRef props = assertion->props; 87 88 switch(action) { 89 90 case kACreateLog: 91 logBT = true; 92 actionStr = CFSTR(kPMASLAssertionActionCreate); 93 break; 94 95 case kACreateRetain: 96 logBT = true; 97 actionStr = CFSTR(kPMASLAssertionActionRetain); 98 break; 99 100 case kATurnOnLog: 101 logBT = true; 102 actionStr = CFSTR(kPMASLAssertionActionTurnOn); 103 break; 104 105 case kAReleaseLog: 106 actionStr = CFSTR(kPMASLAssertionActionRelease); 107 break; 108 109 case kAClientDeathLog: 110 actionStr = CFSTR(kPMASLAssertionActionClientDeath); 111 break; 112 113 case kATimeoutLog: 114 actionStr = CFSTR(kPMASLAssertionActionTimeOut); 115 break; 116 117 case kATurnOffLog: 118 actionStr = CFSTR(kPMASLAssertionActionTurnOff); 119 break; 120 121 default: 122 return; 123 } 124 125 if (!activity.log) { 126 activity.log = CFArrayCreateMutable(NULL, AA_MAX_ENTRIES, &kCFTypeArrayCallBacks); 127 128 if (!activity.log) return; 129 130 activity.unreadCnt = UINT_MAX; 131 // Send a high water mark notification to force a read by powerlog after powerd's crash 132 notify_post(kIOPMAssertionsLogBufferHighWM); 133 } 134 135 entry = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, 136 &kCFTypeDictionaryValueCallBacks); 137 if (!entry) return; 138 139 // Current time of this activity 140 time = CFDateCreate(0, CFAbsoluteTimeGetCurrent()); 141 if (time) { 142 CFDictionarySetValue(entry, kIOPMAssertionActivityTime, time); 143 CFRelease(time); 144 } 145 else { 146 // Not much to log, if we can't even get the time of activity 147 CFRelease(entry); 148 return; 149 } 150 151 // Assertion type 152 if ((type = CFDictionaryGetValue(props, kIOPMAssertionTypeKey)) != NULL) 153 CFDictionarySetValue(entry, kIOPMAssertionTypeKey, type); 154 155 // Assertion name 156 if ((name = CFDictionaryGetValue(props, kIOPMAssertionNameKey)) != NULL) 157 CFDictionarySetValue(entry, kIOPMAssertionNameKey, name); 158 159 // Type of assertion action 160 CFDictionarySetValue(entry, kIOPMAssertionActivityAction, actionStr); 161 162 // PID owning this assertion 163 if ((pid_cf = CFNumberCreate(NULL, kCFNumberIntType, &assertion->pinfo->pid)) != NULL) { 164 CFDictionarySetValue(entry, kIOPMAssertionPIDKey, pid_cf); 165 CFRelease(pid_cf); 166 } 167 168 // Retain count 169 if ((retain_cf = CFNumberCreate(NULL, kCFNumberIntType, &assertion->retainCnt)) != NULL) { 170 CFDictionarySetValue(entry, kIOPMAssertionRetainCountKey, retain_cf); 171 CFRelease(retain_cf); 172 } 173 174 // Assertion ID 175 if ((uniqueAID = CFDictionaryGetValue(props, kIOPMAssertionGlobalUniqueIDKey)) != NULL) 176 CFDictionarySetValue(entry, kIOPMAssertionGlobalUniqueIDKey, uniqueAID); 177 178 // Assertion on behalf of PID 179 if ((onBehalfPid = CFDictionaryGetValue(props, kIOPMAssertionOnBehalfOfPID)) != NULL) 180 CFDictionarySetValue(entry, kIOPMAssertionOnBehalfOfPID, onBehalfPid); 181 182 if ((onBehalfPidStr = CFDictionaryGetValue(props, kIOPMAssertionOnBehalfOfPIDReason)) != NULL) 183 CFDictionarySetValue(entry, kIOPMAssertionOnBehalfOfPIDReason, onBehalfPidStr); 184 185 if (logBT) { 186 // Backtrace of assertion creation 187 if ((btSymbols = CFDictionaryGetValue(props, kIOPMAssertionCreatorBacktrace)) != NULL) 188 CFDictionarySetValue(entry, kIOPMAssertionCreatorBacktrace, btSymbols); 189 } 190 191 CFArraySetValueAtIndex(activity.log, (activity.idx % AA_MAX_ENTRIES), entry); 192 activity.idx++; 193 194 CFRelease(entry); 195 196 if ((activity.unreadCnt != UINT_MAX) && (++activity.unreadCnt >= 0.9*AA_MAX_ENTRIES)) { 197 notify_post(kIOPMAssertionsLogBufferHighWM); 198 activity.unreadCnt = UINT_MAX; 199 } 200} 201 202#if !TARGET_OS_EMBEDDED 203 204__private_extern__ void logASLAssertionTypeSummary( kerAssertionType type) 205{ 206 applyToAllAssertionsSync(&gAssertionTypes[type], false, 207 ^(assertion_t *assertion) { 208 logAssertionEvent(kASummaryLog, assertion); 209 }); 210} 211 212static void printAggregateAssertionsToBuf(char *aBuf, int bufsize, uint32_t kbits) 213{ 214 size_t printed = 0; 215 216 snprintf(aBuf, bufsize, "[System:"); 217 218 if (getAssertionLevel(kPreventIdleType)) { 219 printed += strlcat(aBuf, " PrevIdle", bufsize); 220 } 221 if (getAssertionLevel(kPreventDisplaySleepType)) { 222 printed += strlcat(aBuf, " PrevDisp", bufsize); 223 } 224 if (getAssertionLevel(kPreventSleepType)) { 225 printed += strlcat(aBuf, " PrevSleep", bufsize); 226 } 227 if (getAssertionLevel(kDeclareUserActivityType)) { 228 printed += strlcat(aBuf, " DeclUser", bufsize); 229 } 230 if (getAssertionLevel(kPushServiceTaskType)) { 231 printed += strlcat(aBuf, " PushSrvc", bufsize); 232 } 233 if (getAssertionLevel(kBackgroundTaskType)) { 234 printed += strlcat(aBuf, " BGTask", bufsize); 235 } 236 if (getAssertionLevel(kDeclareSystemActivityType)) { 237 printed += strlcat(aBuf, " SysAct", bufsize); 238 } 239 if (getAssertionLevel(kSRPreventSleepType)) { 240 printed += strlcat(aBuf, " SRPrevSleep", bufsize); 241 } 242 if (getAssertionLevel(kTicklessDisplayWakeType)) { 243 printed += strlcat(aBuf, " DispWake", bufsize); 244 } 245 if (getAssertionLevel(kIntPreventDisplaySleepType)) { 246 printed += strlcat(aBuf, " IntPrevDisp", bufsize); 247 } 248 if (getAssertionLevel(kNetworkAccessType)) { 249 printed += strlcat(aBuf, " NetAcc", bufsize); 250 } 251 if (getAssertionLevel(kInteractivePushServiceType)) { 252 printed += strlcat(aBuf, " IPushSrvc", bufsize); 253 } 254 if (kbits & kIOPMDriverAssertionCPUBit) { 255 printed += strlcat(aBuf, " kCPU", bufsize); 256 } 257 if (kbits & kIOPMDriverAssertionPreventDisplaySleepBit) { 258 printed += strlcat(aBuf, " kDisp", bufsize); 259 } 260 261 if (0 == printed) { 262 strlcat(aBuf, " No Assertions", bufsize); 263 } 264 265 strlcat(aBuf, "]", bufsize); 266 267 return; 268} 269 270 271static void logAssertionToASL(assertLogAction action, 272 assertion_t *assertion) 273{ 274 const int kLongStringLen = 200; 275 const int kShortStringLen = 10; 276 aslmsg m; 277 CFStringRef foundAssertionType = NULL; 278 CFStringRef foundAssertionName = NULL; 279 CFDateRef foundDate = NULL; 280 CFStringRef procName = NULL; 281 char proc_name_buf[kProcNameBufLen]; 282 char pid_buf[kShortStringLen]; 283 char assertionTypeCString[kLongStringLen]; 284 char assertionNameCString[kLongStringLen]; 285 char ageString[kShortStringLen]; 286 char aslMessageString[kLongStringLen]; 287 char assertionsBuf[kLongStringLen]; 288 CFMutableDictionaryRef assertionDictionary; 289 char *assertionAction = NULL; 290 assertionType_t *assertType = NULL; 291 292 293 if (assertion->state & kAssertionSkipLogging) return; 294 295 if (!(gDebugFlags & kIOPMDebugLogAssertionSynchronous)) { 296 297 assertType = &gAssertionTypes[assertion->kassert]; 298 if ((action == kACreateLog) || (action == kATurnOnLog)) 299 { 300 /* 301 * Log on create 302 * - if this assertion type has to be logged on create, or 303 * - if display is asleep 304 */ 305 if (!(assertType->flags & kAssertionTypeLogOnCreate)) 306 return; 307 assertion->state |= kAssertionStateLogged; 308 } 309 else if ( (action == kAReleaseLog) || (action == kAClientDeathLog) || 310 (action == kATimeoutLog) || (action == kATurnOffLog) ) 311 { 312 uint64_t delay; 313 314 if (isDisplayAsleep()) { 315 delay = DISPLAY_OFF_ASSERTION_LOG_DELAY; 316 } 317 else { 318 delay = DISPLAY_ON_ASSERTION_LOG_DELAY; 319 } 320 /* 321 * Log on release 322 * - if they are logged when created, or 323 * - if they are released after delay secs 324 */ 325 if (!(assertion->state & kAssertionStateLogged) && 326 (getMonotonicTime() - assertion->createTime < delay)) 327 return; 328 329 } 330 } 331 332 switch(action) { 333 case kACreateLog: 334 assertionAction = kPMASLAssertionActionCreate; 335 break; 336 case kATurnOnLog: 337 assertionAction = kPMASLAssertionActionTurnOn; 338 break; 339 case kAReleaseLog: 340 assertionAction = kPMASLAssertionActionRelease; 341 break; 342 case kAClientDeathLog: 343 assertionAction = kPMASLAssertionActionClientDeath; 344 break; 345 case kATimeoutLog: 346 assertionAction = kPMASLAssertionActionTimeOut; 347 break; 348 case kACapExpiryLog: 349 assertionAction = kPMASlAssertionActionCapTimeOut; 350 break; 351 case kATurnOffLog: 352 assertionAction = kPMASLAssertionActionTurnOff; 353 break; 354 case kASummaryLog: 355 assertionAction = kPMASLAssertionActionSummary; 356 break; 357 default: 358 return; 359 360 } 361 362 m = new_msg_pmset_log(); 363 assertionDictionary = assertion->props; 364 if (assertionDictionary) 365 { 366 /* 367 * Log the assertion type: 368 */ 369 foundAssertionType = CFDictionaryGetValue(assertionDictionary, kIOPMAssertionTypeKey); 370 if (foundAssertionType) { 371 CFStringGetCString(foundAssertionType, assertionTypeCString, 372 sizeof(assertionTypeCString), kCFStringEncodingUTF8); 373 asl_set(m, kPMASLAssertionNameKey, assertionTypeCString); 374 } 375 376 foundAssertionName = CFDictionaryGetValue(assertionDictionary, kIOPMAssertionNameKey); 377 if (foundAssertionName) { 378 CFStringGetCString(foundAssertionName, assertionNameCString, 379 sizeof(assertionNameCString), kCFStringEncodingUTF8); 380 } 381 382 /* 383 * Assertion's age 384 */ 385 if ((foundDate = CFDictionaryGetValue(assertionDictionary, kIOPMAssertionCreateDateKey))) 386 { 387 CFAbsoluteTime createdCFTime = CFDateGetAbsoluteTime(foundDate); 388 int createdSince = (int)(CFAbsoluteTimeGetCurrent() - createdCFTime); 389 int hours = createdSince / 3600; 390 int minutes = (createdSince / 60) % 60; 391 int seconds = createdSince % 60; 392 snprintf(ageString, sizeof(ageString), "%02d:%02d:%02d ", hours, minutes, seconds); 393 } 394 395 /* 396 * Retain count 397 */ 398 int retainCount = assertion->retainCnt; 399 if (1 != retainCount) 400 { 401 char retainCountBuf[kShortStringLen]; 402 snprintf(retainCountBuf, sizeof(retainCountBuf), "%d", retainCount); 403 asl_set(m, "RetainCount", retainCountBuf); 404 } 405 } 406 407 if ((procName = assertion->pinfo->name)) 408 { 409 CFStringGetCString(procName, proc_name_buf, sizeof(proc_name_buf), kCFStringEncodingUTF8); 410 } 411 412 printAggregateAssertionsToBuf(assertionsBuf, sizeof(assertionsBuf), getKerAssertionBits()); 413 414 pid_buf[0] = 0; 415 if (0 < snprintf(pid_buf, kShortStringLen, "%d", assertion->pinfo->pid)) { 416 asl_set(m, kPMASLPIDKey, pid_buf); 417 } 418 419 snprintf(aslMessageString, sizeof(aslMessageString), "PID %s(%s) %s %s %s%s%s %s id:0x%llx %s", 420 pid_buf, 421 procName ? proc_name_buf:"?", 422 assertionAction, 423 foundAssertionType ? assertionTypeCString:"", 424 foundAssertionName?"\"":"", foundAssertionName ? assertionNameCString:"", 425 foundAssertionName?"\"":"", 426 foundDate?ageString:"", 427 (((uint64_t)assertion->kassert) << 32) | (assertion->assertionId), 428 assertionsBuf); 429 430 asl_set(m, ASL_KEY_MSG, aslMessageString); 431 asl_set(m, kPMASLActionKey, assertionAction); 432 asl_set(m, kPMASLDomainKey, kPMASLDomainPMAssertions); 433 asl_send(NULL, m); 434 asl_free(m); 435 436} 437 438 439void logASLAssertionsAggregate( ) 440{ 441 aslmsg m; 442 char aslMessageString[100]; 443 char assertionsBuf[100]; 444 static int prevPwrSrc = -1; 445 static uint32_t prevAssertionBits = 0; 446 int pwrSrc; 447 448 pwrSrc = _getPowerSource(); 449 if ( (prevPwrSrc == pwrSrc) && (prevAssertionBits == getKerAssertionBits()) ) 450 return; 451 452 prevPwrSrc = pwrSrc; 453 prevAssertionBits = getKerAssertionBits(); 454 455 printAggregateAssertionsToBuf(assertionsBuf, sizeof(assertionsBuf), getKerAssertionBits()); 456 457 snprintf(aslMessageString, sizeof(aslMessageString), "Summary- %s Using %s", 458 assertionsBuf, 459 ( pwrSrc == kBatteryPowered) ? "Batt" : "AC"); 460 461 m = new_msg_pmset_log(); 462 asl_set(m, ASL_KEY_MSG, aslMessageString); 463 asl_set(m, kPMASLActionKey, kPMASLAssertionActionSummary); 464 asl_send(NULL, m); 465 asl_free(m); 466 // 467 // for (int i=0; i<kIOPMNumAssertionTypes; i++) { 468 // logASLAssertionTypeSummary(gAssertionTypes[i].kassert); 469 // } 470} 471 472void logASLAllAssertions( ) 473{ 474 for (int i=0; i<kIOPMNumAssertionTypes; i++) { 475 logASLAssertionTypeSummary(gAssertionTypes[i].kassert); 476 } 477} 478 479#endif // TARGET_OS_EMBEDDED 480 481void logAssertionEvent(assertLogAction action, 482 assertion_t *assertion) 483{ 484 485#if !TARGET_OS_EMBEDDED 486 if (gDebugFlags & kIOPMDebugAssertionASLLog) 487 logAssertionToASL(action, assertion); 488#endif 489 490 if (gActivityLogCnt) 491 logAssertionActivity(action, assertion); 492 493} 494 495void setAssertionActivityLog(int value) 496{ 497#if !TARGET_OS_EMBEDDED 498 // Enabled by default on embedded 499 if (value) 500 gActivityLogCnt++; 501 else if (gActivityLogCnt) 502 gActivityLogCnt--; 503#endif 504} 505 506kern_return_t _io_pm_assertion_activity_log ( 507 mach_port_t server __unused, 508 audit_token_t token, 509 vm_offset_t *log, 510 mach_msg_type_number_t *logSize, 511 uint32_t *refCnt, 512 uint32_t *overflow, 513 int *rc) 514{ 515 CFRange range; 516 CFIndex startIdx, endIdx; 517 CFDataRef serializedLog = NULL; 518 uint32_t readFromIdx; 519 uint32_t writeToIdx; 520 CFMutableArrayRef updates = NULL; 521 CFIndex arrCnt; 522 static bool firstcall = true; 523 524 if ((log == NULL) || (overflow == NULL)) 525 { 526 *rc = kIOReturnBadArgument; 527 goto exit; 528 } 529 530 531 *rc = kIOReturnNotFound; 532 *log = NULL; 533 *logSize = 0; 534 readFromIdx = *refCnt; 535 writeToIdx = activity.idx; 536 *overflow = false; 537 538 if (auditTokenHasEntitlement(token, CFSTR("com.apple.private.iokit.powerlogging"))) 539 { 540 activity.unreadCnt = 0; 541 if (firstcall) { 542 *overflow = true; 543 *refCnt = readFromIdx = UINT_MAX; 544 firstcall = false; 545 } 546 } 547 548 if (!activity.log) { 549 activity.log = CFArrayCreateMutable(NULL, AA_MAX_ENTRIES, &kCFTypeArrayCallBacks); 550 551 if (!activity.log) { 552 *rc = kIOReturnNoMemory; 553 goto exit; 554 } 555 } 556 arrCnt = CFArrayGetCount(activity.log); 557 558 if ((readFromIdx == writeToIdx) || (arrCnt == 0)) { 559 goto exit; 560 } 561 if ((readFromIdx % AA_MAX_ENTRIES) >= arrCnt) { 562 // can happen in case of powerd crash and client provides old refCnt 563 *overflow = true; 564 asl_log(0,0,ASL_LEVEL_ERR, "Unexpected readFromIdx %d. arrCnt=%ld\n", readFromIdx, arrCnt); 565 *refCnt = readFromIdx = UINT_MAX; 566 } 567 568 if ((readFromIdx == UINT_MAX) && (writeToIdx <= AA_MAX_ENTRIES)) { 569 startIdx = endIdx = 0; 570 if (isA_CFArray(activity.log)) { 571 endIdx = arrCnt; 572 } 573 if (endIdx == 0) { 574 goto exit; // No entries 575 } 576 endIdx -= 1; 577 } 578 else if ((writeToIdx > readFromIdx + AA_MAX_ENTRIES) || (writeToIdx < readFromIdx)) { 579 startIdx = writeToIdx % AA_MAX_ENTRIES; 580 endIdx = (writeToIdx -1) % AA_MAX_ENTRIES; 581 *overflow = true; 582 if (startIdx >= arrCnt) { 583 // can happen in case of powerd crash and client provides old refCnt 584 startIdx = 0; 585 } 586 } 587 else { 588 startIdx = readFromIdx % AA_MAX_ENTRIES; 589 endIdx = (writeToIdx -1) % AA_MAX_ENTRIES; 590 } 591 592 updates = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 593 if (updates == NULL) { 594 goto exit; 595 } 596 597 // Copy log entries in sequential order 598 if (startIdx > endIdx) { 599 if (arrCnt == AA_MAX_ENTRIES) { 600 range = CFRangeMake(startIdx, AA_MAX_ENTRIES-startIdx); 601 CFArrayAppendArray(updates, activity.log, range); 602 } 603 else { 604 asl_log(0,0,ASL_LEVEL_ERR, "arrCnt is %ld. expected %d\n", arrCnt, AA_MAX_ENTRIES); 605 asl_log(0,0,ASL_LEVEL_ERR, "startIdx: %ld endIdx: %ld refCnt: 0x%x readFromIdx: 0x%x writeToIdx: 0x%x\n", 606 startIdx, endIdx, *refCnt, readFromIdx, writeToIdx); 607 *overflow = true; 608 } 609 startIdx = 0; 610 } 611 612 if (arrCnt >= endIdx+1) { 613 range = CFRangeMake(startIdx, endIdx-startIdx+1); 614 CFArrayAppendArray(updates, activity.log, range); 615 } 616 else { 617 asl_log(0,0,ASL_LEVEL_ERR, "final: arrCnt is %ld. expected >= %ld\n", arrCnt, endIdx+1); 618 *overflow = true; 619 *refCnt = activity.idx; 620 goto exit; 621 } 622 623 624 serializedLog = CFPropertyListCreateData(0, updates, 625 kCFPropertyListBinaryFormat_v1_0, 0, NULL); 626 627 if (!serializedLog) 628 goto exit; 629 630 *logSize = (mach_msg_type_number_t)CFDataGetLength(serializedLog); 631 vm_allocate(mach_task_self(), (vm_address_t *)log, *logSize, TRUE); 632 if (*log == 0) 633 goto exit; 634 635 memcpy((void *)*log, CFDataGetBytePtr(serializedLog), *logSize); 636 *rc = kIOReturnSuccess; 637 638 *refCnt = activity.idx; // Number of entries saved since last reset 639 640exit: 641 if (serializedLog) 642 CFRelease(serializedLog); 643 644 if (updates) 645 CFRelease(updates); 646 return KERN_SUCCESS; 647} 648 649 650struct aggregateStats { 651 CFMutableDataRef reportBufs; /* IOReporter's simple array buffers for each process */ 652 uint32_t bufSize; /* Memory size allocated for reportBufs */ 653 uint64_t curTime; 654 655 CFMutableDictionaryRef legend; /* Legend for all channels. Each process has a channel */ 656}; 657 658// This will be moved to IOReportTypes.h later 659#undef IOREPORT_MAKECHTYPE 660#define IOREPORT_NELEMENTSSHIFT 32 661#define IOREPORT_NELEMENTSMASK 0x0000ffff00000000 662#define IOREPORT_MAKECHTYPE(format, categories, nelems) \ 663 ((((uint64_t)(nelems)) << IOREPORT_NELEMENTSSHIFT) | ((format) & 0xff | (uint32_t)(categories) << 16)) 664#define kIOPMStatsGroup CFSTR("I/O Kit Power Management") 665#define kIOPMAssertionsSub CFSTR("Power Assertions") 666 667void updateProcAssertionStats(ProcessInfo *pinfo, struct aggregateStats *aggStats) 668{ 669 void *ptr2cpy = NULL; 670 uint32_t size2cpy = 0; 671 uint64_t duration = 0; 672 uint64_t chType = 0; 673 IOReturn ret; 674 675 676 effectStats_t *stats = NULL; 677 static CFStringRef providerName = NULL; 678 static CFMutableDictionaryRef unitInfo = NULL; 679 680 if (pinfo->reportBuf == NULL) return; 681 682 if (aggStats->reportBufs == NULL) { 683 aggStats->reportBufs = CFDataCreateMutable(NULL, 0); 684 if (!aggStats->reportBufs) return; 685 } 686 687 if (providerName == NULL) { 688 providerName = IOReportCopyCurrentProcessName(); 689 if (providerName == NULL) return; 690 } 691 692 if (unitInfo == NULL) { 693 IOReportUnits unit = kIOReportUnit_s; 694 unitInfo = CFDictionaryCreateMutable(NULL, 1, 695 &kCFTypeDictionaryKeyCallBacks, 696 &kCFTypeDictionaryValueCallBacks); 697 if (!unitInfo) return; 698 699 CFNumberRef unitNum = CFNumberCreate(NULL, kCFNumberLongLongType, &unit); 700 if (!unitNum) return; 701 CFDictionarySetValue(unitInfo, CFSTR(kIOReportLegendUnitKey), unitNum); 702 CFRelease(unitNum); 703 } 704 705 if (aggStats->legend == NULL) { 706 aggStats->legend = IOReportCreateAggregate(0); 707 if (aggStats->legend == NULL) return; 708 } 709 710 chType = IOREPORT_MAKECHTYPE(kIOReportFormatSimpleArray, kIOReportCategoryPower, kMaxEffectStats); 711 ret = IOReportAddChannelDescription(aggStats->legend, getpid(), 712 providerName, pinfo->pid, 713 chType, CFSTR("Assertion duration by process"), 714 kIOPMStatsGroup, kIOPMAssertionsSub, 715 unitInfo, NULL); 716 if (ret != kIOReturnSuccess) 717 return; 718 719 720 for (kerAssertionEffect i = kNoEffect; i < kMaxEffectStats; i++) { 721 stats = &pinfo->stats[i]; 722 if (stats->cnt) { 723 duration = aggStats->curTime - stats->startTime; 724 SIMPLEARRAY_INCREMENTVALUE(pinfo->reportBuf, i, duration); 725 } 726 stats->startTime = aggStats->curTime; 727 } 728 729 SIMPLEARRAY_UPDATEPREP(pinfo->reportBuf, ptr2cpy, size2cpy); 730 CFDataAppendBytes(aggStats->reportBufs, ptr2cpy, size2cpy); 731} 732 733int qcompare(const void *p1, const void*p2) 734{ 735 const ProcessInfo *proc1 = *((ProcessInfo **)p1); 736 const ProcessInfo *proc2 = *((ProcessInfo **)p2); 737 738 if (proc1->create_seq < proc2->create_seq) return -1; 739 if (proc1->create_seq == proc2->create_seq) return 0; 740 return 1; 741 742 743} 744 745/* 746 * This MIG call will return 747 * CFArrayRef procNames : Array of process names for which stats are accumulated 748 * void *reportBufs : IOReporter's simple array buffers for each process. 749 * 750 * This data has to be fed to IOReporter APIs to get stats per process. 751 * Each IOReporter simple array has stats in the below order: 752 * [0]: kNoEffect 753 * [1]: kPrevIdleSlpEffect 754 * [2]: kPrevDemandSlpEffect 755 * [3]: kPrevDisplaySlpEffect 756 */ 757kern_return_t _io_pm_assertion_activity_aggregate ( 758 mach_port_t server __unused, 759 audit_token_t token, 760 vm_offset_t *statsData, 761 mach_msg_type_number_t *statsSize, 762 int *rc) 763{ 764 CFIndex j, cnt; 765 CFDataRef serializedArray = NULL; 766 ProcessInfo **procs = NULL; 767 CFMutableDictionaryRef samples = NULL; 768 struct aggregateStats aggStats; 769 770 *statsSize = 0; 771 *rc = kIOReturnError; 772 773 if (gActivityAggCnt == 0) { 774 *rc = kIOReturnNotOpen; 775 goto exit; 776 } 777 memset(&aggStats, 0, sizeof(aggStats)); 778 aggStats.curTime = getMonotonicTime(); 779 780 cnt = CFDictionaryGetCount(gProcessDict); 781 procs = malloc(cnt*(sizeof(procs))); 782 if (!procs) { 783 *rc = kIOReturnNoMemory; 784 goto exit; 785 } 786 787 memset(procs, 0, cnt*(sizeof(procs))); 788 CFDictionaryGetKeysAndValues(gProcessDict, NULL, (const void **)procs); 789 790 // Sort this array of ProcessInfo structs to return in same order every time. 791 // This is to overcome the limitation in IOReporting(see 16270424) 792 qsort(procs, cnt, sizeof(procs), qcompare); 793 794 for (j = 0; (j < cnt) && (procs[j]); j++) { 795 updateProcAssertionStats(procs[j], &aggStats); 796 } 797 798 samples = IOReportCreateSamplesRaw(aggStats.legend, aggStats.reportBufs, NULL); 799 free(procs); 800 801 *rc = kIOReturnSuccess; 802 803 if (samples == 0) { 804 /* No data collected */ 805 return KERN_SUCCESS; 806 } 807 808 serializedArray = CFPropertyListCreateData(0, samples, 809 kCFPropertyListBinaryFormat_v1_0, 0, NULL); 810 811 if (serializedArray) { 812 *statsSize = (mach_msg_type_number_t)CFDataGetLength(serializedArray); 813 vm_allocate(mach_task_self(), (vm_address_t *)statsData, *statsSize, TRUE); 814 memcpy((void*)*statsData, CFDataGetBytePtr(serializedArray), *statsSize); 815 } 816 else { 817 *rc = kIOReturnInternalError; 818 goto exit; 819 } 820 821exit: 822 if (samples) 823 CFRelease(samples); 824 if (aggStats.legend) 825 CFRelease(aggStats.legend); 826 if (serializedArray) 827 CFRelease(serializedArray); 828 829 if (aggStats.reportBufs) 830 CFRelease(aggStats.reportBufs); 831 832 return KERN_SUCCESS; 833} 834