1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2015 Intel Corporation
3  */
4 
5 #include <rte_log.h>
6 #include <rte_common.h>
7 
8 #include "lthread_diag.h"
9 #include "lthread_queue.h"
10 #include "lthread_pool.h"
11 #include "lthread_objcache.h"
12 #include "lthread_sched.h"
13 #include "lthread_diag_api.h"
14 
15 
16 /* dummy ref value of default diagnostic callback */
17 static uint64_t dummy_ref;
18 
19 #define DIAG_SCHED_STATS_FORMAT \
20 "core %d\n%33s %12s %12s %12s %12s\n"
21 
22 #define DIAG_CACHE_STATS_FORMAT \
23 "%20s %12lu %12lu %12lu %12lu %12lu\n"
24 
25 #define DIAG_QUEUE_STATS_FORMAT \
26 "%20s %12lu %12lu %12lu\n"
27 
28 
29 /*
30  * texts used in diagnostic events,
31  * corresponding diagnostic mask bit positions are given as comment
32  */
33 const char *diag_event_text[] = {
34 	"LTHREAD_CREATE     ",	/* 00 */
35 	"LTHREAD_EXIT       ",	/* 01 */
36 	"LTHREAD_JOIN       ",	/* 02 */
37 	"LTHREAD_CANCEL     ",	/* 03 */
38 	"LTHREAD_DETACH     ",	/* 04 */
39 	"LTHREAD_FREE       ",	/* 05 */
40 	"LTHREAD_SUSPENDED  ",	/* 06 */
41 	"LTHREAD_YIELD      ",	/* 07 */
42 	"LTHREAD_RESCHEDULED",	/* 08 */
43 	"LTHREAD_SLEEP      ",	/* 09 */
44 	"LTHREAD_RESUMED    ",	/* 10 */
45 	"LTHREAD_AFFINITY   ",	/* 11 */
46 	"LTHREAD_TMR_START  ",	/* 12 */
47 	"LTHREAD_TMR_DELETE ",	/* 13 */
48 	"LTHREAD_TMR_EXPIRED",	/* 14 */
49 	"COND_CREATE        ",	/* 15 */
50 	"COND_DESTROY       ",	/* 16 */
51 	"COND_WAIT          ",	/* 17 */
52 	"COND_SIGNAL        ",	/* 18 */
53 	"COND_BROADCAST     ",	/* 19 */
54 	"MUTEX_CREATE       ",	/* 20 */
55 	"MUTEX_DESTROY      ",	/* 21 */
56 	"MUTEX_LOCK         ",	/* 22 */
57 	"MUTEX_TRYLOCK      ",	/* 23 */
58 	"MUTEX_BLOCKED      ",	/* 24 */
59 	"MUTEX_UNLOCKED     ",	/* 25 */
60 	"SCHED_CREATE       ",	/* 26 */
61 	"SCHED_SHUTDOWN     "	/* 27 */
62 };
63 
64 
65 /*
66  * set diagnostic ,ask
67  */
68 void lthread_diagnostic_set_mask(DIAG_USED uint64_t mask)
69 {
70 #if LTHREAD_DIAG
71 	diag_mask = mask;
72 #else
73 	RTE_LOG(INFO, LTHREAD,
74 		"LTHREAD_DIAG is not set, see lthread_diag_api.h\n");
75 #endif
76 }
77 
78 
79 /*
80  * Check consistency of the scheduler stats
81  * Only sensible run after the schedulers are stopped
82  * Count the number of objects lying in caches and queues
83  * and available in the qnode pool.
84  * This should be equal to the total capacity of all
85  * qnode pools.
86  */
87 void
88 _sched_stats_consistency_check(void);
89 void
90 _sched_stats_consistency_check(void)
91 {
92 #if LTHREAD_DIAG
93 	int i;
94 	struct lthread_sched *sched;
95 	uint64_t count = 0;
96 	uint64_t capacity = 0;
97 
98 	for (i = 0; i < LTHREAD_MAX_LCORES; i++) {
99 		sched = schedcore[i];
100 		if (sched == NULL)
101 			continue;
102 
103 		/* each of these queues consumes a stub node */
104 		count += 8;
105 		count += DIAG_COUNT(sched->ready, size);
106 		count += DIAG_COUNT(sched->pready, size);
107 		count += DIAG_COUNT(sched->lthread_cache, available);
108 		count += DIAG_COUNT(sched->stack_cache, available);
109 		count += DIAG_COUNT(sched->tls_cache, available);
110 		count += DIAG_COUNT(sched->per_lthread_cache, available);
111 		count += DIAG_COUNT(sched->cond_cache, available);
112 		count += DIAG_COUNT(sched->mutex_cache, available);
113 
114 		/* the node pool does not consume a stub node */
115 		if (sched->qnode_pool->fast_alloc != NULL)
116 			count++;
117 		count += DIAG_COUNT(sched->qnode_pool, available);
118 
119 		capacity += DIAG_COUNT(sched->qnode_pool, capacity);
120 	}
121 	if (count != capacity) {
122 		RTE_LOG(CRIT, LTHREAD,
123 			"Scheduler caches are inconsistent\n");
124 	} else {
125 		RTE_LOG(INFO, LTHREAD,
126 			"Scheduler caches are ok\n");
127 	}
128 #endif
129 }
130 
131 
132 #if LTHREAD_DIAG
133 /*
134  * Display node pool stats
135  */
136 static inline void
137 _qnode_pool_display(DIAG_USED struct qnode_pool *p)
138 {
139 
140 	printf(DIAG_CACHE_STATS_FORMAT,
141 			p->name,
142 			DIAG_COUNT(p, rd),
143 			DIAG_COUNT(p, wr),
144 			DIAG_COUNT(p, available),
145 			DIAG_COUNT(p, prealloc),
146 			DIAG_COUNT(p, capacity));
147 	fflush(stdout);
148 }
149 #endif
150 
151 
152 #if LTHREAD_DIAG
153 /*
154  * Display queue stats
155  */
156 static inline void
157 _lthread_queue_display(DIAG_USED struct lthread_queue *q)
158 {
159 #if DISPLAY_OBJCACHE_QUEUES
160 	printf(DIAG_QUEUE_STATS_FORMAT,
161 			q->name,
162 			DIAG_COUNT(q, rd),
163 			DIAG_COUNT(q, wr),
164 			DIAG_COUNT(q, size));
165 	fflush(stdout);
166 #else
167 	printf("%s: queue stats disabled\n",
168 			q->name);
169 
170 #endif
171 }
172 #endif
173 
174 #if LTHREAD_DIAG
175 /*
176  * Display objcache stats
177  */
178 static inline void
179 _objcache_display(DIAG_USED struct lthread_objcache *c)
180 {
181 
182 	printf(DIAG_CACHE_STATS_FORMAT,
183 			c->name,
184 			DIAG_COUNT(c, rd),
185 			DIAG_COUNT(c, wr),
186 			DIAG_COUNT(c, available),
187 			DIAG_COUNT(c, prealloc),
188 			DIAG_COUNT(c, capacity));
189 	_lthread_queue_display(c->q);
190 	fflush(stdout);
191 }
192 #endif
193 
194 /*
195  * Display sched stats
196  */
197 void
198 lthread_sched_stats_display(void)
199 {
200 #if LTHREAD_DIAG
201 	int i;
202 	struct lthread_sched *sched;
203 
204 	for (i = 0; i < LTHREAD_MAX_LCORES; i++) {
205 		sched = schedcore[i];
206 		if (sched != NULL) {
207 			printf(DIAG_SCHED_STATS_FORMAT,
208 					sched->lcore_id,
209 					"rd",
210 					"wr",
211 					"present",
212 					"nb preallocs",
213 					"capacity");
214 			_lthread_queue_display(sched->ready);
215 			_lthread_queue_display(sched->pready);
216 			_qnode_pool_display(sched->qnode_pool);
217 			_objcache_display(sched->lthread_cache);
218 			_objcache_display(sched->stack_cache);
219 			_objcache_display(sched->tls_cache);
220 			_objcache_display(sched->per_lthread_cache);
221 			_objcache_display(sched->cond_cache);
222 			_objcache_display(sched->mutex_cache);
223 		fflush(stdout);
224 		}
225 	}
226 	_sched_stats_consistency_check();
227 #else
228 	RTE_LOG(INFO, LTHREAD,
229 		"lthread diagnostics disabled\n"
230 		"hint - set LTHREAD_DIAG in lthread_diag_api.h\n");
231 #endif
232 }
233 
234 /*
235  * Defafult diagnostic callback
236  */
237 static uint64_t
238 _lthread_diag_default_cb(uint64_t time, struct lthread *lt, int diag_event,
239 		uint64_t diag_ref, const char *text, uint64_t p1, uint64_t p2)
240 {
241 	uint64_t _p2;
242 	int lcore = (int) rte_lcore_id();
243 
244 	switch (diag_event) {
245 	case LT_DIAG_LTHREAD_CREATE:
246 	case LT_DIAG_MUTEX_CREATE:
247 	case LT_DIAG_COND_CREATE:
248 		_p2 = dummy_ref;
249 		break;
250 	default:
251 		_p2 = p2;
252 		break;
253 	}
254 
255 	printf("%"PRIu64" %d %8.8lx %8.8lx %s %8.8lx %8.8lx\n",
256 		time,
257 		lcore,
258 		(uint64_t) lt,
259 		diag_ref,
260 		text,
261 		p1,
262 		_p2);
263 
264 	return dummy_ref++;
265 }
266 
267 /*
268  * plug in default diag callback with mask off
269  */
270 RTE_INIT(_lthread_diag_ctor)
271 {
272 	diag_cb = _lthread_diag_default_cb;
273 	diag_mask = 0;
274 }
275 
276 
277 /*
278  * enable diagnostics
279  */
280 void lthread_diagnostic_enable(DIAG_USED diag_callback cb,
281 				DIAG_USED uint64_t mask)
282 {
283 #if LTHREAD_DIAG
284 	if (cb == NULL)
285 		diag_cb = _lthread_diag_default_cb;
286 	else
287 		diag_cb = cb;
288 	diag_mask = mask;
289 #else
290 	RTE_LOG(INFO, LTHREAD,
291 		"LTHREAD_DIAG is not set, see lthread_diag_api.h\n");
292 #endif
293 }
294