xref: /xnu-8792.61.2/osfmk/vm/analytics.c (revision 42e220869062b56f8d7d0726fd4c88954f87902c)
1*42e22086SApple OSS Distributions /*
2*42e22086SApple OSS Distributions  * Copyright (c) 2000-2021 Apple Inc. All rights reserved.
3*42e22086SApple OSS Distributions  *
4*42e22086SApple OSS Distributions  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5*42e22086SApple OSS Distributions  *
6*42e22086SApple OSS Distributions  * This file contains Original Code and/or Modifications of Original Code
7*42e22086SApple OSS Distributions  * as defined in and that are subject to the Apple Public Source License
8*42e22086SApple OSS Distributions  * Version 2.0 (the 'License'). You may not use this file except in
9*42e22086SApple OSS Distributions  * compliance with the License. The rights granted to you under the License
10*42e22086SApple OSS Distributions  * may not be used to create, or enable the creation or redistribution of,
11*42e22086SApple OSS Distributions  * unlawful or unlicensed copies of an Apple operating system, or to
12*42e22086SApple OSS Distributions  * circumvent, violate, or enable the circumvention or violation of, any
13*42e22086SApple OSS Distributions  * terms of an Apple operating system software license agreement.
14*42e22086SApple OSS Distributions  *
15*42e22086SApple OSS Distributions  * Please obtain a copy of the License at
16*42e22086SApple OSS Distributions  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17*42e22086SApple OSS Distributions  *
18*42e22086SApple OSS Distributions  * The Original Code and all software distributed under the License are
19*42e22086SApple OSS Distributions  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20*42e22086SApple OSS Distributions  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21*42e22086SApple OSS Distributions  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22*42e22086SApple OSS Distributions  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23*42e22086SApple OSS Distributions  * Please see the License for the specific language governing rights and
24*42e22086SApple OSS Distributions  * limitations under the License.
25*42e22086SApple OSS Distributions  *
26*42e22086SApple OSS Distributions  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27*42e22086SApple OSS Distributions  */
28*42e22086SApple OSS Distributions 
29*42e22086SApple OSS Distributions /*
30*42e22086SApple OSS Distributions  * Telemetry from the VM is usually colected at a daily cadence.
31*42e22086SApple OSS Distributions  * All of those events are in this file along with a single thread
32*42e22086SApple OSS Distributions  * call for reporting them.
33*42e22086SApple OSS Distributions  *
34*42e22086SApple OSS Distributions  * NB: The freezer subsystem has its own telemetry based on its budget interval
35*42e22086SApple OSS Distributions  * so it's not included here.
36*42e22086SApple OSS Distributions  */
37*42e22086SApple OSS Distributions 
38*42e22086SApple OSS Distributions #include <libkern/coreanalytics/coreanalytics.h>
39*42e22086SApple OSS Distributions #include "vm_compressor_backing_store.h"
40*42e22086SApple OSS Distributions #include <vm/vm_page.h>
41*42e22086SApple OSS Distributions #include <kern/thread_call.h>
42*42e22086SApple OSS Distributions 
43*42e22086SApple OSS Distributions void vm_analytics_tick(void *arg0, void *arg1);
44*42e22086SApple OSS Distributions 
45*42e22086SApple OSS Distributions #define ANALYTICS_PERIOD_HOURS (24ULL)
46*42e22086SApple OSS Distributions 
47*42e22086SApple OSS Distributions static thread_call_t vm_analytics_thread_call;
48*42e22086SApple OSS Distributions 
49*42e22086SApple OSS Distributions CA_EVENT(vm_swapusage,
50*42e22086SApple OSS Distributions     CA_INT, max_alloced,
51*42e22086SApple OSS Distributions     CA_INT, max_used,
52*42e22086SApple OSS Distributions     CA_INT, trial_deployment_id,
53*42e22086SApple OSS Distributions     CA_STATIC_STRING(CA_UUID_LEN), trial_treatment_id,
54*42e22086SApple OSS Distributions     CA_STATIC_STRING(CA_UUID_LEN), trial_experiment_id);
55*42e22086SApple OSS Distributions 
56*42e22086SApple OSS Distributions CA_EVENT(mlock_failures,
57*42e22086SApple OSS Distributions     CA_INT, over_global_limit,
58*42e22086SApple OSS Distributions     CA_INT, over_user_limit,
59*42e22086SApple OSS Distributions     CA_INT, trial_deployment_id,
60*42e22086SApple OSS Distributions     CA_STATIC_STRING(CA_UUID_LEN), trial_treatment_id,
61*42e22086SApple OSS Distributions     CA_STATIC_STRING(CA_UUID_LEN), trial_experiment_id);
62*42e22086SApple OSS Distributions 
63*42e22086SApple OSS Distributions /*
64*42e22086SApple OSS Distributions  * NB: It's a good practice to include these trial
65*42e22086SApple OSS Distributions  * identifiers in all of our events so that we can
66*42e22086SApple OSS Distributions  * measure the impact of any A/B tests on these metrics.
67*42e22086SApple OSS Distributions  */
68*42e22086SApple OSS Distributions extern uuid_string_t trial_treatment_id;
69*42e22086SApple OSS Distributions extern uuid_string_t trial_experiment_id;
70*42e22086SApple OSS Distributions extern int trial_deployment_id;
71*42e22086SApple OSS Distributions 
72*42e22086SApple OSS Distributions static void
add_trial_uuids(char * treatment_id,char * experiment_id)73*42e22086SApple OSS Distributions add_trial_uuids(char *treatment_id, char *experiment_id)
74*42e22086SApple OSS Distributions {
75*42e22086SApple OSS Distributions 	strlcpy(treatment_id, trial_treatment_id, CA_UUID_LEN);
76*42e22086SApple OSS Distributions 	strlcpy(experiment_id, trial_experiment_id, CA_UUID_LEN);
77*42e22086SApple OSS Distributions }
78*42e22086SApple OSS Distributions 
79*42e22086SApple OSS Distributions static void
report_vm_swapusage()80*42e22086SApple OSS Distributions report_vm_swapusage()
81*42e22086SApple OSS Distributions {
82*42e22086SApple OSS Distributions 	uint64_t max_alloced, max_used;
83*42e22086SApple OSS Distributions 	ca_event_t event = CA_EVENT_ALLOCATE(vm_swapusage);
84*42e22086SApple OSS Distributions 	CA_EVENT_TYPE(vm_swapusage) * e = event->data;
85*42e22086SApple OSS Distributions 
86*42e22086SApple OSS Distributions 	vm_swap_reset_max_segs_tracking(&max_alloced, &max_used);
87*42e22086SApple OSS Distributions 	e->max_alloced = max_alloced;
88*42e22086SApple OSS Distributions 	e->max_used = max_used;
89*42e22086SApple OSS Distributions 	add_trial_uuids(e->trial_treatment_id, e->trial_experiment_id);
90*42e22086SApple OSS Distributions 	e->trial_deployment_id = trial_deployment_id;
91*42e22086SApple OSS Distributions 	CA_EVENT_SEND(event);
92*42e22086SApple OSS Distributions }
93*42e22086SApple OSS Distributions 
94*42e22086SApple OSS Distributions static void
report_mlock_failures()95*42e22086SApple OSS Distributions report_mlock_failures()
96*42e22086SApple OSS Distributions {
97*42e22086SApple OSS Distributions 	ca_event_t event = CA_EVENT_ALLOCATE(mlock_failures);
98*42e22086SApple OSS Distributions 	CA_EVENT_TYPE(mlock_failures) * e = event->data;
99*42e22086SApple OSS Distributions 
100*42e22086SApple OSS Distributions 	e->over_global_limit = os_atomic_load_wide(&vm_add_wire_count_over_global_limit, relaxed);
101*42e22086SApple OSS Distributions 	e->over_user_limit = os_atomic_load_wide(&vm_add_wire_count_over_user_limit, relaxed);
102*42e22086SApple OSS Distributions 
103*42e22086SApple OSS Distributions 	os_atomic_store_wide(&vm_add_wire_count_over_global_limit, 0, relaxed);
104*42e22086SApple OSS Distributions 	os_atomic_store_wide(&vm_add_wire_count_over_user_limit, 0, relaxed);
105*42e22086SApple OSS Distributions 
106*42e22086SApple OSS Distributions 	add_trial_uuids(e->trial_treatment_id, e->trial_experiment_id);
107*42e22086SApple OSS Distributions 	e->trial_deployment_id = trial_deployment_id;
108*42e22086SApple OSS Distributions 	CA_EVENT_SEND(event);
109*42e22086SApple OSS Distributions }
110*42e22086SApple OSS Distributions 
111*42e22086SApple OSS Distributions #if XNU_TARGET_OS_WATCH
112*42e22086SApple OSS Distributions CA_EVENT(compressor_age,
113*42e22086SApple OSS Distributions     CA_INT, hour1,
114*42e22086SApple OSS Distributions     CA_INT, hour6,
115*42e22086SApple OSS Distributions     CA_INT, hour12,
116*42e22086SApple OSS Distributions     CA_INT, hour24,
117*42e22086SApple OSS Distributions     CA_INT, hour36,
118*42e22086SApple OSS Distributions     CA_INT, hour48,
119*42e22086SApple OSS Distributions     CA_INT, hourMax,
120*42e22086SApple OSS Distributions     CA_INT, trial_deployment_id,
121*42e22086SApple OSS Distributions     CA_STATIC_STRING(CA_UUID_LEN), trial_treatment_id,
122*42e22086SApple OSS Distributions     CA_STATIC_STRING(CA_UUID_LEN), trial_experiment_id);
123*42e22086SApple OSS Distributions 
124*42e22086SApple OSS Distributions /**
125*42e22086SApple OSS Distributions  * Compressor age bucket descriptor.
126*42e22086SApple OSS Distributions  */
127*42e22086SApple OSS Distributions typedef struct {
128*42e22086SApple OSS Distributions 	/* Number of segments in this bucket. */
129*42e22086SApple OSS Distributions 	uint64_t count;
130*42e22086SApple OSS Distributions 	/* The bucket's lower bound (inclusive) */
131*42e22086SApple OSS Distributions 	uint64_t lower;
132*42e22086SApple OSS Distributions 	/* The bucket's upper bound (exclusive) */
133*42e22086SApple OSS Distributions 	uint64_t upper;
134*42e22086SApple OSS Distributions } c_reporting_bucket_t;
135*42e22086SApple OSS Distributions #define C_REPORTING_BUCKETS_MAX (UINT64_MAX)
136*42e22086SApple OSS Distributions #ifndef ARRAY_SIZE
137*42e22086SApple OSS Distributions #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
138*42e22086SApple OSS Distributions #endif
139*42e22086SApple OSS Distributions #define HR_TO_S(x) ((x) * 60 * 60)
140*42e22086SApple OSS Distributions 
141*42e22086SApple OSS Distributions /**
142*42e22086SApple OSS Distributions  * Report the age of segments in the compressor.
143*42e22086SApple OSS Distributions  */
144*42e22086SApple OSS Distributions static void
report_compressor_age()145*42e22086SApple OSS Distributions report_compressor_age()
146*42e22086SApple OSS Distributions {
147*42e22086SApple OSS Distributions 	const queue_head_t *c_queues[] = {&c_age_list_head, &c_major_list_head};
148*42e22086SApple OSS Distributions 	c_reporting_bucket_t c_buckets[] = {
149*42e22086SApple OSS Distributions 		{.count = 0, .lower = HR_TO_S(0), .upper = HR_TO_S(1)},  /* [0, 1) hours */
150*42e22086SApple OSS Distributions 		{.count = 0, .lower = HR_TO_S(1), .upper = HR_TO_S(6)},  /* [1, 6) hours */
151*42e22086SApple OSS Distributions 		{.count = 0, .lower = HR_TO_S(6), .upper = HR_TO_S(12)},  /* [6, 12) hours */
152*42e22086SApple OSS Distributions 		{.count = 0, .lower = HR_TO_S(12), .upper = HR_TO_S(24)}, /* [12, 24) hours */
153*42e22086SApple OSS Distributions 		{.count = 0, .lower = HR_TO_S(24), .upper = HR_TO_S(36)}, /* [24, 36) hours */
154*42e22086SApple OSS Distributions 		{.count = 0, .lower = HR_TO_S(36), .upper = HR_TO_S(48)}, /* [36, 48) hours */
155*42e22086SApple OSS Distributions 		{.count = 0, .lower = HR_TO_S(48), .upper = C_REPORTING_BUCKETS_MAX}, /* [48, MAX) hours */
156*42e22086SApple OSS Distributions 	};
157*42e22086SApple OSS Distributions 	clock_sec_t now;
158*42e22086SApple OSS Distributions 	clock_nsec_t nsec;
159*42e22086SApple OSS Distributions 
160*42e22086SApple OSS Distributions 	/* Collect the segments and update the bucket counts. */
161*42e22086SApple OSS Distributions 	lck_mtx_lock_spin_always(c_list_lock);
162*42e22086SApple OSS Distributions 	for (unsigned q = 0; q < ARRAY_SIZE(c_queues); q++) {
163*42e22086SApple OSS Distributions 		c_segment_t c_seg = (c_segment_t) queue_first(c_queues[q]);
164*42e22086SApple OSS Distributions 		while (!queue_end(c_queues[q], (queue_entry_t) c_seg)) {
165*42e22086SApple OSS Distributions 			for (unsigned b = 0; b < ARRAY_SIZE(c_buckets); b++) {
166*42e22086SApple OSS Distributions 				uint32_t creation_ts = c_seg->c_creation_ts;
167*42e22086SApple OSS Distributions 				clock_get_system_nanotime(&now, &nsec);
168*42e22086SApple OSS Distributions 				clock_sec_t age = now - creation_ts;
169*42e22086SApple OSS Distributions 				if ((age >= c_buckets[b].lower) &&
170*42e22086SApple OSS Distributions 				    (age < c_buckets[b].upper)) {
171*42e22086SApple OSS Distributions 					c_buckets[b].count++;
172*42e22086SApple OSS Distributions 					break;
173*42e22086SApple OSS Distributions 				}
174*42e22086SApple OSS Distributions 			}
175*42e22086SApple OSS Distributions 			c_seg = (c_segment_t) queue_next(&c_seg->c_age_list);
176*42e22086SApple OSS Distributions 		}
177*42e22086SApple OSS Distributions 	}
178*42e22086SApple OSS Distributions 	lck_mtx_unlock_always(c_list_lock);
179*42e22086SApple OSS Distributions 
180*42e22086SApple OSS Distributions 	/* Send the ages to CoreAnalytics. */
181*42e22086SApple OSS Distributions 	ca_event_t event = CA_EVENT_ALLOCATE(compressor_age);
182*42e22086SApple OSS Distributions 	CA_EVENT_TYPE(compressor_age) * e = event->data;
183*42e22086SApple OSS Distributions 	e->hour1 = c_buckets[0].count;
184*42e22086SApple OSS Distributions 	e->hour6 = c_buckets[1].count;
185*42e22086SApple OSS Distributions 	e->hour12 = c_buckets[2].count;
186*42e22086SApple OSS Distributions 	e->hour24 = c_buckets[3].count;
187*42e22086SApple OSS Distributions 	e->hour36 = c_buckets[4].count;
188*42e22086SApple OSS Distributions 	e->hour48 = c_buckets[5].count;
189*42e22086SApple OSS Distributions 	e->hourMax = c_buckets[6].count;
190*42e22086SApple OSS Distributions 	add_trial_uuids(e->trial_treatment_id, e->trial_experiment_id);
191*42e22086SApple OSS Distributions 	e->trial_deployment_id = trial_deployment_id;
192*42e22086SApple OSS Distributions 	CA_EVENT_SEND(event);
193*42e22086SApple OSS Distributions }
194*42e22086SApple OSS Distributions #endif /* XNU_TARGET_OS_WATCH */
195*42e22086SApple OSS Distributions 
196*42e22086SApple OSS Distributions static void
schedule_analytics_thread_call()197*42e22086SApple OSS Distributions schedule_analytics_thread_call()
198*42e22086SApple OSS Distributions {
199*42e22086SApple OSS Distributions 	static const uint64_t analytics_period_ns = ANALYTICS_PERIOD_HOURS * 60 * 60 * NSEC_PER_SEC;
200*42e22086SApple OSS Distributions 	uint64_t analytics_period_absolutetime;
201*42e22086SApple OSS Distributions 	nanoseconds_to_absolutetime(analytics_period_ns, &analytics_period_absolutetime);
202*42e22086SApple OSS Distributions 
203*42e22086SApple OSS Distributions 	thread_call_enter_delayed(vm_analytics_thread_call, analytics_period_absolutetime + mach_absolute_time());
204*42e22086SApple OSS Distributions }
205*42e22086SApple OSS Distributions 
206*42e22086SApple OSS Distributions /*
207*42e22086SApple OSS Distributions  * This is the main entry point for reporting periodic analytics.
208*42e22086SApple OSS Distributions  * It's called once every ANALYTICS_PERIOD_HOURS hours.
209*42e22086SApple OSS Distributions  */
210*42e22086SApple OSS Distributions void
vm_analytics_tick(void * arg0,void * arg1)211*42e22086SApple OSS Distributions vm_analytics_tick(void *arg0, void *arg1)
212*42e22086SApple OSS Distributions {
213*42e22086SApple OSS Distributions #pragma unused(arg0, arg1)
214*42e22086SApple OSS Distributions 	report_vm_swapusage();
215*42e22086SApple OSS Distributions 	report_mlock_failures();
216*42e22086SApple OSS Distributions #if XNU_TARGET_OS_WATCH
217*42e22086SApple OSS Distributions 	report_compressor_age();
218*42e22086SApple OSS Distributions #endif /* XNU_TARGET_OS_WATCH */
219*42e22086SApple OSS Distributions 	schedule_analytics_thread_call();
220*42e22086SApple OSS Distributions }
221*42e22086SApple OSS Distributions 
222*42e22086SApple OSS Distributions static void
vm_analytics_init()223*42e22086SApple OSS Distributions vm_analytics_init()
224*42e22086SApple OSS Distributions {
225*42e22086SApple OSS Distributions 	vm_analytics_thread_call = thread_call_allocate_with_options(vm_analytics_tick, NULL, THREAD_CALL_PRIORITY_KERNEL, THREAD_CALL_OPTIONS_ONCE);
226*42e22086SApple OSS Distributions 	schedule_analytics_thread_call();
227*42e22086SApple OSS Distributions }
228*42e22086SApple OSS Distributions 
229*42e22086SApple OSS Distributions STARTUP(THREAD_CALL, STARTUP_RANK_MIDDLE, vm_analytics_init);
230