/************************************************************************* MODULE : $RCSfile: targdata.c $ PART OF : CTC/LIBRARY Host-Target support VERSION : $Revision: 1.20 $, $Date: 2017/03/28 13:42:39 $ AUTHOR : $Author: olavi $ LAST EDITED : 28.03.2017/op DESCRIPTION : targdata.c implements functions used by the CTC++ instrumented code. This file should not be modified by the user. Copyright (c) 1998-2013 Testwell Oy Copyright (c) 2013-2017 Verifysoft Technology GmbH **************************************************************************/ /* Revision history $Log: targdata.c $ Revision 1.20 2017/03/28 13:42:39 olavi - changed from register_module2() the parameters types 'long' and 'unsigned long' to CTC_LONG and CTC_ULONG for ensuring 32-bit. Revision 1.19 2016/12/19 15:01:16 olavi - added to ctc_append_all() a watchdog against parallel write-outs of two threads (later just returns), and now all write-outs go via it. - #ifdef __CTC__ #error ... #endif guard on file's instrumentation changed to #pragma CTC SKIP ... #pragma CTC ENDSKIP (works better...) Revision 1.18 2015/11/17 11:38:48 seppo - In function ctc_register_module2, casted const away from dat_file and from source_path to suppress compiler warnings. Revision 1.17 2013/11/27 08:30:02 seppo - Removed the (no longer needed) include of limits.h Revision 1.16 2013/10/16 10:31:36 seppo - Introduced the concept of "test cases" - Added new API function ctc_set_testcase that writes out any coverage data in memory, and stores the argument testcase_name in the new global private variable current_testcase_name - In function module_data_prepare, added the handling of current test case, if there is one, and coverage data is then written to a file named + + ".dat" - Added also #define MAX_FILE_NAME_LENGTH Revision 1.15 2011/08/09 10:23:18 seppo - Removed end counters (ctc_e), and added max timers (ctc_maxtime). "Function counters" substituted for "start counters". - Number of timers (and maxtimers) renamed to num_time (was: num_t) - Added #ifdef __CTC__ guard to prevent compilation with CTC++ - Added new timing instrumentation variables needed for max timers: interface variables ctc_curf_elapsed and ctc_curf_maxtp, and global private variable ctc_sink_maxtimer - Added CTC_CWRAP_START and CTC_CWRAP_END (extern "C" wrappers) around the functions used by the instrumented code and around the variables accessed by the instrumented code - Removed parameter 'foo' from function ctc_register_module - In function ctc_register_module2, added parameter ctc_maxtime, removed parameters ctc_e and foo, and replaced parameter num_t by num_time. CTC_MODULE_INFO's new members rewrite_count and update_count are initialized to zero. - The output data now contains a module marker (indicating the start of a new module) and maxtimers, but does no longer contain end counters. Hence, MODULE_DATA_ARRAYS is now 12 (was: 11). Also the order has changed, now the timestamp comes before the sourcefile and datafile names. (function module_data_prepare) - The array of sizes in output data does no longer contain the size of a timestamp "array" (the size is known to be always 1). Hence, SIZE_ARRAY_SIZE is now 7 (was: 8). - Removed the checks that tested whether there were too many (more than MAX_COUNTER_LIMIT) counters (in which case module information was assumed to be corrupted) (functions ctc_register_module/2, module_data_prepare) - Removed obsolete function ctc_detach_datafile Revision 1.14 2009/11/18 07:39:25 seppo - Added more checks that module information is not corrupted (function module_data_prepare) - Substituted '!defined(NO_STDLIB)' for each 'defined(USE_ATEXIT)' - Added macro CTCAPI for API functions' optional calling convention specifier, and removed the BUILD_DLL macro - In function ctc_register_module2, pointers to counter/timer arrays are now returned only after having added the new module to the module list - Substituted 0 for each NULL Revision 1.13 2009/09/23 05:46:00 seppo - Added checks that module information is not corrupted (functions ctc_register_module, ctc_register_module2) Revision 1.12 2009/02/16 09:47:14 seppo - Added support for building the target side as a DLL Revision 1.11 2007/05/18 10:03:36 olavi - use 0 instead on NULL Revision 1.10 2007/05/17 14:16:33 olavi - Optimised coverage write-out procedure. Now modules (files) are not written out that have all zero counters - Optimised datafile name allocation in ctc_register_module2(). Now existing datafile allocation is used if such exists(no new heap reserv.) - timestamp's type changed from 'unsigned long' to 'long' Revision 1.9 2005/06/09 12:03:34 seppo - Fixed a bug regarding the transferring of timer values; negative values are no longer substituted with zeros - Added explicit type casts such that the code is now also compilable as C++ - Fixed a signed/unsigned comparison warning - Simplified a bit the code of the functions ctc_register_module/2; local static variable exit_handler_installed removed, global static variable module_list utilized instead - Added 'const' qualifier to the parameters source_path and dat_file of ctc_register_module2() Revision 1.8 2003/11/26 18:50:42 seppo - Added new functions ctc_register_module2, ctc_detach_datafile, and ctc_dec that may called by the instrumented code (as of CTC++ v5.2) - The function ctc_quit (#pragma CTC QUIT) now calls the terminating function ctc_exit (previously did nothing) - Replaced the type signed long by CTC_TIMER (timing instrumentation variables ctc_curf_start, ctc_sink_timer, and ctc_curf_tp) - Substituted enum values CTC_FALSE and CTC_TRUE for FALSE and TRUE - Removed obsolete functions ctc_start_timer and ctc_stop_timer Revision 1.7 2001/02/08 17:19:44 olavi Bug corrected, now works also with timing instrumentation Revision 1.6 2001/01/26 11:42:18 olavi Polished the code so that no more compiler warnings. Revision 1.5 2000/05/22 12:16:36 tino Target side library module updated according to CTC++ 5.0 library. Revision 1.4 1998/04/16 17:01:02 pasi - Added the missing ctc_send_data() to ctc_append() Revision 1.3 1998/03/02 19:28:28 pasi - The counters are cleared after they are sent. - A local copy of monitor number is used for thread safety Revision 1.2 1998/02/24 20:57:20 pasi - Now provides the character by character transfer */ /* even if attempted, do not instrument this file */ #ifdef __CTC__ #pragma CTC SKIP #endif /********************** STANDARD LIBRARY INCLUDES ************************/ /* - NO_STDLIB: if this is NOT defined, the atexit() system function is used to register the ctc_send_data() function so that ctc_send_data() is called when the program exits. */ #if !defined(NO_STDLIB) # include /* atexit() */ #endif /* NO_STDLIB */ /********************** APPLICATION INCLUDES *****************************/ #include "ctctypes.h" #include "targdata.h" #include "transfer.h" /**************************** PRIVATE DEFINES ****************************/ /* Optional macros for API functions: - CTCEXPORT: export specifier, by default empty; may be defined if the target side is built as a DLL - CTCAPI: calling convention specifier or some such, by default empty */ #if !defined(CTCEXPORT) # define CTCEXPORT #endif #if !defined(CTCAPI) # define CTCAPI #endif /************************ GLOBAL VARIABLE DEFINITIONS ********************/ /* ctc_curf_start, ctc_curf_tp, ctc_curf_elapsed, and ctc_curf_maxtp are interface variables, used if (exclusive) timing instrumentation is selected, accessed by the instrumented code */ CTC_CWRAP_START CTCEXPORT CTC_TIMER ctc_curf_start = 0; static CTC_TIMER ctc_sink_timer = 0; CTCEXPORT CTC_TIMER* ctc_curf_tp = &ctc_sink_timer; CTCEXPORT CTC_TIMER ctc_curf_elapsed = 0; static CTC_TIMER ctc_sink_maxtimer = 0; CTCEXPORT CTC_TIMER* ctc_curf_maxtp = &ctc_sink_maxtimer; CTC_CWRAP_END /**************************** PRIVATE TYPES ******************************/ /* - t_state: state of the coverage information transfer - BOOLEAN: an enumeration masquerading a boolean */ enum t_state { t_start, t_prepare_module, t_module_data, t_end, t_finished }; typedef enum { CTC_FALSE, CTC_TRUE } BOOLEAN; /*************************** PRIVATE VARIABLES ***************************/ /* - module_list: pointer to the list of registered modules' info structs */ static CTC_MODULE_INFO* module_list = 0; /* Current state of data transfer and characters to transfer: - transfer_state: the current state of the transfer - transfer_buffer: characters waiting for transfer - transfer_index: index of the next character in transfer_buffer */ static enum t_state transfer_state; static char transfer_buffer[MAX_CHARS + 1]; static unsigned transfer_index; /* - current_testcase_name: name of the currently active test case or, if there is not one, an empty string */ static char current_testcase_name[MAX_TESTCASE_NAME + 1] = ""; /***************** PRIVATE EXTERNAL FUNCTION DECLARATIONS ****************/ /* Functions called by the instrumented code: */ CTC_CWRAP_START CTCEXPORT void CTCAPI ctc_register_module(CTC_MODULE_INFO*); CTCEXPORT void CTCAPI ctc_register_module2(const char*, const char*, CTC_LONG, CTC_ULONG, CTC_ULONG, CTC_ULONG, CTC_ULONG, CTC_ULONG, CTC_COUNTER**, CTC_COUNTER**, CTC_COUNTER**, CTC_COUNTER**, CTC_COUNTER**, CTC_TIMER**, CTC_TIMER**); CTCEXPORT void CTCAPI ctc_init(void); CTCEXPORT void CTCAPI ctc_quit(void); CTCEXPORT void CTCAPI ctc_append_all(void); CTCEXPORT void CTCAPI ctc_inc(CTC_COUNTER*); CTCEXPORT void CTCAPI ctc_dec(CTC_COUNTER*); CTCEXPORT int CTCAPI ctc_condition(CTC_COUNTER*, CTC_COUNTER*, unsigned, int); CTCEXPORT void CTCAPI ctc_set_testcase(const char*); CTC_CWRAP_END /********************** PRIVATE FUNCTION DECLARATIONS ********************/ static void nullify_counters(CTC_MODULE_INFO *info); static int fill_buffer(char* buffer); static int module_is_all_zero(CTC_MODULE_INFO* module); static void module_data_prepare(CTC_MODULE_INFO* info); static int module_data_get(char* buffer); static void number_to_string(unsigned long i, char* buffer); static unsigned my_strlen(const char* s); static int my_strcmp(const char* s1, const char* s2); static char* my_strcpy(char* dest, const char* src); /********************** APPL. SPECIFIC MODULE INCLUDES *******************/ /********************** PUBLIC FUNCTIONS *********************************/ /************************************************************************* FUNCTION : ctc_register_module DESCRIPTION : This function is called when the first instrumented function of a module is called. **************************************************************************/ void CTCAPI ctc_register_module( CTC_MODULE_INFO* info) { /* Check that module information is not corrupted */ if (info->source_path == 0 || info->source_path[0] == '\0' || info->dat_file == 0 || info->dat_file[0] == '\0' || info->time_stamp == 0 || info->num_s == 0 /* Must be at least one function counter! */ || info->ctc_s == 0 || info->ctc_j == 0 || info->ctc_t == 0 || info->ctc_f == 0 || info->ctc_c == 0 || info->ctc_time == 0 || info->ctc_maxtime == 0) { ctc_show_error(corrupted_module); ctc_exit(1); } #if !defined NO_STDLIB if (module_list == 0) { /* First time */ (void) atexit(ctc_append_all); } #endif /* NO_STDLIB */ nullify_counters(info); /* Add the module to the head of the list */ info->next = module_list; module_list = info; } /************************************************************************* FUNCTION : ctc_register_module2 ARGUMENTS : - source_path: Path of the source file - dat_file : Path of the datafile (usually absolute) - time_stamp : Time of the instrumentation - num_s : Number of function counters - num_j : Number of jump counters - num_tf : Number of true/false counters - num_c : Number of multicondition counters - num_time : Number of timer/maxtimer counters - ctc_s (out): Pointer to function counter array - ctc_j (out): Pointer to jump counter array - ctc_t (out): Pointer to true counter array - ctc_f (out): Pointer to false counter array - ctc_c (out): Pointer to multicondition counter array - ctc_time (out): Pointer to timer counter array - ctc_maxtime (out): Pointer to maxtimer counter array DESCRIPTION : This function is an alternative to ctc_register_module, and may be called instead of ctc_register_module when the first instrumented function of a module is called. Unlike ctc_register_module, the counter arrays and other module information are allocated from the heap. **************************************************************************/ void CTCAPI ctc_register_module2( const char* source_path, const char* dat_file, CTC_LONG time_stamp, CTC_ULONG num_s, CTC_ULONG num_j, CTC_ULONG num_tf, CTC_ULONG num_c, CTC_ULONG num_time, CTC_COUNTER** ctc_s, CTC_COUNTER** ctc_j, CTC_COUNTER** ctc_t, CTC_COUNTER** ctc_f, CTC_COUNTER** ctc_c, CTC_TIMER** ctc_time, CTC_TIMER** ctc_maxtime) { CTC_STATUS status = ok; CTC_MODULE_INFO* info; CTC_MODULE_INFO* p; /* Check that module information is not corrupted */ if (source_path == 0 || source_path[0] == '\0' || dat_file == 0 || dat_file[0] == '\0' || time_stamp == 0 || num_s == 0 /* Must be at least one function counter! */ || ctc_s == 0 || ctc_j == 0 || ctc_t == 0 || ctc_f == 0 || ctc_c == 0 || ctc_time == 0 || ctc_maxtime == 0) { ctc_show_error(corrupted_module); ctc_exit(1); } #if !defined NO_STDLIB if (module_list == 0) { /* First time */ (void) atexit(ctc_append_all); } #endif /* NO_STDLIB */ /* Allocate memory for a new module list element */ info = (CTC_MODULE_INFO*) ctc_alloc(sizeof(CTC_MODULE_INFO)); if (info == 0) { status = out_of_memory; } else { info->time_stamp = time_stamp; info->rewrite_count = 0; info->update_count = 0; info->num_s = num_s; info->num_j = num_j; info->num_tf = num_tf; info->num_c = num_c; info->num_time = num_time; info->ctc_s = 0; info->ctc_j = 0; info->ctc_t = 0; info->ctc_f = 0; info->ctc_c = 0; info->ctc_time = 0; info->ctc_maxtime = 0; /* Allocate memory for the path of the source file and copy the path */ info->source_path = (char*) ctc_alloc(sizeof(char) * (my_strlen(source_path) + 1)); if (info->source_path == 0) { status = out_of_memory; } else { (void) my_strcpy((char*) info->source_path, source_path); } } if (status == ok) { /* If path of datafile is already known, thanks to some previous registered module (file), use that allocated area */ p = module_list; while (p != 0) { if (my_strcmp(p->dat_file, dat_file) == 0) { /* we have a hit, p has pointer to it */ break; } p = p->next; /* NULL at the end... */ } if (p != 0) { /* Use the previously allocated datafile name */ info->dat_file = p->dat_file; } else { /* Allocate memory for the path of the datafile and copy the path */ info->dat_file = (char*) ctc_alloc(sizeof(char) * (my_strlen(dat_file) + 1)); if (info->dat_file == 0) { status = out_of_memory; } else { (void) my_strcpy((char*) info->dat_file, dat_file); } } } /* Allocate memory for the counter arrays */ if (status == ok) { /* function counters */ info->ctc_s = (CTC_COUNTER*) ctc_alloc(sizeof(CTC_COUNTER) * num_s); if (info->ctc_s == 0) { status = out_of_memory; } } if (status == ok && num_j > 0) { /* jump counters */ info->ctc_j = (CTC_COUNTER*) ctc_alloc(sizeof(CTC_COUNTER) * num_j); if (info->ctc_j == 0) { status = out_of_memory; } } if (status == ok && num_tf > 0) { /* true counters */ info->ctc_t = (CTC_COUNTER*) ctc_alloc(sizeof(CTC_COUNTER) * num_tf); if (info->ctc_t == 0) { status = out_of_memory; } } if (status == ok && num_tf > 0) { /* false counters */ info->ctc_f = (CTC_COUNTER*) ctc_alloc(sizeof(CTC_COUNTER) * num_tf); if (info->ctc_f == 0) { status = out_of_memory; } } if (status == ok && num_c > 0) { /* multicondition counters */ info->ctc_c = (CTC_COUNTER*) ctc_alloc(sizeof(CTC_COUNTER) * num_c); if (info->ctc_c == 0) { status = out_of_memory; } } if (status == ok && num_time > 0) { /* timer counters */ info->ctc_time = (CTC_TIMER*) ctc_alloc(sizeof(CTC_TIMER) * num_time); if (info->ctc_time == 0) { status = out_of_memory; } } if (status == ok && num_time > 0) { /* max timer counters */ info->ctc_maxtime = (CTC_TIMER*) ctc_alloc(sizeof(CTC_TIMER) * num_time); if (info->ctc_maxtime == 0) { status = out_of_memory; } } if (status == ok) { nullify_counters(info); /* Add the module to the head of the list */ info->next = module_list; module_list = info; /* Return pointers to the counter arrays to the instrumented code */ *ctc_s = info->ctc_s; *ctc_j = info->ctc_j; *ctc_t = info->ctc_t; *ctc_f = info->ctc_f; *ctc_c = info->ctc_c; *ctc_time = info->ctc_time; *ctc_maxtime = info->ctc_maxtime; } if (status != ok) { ctc_show_error(status); ctc_exit(1); } } /************************************************************************* FUNCTION : ctc_init DESCRIPTION : This function is called when a "#pragma CTC INIT" is executed in the source code. *************************************************************************/ void CTCAPI ctc_init(void) { CTC_MODULE_INFO* module; module = module_list; while (module != 0) { nullify_counters(module); module = module->next; } } /************************************************************************* FUNCTION : ctc_quit DESCRIPTION : This function is called when a "#pragma CTC QUIT" is executed in the source code. **************************************************************************/ void CTCAPI ctc_quit(void) { ctc_exit(0); } /************************************************************************* FUNCTION : ctc_append_all DESCRIPTION : Write out (initial or append) coverage data to datafile. **************************************************************************/ void CTCAPI ctc_append_all(void) { static BOOLEAN now_sending = CTC_FALSE; /* Flag if (parallel) sending is going on */ if (now_sending) { /* Sending going on from another thread. It already writes out my coverage data, too. Do nothing */ return; } else { now_sending = CTC_TRUE; /* Reserve for me ... */ } ctc_send_data(); now_sending = CTC_FALSE; /* sending done, new sending can start */ } /************************************************************************* FUNCTION : ctc_inc DESCRIPTION : Increments the index'th counter array element by 1 if an overflow will not take place. Otherwise the value is left unchanged. *************************************************************************/ void CTCAPI ctc_inc( CTC_COUNTER *counter) { if (*counter < MAX_COUNTER) (*counter)++; } /************************************************************************* FUNCTION : ctc_dec DESCRIPTION : Decrements the index'th counter array element by 1. *************************************************************************/ void CTCAPI ctc_dec( CTC_COUNTER *counter) { /* Unresolved problem: ctc_dec does not know whether the counter value equalling MAX_COUNTER is an exact value or "greater than MAX_COUNTER" (a value no more incremented by ctc_inc to avoid overflow). */ (*counter)--; } /************************************************************************* FUNCTION : ctc_condition DESCRIPTION : Increments the index'th true or false counter array element by 1 if an overflow will not take place. Otherwise the value is left unchanged. The true or the false counter array is selected according to the value of the condition. The given value of the condition is also returned. *************************************************************************/ int CTCAPI ctc_condition( CTC_COUNTER *ctc_t, CTC_COUNTER *ctc_f, unsigned index, int cond) { if (cond) /* increment index'th true counter */ { if (ctc_t[index] < MAX_COUNTER) ctc_t[index]++; } else if (ctc_f[index] < MAX_COUNTER) ctc_f[index]++; return cond; } /************************************************************************* FUNCTION : ctc_set_testcase DESCRIPTION : Writes out the current coverage data (possibly data of a preceding test case), and stores a new test case name *************************************************************************/ void CTCAPI ctc_set_testcase( const char* testcase_name) { /* Append data in memory to the current datafile */ ctc_append_all(); if (my_strlen(testcase_name) > MAX_TESTCASE_NAME) { ctc_show_error(bad_testcase_name); ctc_exit(1); } (void) my_strcpy(current_testcase_name, testcase_name); } /************************************************************************* FUNCTION : ctc_prepare_upload PURPOSE : Prepares the coverage data for transfer ARGUMENTS : None RETURNS : void *************************************************************************/ void ctc_prepare_upload(void) { /* Initialize transfer state */ transfer_state = t_start; *transfer_buffer = '\0'; transfer_index = 0; } /************************************************************************* FUNCTION : ctc_get_character PURPOSE : Serializes the coverage data into a character stream. After the last character is read, the accumulated coverage data is cleared. ARGUMENTS : None RETURNS : unsigned character or -1 when there is no more data *************************************************************************/ int ctc_get_character(void) { /* If buffer is empty, get next item */ if (transfer_buffer[transfer_index] == '\0') { if (fill_buffer(transfer_buffer)) { /* Out of items, return end marker */ return -1; } } /* Return next character */ return transfer_buffer[transfer_index++]; } /************************** PRIVATE FUNCTIONS ****************************/ /************************************************************************* FUNCTION : fill_buffer PURPOSE : Fills the output buffer with characters ARGUMENTS : - buffer: the output buffer RETURNS : 1 on end of data 0 on more data **************************************************************************/ static int fill_buffer(char* buffer) { static CTC_MODULE_INFO* module; BOOLEAN finished = CTC_FALSE; int result = 0; transfer_index = 0; while (!finished) { switch (transfer_state) { case t_start: /* at the very beginning of "write-out burst", start sequence now */ (void) my_strcpy(transfer_buffer, START_SEQ); finished = CTC_TRUE; /* and then, later, consider the first module */ module = module_list; transfer_state = t_prepare_module; break; case t_prepare_module: if (module != 0) { /* prepare next module */ if (module_is_all_zero(module)) { /* But skip it if it has zeros in all counters! */ module = module->next; transfer_state = t_prepare_module; } else { /* ok, there was some non-zero counters, output this... */ module_data_prepare(module); module = module->next; transfer_state = t_module_data; } } else { transfer_state = t_end; } break; case t_module_data: if (!module_data_get(buffer)) { finished = CTC_TRUE; } else { transfer_state = t_prepare_module; } break; case t_end: (void) my_strcpy(transfer_buffer, END_SEQ); transfer_state = t_finished; finished = CTC_TRUE; break; case t_finished: result = 1; finished = CTC_TRUE; break; } } return result; } /************************************************************************* FUNCTION : module_is_all_zero PURPOSE : Check if module's counters are all zero ARGUMENTS : - module: pointer to the module's counter (and timer) data RETURNS : 1 (true) the module has all zero counters 0 (false) otherwise **************************************************************************/ static int module_is_all_zero(CTC_MODULE_INFO* module) { unsigned long i; /* each vector... first non-zero decides... */ for (i = 0; i < module->num_s; i++) { if (module->ctc_s[i] != 0) {return 0; } } for (i = 0; i < module->num_j; i++) { if (module->ctc_j[i] != 0) {return 0; } } for (i = 0; i < module->num_tf; i++) { if (module->ctc_t[i] != 0) {return 0; } } for (i = 0; i < module->num_tf; i++) { if (module->ctc_f[i] != 0) {return 0; } } for (i = 0; i < module->num_c; i++) { if (module->ctc_c[i] != 0) {return 0; } } /* timings not considered...! */ /* when got this far, all was zero! */ return 1; } /************************************************************************* The following functions, module_data_prepare() and module_data_get() serialize the coverage/timer data of a single module into a number stream. The data is internally represented as an array of different (string, counter or timer) arrays. Alternatively the data could have been serialized with "states", thus requiring less data space but more program statements. Module information is arranged as A_DATA arrays: - module_data: data arrays to transfer - module_data_current_array: index of the current data array - module_data_current_array_index: current index of the current data array - array_sizes: array of the sizes of the arrays to transmit The types used are: - a_type: type of a data array - A_DATA: structure holding the information of a single array, be it a string, counter or timer array The macros used are: - SIZE_ARRAY_SIZE: size of the array of sizes to transmit - MODULE_DATA_ARRAYS: number of A_DATA arrays for one module */ #define SIZE_ARRAY_SIZE 7 #define MODULE_DATA_ARRAYS 12 enum a_type { a_string, a_stamp, a_counter, a_timer }; typedef struct A_DATA { enum a_type array_type; unsigned long array_size; union { const char* string; CTC_COUNTER* counter; CTC_TIMER* timer; } array; } A_DATA; static A_DATA module_data[MODULE_DATA_ARRAYS]; static int module_data_current_array; static int module_data_current_array_index; static CTC_COUNTER array_sizes[SIZE_ARRAY_SIZE]; /* - MAX_FILE_NAME_LENGTH: the maximum length of a testcase datafile name */ #define MAX_FILE_NAME_LENGTH 260 /************************************************************************* FUNCTION : module_data_prepare PURPOSE : Prepares the module data for array by array transfer ARGUMENTS : - info: module information struct RETURNS : void DESCRIPTION : *************************************************************************/ static void module_data_prepare(CTC_MODULE_INFO* info) { int index = 0; static CTC_COUNTER module_marker; static const char dat_extension[] = ".dat"; static char current_datafile_path[MAX_FILE_NAME_LENGTH] = ""; /* Check that module information is not corrupted */ if (info->source_path == 0 || info->source_path[0] == '\0' || info->dat_file == 0 || info->dat_file[0] == '\0' || info->time_stamp == 0 || info->num_s == 0 /* Must be at least one function counter! */ || info->ctc_s == 0) { ctc_show_error(corrupted_module); ctc_exit(1); } /* First is the "module marker" */ module_marker = MODULE_MARKER; module_data[index].array_type = a_counter; module_data[index].array_size = 1; module_data[index].array.counter = &module_marker; /* Next is the array of sizes */ index++; module_data[index].array_type = a_counter; module_data[index].array_size = SIZE_ARRAY_SIZE; module_data[index].array.counter = array_sizes; array_sizes[0] = (CTC_COUNTER) my_strlen(info->source_path); if (current_testcase_name[0] == '\0') { array_sizes[1] = (CTC_COUNTER) my_strlen(info->dat_file); } else { /* Datafile name length + testcase name length */ array_sizes[1] = (CTC_COUNTER) (my_strlen(info->dat_file) + my_strlen(current_testcase_name)); } array_sizes[2] = (CTC_COUNTER) info->num_s; array_sizes[3] = (CTC_COUNTER) info->num_j; array_sizes[4] = (CTC_COUNTER) info->num_tf; array_sizes[5] = (CTC_COUNTER) info->num_c; array_sizes[6] = (CTC_COUNTER) info->num_time; index++; module_data[index].array_type = a_stamp; module_data[index].array_size = 1; module_data[index].array.counter = (CTC_COUNTER*) &(info->time_stamp); index++; module_data[index].array_type = a_string; module_data[index].array_size = my_strlen(info->source_path); module_data[index].array.string = info->source_path; index++; module_data[index].array_type = a_string; if (current_testcase_name[0] == '\0') { module_data[index].array_size = my_strlen(info->dat_file); module_data[index].array.string = info->dat_file; } else { /* Datafile name + testcase name, e.g., "PROJ1.dat" + "test13" => PROJ1test13.dat */ unsigned int i, len; len = my_strlen(info->dat_file) - my_strlen(dat_extension); for (i = 0; i < len; i++) { current_datafile_path[i] = info->dat_file[i]; } (void) my_strcpy(current_datafile_path + len, current_testcase_name); len = my_strlen(current_datafile_path); (void) my_strcpy(current_datafile_path + len, dat_extension); module_data[index].array_size = my_strlen(current_datafile_path); module_data[index].array.string = current_datafile_path; } index++; module_data[index].array_type = a_counter; module_data[index].array_size = info->num_s; module_data[index].array.counter = info->ctc_s; index++; module_data[index].array_type = a_counter; module_data[index].array_size = info->num_j; module_data[index].array.counter = info->ctc_j; index++; module_data[index].array_type = a_counter; module_data[index].array_size = info->num_tf; module_data[index].array.counter = info->ctc_t; index++; module_data[index].array_type = a_counter; module_data[index].array_size = info->num_tf; module_data[index].array.counter = info->ctc_f; index++; module_data[index].array_type = a_counter; module_data[index].array_size = info->num_c; module_data[index].array.counter = info->ctc_c; index++; module_data[index].array_type = a_timer; module_data[index].array_size = info->num_time; module_data[index].array.timer = info->ctc_time; index++; module_data[index].array_type = a_timer; module_data[index].array_size = info->num_time; module_data[index].array.timer = info->ctc_maxtime; module_data_current_array = 0; module_data_current_array_index = 0; } /************************************************************************* FUNCTION : module_data_get PURPOSE : Gets the next number in the module data arrays into the output buffer. ARGUMENTS : - buffer: output buffer RETURNS : 0 on success 1 on end of data *************************************************************************/ static int module_data_get(char* buffer) { A_DATA* current_array_ptr; unsigned long number = 0; /* If the current array is finished, and there are more arrays, get the next array. A loop is used so that empty arrays (== array which is "already finished") are skipped. */ while (module_data_current_array < MODULE_DATA_ARRAYS && (unsigned long) module_data_current_array_index == module_data[module_data_current_array].array_size) { /* next data array */ module_data_current_array++; module_data_current_array_index = 0; } if (module_data_current_array == MODULE_DATA_ARRAYS) { /* end of arrays */ return 1; } current_array_ptr = &(module_data[module_data_current_array]); switch (current_array_ptr->array_type) { case a_string: number = (unsigned long) current_array_ptr->array.string [module_data_current_array_index]; break; case a_stamp: number = (unsigned long) current_array_ptr->array.counter [module_data_current_array_index]; break; case a_counter: number = (unsigned long) current_array_ptr->array.counter [module_data_current_array_index]; /* Clear the counter after data is read */ current_array_ptr->array.counter[module_data_current_array_index] = 0; break; case a_timer: number = (unsigned long) current_array_ptr->array.timer [module_data_current_array_index]; /* Clear the counter after data is read */ current_array_ptr->array.timer[module_data_current_array_index] = 0; break; } number_to_string(number, buffer); module_data_current_array_index++; return 0; } /************************************************************************* FUNCTION : number_to_string PURPOSE : Prints the given number as a string, ends the string with the END_NUMBER delimiter. ARGUMENTS : - i: the number to print - buffer: buffer into which to print (must have space for the number and the delimiter + '\0') RETURNS : void DESCRIPTION : **************************************************************************/ static void number_to_string(unsigned long i, char* buffer) { int end = 0; int start = 0; char start_c; /* Map into number_base (results in a reversed string) */ do { buffer[end++] = NUMBER_MAP[i % NUMBER_BASE]; i = i / NUMBER_BASE; } while (i && end < (MAX_CHARS - 1)); /* Add number separator to end */ buffer[end] = (char) END_NUMBER; buffer[end + 1] = '\0'; /* Reverse number portion to correct order */ end--; while (start < end) { start_c = buffer[start]; buffer[start] = buffer[end]; buffer[end] = start_c; start++; end--; } } /************************************************************************* FUNCTION : nullify_counters DESCRIPTION : Loops through counter arrays and sets each element to zero ARGUMENTS : - info: pointer to module information struct RETURNS : void DESCRIPTION : **************************************************************************/ static void nullify_counters( CTC_MODULE_INFO *info) { unsigned long i; for (i = 0; i < info->num_s; i++) { (info->ctc_s)[i] = (CTC_COUNTER) 0; } for (i = 0; i < info->num_j; i++) { (info->ctc_j)[i] = (CTC_COUNTER) 0; } for (i = 0; i < info->num_tf; i++) { (info->ctc_t)[i] = (CTC_COUNTER) 0; (info->ctc_f)[i] = (CTC_COUNTER) 0; } for (i = 0; i < info->num_c; i++) { (info->ctc_c)[i] = (CTC_COUNTER) 0; } for (i = 0; i < info->num_time; i++) { (info->ctc_time)[i] = (CTC_TIMER) 0; (info->ctc_maxtime)[i] = (CTC_TIMER) 0; } } /************************************************************************* FUNCTION : my_strlen PURPOSE : A local strlen implementation **************************************************************************/ static unsigned my_strlen(const char* s) { unsigned i = 0; while (*s++) { i++; } return i; } /************************************************************************* FUNCTION : my_strcmp PURPOSE : A local strcmp implementation **************************************************************************/ static int my_strcmp(const char* s1, const char* s2) { while (1) { if (*s1 == 0 && *s2 == 0) { return 0; } /* s1 == s2 */ else if (*s1 == 0) { return -1; } /* s1 < s2 */ else if (*s2 == 0) { return 1; } /* s1 > s2 */ else if (*s1 > *s2) { return 1; } /* s1 > s2 */ else if (*s1 < *s2) { return -1; } /* s1 < s2 */ else { s1++; s2++; /* see next char... */ } } } /************************************************************************* FUNCTION : my_strcpy PURPOSE : A local strcpy implementation **************************************************************************/ static char* my_strcpy(char* dest, const char* src) { unsigned i = 0; while ((dest[i] = src[i])) i++; return dest; } #ifdef __CTC__ #pragma CTC ENDSKIP #endif /* EOF $RCSfile: targdata.c $ */