From 502c1fb7b48f799d36d2ee04d978b3bb54fb0d00 Mon Sep 17 00:00:00 2001 From: mquinson Date: Tue, 14 Dec 2010 12:54:46 +0000 Subject: [PATCH] get the good parts of the replay without stack into the regular replay git-svn-id: svn+ssh://scm.gforge.inria.fr/svn/simgrid/simgrid/trunk@9233 48e7efb5-ca39-0410-a469-dd3cf9ba447f --- ChangeLog | 4 ++ buildtools/Cmake/DefinePackages.cmake | 6 +- buildtools/Cmake/MakeExeLib.cmake | 2 - examples/msg/actions/actions.c | 89 +++++++++++++-------------- include/msg/msg.h | 3 +- include/xbt/replay_trace_reader.h | 20 ++++++ include/xbt/str.h | 1 + src/msg/msg_actions.c | 67 +++++--------------- src/replay/replay_trace_reader.c | 61 ------------------ src/xbt/xbt_str.c | 35 ++++++++++- 10 files changed, 120 insertions(+), 168 deletions(-) create mode 100644 include/xbt/replay_trace_reader.h delete mode 100644 src/replay/replay_trace_reader.c diff --git a/ChangeLog b/ChangeLog index 13cc83b929..b28bb47032 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ SimGrid (3.6) unstable; urgency=low + MSG + * Change the prototypes of action replay. Sorry for inconvenience, + but this is really more efficient this way. + SURF * New model for multi-core CPUs. You can now use the core attribute to precise the number of cores of a host. This is a basic model. Every diff --git a/buildtools/Cmake/DefinePackages.cmake b/buildtools/Cmake/DefinePackages.cmake index 49cb02224b..df6b63503e 100644 --- a/buildtools/Cmake/DefinePackages.cmake +++ b/buildtools/Cmake/DefinePackages.cmake @@ -165,6 +165,7 @@ set(XBT_SRC src/xbt/graphxml_parse.c src/xbt/setset.c src/xbt/parmap.c + src/xbt/xbt_replay_trace_reader.c ) if(HAVE_MMAP) @@ -216,10 +217,6 @@ set(SIMIX_SRC src/simix/smx_user.c src/simix/smx_smurf.c src/simix/smx_context_raw.c - src/replay/replay.h - src/replay/replay.c - src/replay/state_machine_context.c - src/replay/replay_trace_reader.c ) set(MSG_SRC @@ -367,6 +364,7 @@ set(install_HEADERS include/xbt/queue.h include/xbt/setset.h include/xbt/mmalloc.h + include/xbt/replay_trace_reader.h include/mc/modelchecker.h include/msg/msg.h include/msg/datatypes.h diff --git a/buildtools/Cmake/MakeExeLib.cmake b/buildtools/Cmake/MakeExeLib.cmake index d22304df30..1d920f6480 100644 --- a/buildtools/Cmake/MakeExeLib.cmake +++ b/buildtools/Cmake/MakeExeLib.cmake @@ -239,5 +239,3 @@ add_subdirectory(${CMAKE_HOME_DIRECTORY}/examples/simdag/scheduling) if(enable_smpi) add_subdirectory(${CMAKE_HOME_DIRECTORY}/examples/smpi) endif(enable_smpi) - -add_subdirectory(${CMAKE_HOME_DIRECTORY}/src/replay) diff --git a/examples/msg/actions/actions.c b/examples/msg/actions/actions.c index 4f63ad121d..3d13b5ea18 100644 --- a/examples/msg/actions/actions.c +++ b/examples/msg/actions/actions.c @@ -18,7 +18,7 @@ XBT_LOG_NEW_DEFAULT_CATEGORY(actions, "Messages specific for this msg example"); int communicator_size = 0; -static void action_Isend(xbt_dynar_t action); +static void action_Isend(char*const* action); typedef struct { int last_Irecv_sender_id; @@ -61,20 +61,18 @@ static void asynchronous_cleanup(void) { } /* My actions */ -static void action_send(xbt_dynar_t action) +static void action_send(char*const* action) { char *name = NULL; char to[250]; - char *size_str = xbt_dynar_get_as(action, 3, char *); + const char *size_str = action[3]; double size=parse_double(size_str); double clock = MSG_get_clock(); /* this "call" is free thanks to inlining */ - sprintf(to, "%s_%s", MSG_process_get_name(MSG_process_self()), - xbt_dynar_get_as(action, 2, char *)); - // char *to = xbt_dynar_get_as(action, 2, char *); + sprintf(to, "%s_%s", MSG_process_get_name(MSG_process_self()),action[2]); if (XBT_LOG_ISENABLED(actions, xbt_log_priority_verbose)) - name = xbt_str_join(action, " "); + name = xbt_str_join_array(action, " "); #ifdef HAVE_TRACING int rank = get_rank(MSG_process_get_name(MSG_process_self())); @@ -102,41 +100,38 @@ static void action_send(xbt_dynar_t action) asynchronous_cleanup(); } -static void action_Isend(xbt_dynar_t action) +static void action_Isend(char*const* action) { char to[250]; - // char *to = xbt_dynar_get_as(action, 2, char *); - char *size = xbt_dynar_get_as(action, 3, char *); + const char *size = action[3]; double clock = MSG_get_clock(); process_globals_t globals = (process_globals_t) MSG_process_get_data(MSG_process_self()); - sprintf(to, "%s_%s", MSG_process_get_name(MSG_process_self()), - xbt_dynar_get_as(action, 2, char *)); - + sprintf(to, "%s_%s", MSG_process_get_name(MSG_process_self()),action[2]); msg_comm_t comm = MSG_task_isend( MSG_task_create(to,0,parse_double(size),NULL), to); xbt_dynar_push(globals->isends,&comm); DEBUG1("Isend on %s", MSG_process_get_name(MSG_process_self())); - VERB2("%s %f", xbt_str_join(action, " "), MSG_get_clock() - clock); + VERB2("%s %f", xbt_str_join_array(action, " "), MSG_get_clock() - clock); asynchronous_cleanup(); } -static void action_recv(xbt_dynar_t action) +static void action_recv(char*const* action) { char *name = NULL; char mailbox_name[250]; m_task_t task = NULL; double clock = MSG_get_clock(); - sprintf(mailbox_name, "%s_%s", xbt_dynar_get_as(action, 2, char *), + sprintf(mailbox_name, "%s_%s", action[2], MSG_process_get_name(MSG_process_self())); if (XBT_LOG_ISENABLED(actions, xbt_log_priority_verbose)) - name = xbt_str_join(action, " "); + name = xbt_str_join_array(action, " "); #ifdef HAVE_TRACING int rank = get_rank(MSG_process_get_name(MSG_process_self())); @@ -160,7 +155,7 @@ static void action_recv(xbt_dynar_t action) asynchronous_cleanup(); } -static void action_Irecv(xbt_dynar_t action) +static void action_Irecv(char*const* action) { char mailbox[250]; double clock = MSG_get_clock(); @@ -176,7 +171,7 @@ static void action_Irecv(xbt_dynar_t action) TRACE_smpi_ptp_in(rank, src_traced, rank, "Irecv"); #endif - sprintf(mailbox, "%s_%s", xbt_dynar_get_as(action, 2, char *), + sprintf(mailbox, "%s_%s", action[2], MSG_process_get_name(MSG_process_self())); m_task_t t=NULL; xbt_dynar_push(globals->tasks,&t); @@ -186,7 +181,7 @@ static void action_Irecv(xbt_dynar_t action) mailbox); xbt_dynar_push(globals->irecvs,&c); - VERB2("%s %f", xbt_str_join(action, " "), MSG_get_clock() - clock); + VERB2("%s %f", xbt_str_join_array(action, " "), MSG_get_clock() - clock); #ifdef HAVE_TRACING TRACE_smpi_ptp_out(rank, src_traced, rank, "Irecv"); @@ -196,7 +191,7 @@ static void action_Irecv(xbt_dynar_t action) } -static void action_wait(xbt_dynar_t action) +static void action_wait(char*const* action) { char *name = NULL; m_task_t task = NULL; @@ -205,10 +200,10 @@ static void action_wait(xbt_dynar_t action) process_globals_t globals = (process_globals_t) MSG_process_get_data(MSG_process_self()); xbt_assert1(xbt_dynar_length(globals->irecvs), - "action wait not preceeded by any irecv: %s", xbt_str_join(action," ")); + "action wait not preceded by any irecv: %s", xbt_str_join_array(action," ")); if (XBT_LOG_ISENABLED(actions, xbt_log_priority_verbose)) - name = xbt_str_join(action, " "); + name = xbt_str_join_array(action, " "); #ifdef HAVE_TRACING process_globals_t counters = (process_globals_t) MSG_process_get_data(MSG_process_self()); int src_traced = counters->last_Irecv_sender_id; @@ -234,7 +229,7 @@ static void action_wait(xbt_dynar_t action) } /* FIXME: that's a poor man's implementation: we should take the message exchanges into account */ -static void action_barrier(xbt_dynar_t action) +static void action_barrier(char*const* action) { char *name = NULL; static smx_mutex_t mutex = NULL; @@ -242,7 +237,7 @@ static void action_barrier(xbt_dynar_t action) static int processes_arrived_sofar=0; if (XBT_LOG_ISENABLED(actions, xbt_log_priority_verbose)) - name = xbt_str_join(action, " "); + name = xbt_str_join_array(action, " "); if (mutex == NULL) { // first arriving on the barrier mutex = SIMIX_req_mutex_init(); @@ -274,13 +269,13 @@ static void action_barrier(xbt_dynar_t action) } -static void action_reduce(xbt_dynar_t action) +static void action_reduce(char*const* action) { int i; char *reduce_identifier; char mailbox[80]; - double comm_size = parse_double(xbt_dynar_get_as(action, 2, char *)); - double comp_size = parse_double(xbt_dynar_get_as(action, 3, char *)); + double comm_size = parse_double(action[2]); + double comp_size = parse_double(action[3]); m_task_t comp_task = NULL; const char *process_name; double clock = MSG_get_clock(); @@ -324,16 +319,16 @@ static void action_reduce(xbt_dynar_t action) mailbox); } - VERB2("%s %f", xbt_str_join(action, " "), MSG_get_clock() - clock); + VERB2("%s %f", xbt_str_join_array(action, " "), MSG_get_clock() - clock); free(reduce_identifier); } -static void action_bcast(xbt_dynar_t action) +static void action_bcast(char*const* action) { int i; char *bcast_identifier; char mailbox[80]; - double comm_size = parse_double(xbt_dynar_get_as(action, 2, char *)); + double comm_size = parse_double(action[2]); m_task_t task = NULL; const char *process_name; double clock = MSG_get_clock(); @@ -373,19 +368,19 @@ static void action_bcast(xbt_dynar_t action) DEBUG2("%s: %s has received", bcast_identifier, process_name); } - VERB2("%s %f", xbt_str_join(action, " "), MSG_get_clock() - clock); + VERB2("%s %f", xbt_str_join_array(action, " "), MSG_get_clock() - clock); free(bcast_identifier); } -static void action_sleep(xbt_dynar_t action) +static void action_sleep(char*const* action) { char *name = NULL; - char *duration = xbt_dynar_get_as(action, 2, char *); + const char *duration = action[2]; double clock = MSG_get_clock(); if (XBT_LOG_ISENABLED(actions, xbt_log_priority_verbose)) - name = xbt_str_join(action, " "); + name = xbt_str_join_array(action, " "); DEBUG1("Entering %s", name); MSG_process_sleep(parse_double(duration)); @@ -395,12 +390,12 @@ static void action_sleep(xbt_dynar_t action) free(name); } -static void action_allReduce(xbt_dynar_t action) { +static void action_allReduce(char*const* action) { int i; char *allreduce_identifier; char mailbox[80]; - double comm_size = parse_double(xbt_dynar_get_as(action, 2, char *)); - double comp_size = parse_double(xbt_dynar_get_as(action, 3, char *)); + double comm_size = parse_double(action[2]); + double comp_size = parse_double(action[3]); m_task_t task = NULL, comp_task = NULL; const char *process_name; double clock = MSG_get_clock(); @@ -463,33 +458,33 @@ static void action_allReduce(xbt_dynar_t action) { DEBUG2("%s: %s has received", allreduce_identifier, process_name); } - VERB2("%s %f", xbt_str_join(action, " "), MSG_get_clock() - clock); + VERB2("%s %f", xbt_str_join_array(action, " "), MSG_get_clock() - clock); free(allreduce_identifier); } -static void action_comm_size(xbt_dynar_t action) +static void action_comm_size(char*const* action) { char *name = NULL; - char *size = xbt_dynar_get_as(action, 2, char *); + const char *size = action[2]; double clock = MSG_get_clock(); if (XBT_LOG_ISENABLED(actions, xbt_log_priority_verbose)) - name = xbt_str_join(action, " "); + name = xbt_str_join_array(action, " "); communicator_size = parse_double(size); VERB2("%s %f", name, MSG_get_clock() - clock); if (XBT_LOG_ISENABLED(actions, xbt_log_priority_verbose)) free(name); } -static void action_compute(xbt_dynar_t action) +static void action_compute(char*const* action) { char *name = NULL; - char *amout = xbt_dynar_get_as(action, 2, char *); + const char *amout = action[2]; m_task_t task = MSG_task_create(name, parse_double(amout), 0, NULL); double clock = MSG_get_clock(); if (XBT_LOG_ISENABLED(actions, xbt_log_priority_verbose)) - name = xbt_str_join(action, " "); + name = xbt_str_join_array(action, " "); DEBUG1("Entering %s", name); MSG_task_execute(task); MSG_task_destroy(task); @@ -498,7 +493,7 @@ static void action_compute(xbt_dynar_t action) free(name); } -static void action_init(xbt_dynar_t action) +static void action_init(char*const* action) { #ifdef HAVE_TRACING TRACE_smpi_init(get_rank(MSG_process_get_name(MSG_process_self()))); @@ -512,7 +507,7 @@ static void action_init(xbt_dynar_t action) } -static void action_finalize(xbt_dynar_t action) +static void action_finalize(char*const* action) { #ifdef HAVE_TRACING TRACE_smpi_finalize(get_rank(MSG_process_get_name(MSG_process_self()))); diff --git a/include/msg/msg.h b/include/msg/msg.h index 7b5e58e301..1ce1cf5fef 100644 --- a/include/msg/msg.h +++ b/include/msg/msg.h @@ -216,8 +216,7 @@ XBT_PUBLIC(MSG_error_t) double timeout); /************************** Action handling **********************************/ -typedef void (*msg_action_fun) (xbt_dynar_t args); -XBT_PUBLIC(void) MSG_action_paranoid_mode_set(int mode); +typedef void (*msg_action_fun) (char*const* args); XBT_PUBLIC(void) MSG_action_register(const char *action_name, msg_action_fun function); XBT_PUBLIC(void) MSG_action_unregister(const char *action_name); diff --git a/include/xbt/replay_trace_reader.h b/include/xbt/replay_trace_reader.h new file mode 100644 index 0000000000..ae005c3ece --- /dev/null +++ b/include/xbt/replay_trace_reader.h @@ -0,0 +1,20 @@ +/* xbt/replay_trace_reader.h -- Tools to parse a replay file */ + +/* Copyright (c) 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 "xbt/misc.h" /* SG_BEGIN_DECL */ + +SG_BEGIN_DECL() + +typedef struct s_replay_trace_reader *xbt_replay_trace_reader_t; + +xbt_replay_trace_reader_t xbt_replay_trace_reader_new(const char*filename); +char * const *xbt_replay_trace_reader_get(xbt_replay_trace_reader_t reader); +void xbt_replay_trace_reader_free(xbt_replay_trace_reader_t *reader); +const char *xbt_replay_trace_reader_position(xbt_replay_trace_reader_t reader); + +SG_END_DECL() diff --git a/include/xbt/str.h b/include/xbt/str.h index e2f5726862..93a987bc5e 100644 --- a/include/xbt/str.h +++ b/include/xbt/str.h @@ -37,6 +37,7 @@ XBT_PUBLIC(xbt_dynar_t) xbt_str_split_quoted_in_place(char *s); XBT_PUBLIC(xbt_dynar_t) xbt_str_split_str(const char *s, const char *sep); XBT_PUBLIC(char *) xbt_str_join(xbt_dynar_t dynar, const char *sep); +XBT_PUBLIC(char *) xbt_str_join_array(char*const* strs, const char *sep); /* */ XBT_PUBLIC(void) xbt_str_subst(char *str, char from, char to, int amount); diff --git a/src/msg/msg_actions.c b/src/msg/msg_actions.c index aab6aaac45..b7a54c516d 100644 --- a/src/msg/msg_actions.c +++ b/src/msg/msg_actions.c @@ -8,18 +8,11 @@ #include "msg/private.h" #include "xbt/str.h" #include "xbt/dynar.h" +#include "xbt/replay_trace_reader.h" XBT_LOG_NEW_DEFAULT_SUBCATEGORY(msg_action, msg, "MSG actions for trace driven simulation"); -static int paranoid_action_replayer=1; -/** \ingroup msg_actions - * \brief set the paranoid mode: true if we must check our input, false if it's well formated - */ -void MSG_action_paranoid_mode_set(int mode) { - paranoid_action_replayer = mode; -} - static xbt_dict_t action_funs; static xbt_dict_t action_queues; @@ -28,7 +21,7 @@ static FILE *action_fp = NULL; static char *action_line = NULL; static size_t action_len = 0; -static xbt_dynar_t action_get_action(char *name); +static char*const* action_get_action(char *name); /** \ingroup msg_actions * \brief Registers a function to handle a kind of action @@ -58,20 +51,13 @@ void MSG_action_unregister(const char *action_name) static int MSG_action_runner(int argc, char *argv[]) { - xbt_dynar_t evt = NULL; - char *line = NULL; - size_t line_len = 0; - FILE *fp = NULL; - char *comment = NULL; - char *evtname = NULL; - ssize_t read; + char *const*evt; if (action_fp) { // A unique trace file while ((evt = action_get_action(argv[0]))) { - msg_action_fun function = - xbt_dict_get(action_funs, xbt_dynar_get_as(evt, 1, char *)); + msg_action_fun function = xbt_dict_get(action_funs, evt[1]); (*function) (evt); - xbt_dynar_free(&evt); + free((char**)evt); } } else { // Should have got my trace file in argument xbt_assert1(argc >= 2, @@ -79,39 +65,18 @@ static int MSG_action_runner(int argc, char *argv[]) "and no process-wide trace file provided in deployment file. Aborting.", argv[0] ); - - fp = fopen(argv[1], "r"); - xbt_assert2(fp != NULL, "Cannot open %s: %s", argv[1], - strerror(errno)); - - // Read lines and execute them until I reach the end of file - while ((read = getline(&line, &line_len, fp)) != -1) { - // cleanup and split the string I just read - if (paranoid_action_replayer) { - comment = strchr(line, '#'); - if (comment != NULL) - *comment = '\0'; - xbt_str_trim(line, NULL); - } - evt = xbt_str_split_quoted_in_place(line); - if (xbt_dynar_length(evt)==0) { - xbt_dynar_free(&evt); - continue; - } - - evtname = xbt_dynar_get_as(evt, 0, char *); - if (!strcmp(argv[0], evtname)) { - msg_action_fun function = - xbt_dict_get(action_funs, xbt_dynar_get_as(evt, 1, char *)); + xbt_replay_trace_reader_t reader = xbt_replay_trace_reader_new(argv[1]); + while ((evt=xbt_replay_trace_reader_get(reader))) { + if (!strcmp(argv[0],evt[0])) { + msg_action_fun function = xbt_dict_get(action_funs, evt[1]); (*function) (evt); + free((char**)evt); } else { - WARN1("Ignore trace element not for me: %s", - xbt_str_join(evt, " ")); + WARN1("%s: Ignore trace element not for me", + xbt_replay_trace_reader_position(reader)); } - xbt_dynar_free(&evt); } - fclose(fp); - + xbt_replay_trace_reader_free(&reader); } return 0; } @@ -130,7 +95,7 @@ void _MSG_action_exit() } -static xbt_dynar_t action_get_action(char *name) +static char*const* action_get_action(char *name) { ssize_t read; xbt_dynar_t evt = NULL; @@ -159,7 +124,7 @@ static xbt_dynar_t action_get_action(char *name) // if it's for me, I'm done evtname = xbt_dynar_get_as(evt, 0, char *); if (!strcmp(name, evtname)) { - return evt; + return xbt_dynar_to_array(evt); } else { // Else, I have to store it for the relevant colleague xbt_dynar_t otherqueue = @@ -176,7 +141,7 @@ static xbt_dynar_t action_get_action(char *name) } else { // Get something from my queue and return it xbt_dynar_shift(myqueue, &evt); - return evt; + return xbt_dynar_to_array(evt); } diff --git a/src/replay/replay_trace_reader.c b/src/replay/replay_trace_reader.c deleted file mode 100644 index 2804d4192d..0000000000 --- a/src/replay/replay_trace_reader.c +++ /dev/null @@ -1,61 +0,0 @@ -/* Copyright (c) 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_config.h" -#include -#include "replay.h" -#include "xbt/str.h" - -XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(replay); - -typedef struct s_replay_trace_reader { - FILE *fp; - char *line; - size_t line_len; - char *position; /* stable storage */ - char *filename; int linenum; -} s_replay_trace_reader_t; - -replay_trace_reader_t replay_trace_reader_new(const char*filename) { - replay_trace_reader_t res = xbt_new0(s_replay_trace_reader_t,1); - res->fp = fopen(filename, "r"); - xbt_assert2(res->fp != NULL, "Cannot open %s: %s", filename, - strerror(errno)); - res->filename = xbt_strdup(filename); - return res; -} - -const char *replay_trace_reader_position(replay_trace_reader_t reader) { - if (reader->position) - free(reader->position); - reader->position = bprintf("%s:%d",reader->filename,reader->linenum); - return (const char*)reader->position; -} -const char * const *replay_trace_reader_get(replay_trace_reader_t reader) { - ssize_t read; - read = getline(&reader->line, &reader->line_len, reader->fp); - //INFO1("got from trace: %s",reader->line); - reader->linenum++; - if (read==-1) - return NULL; /* end of file */ - char *comment = strchr(reader->line, '#'); - if (comment != NULL) - *comment = '\0'; - xbt_str_trim(reader->line, NULL); - if (reader->line[0] == '\0') - return replay_trace_reader_get(reader); /* Get next line */ - - return xbt_dynar_to_array(xbt_str_split_quoted_in_place(reader->line)); -} - -void replay_trace_reader_free(replay_trace_reader_t *reader) { - free((*reader)->filename); - if ((*reader)->position) - free((*reader)->position); - fclose((*reader)->fp); - free((*reader)->line); - free(*reader); - *reader=NULL; -} diff --git a/src/xbt/xbt_str.c b/src/xbt/xbt_str.c index 9a6cfa510a..a0b6bfa015 100644 --- a/src/xbt/xbt_str.c +++ b/src/xbt/xbt_str.c @@ -445,6 +445,7 @@ xbt_dynar_t xbt_str_split_quoted(const char *s) } free(str_to_free); xbt_dynar_shrink(res, 0); + xbt_dynar_free(&parsed); return res; } @@ -509,7 +510,6 @@ XBT_TEST_UNIT("xbt_str_split_str", test_split_str, "test the function xbt_str_sp #endif /* SIMGRID_TEST */ /** @brief Join a set of strings as a single string */ - char *xbt_str_join(xbt_dynar_t dyn, const char *sep) { int len = 1, dyn_len = xbt_dynar_length(dyn); @@ -536,6 +536,39 @@ char *xbt_str_join(xbt_dynar_t dyn, const char *sep) } return res; } +/** @brief Join a set of strings as a single string + * + * The parameter must be a NULL-terminated array of chars, + * just like xbt_dynar_to_array() produces + */ +char *xbt_str_join_array(char*const* strs, const char *sep) +{ + char *res,*q; + int amount_strings=0; + int len=0; + int i; + + if ((!strs) || (!strs[0])) + return xbt_strdup(""); + + /* compute the length before malloc */ + for (i=0;strs[i];i++) { + len += strlen(strs[i]); + amount_strings++; + } + len += strlen(sep) * amount_strings; + + /* Do the job */ + q = res = xbt_malloc(len); + for (i=0;strs[i];i++) { + if (i!=0) { // not first loop + q += sprintf(q, "%s%s", sep, strs[i]); + } else { + q += sprintf(q,"%s",strs[i]); + } + } + return res; +} #if defined(SIMGRID_NEED_GETLINE) || defined(DOXYGEN) /** @brief Get a single line from the stream (reimplementation of the GNU getline) -- 2.20.1