xref: /illumos-gate/usr/src/lib/libc/port/threads/tdb_agent.c (revision 54034eb2d6e7d811adf4a1fe5105eac6fea6b0b5)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This file contains most of the functionality
31  * required to support the threads portion of libc_db.
32  */
33 
34 #include "lint.h"
35 #include "thr_uberdata.h"
36 
37 static void
38 tdb_event_ready(void) {}
39 
40 static void
41 tdb_event_sleep(void) {}
42 
43 static void
44 tdb_event_switchto(void) {}
45 
46 static void
47 tdb_event_switchfrom(void) {}
48 
49 static void
50 tdb_event_lock_try(void) {}
51 
52 static void
53 tdb_event_catchsig(void) {}
54 
55 static void
56 tdb_event_idle(void) {}
57 
58 static void
59 tdb_event_create(void) {}
60 
61 static void
62 tdb_event_death(void) {}
63 
64 static void
65 tdb_event_preempt(void) {}
66 
67 static void
68 tdb_event_pri_inherit(void) {}
69 
70 static void
71 tdb_event_reap(void) {}
72 
73 static void
74 tdb_event_concurrency(void) {}
75 
76 static void
77 tdb_event_timeout(void) {}
78 
79 /*
80  * uberflags.uf_tdb_register_sync is set to REGISTER_SYNC_ENABLE by a debugger
81  * to empty the table and then enable synchronization object registration.
82  *
83  * uberflags.uf_tdb_register_sync is set to REGISTER_SYNC_DISABLE by a debugger
84  * to empty the table and then disable synchronization object registration.
85  */
86 
87 const tdb_ev_func_t tdb_events[TD_MAX_EVENT_NUM - TD_MIN_EVENT_NUM + 1] = {
88 	tdb_event_ready,
89 	tdb_event_sleep,
90 	tdb_event_switchto,
91 	tdb_event_switchfrom,
92 	tdb_event_lock_try,
93 	tdb_event_catchsig,
94 	tdb_event_idle,
95 	tdb_event_create,
96 	tdb_event_death,
97 	tdb_event_preempt,
98 	tdb_event_pri_inherit,
99 	tdb_event_reap,
100 	tdb_event_concurrency,
101 	tdb_event_timeout
102 };
103 
104 #if TDB_HASH_SHIFT != 15
105 #error "this is all broken because TDB_HASH_SHIFT is not 15"
106 #endif
107 
108 static uint_t
109 tdb_addr_hash(void *addr)
110 {
111 	/*
112 	 * This knows for a fact that the hash table has
113 	 * 32K entries; that is, that TDB_HASH_SHIFT is 15.
114 	 */
115 #ifdef	_LP64
116 	uint64_t value60 = ((uintptr_t)addr >> 4);	/* 60 bits */
117 	uint32_t value30 = (value60 >> 30) ^ (value60 & 0x3fffffff);
118 #else
119 	uint32_t value30 = ((uintptr_t)addr >> 2);	/* 30 bits */
120 #endif
121 	return ((value30 >> 15) ^ (value30 & 0x7fff));
122 }
123 
124 static tdb_sync_stats_t *
125 alloc_sync_addr(void *addr)
126 {
127 	uberdata_t *udp = curthread->ul_uberdata;
128 	tdb_t *tdbp = &udp->tdb;
129 	tdb_sync_stats_t *sap;
130 
131 	ASSERT(MUTEX_OWNED(&udp->tdb_hash_lock, curthread));
132 
133 	if ((sap = tdbp->tdb_sync_addr_free) == NULL) {
134 		void *vaddr;
135 		int i;
136 
137 		/*
138 		 * Don't keep trying after mmap() has already failed.
139 		 */
140 		if (tdbp->tdb_hash_alloc_failed)
141 			return (NULL);
142 
143 		/* double the allocation each time */
144 		tdbp->tdb_sync_alloc *= 2;
145 		if ((vaddr = mmap(NULL,
146 		    tdbp->tdb_sync_alloc * sizeof (tdb_sync_stats_t),
147 		    PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON,
148 		    -1, (off_t)0)) == MAP_FAILED) {
149 			tdbp->tdb_hash_alloc_failed = 1;
150 			return (NULL);
151 		}
152 		sap = tdbp->tdb_sync_addr_free = vaddr;
153 		for (i = 1; i < tdbp->tdb_sync_alloc; sap++, i++)
154 			sap->next = (uintptr_t)(sap + 1);
155 		sap->next = (uintptr_t)0;
156 		tdbp->tdb_sync_addr_last = sap;
157 
158 		sap = tdbp->tdb_sync_addr_free;
159 	}
160 
161 	tdbp->tdb_sync_addr_free = (tdb_sync_stats_t *)(uintptr_t)sap->next;
162 	sap->next = (uintptr_t)0;
163 	sap->sync_addr = (uintptr_t)addr;
164 	(void) memset(&sap->un, 0, sizeof (sap->un));
165 	return (sap);
166 }
167 
168 static void
169 initialize_sync_hash()
170 {
171 	uberdata_t *udp = curthread->ul_uberdata;
172 	tdb_t *tdbp = &udp->tdb;
173 	uint64_t *addr_hash;
174 	tdb_sync_stats_t *sap;
175 	void *vaddr;
176 	int i;
177 
178 	if (tdbp->tdb_hash_alloc_failed)
179 		return;
180 	lmutex_lock(&udp->tdb_hash_lock);
181 	if (udp->uberflags.uf_tdb_register_sync == REGISTER_SYNC_DISABLE) {
182 		/*
183 		 * There is no point allocating the hash table
184 		 * if we are disabling registration.
185 		 */
186 		udp->uberflags.uf_tdb_register_sync = REGISTER_SYNC_OFF;
187 		lmutex_unlock(&udp->tdb_hash_lock);
188 		return;
189 	}
190 	if (tdbp->tdb_sync_addr_hash != NULL || tdbp->tdb_hash_alloc_failed) {
191 		lmutex_unlock(&udp->tdb_hash_lock);
192 		return;
193 	}
194 	/* start with a free list of 2k elements */
195 	tdbp->tdb_sync_alloc = 2*1024;
196 	if ((vaddr = mmap(NULL, TDB_HASH_SIZE * sizeof (uint64_t) +
197 	    tdbp->tdb_sync_alloc * sizeof (tdb_sync_stats_t),
198 	    PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON,
199 	    -1, (off_t)0)) == MAP_FAILED) {
200 		tdbp->tdb_hash_alloc_failed = 1;
201 		return;
202 	}
203 	addr_hash = vaddr;
204 
205 	/* initialize the free list */
206 	tdbp->tdb_sync_addr_free = sap =
207 	    (tdb_sync_stats_t *)&addr_hash[TDB_HASH_SIZE];
208 	for (i = 1; i < tdbp->tdb_sync_alloc; sap++, i++)
209 		sap->next = (uintptr_t)(sap + 1);
210 	sap->next = (uintptr_t)0;
211 	tdbp->tdb_sync_addr_last = sap;
212 
213 	/* insert &udp->tdb_hash_lock itself into the new (empty) table */
214 	udp->tdb_hash_lock_stats.next = (uintptr_t)0;
215 	udp->tdb_hash_lock_stats.sync_addr = (uintptr_t)&udp->tdb_hash_lock;
216 	addr_hash[tdb_addr_hash(&udp->tdb_hash_lock)] =
217 	    (uintptr_t)&udp->tdb_hash_lock_stats;
218 
219 	tdbp->tdb_register_count = 1;
220 	/* assign to tdb_sync_addr_hash only after fully initialized */
221 	membar_producer();
222 	tdbp->tdb_sync_addr_hash = addr_hash;
223 	lmutex_unlock(&udp->tdb_hash_lock);
224 }
225 
226 tdb_sync_stats_t *
227 tdb_sync_obj_register(void *addr, int *new)
228 {
229 	ulwp_t *self = curthread;
230 	uberdata_t *udp = self->ul_uberdata;
231 	tdb_t *tdbp = &udp->tdb;
232 	uint64_t *sapp;
233 	tdb_sync_stats_t *sap = NULL;
234 	int locked = 0;
235 	int i;
236 
237 	/*
238 	 * Don't start statistics collection until
239 	 * we have initialized the primary link map.
240 	 */
241 	if (!self->ul_primarymap)
242 		return (NULL);
243 
244 	if (new)
245 		*new = 0;
246 	/*
247 	 * To avoid recursion problems, we must do two things:
248 	 * 1. Make a special case for tdb_hash_lock (we use it internally).
249 	 * 2. Deal with the dynamic linker's lock interface:
250 	 *    When calling any external function, we may invoke the
251 	 *    dynamic linker.  It grabs a lock, which calls back here.
252 	 *    This only happens on the first call to the external
253 	 *    function, so we can just return NULL if we are called
254 	 *    recursively (and miss the first count).
255 	 */
256 	if (addr == (void *)&udp->tdb_hash_lock)
257 		return (&udp->tdb_hash_lock_stats);
258 	if (self->ul_sync_obj_reg)		/* recursive call */
259 		return (NULL);
260 	self->ul_sync_obj_reg = 1;
261 
262 	/*
263 	 * On the first time through, initialize the hash table and free list.
264 	 */
265 	if (tdbp->tdb_sync_addr_hash == NULL) {
266 		initialize_sync_hash();
267 		if (tdbp->tdb_sync_addr_hash == NULL) {	/* utter failure */
268 			udp->uberflags.uf_tdb_register_sync = REGISTER_SYNC_OFF;
269 			goto out;
270 		}
271 	}
272 	membar_consumer();
273 
274 	sapp = &tdbp->tdb_sync_addr_hash[tdb_addr_hash(addr)];
275 	if (udp->uberflags.uf_tdb_register_sync == REGISTER_SYNC_ON) {
276 		/*
277 		 * Look up an address in the synchronization object hash table.
278 		 * No lock is required since it can only deliver a false
279 		 * negative, in which case we fall into the locked case below.
280 		 */
281 		for (sap = (tdb_sync_stats_t *)(uintptr_t)*sapp; sap != NULL;
282 		    sap = (tdb_sync_stats_t *)(uintptr_t)sap->next) {
283 			if (sap->sync_addr == (uintptr_t)addr)
284 				goto out;
285 		}
286 	}
287 
288 	/*
289 	 * The search with no lock held failed or a special action is required.
290 	 * Grab tdb_hash_lock to do special actions and/or get a precise result.
291 	 */
292 	lmutex_lock(&udp->tdb_hash_lock);
293 	locked = 1;
294 
295 	switch (udp->uberflags.uf_tdb_register_sync) {
296 	case REGISTER_SYNC_ON:
297 		break;
298 	case REGISTER_SYNC_OFF:
299 		goto out;
300 	default:
301 		/*
302 		 * For all debugger actions, first zero out the
303 		 * statistics block of every element in the hash table.
304 		 */
305 		for (i = 0; i < TDB_HASH_SIZE; i++)
306 			for (sap = (tdb_sync_stats_t *)
307 			    (uintptr_t)tdbp->tdb_sync_addr_hash[i];
308 			    sap != NULL;
309 			    sap = (tdb_sync_stats_t *)(uintptr_t)sap->next)
310 				(void) memset(&sap->un, 0, sizeof (sap->un));
311 
312 		switch (udp->uberflags.uf_tdb_register_sync) {
313 		case REGISTER_SYNC_ENABLE:
314 			udp->uberflags.uf_tdb_register_sync = REGISTER_SYNC_ON;
315 			break;
316 		case REGISTER_SYNC_DISABLE:
317 		default:
318 			udp->uberflags.uf_tdb_register_sync = REGISTER_SYNC_OFF;
319 			goto out;
320 		}
321 		break;
322 	}
323 
324 	/*
325 	 * Perform the search while holding tdb_hash_lock.
326 	 * Keep track of the insertion point.
327 	 */
328 	while ((sap = (tdb_sync_stats_t *)(uintptr_t)*sapp) != NULL) {
329 		if (sap->sync_addr == (uintptr_t)addr)
330 			break;
331 		sapp = &sap->next;
332 	}
333 
334 	/*
335 	 * Insert a new element if necessary.
336 	 */
337 	if (sap == NULL && (sap = alloc_sync_addr(addr)) != NULL) {
338 		*sapp = (uintptr_t)sap;
339 		tdbp->tdb_register_count++;
340 		if (new)
341 			*new = 1;
342 	}
343 
344 out:
345 	if (locked)
346 		lmutex_unlock(&udp->tdb_hash_lock);
347 	self->ul_sync_obj_reg = 0;
348 	return (sap);
349 }
350 
351 void
352 tdb_sync_obj_deregister(void *addr)
353 {
354 	uberdata_t *udp = curthread->ul_uberdata;
355 	tdb_t *tdbp = &udp->tdb;
356 	uint64_t *sapp;
357 	tdb_sync_stats_t *sap;
358 	uint_t hash;
359 
360 	/*
361 	 * tdb_hash_lock is never destroyed.
362 	 */
363 	ASSERT(addr != &udp->tdb_hash_lock);
364 
365 	/*
366 	 * Avoid acquiring tdb_hash_lock if lock statistics gathering has
367 	 * never been initiated or there is nothing in the hash bucket.
368 	 * (Once the hash table is allocated, it is never deallocated.)
369 	 */
370 	if (tdbp->tdb_sync_addr_hash == NULL ||
371 	    tdbp->tdb_sync_addr_hash[hash = tdb_addr_hash(addr)] == NULL)
372 		return;
373 
374 	lmutex_lock(&udp->tdb_hash_lock);
375 	sapp = &tdbp->tdb_sync_addr_hash[hash];
376 	while ((sap = (tdb_sync_stats_t *)(uintptr_t)*sapp) != NULL) {
377 		if (sap->sync_addr == (uintptr_t)addr) {
378 			/* remove it from the hash table */
379 			*sapp = sap->next;
380 			tdbp->tdb_register_count--;
381 			/* clear it */
382 			sap->next = (uintptr_t)0;
383 			sap->sync_addr = (uintptr_t)0;
384 			/* insert it on the tail of the free list */
385 			if (tdbp->tdb_sync_addr_free == NULL) {
386 				tdbp->tdb_sync_addr_free = sap;
387 				tdbp->tdb_sync_addr_last = sap;
388 			} else {
389 				tdbp->tdb_sync_addr_last->next = (uintptr_t)sap;
390 				tdbp->tdb_sync_addr_last = sap;
391 			}
392 			break;
393 		}
394 		sapp = &sap->next;
395 	}
396 	lmutex_unlock(&udp->tdb_hash_lock);
397 }
398 
399 /*
400  * Return a mutex statistics block for the given mutex.
401  */
402 tdb_mutex_stats_t *
403 tdb_mutex_stats(mutex_t *mp)
404 {
405 	tdb_sync_stats_t *tssp;
406 
407 	/* avoid stealing the cache line unnecessarily */
408 	if (mp->mutex_magic != MUTEX_MAGIC)
409 		mp->mutex_magic = MUTEX_MAGIC;
410 	if ((tssp = tdb_sync_obj_register(mp, NULL)) == NULL)
411 		return (NULL);
412 	tssp->un.type = TDB_MUTEX;
413 	return (&tssp->un.mutex);
414 }
415 
416 /*
417  * Return a condvar statistics block for the given condvar.
418  */
419 tdb_cond_stats_t *
420 tdb_cond_stats(cond_t *cvp)
421 {
422 	tdb_sync_stats_t *tssp;
423 
424 	/* avoid stealing the cache line unnecessarily */
425 	if (cvp->cond_magic != COND_MAGIC)
426 		cvp->cond_magic = COND_MAGIC;
427 	if ((tssp = tdb_sync_obj_register(cvp, NULL)) == NULL)
428 		return (NULL);
429 	tssp->un.type = TDB_COND;
430 	return (&tssp->un.cond);
431 }
432 
433 /*
434  * Return an rwlock statistics block for the given rwlock.
435  */
436 tdb_rwlock_stats_t *
437 tdb_rwlock_stats(rwlock_t *rwlp)
438 {
439 	tdb_sync_stats_t *tssp;
440 
441 	/* avoid stealing the cache line unnecessarily */
442 	if (rwlp->magic != RWL_MAGIC)
443 		rwlp->magic = RWL_MAGIC;
444 	if ((tssp = tdb_sync_obj_register(rwlp, NULL)) == NULL)
445 		return (NULL);
446 	tssp->un.type = TDB_RWLOCK;
447 	return (&tssp->un.rwlock);
448 }
449 
450 /*
451  * Return a semaphore statistics block for the given semaphore.
452  */
453 tdb_sema_stats_t *
454 tdb_sema_stats(sema_t *sp)
455 {
456 	tdb_sync_stats_t *tssp;
457 	int new;
458 
459 	/* avoid stealing the cache line unnecessarily */
460 	if (sp->magic != SEMA_MAGIC)
461 		sp->magic = SEMA_MAGIC;
462 	if ((tssp = tdb_sync_obj_register(sp, &new)) == NULL)
463 		return (NULL);
464 	tssp->un.type = TDB_SEMA;
465 	if (new) {
466 		tssp->un.sema.sema_max_count = sp->count;
467 		tssp->un.sema.sema_min_count = sp->count;
468 	}
469 	return (&tssp->un.sema);
470 }
471