1 /*
2  * libstatgrab
3  * https://libstatgrab.org
4  * Copyright (C) 2003-2004 Peter Saunders
5  * Copyright (C) 2003-2019 Tim Bishop
6  * Copyright (C) 2003-2013 Adam Sampson
7  * Copyright (C) 2012-2019 Jens Rehsack
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301, USA.
23  */
24 
25 #ifndef STATGRAB_GLOBALS_H
26 #define STATGRAB_GLOBALS_H
27 
28 typedef sg_error (*comp_global_init_function)(unsigned id);
29 typedef void (*comp_global_destroy_function)(void);
30 typedef void (*comp_global_cleanup_function)(void *);
31 
32 struct sg_comp_status {
33 	sg_error init_error;
34 };
35 
36 #if defined(ENABLE_THREADS) && defined(HAVE_PTHREAD)
37 #define DECLARE_REQUIRED_COMP_LOCKS(comp,...) static const char * sg_##comp##_lock_names[] = { __VA_ARGS__ };
38 #define REQUIRED_COMP_LOCKS(comp) sg_##comp##_lock_names,
39 #else
40 #define DECLARE_REQUIRED_COMP_LOCKS(comp,...)
41 #define REQUIRED_COMP_LOCKS(comp)
42 #endif
43 
44 struct sg_comp_init {
45 	comp_global_init_function init_fn;
46 	comp_global_destroy_function destroy_fn;
47 	comp_global_cleanup_function cleanup_fn;
48 	size_t static_buf_size;
49 #if defined(ENABLE_THREADS) && defined(HAVE_PTHREAD)
50 	const char **required_locks;
51 #endif
52 	struct sg_comp_status *status;
53 };
54 
55 void *sg_comp_get_tls(unsigned id);
56 
57 #define DEFAULT_INIT_COMP(comp,...)			\
58 	static sg_error sg_##comp##_init_comp(unsigned);\
59 	static void sg_##comp##_destroy_comp(void);	\
60 	static void sg_##comp##_cleanup_comp(void *);	\
61 							\
62 	static struct sg_comp_status sg_##comp##_status;\
63 	DECLARE_REQUIRED_COMP_LOCKS(comp, __VA_ARGS__)	\
64 							\
65 	__sg_private				    \
66 	const struct sg_comp_init sg_##comp##_init = {	\
67 		sg_##comp##_init_comp,			\
68 		sg_##comp##_destroy_comp,		\
69 		sg_##comp##_cleanup_comp,		\
70 		sizeof(struct sg_##comp##_glob),	\
71 		REQUIRED_COMP_LOCKS(comp)		\
72 		&sg_##comp##_status			\
73 	};						\
74 	static unsigned int sg_##comp##_glob_id
75 
76 #define GLOBAL_GET_TLS(comp) (struct sg_##comp##_glob *)(sg_comp_get_tls(sg_##comp##_glob_id))
77 #define GLOBAL_SET_ID(comp,id) sg_##comp##_glob_id = (id)
78 
79 #define EASY_COMP_INIT_FN(comp)				\
80 	static sg_error sg_##comp##_init_comp(unsigned id){ \
81 		GLOBAL_SET_ID(comp,id);			\
82 		return SG_ERROR_NONE;			\
83 	}
84 
85 #define EASY_COMP_DESTROY_FN(comp)			\
86 	static void sg_##comp##_destroy_comp(void){}
87 
88 #define EASY_COMP_CLEANUP_FN(comp,nvect)		\
89 	static void sg_##comp##_cleanup_comp(void *p){	\
90 		struct sg_##comp##_glob *comp##_glob = p; \
91 		unsigned i;				\
92 		TRACE_LOG_FMT(#comp, "sg_" #comp "_cleanup_comp(%p)", p); \
93 		assert(comp##_glob);			\
94 		for( i = 0; i < nvect; ++i )		\
95 			sg_vector_free(comp##_glob->comp##_vectors[i]); \
96 		memset(comp##_glob->comp##_vectors, 0, sizeof(comp##_glob->comp##_vectors) ); \
97 	}
98 
99 #define EASY_COMP_SETUP(comp,nvect,...)			\
100 	struct sg_##comp##_glob {			\
101 		sg_vector *comp##_vectors[nvect];	\
102 	};						\
103 							\
104 	static unsigned int sg_##comp##_glob_id;	\
105 							\
106 	EASY_COMP_INIT_FN(comp)				\
107 	EASY_COMP_DESTROY_FN(comp)			\
108 	EASY_COMP_CLEANUP_FN(comp,nvect)		\
109 							\
110 	static struct sg_comp_status sg_##comp##_status;\
111 	DECLARE_REQUIRED_COMP_LOCKS(comp, __VA_ARGS__)	\
112 							\
113 	__sg_private				    \
114 	const struct sg_comp_init sg_##comp##_init = {	\
115 		sg_##comp##_init_comp,			\
116 		sg_##comp##_destroy_comp,		\
117 		sg_##comp##_cleanup_comp,		\
118 		sizeof(struct sg_##comp##_glob),	\
119 		REQUIRED_COMP_LOCKS(comp)		\
120 		&sg_##comp##_status			\
121 	}
122 
123 #define EXTENDED_COMP_SETUP(comp,nvect,...)		\
124 	static sg_error sg_##comp##_init_comp(unsigned);\
125 	static void sg_##comp##_destroy_comp(void);	\
126 	static void sg_##comp##_cleanup_comp(void *);	\
127 							\
128 	struct sg_##comp##_glob {			\
129 		sg_vector *comp##_vectors[nvect];	\
130 	};						\
131 							\
132 	static unsigned int sg_##comp##_glob_id;	\
133 	DECLARE_REQUIRED_COMP_LOCKS(comp, __VA_ARGS__)	\
134 							\
135 	static struct sg_comp_status sg_##comp##_status;\
136 	__sg_private				    \
137 	const struct sg_comp_init sg_##comp##_init = {	\
138 		sg_##comp##_init_comp,			\
139 		sg_##comp##_destroy_comp,		\
140 		sg_##comp##_cleanup_comp,		\
141 		sizeof(struct sg_##comp##_glob),	\
142 		REQUIRED_COMP_LOCKS(comp)		\
143 		&sg_##comp##_status			\
144 	}
145 
146 #define EASY_COMP_ACCESS(fn,comp,name,idx)		\
147 	sg_##name##_stats *fn(size_t *entries) {	\
148 							\
149 		struct sg_##comp##_glob *comp##_glob = GLOBAL_GET_TLS(comp); \
150 		sg_error rc;				\
151 		TRACE_LOG(#comp, "entering " #fn); \
152 		if( !comp##_glob ) {			\
153 			/* assuming error comp can't neither */ \
154 			ERROR_LOG(#comp, #fn " failed - cannot get glob"); \
155 			if(entries)			\
156 				*entries = 0;		\
157 			return NULL;			\
158 		}					\
159 							\
160 		if(!comp##_glob->comp##_vectors[idx])	\
161 			comp##_glob->comp##_vectors[idx] = sg_vector_create(1, 1, 1, & VECTOR_INIT_INFO(sg_##name##_stats)); \
162 							\
163 		if(comp##_glob->comp##_vectors[idx]){	\
164 			sg_##name##_stats *name##_stats = (sg_##name##_stats *)VECTOR_DATA(comp##_glob->comp##_vectors[idx]); \
165 			TRACE_LOG_FMT(#comp, "calling " #fn "_int(%p)", name##_stats); \
166 			rc = fn##_int(name##_stats);	\
167 			if(SG_ERROR_NONE == rc){	\
168 				TRACE_LOG(#comp, #fn " succeded"); \
169 				sg_clear_error();	\
170 				if(entries)		\
171 					*entries = VECTOR_ITEM_COUNT(comp##_glob->comp##_vectors[idx]); \
172 				return name##_stats;	\
173 			}				\
174 		}					\
175 		else {					\
176 			rc = sg_get_error();		\
177 		}					\
178 							\
179 		WARN_LOG_FMT(#comp, #fn " failed with %s", sg_str_error(rc)); \
180 							\
181 		if(entries)				\
182 			*entries = 0;			\
183 							\
184 		return NULL;				\
185 	}						\
186 							\
187 	sg_##name##_stats *fn##_r(size_t *entries) {	\
188 							\
189 		sg_vector *name##_stats_vector = sg_vector_create(1, 1, 1, & VECTOR_INIT_INFO(sg_##name##_stats)); \
190 		sg_error rc;				\
191 		TRACE_LOG(#comp, "entering " #fn "_r"); \
192 		if(name##_stats_vector) {		\
193 			sg_##name##_stats *name##_stats = (sg_##name##_stats *)VECTOR_DATA(name##_stats_vector); \
194 			TRACE_LOG_FMT(#comp, "calling " #fn "_int(%p)", name##_stats); \
195 			rc = fn##_int(name##_stats);	\
196 			if(SG_ERROR_NONE == rc){ 	\
197 				TRACE_LOG(#comp, #fn "_r succeded"); \
198 				sg_clear_error();	\
199 				if(entries)		\
200 					*entries = VECTOR_ITEM_COUNT(name##_stats_vector); \
201 				return name##_stats;	\
202 			}				\
203 			sg_vector_free(name##_stats_vector); \
204 		}					\
205 		else {					\
206 			rc = sg_get_error();		\
207 		}					\
208 							\
209 		WARN_LOG_FMT(#comp, #fn "_r failed with %s", sg_str_error(rc)); \
210 							\
211 		if(entries)				\
212 			*entries = 0;			\
213 							\
214 		return NULL;				\
215 	}
216 
217 #define MULTI_COMP_ACCESS(fn,comp,name,idx)		\
218 	sg_##name##_stats *fn(size_t *entries) {	\
219 							\
220 		struct sg_##comp##_glob *comp##_glob = GLOBAL_GET_TLS(comp); \
221 		sg_error rc;				\
222 		TRACE_LOG(#comp, "entering " #fn); \
223 		if( !comp##_glob ) {			\
224 			/* assuming error comp can't neither */ \
225 			ERROR_LOG(#comp, #fn " failed - cannot get glob"); \
226 			if(entries)			\
227 				*entries = 0;		\
228 			return NULL;			\
229 		}					\
230 							\
231 		if(!comp##_glob->comp##_vectors[idx])	\
232 			comp##_glob->comp##_vectors[idx] = VECTOR_CREATE(sg_##name##_stats, 16); \
233 		else					\
234 			VECTOR_CLEAR(comp##_glob->comp##_vectors[idx]); \
235 							\
236 		if(comp##_glob->comp##_vectors[idx]){	\
237 			TRACE_LOG_FMT(#comp, "calling " #fn "_int(%p)", &comp##_glob->comp##_vectors[idx]); \
238 			rc = fn##_int(&comp##_glob->comp##_vectors[idx]); \
239 			if(SG_ERROR_NONE == rc) {	\
240 				sg_##name##_stats *name##_stats = (sg_##name##_stats *)VECTOR_DATA(comp##_glob->comp##_vectors[idx]); \
241 				TRACE_LOG(#comp, #fn " succeded"); \
242 				sg_clear_error();	\
243 				if(entries)		\
244 					*entries = VECTOR_ITEM_COUNT(comp##_glob->comp##_vectors[idx]); \
245 				return name##_stats;	\
246 			}				\
247 		}					\
248 		else {					\
249 			rc = sg_get_error();		\
250 		}					\
251 							\
252 		WARN_LOG_FMT(#comp, #fn " failed with %s", sg_str_error(rc)); \
253 							\
254 		if(entries)				\
255 			*entries = 0;			\
256 							\
257 		return NULL;				\
258 	}						\
259 							\
260 	sg_##name##_stats *fn##_r(size_t *entries) {	\
261 		sg_vector *name##_vector = VECTOR_CREATE(sg_##name##_stats, 16); \
262 		sg_error rc;				\
263 		TRACE_LOG(#comp, "entering " #fn); \
264 		if(name##_vector) {			\
265 			TRACE_LOG_FMT(#comp, "calling " #fn "_int(%p)", &name##_vector); \
266 			rc = fn##_int(&name##_vector);	\
267 			if( SG_ERROR_NONE == rc ) {	\
268 				sg_##name##_stats *name##_stats = (sg_##name##_stats *)VECTOR_DATA(name##_vector); \
269 				TRACE_LOG(#comp, #fn " succeded"); \
270 				sg_clear_error();	\
271 				if(entries)		\
272 					*entries = VECTOR_ITEM_COUNT(name##_vector); \
273 				return name##_stats;	\
274 			}				\
275 			sg_vector_free(name##_vector);	\
276 		}					\
277 		else {					\
278 			rc = sg_get_error();		\
279 		}					\
280 							\
281 		WARN_LOG_FMT(#comp, #fn "_r failed with %s", sg_str_error(rc)); \
282 							\
283 		if(entries)				\
284 			*entries = 0;			\
285 							\
286 		return NULL;				\
287 	}
288 
289 #define EASY_COMP_DIFF(fn,getfn,comp,name,diffidx,nowidx) \
290 	sg_##name##_stats *fn(size_t *entries) {	\
291 							\
292 		struct sg_##comp##_glob *comp##_glob = GLOBAL_GET_TLS(comp); \
293 		TRACE_LOG(#comp, "entering " #fn);	\
294 		if( !comp##_glob ) {			\
295 			/* assuming error comp can't neither */ \
296 			ERROR_LOG(#comp, #fn " failed - cannot get glob"); \
297 			if(entries)			\
298 				*entries = 0;		\
299 			return NULL;			\
300 		}					\
301 							\
302 		if(!comp##_glob->comp##_vectors[nowidx]) { \
303 			TRACE_LOG_FMT(#comp, #fn " has nothing to compare with - calling %s", #getfn); \
304 			return getfn(entries);		\
305 		}					\
306 							\
307 		if(!comp##_glob->comp##_vectors[diffidx]) \
308 			comp##_glob->comp##_vectors[diffidx] = sg_vector_create(1, 1, 1, & VECTOR_INIT_INFO(sg_##name##_stats)); \
309 							\
310 		if(comp##_glob->comp##_vectors[diffidx]) { \
311 			sg_##name##_stats name##_last = *((sg_##name##_stats *)VECTOR_DATA(comp##_glob->comp##_vectors[nowidx])); \
312 			sg_##name##_stats *name##_diff = (sg_##name##_stats *)VECTOR_DATA(comp##_glob->comp##_vectors[diffidx]); \
313 			sg_##name##_stats *name##_now = getfn(NULL); \
314 							\
315 			TRACE_LOG_FMT(#comp, "calling " #fn "_diff(%p, %p, %p)", name##_diff, name##_now, &name##_last); \
316 			if( (NULL != name##_now) && ( SG_ERROR_NONE == fn##_int(name##_diff, name##_now, &name##_last) ) ) { \
317 				TRACE_LOG(#comp, #fn " succeded"); \
318 				sg_clear_error();	\
319 				if(entries)		\
320 					*entries = VECTOR_ITEM_COUNT(comp##_glob->comp##_vectors[diffidx]); \
321 				return name##_diff;	\
322 			}				\
323 		}					\
324 							\
325 		WARN_LOG_FMT(#comp, #fn " failed with %s", sg_str_error(sg_get_error())); \
326 							\
327 		if(entries)				\
328 			*entries = 0;			\
329 							\
330 		return NULL;				\
331 	}						\
332 							\
333 	sg_##name##_stats *fn##_between(const sg_##name##_stats *name##_now, const sg_##name##_stats *name##_last, size_t *entries) { \
334 							\
335 		sg_vector *name##_diff_vector;		\
336 							\
337 		TRACE_LOG(#comp, "entering " #fn "_between"); \
338 		name##_diff_vector = sg_vector_create(1, 1, 1, & VECTOR_INIT_INFO(sg_##name##_stats)); \
339 		if(name##_diff_vector){			\
340 			sg_error rc;			\
341 			sg_##name##_stats *name##_diff = (sg_##name##_stats *)VECTOR_DATA(name##_diff_vector); \
342 			TRACE_LOG_FMT(#comp, "calling " #fn "_diff(%p, %p, %p)", name##_diff, name##_now, &name##_last); \
343 			rc = fn##_int(name##_diff, name##_now, name##_last); \
344 			if( SG_ERROR_NONE == rc ) {	\
345 				TRACE_LOG(#comp, #fn "_between succeded"); \
346 				sg_clear_error();	\
347 				if(entries)		\
348 					*entries = VECTOR_ITEM_COUNT(name##_diff_vector); \
349 				return name##_diff;	\
350 			}				\
351 			sg_vector_free(name##_diff_vector); \
352 		}					\
353 							\
354 		WARN_LOG_FMT(#comp, #fn "_between failed with %s", sg_str_error(sg_get_error())); \
355 							\
356 		if(entries)				\
357 			*entries = 0;			\
358 							\
359 		return NULL;				\
360 	}
361 
362 #define MULTI_COMP_DIFF(fn,getfn,comp,name,diffidx,nowidx) \
363 	sg_##name##_stats *fn(size_t *entries){		\
364 							\
365 		struct sg_##comp##_glob *comp##_glob = GLOBAL_GET_TLS(comp); \
366 		TRACE_LOG(#comp, "entering " #fn); \
367 		if( !comp##_glob ) {			\
368 			/* assuming error comp can't neither */ \
369 			ERROR_LOG(#comp, #fn " failed - cannot get glob"); \
370 			if(entries)			\
371 				*entries = 0;		\
372 			return NULL;			\
373 		}					\
374 							\
375 		if(!comp##_glob->comp##_vectors[nowidx]){ \
376 			TRACE_LOG_FMT(#comp, #fn " has nothing to compare with - calling %s", #getfn); \
377 			return getfn(entries);		\
378 		}					\
379 							\
380 		if(!comp##_glob->comp##_vectors[diffidx]) \
381 			comp##_glob->comp##_vectors[diffidx] = VECTOR_CREATE(sg_##name##_stats, comp##_glob->comp##_vectors[nowidx]->used_count); \
382 							\
383 		if( comp##_glob->comp##_vectors[diffidx] ) { \
384 			sg_error rc;			\
385 			sg_vector *last = sg_vector_clone(comp##_glob->comp##_vectors[nowidx]); \
386 							\
387 			if(!last)			\
388 				goto err_out;		\
389 							\
390 			TRACE_LOG(#comp, "calling " #getfn "(NULL)"); \
391 			getfn(NULL);			\
392 							\
393 			rc = sg_vector_compute_diff(&comp##_glob->comp##_vectors[diffidx], comp##_glob->comp##_vectors[nowidx], last); \
394 			sg_vector_free( last );		\
395 							\
396 			if( SG_ERROR_NONE == rc ) {	\
397 				TRACE_LOG(#comp, #fn " succeded"); \
398 				sg_clear_error();	\
399 				if(entries)		\
400 					*entries = VECTOR_ITEM_COUNT(comp##_glob->comp##_vectors[diffidx]); \
401 				return VECTOR_DATA(comp##_glob->comp##_vectors[diffidx]); \
402 			}				\
403 		}					\
404 							\
405 	err_out:					\
406 		if(entries)				\
407 			*entries = 0;			\
408 							\
409 		WARN_LOG_FMT(#comp, #fn " failed with %s", sg_str_error(sg_get_error())); \
410 							\
411 		return NULL;				\
412 	}						\
413 							\
414 	sg_##name##_stats *				\
415 	fn##_between(const sg_##name##_stats *cur,	\
416 		     const sg_##name##_stats *last,	\
417 		     size_t *entries) {			\
418 							\
419 		sg_vector *name##_diff_vector;		\
420 		sg_error rc;				\
421 							\
422 		TRACE_LOG(#comp, "entering " #fn "_between"); \
423 		name##_diff_vector = VECTOR_CREATE(sg_##name##_stats, 1); \
424 		if(name##_diff_vector){			\
425 			rc = sg_vector_compute_diff(&name##_diff_vector, VECTOR_ADDRESS_CONST(cur), VECTOR_ADDRESS_CONST(last)); \
426 							\
427 			if( SG_ERROR_NONE == rc ) {	\
428 				TRACE_LOG(#comp, #fn "_between succeded"); \
429 				sg_clear_error();	\
430 				if(entries)		\
431 					*entries = VECTOR_ITEM_COUNT(name##_diff_vector); \
432 				return VECTOR_DATA(name##_diff_vector); \
433 			}				\
434 							\
435 			sg_vector_free(name##_diff_vector); \
436 		}					\
437 		else {					\
438 			rc = sg_get_error();		\
439 		}					\
440 							\
441 		WARN_LOG_FMT(#comp, #fn "_between failed with %s", sg_str_error(rc)); \
442 							\
443 		if(entries)				\
444 			*entries = 0;			\
445 							\
446 		return NULL;				\
447 	}
448 
449 __sg_private sg_error sg_comp_init(int ignore_init_errors);
450 __sg_private sg_error sg_comp_destroy(void);
451 __sg_private sg_error sg_global_lock(void);
452 __sg_private sg_error sg_global_unlock(void);
453 
454 #endif /* STATGRAB_GLOBALS_H */
455