]>
Commit | Line | Data |
---|---|---|
f427ee49 A |
1 | /* |
2 | * Copyright (c) 2019 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_OSREFERENCE_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. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
14 | * | |
15 | * Please obtain a copy of the License at | |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
25 | * | |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | |
27 | */ | |
28 | ||
29 | #include <darwintest.h> | |
30 | #include <darwintest_multiprocess.h> | |
31 | #include <stdio.h> | |
32 | #include <unistd.h> | |
33 | #include <sys/wait.h> | |
34 | #include <pthread.h> | |
35 | #include <stdlib.h> | |
36 | #include <signal.h> | |
37 | #include <dispatch/dispatch.h> | |
38 | #include <fcntl.h> | |
39 | #include <string.h> | |
40 | #include <errno.h> | |
41 | #include <sys/ioctl.h> | |
42 | #include <sys/socket.h> | |
43 | #include <sys/kern_event.h> | |
44 | #include <sys/sysctl.h> | |
45 | #include "../bsd/sys/kern_memorystatus.h" | |
46 | ||
47 | static int | |
48 | read_event(int so) | |
49 | { | |
50 | ssize_t status; | |
51 | char buf[256]; | |
52 | struct kern_event_msg *ev_msg = (struct kern_event_msg *)&buf[0]; | |
53 | ||
54 | status = recv(so, &buf, sizeof(buf), 0); | |
55 | if (status == -1) { | |
56 | T_LOG("recv() failed: %s", strerror(errno)); | |
57 | return -1; | |
58 | } | |
59 | ||
60 | if (ev_msg->total_size > status) { | |
61 | T_LOG("missed SYSPROTO_EVENT event, buffer not big enough"); | |
62 | return -1; | |
63 | } | |
64 | ||
65 | if (ev_msg->vendor_code == KEV_VENDOR_APPLE && ev_msg->kev_class == KEV_SYSTEM_CLASS && ev_msg->kev_subclass == KEV_DIRTYSTATUS_SUBCLASS) { | |
66 | if (ev_msg->event_code == kDirtyStatusChangeNote) { | |
67 | dirty_status_change_event_t *ev_data = (dirty_status_change_event_t *)&ev_msg->event_data; | |
68 | switch (ev_data->dsc_event_type) { | |
69 | case kDirtyStatusChangedClean: | |
70 | case kDirtyStatusChangedDirty: | |
71 | break; | |
72 | default: | |
73 | T_LOG("Unknown event type %d", ev_data->dsc_event_type); | |
74 | return -1; | |
75 | } | |
76 | T_LOG("Process: %s, status: %s, pages: %llu, timestamp: %llu, priority: %d", | |
77 | ev_data->dsc_process_name, ev_data->dsc_event_type == kDirtyStatusChangedDirty ? "dirty" : "clean", ev_data->dsc_pages, ev_data->dsc_time, ev_data->dsc_priority); | |
78 | return 1; | |
79 | } else { | |
80 | T_LOG("Ignoring message with code: %d", ev_msg->event_code); | |
81 | } | |
82 | } else { | |
83 | T_LOG(("Unexpected event with vendor code: %d"), ev_msg->vendor_code); | |
84 | return -1; | |
85 | } | |
86 | return 0; | |
87 | } | |
88 | ||
89 | ||
90 | T_DECL(dirtiness_tracking, | |
91 | "Check if we are able to receive dirtiness-tracking events from the kernel") | |
92 | { | |
93 | int so, status; | |
94 | struct kev_request kev_req; | |
95 | int enable_sysctl = 1; | |
96 | ||
97 | // First try enabling the dirtystatus_tracking sysctl if available | |
98 | if (sysctlbyname("kern.dirtystatus_tracking_enabled", NULL, NULL, &enable_sysctl, sizeof(enable_sysctl)) != 0) { | |
99 | T_SKIP("The kern.dirtystatus_tracking_enabled sysctl is not available, skipping..."); | |
100 | } | |
101 | /* Open an event socket */ | |
102 | so = socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT); | |
103 | if (so != -1) { | |
104 | /* establish filter to return all events */ | |
105 | kev_req.vendor_code = KEV_VENDOR_APPLE; | |
106 | kev_req.kev_class = KEV_SYSTEM_CLASS;/* Not used if vendor_code is 0 */ | |
107 | kev_req.kev_subclass = KEV_DIRTYSTATUS_SUBCLASS; /* Not used if either kev_class OR vendor_code are 0 */ | |
108 | status = ioctl(so, SIOCSKEVFILT, &kev_req); | |
109 | if (status) { | |
110 | so = -1; | |
111 | T_FAIL("could not establish event filter, ioctl() failed: %s", strerror(errno)); | |
112 | T_END; | |
113 | } | |
114 | } else { | |
115 | T_FAIL("could not open event socket, socket() failed: %s", strerror(errno)); | |
116 | T_END; | |
117 | } | |
118 | ||
119 | if (so != -1) { | |
120 | int yes = 1; | |
121 | ||
122 | status = ioctl(so, FIONBIO, &yes); | |
123 | if (status) { | |
124 | (void) close(so); | |
125 | so = -1; | |
126 | T_FAIL( "could not set non-blocking io, ioctl() failed: %s", strerror(errno)); | |
127 | T_END; | |
128 | } | |
129 | } | |
130 | ||
131 | if (so == -1) { | |
132 | T_FAIL("memory monitor disabled"); | |
133 | T_END; | |
134 | } | |
135 | ||
136 | ||
137 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); | |
138 | ||
139 | dispatch_source_t read_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, | |
140 | (uintptr_t)so, 0, queue); | |
141 | ||
142 | dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); | |
143 | dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, 15 * NSEC_PER_SEC), 5 * NSEC_PER_SEC, 1 * NSEC_PER_SEC); | |
144 | ||
145 | dispatch_source_set_event_handler(read_source, ^{ | |
146 | int rc = read_event(so); | |
147 | if (rc != 0) { | |
148 | dispatch_source_cancel(read_source); | |
149 | if (rc == 1) { | |
150 | T_PASS("Dirtiness-tracking Kevent successfully received"); | |
151 | } else { | |
152 | T_FAIL("Could not read from the system socket, aborting data collection"); | |
153 | } | |
154 | } | |
155 | }); | |
156 | ||
157 | dispatch_source_set_cancel_handler(read_source, ^{ | |
158 | close(so); | |
159 | dispatch_cancel(timer); | |
160 | T_END; | |
161 | }); | |
162 | ||
163 | dispatch_activate(read_source); | |
164 | ||
165 | dispatch_source_set_event_handler(timer, ^{ | |
166 | dispatch_cancel(read_source); | |
167 | T_FAIL("Timeout expired, no events received from the kernel"); | |
168 | T_END; | |
169 | }); | |
170 | dispatch_activate(timer); | |
171 | ||
172 | dispatch_main(); | |
173 | } |