SimGrid
|
Let's have another look at the output of the program we came up with in lesson 5:
$ ./test_server 12345 & ./test_client 127.0.0.1 12345 Client ready; listening on 1024 we sent the data to the server on 127.0.0.1. Let's do it again for fun Ok. Enough. Have a rest, and then kill the server Cool, we received the message from 127.0.0.1:1024. Cool, we received the message from 127.0.0.1:1024. [arthur:client:(28822) 0.000027] [gras/INFO] Exiting GRAS Argh, killed by 127.0.0.1:1024! Bye folks... [arthur:server:(28820) 0.000013] [gras/INFO] Exiting GRAS $ $ ./test_simulator platform.xml test.xml Client ready; listening on 1024 we sent the data to the server on Jacquelin. Let's do it again for fun Cool, we received the message from Boivin:1024. Ok. Enough. Have a rest, and then kill the server Cool, we received the message from Boivin:1024. [Boivin:client:(2) 0.000000] [gras/INFO] Exiting GRAS Argh, killed by Boivin:1024! Bye folks... [Jacquelin:server:(1) 0.000000] [gras/INFO] Exiting GRAS $
It is a bit difficult to read, isn't it? Indeed, it is hard to identify which process printed which line. It would be possible to add [server] in any messages comming from the server and do the same for every process running. Idealy, we would also add the location at which the message was generated (using __FILE__ and __LINE__) to help debuging, as well as a timestamping so that we can still reorder the messages in RL when they get intermixed (yeah, it happen, and there is not much to do against it). At the end, each time we would like to print a little "hello" debugging message, we would have to write 3 lines of arguments to fprintf, which is not that practical.
That is why there is a support for proper logging in GRAS. Technically speaking, it is not part of GRAS but of XBT, which is the toolbox on which the whole SimGrid library is built, but that's the same for us.
This logging library follows the spirit of another one called log4j, which is more or less the reference in the domain. The original version is for Java, as the name implies, and there was reimplementation for Python (log4py), C/C++ (log4c) and so on. Since one of the credo of the GRAS framework is that we don't want any external dependency to ease the deployment in grid settings, we reimplemented a version of our own.
One of the strong idea of log4j is that log events get structured to give the user a fine control at run time of what gets displayed and what don't. For that, log event are produced into log channels at a given log priority. Then, you can select the minimal priority an event should have on a given channel to get displayed.
Then, to keep things managable even when the number of channels increase, the channels form a tree and properties get inherited from parent channel to childs. Have a look at the existing channels in SimGrid: Existing log categories. You see that for example, the gras
channel have 5 subchannels (at time of writing): gras_ddt
, gras_msg
, gras_timer
, gras_trp
and gras_virtu
. If you open or close the gras
channel, it automatically affects all those subchannels (and their respective subchannels too). Finally, channels are not just open or closed, but filter messages below a given priority (as we said). The priorities are defined by type e_xbt_log_priority_t.
That is all you really need to know about the logs before diving into practice. If you want more information on that topic, refer to the Logging support section, which contains much more information than this page.
Enough with theory, let's change our example so that it uses proper loggings. The first thing to do is to add a new channel in the existing hierarchy. There is 4 macros to create log channels, depending on the kind of channel we want:
What we want here is a root category (it does not belong to any existing channel, for sure), and we want it to be the default one in our file (of course, it's the only one).
XBT_LOG_NEW_DEFAULT_CATEGORY(test, "My little example");
Then, we change any call to fprintf to one of the logging macros. There is a plenty of them, called <priority><nb args>, such as XBT_DEBUG, which produces a debuging log event onto the default category. Here is a list of the existing macros: XBT_DEBUG, XBT_VERB, XBT_INFO, XBT_WARN, XBT_ERROR and XBT_CRITICAL.
Note also that there is no need to add a '\n' at the end of your format line, it gets automatically added.
Once we changed any fprintf of our code to some of these macros, the program may read:
/* Copyright (c) 2006, 2007, 2010. The SimGrid Team. * All rights reserved. */ /* This program is free software; you can redistribute it and/or modify it * under the terms of the license (GNU LGPL) which comes with this package. */ #include <gras.h> XBT_LOG_NEW_DEFAULT_CATEGORY(test, "My little example"); typedef struct { int killed; } server_data_t; int server_kill_cb(gras_msg_cb_ctx_t ctx, void *payload) { gras_socket_t client = gras_msg_cb_ctx_from(ctx); server_data_t *globals = (server_data_t *) gras_userdata_get(); XBT_CRITICAL("Argh, killed by %s:%d! Bye folks...", gras_socket_peer_name(client), gras_socket_peer_port(client)); globals->killed = 1; return 0; } /* end_of_kill_callback */ int server_hello_cb(gras_msg_cb_ctx_t ctx, void *payload) { gras_socket_t client = gras_msg_cb_ctx_from(ctx); XBT_INFO("Cool, we received the message from %s:%d.", gras_socket_peer_name(client), gras_socket_peer_port(client)); return 0; } /* end_of_hello_callback */ int server(int argc, char *argv[]) { gras_socket_t mysock; /* socket on which I listen */ server_data_t *globals; gras_init(&argc, argv); globals = gras_userdata_new(server_data_t *); globals->killed = 0; gras_msgtype_declare("hello", NULL); gras_msgtype_declare("kill", NULL); mysock = gras_socket_server(atoi(argv[1])); gras_cb_register("hello", &server_hello_cb); gras_cb_register("kill", &server_kill_cb); while (!globals->killed) { gras_msg_handle(-1); /* blocking */ } gras_exit(); return 0; } int client(int argc, char *argv[]) { gras_socket_t mysock; /* socket on which I listen */ gras_socket_t toserver; /* socket used to write to the server */ gras_init(&argc, argv); gras_msgtype_declare("hello", NULL); gras_msgtype_declare("kill", NULL); mysock = gras_socket_server_range(1024, 10000, 0, 0); XBT_VERB("Client ready; listening on %d", gras_socket_my_port(mysock)); gras_os_sleep(1.5); /* sleep 1 second and half */ toserver = gras_socket_client(argv[1], atoi(argv[2])); gras_msg_send(toserver, "hello", NULL); XBT_INFO("we sent the data to the server on %s. Let's do it again for fun", gras_socket_peer_name(toserver)); gras_msg_send(toserver, "hello", NULL); XBT_INFO("Ok. Enough. Have a rest, and then kill the server"); gras_os_sleep(5); /* sleep 1 second and half */ gras_msg_send(toserver, "kill", NULL); gras_exit(); return 0; }
And the output now looks better:
$ ./test_server 12345 & ./test_client 127.0.0.1 12345 [arthur:client:(27755) 0.000016] [test/INFO] we sent the data to the server on 127.0.0.1. Let's do it again for fun [arthur:client:(27755) 0.000148] [test/INFO] Ok. Enough. Have a rest, and then kill the server [arthur:client:(27755) 5.000415] [gras/INFO] Exiting GRAS [arthur:server:(27752) 0.000010] [test/INFO] Cool, we received the message from 127.0.0.1:1024. [arthur:server:(27752) 0.000409] [test/INFO] Cool, we received the message from 127.0.0.1:1024. [arthur:server:(27752) 5.001363] test.c:15: [test/CRITICAL] Argh, killed by 127.0.0.1:1024! Bye folks... [arthur:server:(27752) 5.001409] [gras/INFO] Exiting GRAS $ $ ./test_simulator platform.xml test.xml [Boivin:client:(2) 0.000000] [test/INFO] we sent the data to the server on Jacquelin. Let's do it again for fun [Jacquelin:server:(1) 0.000000] [test/INFO] Cool, we received the message from Boivin:1024. [Boivin:client:(2) 0.000537] [test/INFO] Ok. Enough. Have a rest, and then kill the server [Jacquelin:server:(1) 0.000537] [test/INFO] Cool, we received the message from Boivin:1024. [Boivin:client:(2) 5.001074] [gras/INFO] Exiting GRAS [Jacquelin:server:(1) 5.001074] test.c:15: [test/CRITICAL] Argh, killed by Boivin:1024! Bye folks... [Jacquelin:server:(1) 5.001074] [gras/INFO] Exiting GRAS $
Once we changed our program to use proper logging, it is naturally possible to choose at run time what we want to see. For example, if we want more details about our code, we should pass --log=test.thres:verbose
on the command line of our programs to change the thres
old. Note that a VERBOSE line appears on client side:
$ ./test_server 12345 --log=test.thres:verbose & ./test_client 127.0.0.1 12345 --log=test.thres:verbose [arthur:client:(27681) 0.000012] test.c:65: [test/VERBOSE] Client ready; listening on 1024 [arthur:client:(27681) 1.500865] [test/INFO] we sent the data to the server on 127.0.0.1. Let's do it again for fun [arthur:client:(27681) 1.500972] [test/INFO] Ok. Enough. Have a rest, and then kill the server [arthur:client:(27681) 6.501225] [gras/INFO] Exiting GRAS [arthur:server:(27677) 0.000015] [test/INFO] Cool, we received the message from 127.0.0.1:1024. [arthur:server:(27677) 0.000144] [test/INFO] Cool, we received the message from 127.0.0.1:1024. [arthur:server:(27677) 5.000244] test.c:15: [test/CRITICAL] Argh, killed by 127.0.0.1:1024! Bye folks... [arthur:server:(27677) 5.000296] [gras/INFO] Exiting GRAS $ $ ./test_simulator platform.xml test.xml --log=test.thres:verbose [Boivin:client:(2) 0.000000] test.c:65: [test/VERBOSE] Client ready; listening on 1024 [Boivin:client:(2) 1.500552] [test/INFO] we sent the data to the server on Jacquelin. Let's do it again for fun [Jacquelin:server:(1) 1.500552] [test/INFO] Cool, we received the message from Boivin:1024. [Boivin:client:(2) 1.501089] [test/INFO] Ok. Enough. Have a rest, and then kill the server [Jacquelin:server:(1) 1.501089] [test/INFO] Cool, we received the message from Boivin:1024. [Boivin:client:(2) 6.501626] [gras/INFO] Exiting GRAS [Jacquelin:server:(1) 6.501626] test.c:15: [test/CRITICAL] Argh, killed by Boivin:1024! Bye folks... [Jacquelin:server:(1) 6.501626] [gras/INFO] Exiting GRAS $
On the contrary, if we want to reduce the amount of logging, we may want to do pass --log=test.thres:error
:
As with SimGrid 3.3, it is also possible to change how each and every message get displayed from the command line without even recompiling your code. This is done by changing the fmt
field of the category you want to change. The value of this field is somehow similar to printf's format strings, with several existing specifiers.
For example, if you just want the message you passed to the macro without any decoration about the host which raised it, its pid and everything, just pass --log=test.fmt:m
:
$ ./test_server 12345 --log=test.fmt:%m%n & ./test_client 127.0.0.1 12345 --log=test.fmt:%m%n Cool, we received the message from 127.0.0.1:1024. nCool, we received the message from 127.0.0.1:1024. nArgh, killed by 127.0.0.1:1024! Bye folks... n[arthur:server:(27630) 0.000011] [gras/INFO] Exiting GRAS we sent the data to the server on 127.0.0.1. Let's do it again for fun Ok. Enough. Have a rest, and then kill the server [arthur:client:(27634) 0.000019] [gras/INFO] Exiting GRAS $ $ ./test_simulator platform.xml test.xml --log=test.fmt:%m%n we sent the data to the server on Jacquelin. Let's do it again for fun Cool, we received the message from Boivin:1024. Ok. Enough. Have a rest, and then kill the server Cool, we received the message from Boivin:1024. [Boivin:client:(2) 0.000000] [gras/INFO] Exiting GRAS Argh, killed by Boivin:1024! Bye folks... [Jacquelin:server:(1) 0.000000] [gras/INFO] Exiting GRAS $
For debuging purpose, you can even ask to get the backtrace at each execution point:
$ ./test_server 12345 --log=test.fmt:"[%r] %m%n%b%n%n" & ./test_client 127.0.0.1 12345 --log=test.fmt:"[%r] %m%n%b%n%n" $ $ ./test_simulator platform.xml test.xml --log=test.fmt:[%r]%m%n%b%n%n $
Again, you should refer to the Logging support section for more information on how to configure the logs. Or you can proceed with the next lesson, of course.
Back to the main Simgrid Documentation page |
The version of Simgrid documented here is v3.6.1. Documentation of other versions can be found in their respective archive files (directory doc/html). |
Generated for SimGridAPI by
![]() |