The XBT toolbox
XBT is not an interface to describe your application, but rather a toolbox of features that are used everywhere in SimGrid. The code described in this page is not specific to any of the existing interfaces, and it’s used in all of them.
Logging API
As introduced in Textual logging, the SimGrid logging mechanism allows to configure at runtime the messages that should be displayed and those that should be omitted. Each message produced in the code is given a category (denoting its topic) and a priority. Then at runtime, each category is given a threshold (only messages of priority higher than that threshold are displayed), a layout (deciding how the messages in this category are formatted), and an appender (deciding what to do with the message: either print on stderr or to a file).
This section documents the provided API. To see how to configure these features at runtime, please refer to Logging configuration.
Declaring categories
Typically, there will be a category for each module of the implementation, so that users can independently control the logging for each module. Refer to the logging_categories section for a list of all existing categories in SimGrid.
-
XBT_LOG_NEW_CATEGORY(category, description)
Creates a new category that is not within any existing categories. It will be located right below the
rootcategory.categorymust be a valid identifier (such asmycat) with no quote or anything. It should also be unique over the whole binary.descriptionmust be a string, between quotes.
-
XBT_LOG_NEW_SUBCATEGORY(category, parent_category, description)
Creates a new category under the provided
parent_category.
-
XBT_LOG_NEW_DEFAULT_CATEGORY(category, description)
Similar to
XBT_LOG_NEW_CATEGORY, and the created category is the default one in the current source file.
-
XBT_LOG_NEW_DEFAULT_SUBCATEGORY(category, parent_category, description)
Similar to
XBT_LOG_NEW_SUBCATEGORY, and the created category is the default one in the current source file.
-
XBT_LOG_EXTERNAL_CATEGORY(category)
Make an external category (i.e., a category declared in another source file) visible from this source file. In each source file, at most one one category can be the default one.
-
XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(category)
Use an external category as default category in this source file.
Logging messages
Default category
-
XBT_CRITICAL(format_string, parameters...)
Report a fatal error to the default category.
-
XBT_ERROR(format_string, parameters...)
Report an error to the default category.
-
XBT_WARN(format_string, parameters...)
Report a warning or an important information to the default category.
-
XBT_INFO(format_string, parameters...)
Report an information of regular importance to the default category.
-
XBT_VERB(format_string, parameters...)
Report a verbose information to the default category.
-
XBT_DEBUG(format_string, parameters...)
Report a debug-only information to the default category.
For each of the logging macros, the first parameter must be a printf-like format string, and the subsequent parameters must match this format. If you compile with the -Wall option, the compiler will warn you for unmatched arguments, such as passing a pointer while the format string expects an integer. Using this option is usually a good idea.
Here is an example: XBT_WARN("Values are: %d and '%s'", 5, "oops");
-
XBT_IN(format_string, parameters...)
Report that the execution flow enters a given function (which name is displayed automatically).
-
XBT_OUT(format_string, parameters...)
Report that the execution flow exits a given function (which name is displayed automatically).
-
XBT_HERE(format_string, parameters...)
Report that the execution flow reaches a given location.
Specific category
-
XBT_CCRITICAL(category, format_string, parameters...)
Report a fatal error to the specified
category.
-
XBT_CERROR(category, format_string, parameters...)
Report an error to the specified
category.
-
XBT_CWARN(category, format_string, parameters...)
Report a warning or an important information to the specified
category.
-
XBT_CINFO(category, format_string, parameters...)
Report an information of regular importance to the specified
category.
-
XBT_CVERB(category, format_string, parameters...)
Report a verbose information to the specified
category.
-
XBT_CDEBUG(category, format_string, parameters...)
Report a debug-only information to the specified
category.
Of course, the specified category must be visible from this source file, either because it was created there (e.g. with XBT_LOG_NEW_CATEGORY) or because it was made
visible with XBT_LOG_EXTERNAL_CATEGORY.
Other functions
-
XBT_LOG_ISENABLED(category, priority)
Returns true if that category displays the messages of that priority. It’s useful to compute a value that is used only in the logging, such as the textual representation of a non-trivial object.
The specified priority must be one of
xbt_log_priority_trace,xbt_log_priority_debug,xbt_log_priority_verbose,xbt_log_priority_info,xbt_log_priority_warning,xbt_log_priority_errororxbt_log_priority_critical.
-
void xbt_log_control_set(const char *setting)
Sets the provided
settingas if it was passed in a--logcommand-line parameter.
You should not use any of the macros which name starts with ‘_’.
Full example
#include "xbt/log.h"
/* create a category and a default subcategory */
XBT_LOG_NEW_CATEGORY(VSS);
XBT_LOG_NEW_DEFAULT_SUBCATEGORY(SA, VSS);
int main() {
/* Now set the parent's priority. (the string would typically be a runtime option) */
xbt_log_control_set("SA.thresh:info");
/* This request is enabled, because WARNING >= INFO. */
XBT_CWARN(VSS, "Low fuel level.");
/* This request is disabled, because DEBUG < INFO. */
XBT_CDEBUG(VSS, "Starting search for nearest gas station.");
/* The default category SA inherits its priority from VSS. Thus,
the following request is enabled because INFO >= INFO. */
XBT_INFO("Located nearest gas station.");
/* This request is disabled, because DEBUG < INFO. */
XBT_DEBUG("Exiting gas station search");
}
Performance concern
This module is highly optimized. Messages that will not be displayed are not even built. For example, using XBT_DEBUG in a category that turns debug messages off only costs a
single integer comparison at runtime, and the parameters are not even evaluated.
You can even specify a compile-time threshold that will completely remove every logging below the specified priority. Passing -DNDEBUG to cmake disables every logging of
priority below INFO while -DNLOG removes any logging at compile time. Note that using this feature may hinder the stability of SimGrid, as we consider the logs to be fast
enough to not thoughtfully test the case where they are removed at compile time.
Dynamic arrays
As SimGrid used to be written in pure C, it used to rely on custom data containers such as dynamic arrays and dictionnaries. Nowadays, the standard library of C++ is used internally, but some part of the interface still rely on the old containers, that are thus still available.
Warning
You should probably not start a new project using these data structures, as we will deprecate them from SimGrid as soon as possible. Better implementations exist out there anyway, in particular if you’re not writting pure C code.
-
typedef struct xbt_dynar_s *xbt_dynar_t
Dynar data type (opaque type)
These are the SimGrid version of the DYNamically sized ARays, which all C programmer recode one day or another. For performance concerns, the content of dynars must be homogeneous (in contrary to
xbt_dict_t). You thus have to provide the function which will be used to free the content at structure creation (of type void_f_pvoid_t).
-
typedef const struct xbt_dynar_s *const_xbt_dynar_t
constant dynar
Creation and destruction
-
xbt_dynar_t xbt_dynar_new(const unsigned long elm_size, void_f_pvoid_t free_f)
Creates a new dynar. If a
free_fis provided, the elements have to be pointer of pointer. That is to say that dynars can contain either base types (int, char, double, etc) or pointer of pointers (struct **).- Parameters:
elmsize – size of each element in the dynar
free_f – function to call each time we want to get rid of an element (or nullptr if nothing to do).
-
void xbt_dynar_free(xbt_dynar_t *dynar)
Destructor: kilkil a dynar and its content.
-
void xbt_dynar_free_container(xbt_dynar_t *dynar)
Destructor of the structure leaving the content unmodified. Ie, the array is freed, but the content is not touched (the free_f function is not used).
- Parameters:
dynar – poor victim
Dynars as regular arrays
-
void xbt_dynar_get_cpy(const_xbt_dynar_t dynar, unsigned long idx, void *dst)
Retrieve a copy of the Nth element of a dynar.
- Parameters:
dynar – information dealer
idx – index of the slot we want to retrieve
dst – [out] where to put the result to.
-
void xbt_dynar_insert_at(xbt_dynar_t dynar, int idx, const void *src)
Set the Nth dynar’s element, expanding the dynar and sliding the previous values to the right.
Set the Nth element of a dynar, expanding the dynar if needed, and moving the previously existing value and all subsequent ones to one position right in the dynar.
-
void xbt_dynar_remove_at(xbt_dynar_t dynar, int idx, void *dst)
Remove the Nth element, sliding other values to the left.
Get the Nth element of a dynar, removing it from the dynar and moving all subsequent values to one position left in the dynar.
If the object argument of this function is a non-null pointer, the removed element is copied to this address. If not, the element is freed using the free_f function passed at dynar creation.
-
int xbt_dynar_member(const_xbt_dynar_t dynar, const void *elem)
Returns a boolean indicating whether the element is part of the dynar.
Beware that if your dynar contains pointed values (such as strings) instead of scalar, this function is probably not what you want. It would compare the pointer values, not the pointed elements.
-
void xbt_dynar_sort(const_xbt_dynar_t dynar, int_f_cpvoid_cpvoid_t compar_fn)
Sorts a dynar according to the function
compar_fnThis function simply apply the classical qsort(3) function to the data stored in the dynar. You should thus refer to the libc documentation, or to some online tutorial on how to write a comparison function. Here is a quick example if you have integers in your dynar:
int cmpfunc (const void * a, const void * b) { int intA = *(int*)a; int intB = *(int*)b; return intA - intB; }
- Parameters:
dynar – the dynar to sort
compar_fn – comparison function of type (int (compar_fn*) (const void*) (const void*)).
Dynar size
-
int xbt_dynar_is_empty(const_xbt_dynar_t dynar)
check if a dynar is empty
-
unsigned long xbt_dynar_length(const_xbt_dynar_t dynar)
Count of dynar’s elements.
-
void xbt_dynar_reset(xbt_dynar_t dynar)
Frees the content and set the size to 0.
Perl-like interface
-
void xbt_dynar_push(xbt_dynar_t dynar, const void *src)
Add an element at the end of the dynar.
-
void xbt_dynar_pop(xbt_dynar_t dynar, void *dst)
Get and remove the last element of the dynar.
-
void xbt_dynar_unshift(xbt_dynar_t dynar, const void *src)
Add an element at the beginning of the dynar.
This is less efficient than xbt_dynar_push()
-
void xbt_dynar_shift(xbt_dynar_t dynar, void *dst)
Get and remove the first element of the dynar.
This is less efficient than xbt_dynar_pop()
-
void xbt_dynar_map(const_xbt_dynar_t dynar, void_f_pvoid_t op)
Apply a function to each member of a dynar.
The mapped function may change the value of the element itself, but should not mess with the structure of the dynar.
Direct content manipulation
Those functions do not retrieve the content, but only their address.
-
void *xbt_dynar_set_at_ptr(const xbt_dynar_t dynar, unsigned long idx)
-
void *xbt_dynar_get_ptr(const_xbt_dynar_t dynar, unsigned long idx)
Retrieve a pointer to the Nth element of a dynar.
Note that the returned value is the actual content of the dynar. Make a copy before fooling with it.
-
void *xbt_dynar_insert_at_ptr(xbt_dynar_t dynar, int idx)
Make room for a new element, and return a pointer to it.
You can then use regular affectation to set its value instead of relying on the slow memcpy. This is what xbt_dynar_insert_at_as() does.
-
void *xbt_dynar_push_ptr(xbt_dynar_t dynar)
Make room at the end of the dynar for a new element, and return a pointer to it.
You can then use regular affectation to set its value instead of relying on the slow memcpy. This is what xbt_dynar_push_as() does.
-
void *xbt_dynar_pop_ptr(xbt_dynar_t dynar)
Mark the last dynar’s element as unused and return a pointer to it.
You can then use regular affectation to set its value instead of relying on the slow memcpy. This is what xbt_dynar_pop_as() does.
Dynars of scalars
While the other functions use a memcpy to retrieve the content into the user provided area, those ones use a regular affectation. It only works for scalar values, but should be a little faster.
-
xbt_dynar_get_as(dynar, idx, type)
Quick retrieval of scalar content.
-
xbt_dynar_set_as(dynar, idx, type, val)
Quick setting of scalar content.
-
xbt_dynar_getlast_as(dynar, type)
Quick retrieval of scalar content.
-
xbt_dynar_getfirst_as(dynar, type)
Quick retrieval of scalar content.
-
xbt_dynar_push_as(dynar, type, value)
Quick insertion of scalar content.
-
xbt_dynar_pop_as(dynar, type)
Quick removal of scalar content.
Iterating over Dynars
-
xbt_dynar_foreach(_dynar, _cursor, _data)
Iterates over the whole dynar.
Here is an example of usage:
xbt_dynar_t dyn; unsigned int cpt; char* str; xbt_dynar_foreach (dyn,cpt,str) { printf("Seen %s\n",str); }
Note that underneath, that’s a simple for loop with no real black magic involved. It’s perfectly safe to interrupt a foreach with a break or a return statement, but inserting or removing elements during the traversal is probably a bad idea (even if you can fix side effects by manually changing the counter).
- Parameters:
_dynar – what to iterate over
_cursor – an unsigned integer used as cursor
Example with scalar values
xbt_dynar_t d = xbt_dynar_new(sizeof(int), nullptr);
/* 1. Populate the dynar */
xbt_dynar_t d = xbt_dynar_new(sizeof(int), nullptr);
for (int cpt = 0; cpt < NB_ELEM; cpt++) {
xbt_dynar_push_as(d, int, cpt); /* This is faster (and possible only with scalars) */
/* xbt_dynar_push(d,&cpt); This would also work */
xbt_test_log("Push %d, length=%lu", cpt, xbt_dynar_length(d));
}
/* 2. Traverse manually the dynar */
for (cursor = 0; cursor < NB_ELEM; cursor++) {
int* iptr = (int*)xbt_dynar_get_ptr(d, cursor);
/* 1. Populate further the dynar */
for (int cpt = 0; cpt < NB_ELEM; cpt++) {
xbt_dynar_insert_at(d, cpt, &cpt);
xbt_test_log("Push %d, length=%lu", cpt, xbt_dynar_length(d));
}
/* 3. Traverse the dynar */
int cpt;
xbt_dynar_foreach(d, cursor, cpt) {
xbt_test_assert(cursor == (unsigned int) cpt, "The retrieved value is not the same than the injected one (%u!=%d)", cursor, cpt);
}
/* 4. Reset the values */
for (int i = 0; i < NB_ELEM; i++)
*(int*)xbt_dynar_get_ptr(d, i) = i;
/* 5. Shift all the values */
for (int i = 0; i < NB_ELEM; i++) {
int val;
xbt_dynar_shift(d, &val);
}
// the dynar is empty after shifting all values
/* 5. Free the resources */
xbt_dynar_free(&d);
Example with pointed values
xbt_dynar_t d = xbt_dynar_new(sizeof(char*), &xbt_free_ref);
/// Push/shift example
for (int i = 0; i < NB_ELEM; i++) {
char * val = xbt_strdup("hello");
xbt_dynar_push(d, &val);
}
for (int i = 0; i < NB_ELEM; i++) {
char *val;
xbt_dynar_shift(d, &val);
REQUIRE("hello" == val);
xbt_free(val);
}
xbt_dynar_free(&d);
/// Unshift, traverse and pop example
d = xbt_dynar_new(sizeof(char**), &xbt_free_ref);
for (int i = 0; i < NB_ELEM; i++) {
std::string val = std::to_string(i);
s1 = xbt_strdup(val.c_str());
xbt_dynar_unshift(d, &s1);
}
/* 2. Traverse the dynar with the macro */
xbt_dynar_foreach (d, iter, s1) {
std::string val = std::to_string(NB_ELEM - iter - 1);
REQUIRE(s1 == val); // The retrieved value is not the same than the injected one
}
/* 3. Traverse the dynar with the macro */
for (int i = 0; i < NB_ELEM; i++) {
std::string val = std::to_string(i);
xbt_dynar_pop(d, &s2);
REQUIRE(s2 == val); // The retrieved value is not the same than the injected one
xbt_free(s2);
}
/* 4. Free the resources */
xbt_dynar_free(&d);