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