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