1 /*
2 * Copyright (C) by Argonne National Laboratory
3 * See COPYRIGHT in top-level directory
4 */
5
6 #include "mpiimpl.h"
7
8 int MPIR_T_init_balance = 0;
9
10 #ifdef MPICH_IS_THREADED
11 MPID_Thread_mutex_t mpi_t_mutex;
12 int MPIR_T_is_threaded;
13 #endif
14
15 /* These variables must be initialized in MPI_T_initthread. Especially,
16 * hash table pointers must be initialized to NULL.
17 */
18 int cat_stamp;
19 UT_array *enum_table;
20 UT_array *cat_table;
21 UT_array *cvar_table;
22 UT_array *pvar_table;
23 name2index_hash_t *cat_hash;
24 name2index_hash_t *cvar_hash;
25 name2index_hash_t *pvar_hashs[MPIR_T_PVAR_CLASS_NUMBER];
26
27 /* Create an enum.
28 * IN: enum_name, name of the enum
29 * OUT: handle, handle of the enum
30 */
MPIR_T_enum_create(const char * enum_name,MPI_T_enum * handle)31 void MPIR_T_enum_create(const char *enum_name, MPI_T_enum * handle)
32 {
33 MPIR_T_enum_t *e;
34 static const UT_icd enum_item_icd = { sizeof(enum_item_t), NULL, NULL, NULL };
35
36 MPIR_Assert(enum_name);
37 MPIR_Assert(handle);
38
39 utarray_extend_back(enum_table, MPL_MEM_MPIT);
40 e = (MPIR_T_enum_t *) utarray_back(enum_table);
41 e->name = MPL_strdup(enum_name);
42 MPIR_Assert(e->name);
43 #ifdef HAVE_ERROR_CHECKING
44 e->kind = MPIR_T_ENUM_HANDLE;
45 #endif
46 utarray_new(e->items, &enum_item_icd, MPL_MEM_MPIT);
47 (*handle) = e;
48 }
49
50 /* Add an item to an exisiting enum.
51 * IN: handle, handle to the enum
52 * IN: item_name, name of the item
53 * IN: item_value, value associated with item_name
54 */
MPIR_T_enum_add_item(MPI_T_enum handle,const char * item_name,int item_value)55 void MPIR_T_enum_add_item(MPI_T_enum handle, const char *item_name, int item_value)
56 {
57 enum_item_t *item;
58
59 MPIR_Assert(handle);
60 MPIR_Assert(item_name);
61
62 utarray_extend_back(handle->items, MPL_MEM_MPIT);
63 item = (enum_item_t *) utarray_back(handle->items);
64 item->name = MPL_strdup(item_name);
65 MPIR_Assert(item->name);
66 item->value = item_value;
67 }
68
69 /* Create a new category with name <cat_name>.
70 * The new category is pushed at the back of cat_table.
71 * Aslo, a new hash entry is added for the category in cat_hash.
72 * Return the newly created category.
73 */
MPIR_T_cat_create(const char * cat_name)74 static cat_table_entry_t *MPIR_T_cat_create(const char *cat_name)
75 {
76 int cat_idx;
77 cat_table_entry_t *cat;
78 name2index_hash_t *hash_entry;
79
80 /* New a category */
81 utarray_extend_back(cat_table, MPL_MEM_MPIT);
82 cat = (cat_table_entry_t *) utarray_back(cat_table);
83 cat->name = MPL_strdup(cat_name);
84 cat->desc = NULL;
85 utarray_new(cat->cvar_indices, &ut_int_icd, MPL_MEM_MPIT);
86 utarray_new(cat->pvar_indices, &ut_int_icd, MPL_MEM_MPIT);
87 utarray_new(cat->subcat_indices, &ut_int_icd, MPL_MEM_MPIT);
88
89 /* Record <cat_name, cat_idx> in cat_hash */
90 cat_idx = utarray_len(cat_table) - 1;
91 hash_entry = MPL_malloc(sizeof(name2index_hash_t), MPL_MEM_MPIT);
92 MPIR_Assert(hash_entry);
93 /* Need not to Strdup cat_name, since cat_table and cat_hash co-exist */
94 hash_entry->name = cat_name;
95 hash_entry->idx = cat_idx;
96 HASH_ADD_KEYPTR(hh, cat_hash, hash_entry->name,
97 strlen(hash_entry->name), hash_entry, MPL_MEM_MPIT);
98
99 return cat;
100 }
101
102 /* Add a pvar to an existing or new category
103 * IN: cat_name, name of the category
104 * IN: pvar_index, index of the pvar as defined by MPI_T_pvar_handle_alloc()
105 * If cat_name is NULL or a empty string, nothing happpens.
106 */
MPIR_T_cat_add_pvar(const char * cat_name,int pvar_index)107 int MPIR_T_cat_add_pvar(const char *cat_name, int pvar_index)
108 {
109 int mpi_errno = MPI_SUCCESS;
110 name2index_hash_t *hash_entry;
111 cat_table_entry_t *cat;
112
113 /* NULL or empty string are allowed */
114 if (cat_name == NULL || *cat_name == '\0')
115 goto fn_exit;
116
117 HASH_FIND_STR(cat_hash, cat_name, hash_entry);
118
119 if (hash_entry != NULL) {
120 /* Found it, i.e., category already exists */
121 int cat_idx = hash_entry->idx;
122 cat = (cat_table_entry_t *) utarray_eltptr(cat_table, cat_idx);
123 /* FIXME: Is it worth checking duplicated vars? Probably not */
124 utarray_push_back(cat->pvar_indices, &pvar_index, MPL_MEM_MPIT);
125 } else {
126 /* Not found, so create a new category */
127 cat = MPIR_T_cat_create(cat_name);
128 utarray_push_back(cat->pvar_indices, &pvar_index, MPL_MEM_MPIT);
129 /* Notify categories have been changed */
130 cat_stamp++;
131 }
132 fn_exit:
133 return mpi_errno;
134 }
135
136 /* Add a cvar to an existing or new category
137 * IN: cat_name, name of the category
138 * IN: cvar_index, index of the cvar as defined by MPI_T_cvar_handle_alloc()
139 * If cat_name is NULL or a empty string, nothing happpens.
140 */
MPIR_T_cat_add_cvar(const char * cat_name,int cvar_index)141 int MPIR_T_cat_add_cvar(const char *cat_name, int cvar_index)
142 {
143 int mpi_errno = MPI_SUCCESS;
144 name2index_hash_t *hash_entry;
145 cat_table_entry_t *cat;
146
147 /* NULL or empty string are allowed */
148 if (cat_name == NULL || *cat_name == '\0')
149 goto fn_exit;
150
151 HASH_FIND_STR(cat_hash, cat_name, hash_entry);
152
153 if (hash_entry != NULL) {
154 /* Found it, i.e., category already exists */
155 int cat_idx = hash_entry->idx;
156 cat = (cat_table_entry_t *) utarray_eltptr(cat_table, cat_idx);
157 /* FIXME: Is it worth checking duplicated vars? Probably not */
158 utarray_push_back(cat->cvar_indices, &cvar_index, MPL_MEM_MPIT);
159 } else {
160 /* Not found, so create a new category */
161 cat = MPIR_T_cat_create(cat_name);
162 utarray_push_back(cat->cvar_indices, &cvar_index, MPL_MEM_MPIT);
163 /* Notify categories have been changed */
164 cat_stamp++;
165 }
166
167 fn_exit:
168 return mpi_errno;
169 }
170
171 /* Add a sub-category to an existing or new category
172 * IN: parent_name, name of the parent category
173 * IN: child_name, name of the child category
174 */
MPIR_T_cat_add_subcat(const char * parent_name,const char * child_name)175 int MPIR_T_cat_add_subcat(const char *parent_name, const char *child_name)
176 {
177 int mpi_errno = MPI_SUCCESS;
178 int parent_index, child_index;
179 name2index_hash_t *hash_entry;
180 cat_table_entry_t *parent;
181
182 /* NULL or empty string are allowed */
183 if (parent_name == NULL || *parent_name == '\0' || child_name == NULL || *child_name == '\0') {
184 goto fn_exit;
185 }
186
187 /* Find or create parent */
188 HASH_FIND_STR(cat_hash, parent_name, hash_entry);
189 if (hash_entry != NULL) {
190 /* Found parent in cat_table */
191 parent_index = hash_entry->idx;
192 } else {
193 /* parent is a new category */
194 MPIR_T_cat_create(parent_name);
195 parent_index = utarray_len(cat_table) - 1;
196 }
197
198 /* Find or create child */
199 HASH_FIND_STR(cat_hash, child_name, hash_entry);
200 if (hash_entry != NULL) {
201 /* Found child in cat_table */
202 child_index = hash_entry->idx;
203 } else {
204 /* child is a new category */
205 MPIR_T_cat_create(child_name);
206 child_index = utarray_len(cat_table) - 1;
207 }
208
209 /* Connect parent and child */
210 parent = (cat_table_entry_t *) utarray_eltptr(cat_table, parent_index);
211 utarray_push_back(parent->subcat_indices, &child_index, MPL_MEM_MPIT);
212
213 /* Notify categories have been changed */
214 cat_stamp++;
215
216 fn_exit:
217 return mpi_errno;
218
219 }
220
221 /* Add description to an existing or new category
222 * IN: cat_name, name of the category
223 * IN: cat_desc, description of the category
224 */
MPIR_T_cat_add_desc(const char * cat_name,const char * cat_desc)225 int MPIR_T_cat_add_desc(const char *cat_name, const char *cat_desc)
226 {
227 int cat_idx, mpi_errno = MPI_SUCCESS;
228 name2index_hash_t *hash_entry;
229 cat_table_entry_t *cat;
230
231 /* NULL args are not allowed */
232 MPIR_Assert(cat_name);
233 MPIR_Assert(cat_desc);
234
235 HASH_FIND_STR(cat_hash, cat_name, hash_entry);
236
237 if (hash_entry != NULL) {
238 /* Found it, i.e., category already exists */
239 cat_idx = hash_entry->idx;
240 cat = (cat_table_entry_t *) utarray_eltptr(cat_table, cat_idx);
241 MPIR_Assert(cat->desc == NULL);
242 cat->desc = MPL_strdup(cat_desc);
243 MPIR_Assert(cat->desc);
244 } else {
245 /* Not found, so create a new category */
246 cat = MPIR_T_cat_create(cat_name);
247 cat->desc = MPL_strdup(cat_desc);
248 MPIR_Assert(cat->desc);
249 /* Notify categories have been changed */
250 cat_stamp++;
251 }
252
253 return mpi_errno;
254 }
255
256 /* A low level, generic and internally used interface to register
257 * a cvar to the MPIR_T.
258 *
259 * IN: dtype, MPI datatype for this cvar
260 * IN: name, Name of the cvar
261 * IN: addr, Pointer to the cvar if known at registeration, otherwise NULL.
262 * IN: count, # of elements of this cvar if known at registeration, otherwise 0.
263 * IN: etype, MPI_T_enum or MPI_T_ENUM_NULL
264 * IN: verb, MPI_T_PVAR_VERBOSITY_*
265 * IN: binding, MPI_T_BIND_*
266 * IN: Scope, MPI_T_SCOPE_*
267 * IN: get_addr, If not NULL, it is a callback to get address of the cvar.
268 * IN: get_count, If not NULL, it is a callback to read count of the cvar.
269 * IN: cat, Catogery name of the cvar
270 * IN: desc, Description of the cvar
271 */
MPIR_T_CVAR_REGISTER_impl(MPI_Datatype dtype,const char * name,const void * addr,int count,MPIR_T_enum_t * etype,MPIR_T_verbosity_t verb,MPIR_T_bind_t binding,MPIR_T_scope_t scope,MPIR_T_cvar_get_addr_cb get_addr,MPIR_T_cvar_get_count_cb get_count,MPIR_T_cvar_value_t defaultval,const char * cat,const char * desc)272 void MPIR_T_CVAR_REGISTER_impl(MPI_Datatype dtype, const char *name, const void *addr, int count,
273 MPIR_T_enum_t * etype, MPIR_T_verbosity_t verb,
274 MPIR_T_bind_t binding, MPIR_T_scope_t scope,
275 MPIR_T_cvar_get_addr_cb get_addr, MPIR_T_cvar_get_count_cb get_count,
276 MPIR_T_cvar_value_t defaultval, const char *cat, const char *desc)
277 {
278 name2index_hash_t *hash_entry;
279 cvar_table_entry_t *cvar;
280 int cvar_idx;
281
282 /* Check whether this is a replicated cvar, whose name is unique. */
283 HASH_FIND_STR(cvar_hash, name, hash_entry);
284
285 if (hash_entry != NULL) {
286 /* Found it, the cvar already exists */
287 cvar_idx = hash_entry->idx;
288 cvar = (cvar_table_entry_t *) utarray_eltptr(cvar_table, cvar_idx);
289 /* Should never override an existing & active var */
290 MPIR_Assert(cvar->active != TRUE);
291 cvar->active = TRUE;
292 /* FIXME: Do we need to check consistency between the old and new? */
293 } else {
294 /* Not found, so push the cvar to back of cvar_table */
295 utarray_extend_back(cvar_table, MPL_MEM_MPIT);
296 cvar = (cvar_table_entry_t *) utarray_back(cvar_table);
297 cvar->active = TRUE;
298 cvar->datatype = dtype;
299 cvar->name = MPL_strdup(name);
300 MPIR_Assert(cvar->name);
301 if (dtype != MPI_CHAR) {
302 cvar->addr = (void *) addr;
303 } else {
304 cvar->addr = MPL_malloc(count, MPL_MEM_MPIT);
305 MPIR_Assert(cvar->addr);
306 if (defaultval.str == NULL) {
307 ((char *) (cvar->addr))[0] = '\0';
308 } else {
309 /* Use greater (>), since count includes the terminating '\0', but strlen does not */
310 MPIR_Assert((unsigned) count > strlen(defaultval.str));
311 strcpy(cvar->addr, defaultval.str);
312 }
313 }
314 cvar->count = count;
315 cvar->verbosity = verb;
316 cvar->bind = binding;
317 cvar->scope = scope;
318 cvar->get_addr = get_addr;
319 cvar->get_count = get_count;
320 cvar->defaultval = defaultval;
321 cvar->desc = MPL_strdup(desc);
322 MPIR_Assert(cvar->desc);
323
324 /* Record <name, index> in hash table */
325 cvar_idx = utarray_len(cvar_table) - 1;
326 hash_entry = MPL_malloc(sizeof(name2index_hash_t), MPL_MEM_MPIT);
327 MPIR_Assert(hash_entry);
328 /* Need not to Strdup name, since cvar_table and cvar_hash co-exist */
329 hash_entry->name = name;
330 hash_entry->idx = cvar_idx;
331 HASH_ADD_KEYPTR(hh, cvar_hash, hash_entry->name,
332 strlen(hash_entry->name), hash_entry, MPL_MEM_MPIT);
333
334 /* Add the cvar to a category */
335 MPIR_T_cat_add_cvar(cat, cvar_idx);
336 }
337 }
338
339 /* A low level, generic and internally used interface to register
340 * a pvar to MPIR_T. Other modules should use interfaces defined
341 * for concrete pvar classes.
342 *
343 * IN: varclass, MPI_T_PVAR_CLASS_*
344 * IN: dtype, MPI datatype for this pvar
345 * IN: name, Name of the pvar
346 * IN: addr, Pointer to the pvar if known at registeration, otherwise NULL.
347 * IN: count, # of elements of this pvar if known at registeration, otherwise 0.
348 * IN: etype, MPI_T_enum or MPI_T_ENUM_NULL
349 * IN: verb, MPI_T_PVAR_VERBOSITY_*
350 * IN: binding, MPI_T_BIND_*
351 * IN: flags, Bitwise OR of MPIR_T_R_PVAR_FLAGS_{}
352 * IN: get_value, If not NULL, it is a callback to read the pvar.
353 * IN: get_count, If not NULL, it is a callback to read count of the pvar.
354 * IN: cat, Catogery name of the pvar
355 * IN: desc, Description of the pvar
356 */
MPIR_T_PVAR_REGISTER_impl(MPIR_T_pvar_class_t varclass,MPI_Datatype dtype,const char * name,void * addr,int count,MPIR_T_enum_t * etype,MPIR_T_verbosity_t verb,MPIR_T_bind_t binding,int flags,MPIR_T_pvar_get_value_cb get_value,MPIR_T_pvar_get_count_cb get_count,const char * cat,const char * desc)357 void MPIR_T_PVAR_REGISTER_impl(MPIR_T_pvar_class_t varclass, MPI_Datatype dtype, const char *name,
358 void *addr, int count, MPIR_T_enum_t * etype,
359 MPIR_T_verbosity_t verb, MPIR_T_bind_t binding, int flags,
360 MPIR_T_pvar_get_value_cb get_value,
361 MPIR_T_pvar_get_count_cb get_count, const char *cat,
362 const char *desc)
363 {
364 name2index_hash_t *hash_entry;
365 pvar_table_entry_t *pvar;
366 int pvar_idx;
367 int seq = varclass - MPIR_T_PVAR_CLASS_FIRST;
368
369 /* Check whether this is a replicated pvar, whose name is unique per class */
370 HASH_FIND_STR(pvar_hashs[seq], name, hash_entry);
371
372 if (hash_entry != NULL) {
373 /* Found it, the pvar already exists */
374 pvar_idx = hash_entry->idx;
375 pvar = (pvar_table_entry_t *) utarray_eltptr(pvar_table, pvar_idx);
376 /* Should never override an existing & active var */
377 MPIR_Assert(pvar->active != TRUE);
378 pvar->active = TRUE;
379 /* FIXME: Do we need to check consistency between the old and new? */
380 } else {
381 /* Not found, so push the pvar to back of pvar_table */
382 utarray_extend_back(pvar_table, MPL_MEM_MPIT);
383 pvar = (pvar_table_entry_t *) utarray_back(pvar_table);
384 pvar->active = TRUE;
385 pvar->varclass = varclass;
386 pvar->datatype = dtype;
387 pvar->name = MPL_strdup(name);
388 MPIR_Assert(pvar->name);
389 pvar->addr = addr;
390 pvar->count = count;
391 pvar->enumtype = etype;
392 pvar->verbosity = verb;
393 pvar->bind = binding;
394 pvar->flags = flags;
395 pvar->get_value = get_value;
396 pvar->get_count = get_count;
397 pvar->desc = MPL_strdup(desc);
398 MPIR_Assert(pvar->desc);
399
400 /* Record <name, index> in hash table */
401 pvar_idx = utarray_len(pvar_table) - 1;
402 hash_entry = MPL_malloc(sizeof(name2index_hash_t), MPL_MEM_MPIT);
403 MPIR_Assert(hash_entry);
404 /* Need not to Strdup name, since pvar_table and pvar_hashs co-exist */
405 hash_entry->name = name;
406 hash_entry->idx = pvar_idx;
407 HASH_ADD_KEYPTR(hh, pvar_hashs[seq], hash_entry->name,
408 strlen(hash_entry->name), hash_entry, MPL_MEM_MPIT);
409
410 /* Add the pvar to a category */
411 MPIR_T_cat_add_pvar(cat, utarray_len(pvar_table) - 1);
412 }
413 }
414
415 /* Implements an MPI_T-style strncpy. Here is the description from the draft
416 * standard:
417 *
418 * Several MPI tool information interface functions return one or more
419 * strings. These functions have two arguments for each string to be returned:
420 * an OUT parameter that identifies a pointer to the buffer in which the
421 * string will be returned, and an IN/OUT parameter to pass the length of the
422 * buffer. The user is responsible for the memory allocation of the buffer and
423 * must pass the size of the buffer (n) as the length argument. Let n be the
424 * length value specified to the function. On return, the function writes at
425 * most n - 1 of the string's characters into the buffer, followed by a null
426 * terminator. If the returned string's length is greater than or equal to n,
427 * the string will be truncated to n - 1 characters. In this case, the length
428 * of the string plus one (for the terminating null character) is returned in
429 * the length argument. If the user passes the null pointer as the buffer
430 * argument or passes 0 as the length argument, the function does not return
431 * the string and only returns the length of the string plus one in the length
432 * argument. If the user passes the null pointer as the length argument, the
433 * buffer argument is ignored and nothing is returned.
434 *
435 * So this routine copies up to (*len)-1 characters from src to dst and then
436 * sets *len to (strlen(dst)+1). If dst==NULL, just return (strlen(src)+1) in
437 * *len.
438 *
439 * This routine does not follow MPICH error handling conventions.
440 */
MPIR_T_strncpy(char * dst,const char * src,int * len)441 void MPIR_T_strncpy(char *dst, const char *src, int *len)
442 {
443 /* std. says if len arg is NULL, dst is ignored and nothing is returned (MPI-3, p.563) */
444 if (len) {
445 /* If dst is NULL or *len is 0, just return src length + 1 */
446 if (!dst || !*len) {
447 *len = (src == NULL) ? 1 : strlen(src) + 1;
448 } else {
449 /* MPL_strncpy will always terminate the string */
450 MPIR_Assert(*len > 0);
451 if (src != NULL) {
452 MPL_strncpy(dst, src, *len);
453 *len = (int) strlen(dst) + 1;
454 } else {
455 /* As if an empty string is copied */
456 *dst = '\0';
457 *len = 1;
458 }
459 }
460 }
461 }
462