| /****************************************************************************** |
| *(C) Copyright 2008 Marvell International Ltd. |
| * All Rights Reserved |
| ******************************************************************************/ |
| /***************************************************************************** |
| * Utility Library |
| * |
| * DESCRIPTION |
| * Tracing utility functions. Allows diagnostic operations to be conditionally |
| * executed. |
| * |
| * EXAMPLE USAGE |
| * |
| * #define MY_TRACE_TAG "myTag" |
| * |
| * { |
| * int trace_peg; |
| * char *message[10]; |
| * |
| * if (utlSetProcessName("my process name") != utlSUCCESS) |
| * <handle error here> |
| * |
| * |
| * --- specify what to trace as a glob pattern --- |
| * if (utlAddTraceCriteria("*") != utlSUCCESS) |
| * <handle error here> |
| * |
| * |
| * trace_peg = 0; |
| * |
| * utlTrace(MY_TRACE_TAG, |
| * trace_peg++; |
| * ); |
| * |
| * utlTrace(MY_TRACE_TAG, |
| * utlPrintTrace("my simple trace message\n"); |
| * ); |
| * |
| * <initialize message here> |
| * |
| * utlTrace(MY_TRACE_TAG, |
| * int i; |
| * |
| * utlPrintTrace("more complex trace message:\n"); |
| * for (i = 0; i < utlNumberOf(message); i++) |
| * utlPrintTrace(" message[%d]: %s\n", i, message[i]); |
| * ); |
| * |
| * |
| * if (utlDumpTracePegs(stderr, "*") != utlSUCCESS) |
| * <handle error here> |
| * |
| * if (utlDeleteTraceCriteria("*") != utlSUCCESS) |
| * <handle error here> |
| * } |
| * |
| *****************************************************************************/ |
| |
| #include <stdio.h> |
| #include <stdarg.h> |
| #include <string.h> |
| |
| #include "utlTypes.h" |
| #include "utlError.h" |
| #include "utlMalloc.h" |
| #include "utlMutex.h" |
| #include "utlSemaphore.h" |
| #include "utlStrMatch.h" |
| |
| #include "utlTrace.h" |
| |
| |
| /*--- Configuration ---------------------------------------------------------*/ |
| |
| /*--- amount to increase size of trace criterion database by when too small ---*/ |
| #define utlTRACE_CRITERION_SIZE_INC ((utlTraceCriterionSize_T)4) |
| |
| /*--- amount to increase size of trace tags database by when too small ---*/ |
| #define utlTRACE_SYMBOLS_SIZE_INC ((utlTraceTagsSize_T)32) |
| |
| /*--- maximum length of a trace prefix string ---*/ |
| #define utlTRACE_MAX_PREFIX_LEN ((size_t)100) |
| |
| |
| /*--- Data Types ------------------------------------------------------------*/ |
| |
| #if defined(utlDEBUG) || defined(utlTEST) |
| /*--- Trace criterion database ---*/ |
| typedef struct utlTraceCriteria_S { |
| char *pattern_p; |
| } utlTraceCriteria_T, *utlTraceCriteria_P; |
| typedef const utlTraceCriteria_T *utlTraceCriteria_P2c; |
| |
| typedef size_t utlTraceCriterionSize_T; |
| |
| typedef enum { |
| utlTRACING_DISABLED = 0, |
| utlTRACING_ENABLED = 1 |
| } utlTraceState_T, *utlTraceState_P; |
| |
| typedef long utlTracePeg_T; |
| |
| typedef struct utlTraceTag_S { |
| const char *name_p; |
| utlTraceState_T state; |
| utlTracePeg_T hits; |
| } utlTraceTag_T, *utlTraceTag_P; |
| typedef const utlTraceTag_T *utlTraceTag_P2c; |
| |
| typedef size_t utlTraceTagsSize_T; |
| |
| |
| /*--- Local Data --------------------------------------------------------*/ |
| |
| static utlSemaphore_T trace_semaphore = utlNULL_SEMAPHORE; /* protection for static trace data */ |
| static char proc_name[20] = "<unknown>"; |
| |
| /*--- Trace criterion database ---*/ |
| static utlTraceCriteria_P trace_criterion_p = NULL; |
| static utlTraceCriterionSize_T trace_criterion_count = 0; |
| static utlTraceCriterionSize_T trace_criterion_size = 0; |
| |
| static utlTraceTag_P trace_tags_p = NULL; |
| static utlTraceTagsSize_T trace_tags_count = 0; |
| static utlTraceTagsSize_T trace_tags_size = 0; |
| #endif /* defined(utlDEBUG) || defined(utlTEST) */ |
| |
| /*---------------------------------------------------------------------------* |
| * FUNCTION |
| * utlSetProcessName(name_p) |
| * INPUT |
| * name_p == name of the process |
| * OUTPUT |
| * none |
| * RETURNS |
| * utlSUCCESS for success, utlFAILED for failure |
| * DESCRIPTION |
| * Sets the process name to `name_p'. |
| *---------------------------------------------------------------------------*/ |
| utlReturnCode_T utlSetProcessName(const char *name_p) |
| { |
| #if defined(utlDEBUG) || defined(utlTEST) |
| size_t n; |
| |
| utlAssert(name_p != NULL); |
| |
| /*--- semaphore initialization ---*/ |
| if (!utlIsSemaphoreInitialized(&trace_semaphore)) |
| { |
| if (utlInitSemaphore(&trace_semaphore, utlSEMAPHORE_ATTR_SHARING_ENABLE | |
| utlSEMAPHORE_ATTR_NESTING_ENABLE) != utlSUCCESS) |
| return utlFAILED; |
| } |
| |
| /*--- acquire write-access ---*/ |
| if (utlAcquireExclusiveAccess(&trace_semaphore) != utlSUCCESS) |
| return utlFAILED; |
| |
| n = utlNumberOf(proc_name) - 1; |
| strncpy(proc_name, name_p, n); |
| proc_name[n] = '\0'; |
| |
| /*--- release write-access ---*/ |
| if (utlReleaseExclusiveAccess(&trace_semaphore) != utlSUCCESS) |
| return utlFAILED; |
| #else |
| UNUSEDPARAM(name_p) |
| #endif /* defined(utlDEBUG) || defined(utlTEST) */ |
| |
| return utlSUCCESS; |
| } |
| #if defined(utlDEBUG) || defined(utlTEST) |
| |
| /*---------------------------------------------------------------------------* |
| * FUNCTION |
| * evaluateTraceState(name_p) |
| * INPUT |
| * name_p == name of the tag to evaluage |
| * OUTPUT |
| * none |
| * RETURNS |
| * utlTRACING_ENABLED or utlTRACING_DISABLED |
| * DESCRIPTION |
| * Evaluates `name_p' to determine if it should be traced or not. |
| *---------------------------------------------------------------------------*/ |
| static utlTraceState_T evaluateTraceState(const char *name_p) |
| { |
| utlTraceCriteria_P criteria_p; |
| utlTraceCriteria_P terminal_criteria_p; |
| |
| utlAssert(name_p != NULL); |
| |
| /*--- search for matching trace criteria ---*/ |
| terminal_criteria_p = trace_criterion_p + trace_criterion_count; |
| for (criteria_p = trace_criterion_p; criteria_p != terminal_criteria_p; criteria_p++) |
| if (utlStrMatch(name_p, criteria_p->pattern_p, false) == true) |
| return utlTRACING_ENABLED; |
| |
| return utlTRACING_DISABLED; |
| } |
| #endif /* defined(utlDEBUG) || defined(utlTEST) */ |
| |
| /*---------------------------------------------------------------------------* |
| * FUNCTION |
| * utlAddTraceCriteria(pattern_p) |
| * INPUT |
| * pattern_p == pattern that specifies a new trace criteria |
| * OUTPUT |
| * none |
| * RETURNS |
| * utlSUCCESS for success, utlFAILED for failure |
| * DESCRIPTION |
| * Adds `pattern_p' to the trace criterion database, and then re-evaluates |
| * all existing trace tags. |
| *---------------------------------------------------------------------------*/ |
| utlReturnCode_T utlAddTraceCriteria(const char *pattern_p) |
| { |
| #if defined(utlDEBUG) || defined(utlTEST) |
| utlTraceCriteria_P criteria_p; |
| |
| utlAssert(pattern_p != NULL); |
| |
| /*--- semaphore initialization ---*/ |
| if (!utlIsSemaphoreInitialized(&trace_semaphore)) |
| { |
| if (utlInitSemaphore(&trace_semaphore, utlSEMAPHORE_ATTR_SHARING_ENABLE | |
| utlSEMAPHORE_ATTR_NESTING_ENABLE) != utlSUCCESS) |
| return utlFAILED; |
| } |
| |
| /*--- acquire write-access ---*/ |
| if (utlAcquireExclusiveAccess(&trace_semaphore) != utlSUCCESS) |
| return utlFAILED; |
| |
| /*--- existing trace criterion database too small? ---*/ |
| if (trace_criterion_count >= trace_criterion_size) |
| { |
| utlTraceCriteria_P new_trace_criterion_p; |
| utlTraceCriterionSize_T new_trace_criterion_size; |
| |
| /*--- increase size of existing trace criterion database... ---*/ |
| new_trace_criterion_size = trace_tags_size + utlTRACE_CRITERION_SIZE_INC; |
| if ((new_trace_criterion_p = utlRealloc(trace_criterion_p, |
| sizeof(utlTraceCriteria_T) * |
| new_trace_criterion_size)) == NULL) |
| { |
| /*--- clean ---*/ |
| (void)utlReleaseExclusiveAccess(&trace_semaphore); |
| |
| return utlFAILED; |
| } |
| |
| trace_criterion_p = new_trace_criterion_p; |
| trace_criterion_size = new_trace_criterion_size; |
| } |
| |
| /*--- add `pattern' to end of trace criterion database ---*/ |
| criteria_p = trace_criterion_p + trace_criterion_count; |
| if ((criteria_p->pattern_p = utlMalloc((strlen(pattern_p) + 1) * sizeof(char))) == NULL) |
| { |
| /*--- clean ---*/ |
| (void)utlReleaseExclusiveAccess(&trace_semaphore); |
| |
| return utlFAILED; |
| } |
| (void)strcpy(criteria_p->pattern_p, pattern_p); |
| trace_criterion_count++; |
| |
| /*--- re-evaluate the trace state of all existing trace tags ---*/ |
| { |
| utlTraceTag_P tag_p; |
| utlTraceTag_P terminal_tag_p; |
| |
| terminal_tag_p = trace_tags_p + trace_tags_count; |
| for (tag_p = trace_tags_p; tag_p != terminal_tag_p; tag_p++) |
| tag_p->state = evaluateTraceState(tag_p->name_p); |
| } |
| |
| /*--- release write-access ---*/ |
| if (utlReleaseExclusiveAccess(&trace_semaphore) != utlSUCCESS) |
| return utlFAILED; |
| #else |
| UNUSEDPARAM(pattern_p) |
| #endif /* defined(utlDEBUG) || defined(utlTEST) */ |
| |
| return utlSUCCESS; |
| } |
| |
| /*---------------------------------------------------------------------------* |
| * FUNCTION |
| * utlDeleteTraceCriteria(pattern_p) |
| * INPUT |
| * pattern_p == pattern that specifies the trace criteria to delete |
| * OUTPUT |
| * none |
| * RETURNS |
| * utlSUCCESS for success, utlFAILED for failure |
| * DESCRIPTION |
| * Delete `pattern_p' from the trace criterion database, and then |
| * re-evaluates all existing trace tags. |
| *---------------------------------------------------------------------------*/ |
| utlReturnCode_T utlDeleteTraceCriteria(const char *pattern_p) |
| { |
| #if defined(utlDEBUG) || defined(utlTEST) |
| utlTraceCriteria_P criteria_p; |
| utlTraceCriteria_P prev_criteria_p; |
| utlTraceCriteria_P terminal_criteria_p; |
| |
| utlAssert(pattern_p != NULL); |
| |
| /*--- if semaphore is not initialized ---*/ |
| if (!utlIsSemaphoreInitialized(&trace_semaphore)) |
| { |
| utlError(utlDeleteTraceCriteria, "Delete Trace failed, trace criteria `%s' not found"); |
| |
| return utlFAILED; |
| } |
| |
| /*--- acquire write-access ---*/ |
| if (utlAcquireExclusiveAccess(&trace_semaphore) != utlSUCCESS) |
| return utlFAILED; |
| |
| /*--- search for existing trace criteria ---*/ |
| terminal_criteria_p = trace_criterion_p + trace_criterion_count; |
| for (criteria_p = trace_criterion_p; criteria_p != terminal_criteria_p; criteria_p++) |
| if (strcmp(pattern_p, criteria_p->pattern_p) == 0) |
| break; |
| |
| /*--- not found? ---*/ |
| if (criteria_p == terminal_criteria_p) |
| { |
| utlError(utlDeleteTraceCriteria1, "Delete Trace failed, trace criteria `%s' not found"); |
| |
| /*--- clean ---*/ |
| (void)utlReleaseExclusiveAccess(&trace_semaphore); |
| |
| return utlFAILED; |
| } |
| |
| /*--- release deleted criteria's resources ---*/ |
| utlFree(criteria_p->pattern_p); |
| |
| /*--- close the gap ---*/ |
| for (prev_criteria_p = criteria_p++; criteria_p != terminal_criteria_p; prev_criteria_p = criteria_p++) |
| *prev_criteria_p = *criteria_p; |
| criteria_p->pattern_p = NULL; |
| |
| trace_criterion_count--; |
| |
| /*--- re-evaluate the trace state of all existing trace tags ---*/ |
| { |
| utlTraceTag_P tag_p; |
| utlTraceTag_P terminal_tag_p; |
| |
| terminal_tag_p = trace_tags_p + trace_tags_count; |
| for (tag_p = trace_tags_p; tag_p != terminal_tag_p; tag_p++) |
| tag_p->state = evaluateTraceState(tag_p->name_p); |
| } |
| |
| /*--- release write-access ---*/ |
| if (utlReleaseExclusiveAccess(&trace_semaphore) != utlSUCCESS) |
| return utlFAILED; |
| #else |
| UNUSEDPARAM(pattern_p) |
| #endif /* defined(utlDEBUG) || defined(utlTEST) */ |
| |
| return utlSUCCESS; |
| } |
| |
| /*---------------------------------------------------------------------------* |
| * FUNCTION |
| * utlDumpTracePegs(file_p, pattern_p) |
| * INPUT |
| * file_p == pointer to an open file |
| * pattern_p == which trace pegs to print |
| * OUTPUT |
| * none |
| * RETURNS |
| * The number of bytes successfully printed for success, utlFAILED for |
| * failure. |
| * DESCRIPTION |
| * Prints the trace pegs whose tag name matches `pattern_p' to `file_p'. |
| *---------------------------------------------------------------------------*/ |
| int utlDumpTracePegs( FILE *file_p, |
| const char *pattern_p) |
| { |
| #if defined(utlDEBUG) || defined(utlTEST) |
| utlTraceTag_P tag_p; |
| utlTraceTag_P terminal_tag_p; |
| int bytes_printed; |
| |
| utlAssert(file_p != NULL); |
| utlAssert(pattern_p != NULL); |
| |
| bytes_printed = 0; |
| |
| /*--- acquire read-access ---*/ |
| if (utlAcquireSharedAccess(&trace_semaphore) != utlSUCCESS) |
| return utlFAILED; |
| |
| if (trace_tags_p == NULL) |
| { |
| /*--- clean ---*/ |
| (void)utlReleaseSharedAccess(&trace_semaphore); |
| |
| return bytes_printed; |
| } |
| |
| /*--- for each tag in the trace tags database... ---*/ |
| terminal_tag_p = trace_tags_p + trace_tags_count; |
| for (tag_p = trace_tags_p; tag_p != terminal_tag_p; tag_p++) |
| |
| /*--- if this tag has been hit and if it matches `pattern'... ---*/ |
| if ((tag_p->hits > 0) && |
| (utlStrMatch(tag_p->name_p, pattern_p, false) == true)) |
| { |
| int rc; |
| |
| if ((rc = fprintf(file_p, "%s = %ld\n", tag_p->name_p, |
| tag_p->hits)) == -1) |
| { |
| /*--- clean ---*/ |
| (void)utlReleaseSharedAccess(&trace_semaphore); |
| |
| return utlFAILED; |
| } |
| bytes_printed += rc; |
| } |
| |
| /*--- release read-access ---*/ |
| if (utlReleaseSharedAccess(&trace_semaphore) != utlSUCCESS) |
| return utlFAILED; |
| |
| return bytes_printed; |
| #else |
| UNUSEDPARAM(file_p) |
| UNUSEDPARAM(pattern_p) |
| return 0; |
| #endif /* defined(utlDEBUG) || defined(utlTEST) */ |
| } |
| #if defined(utlDEBUG) || defined(utlTEST) |
| |
| /*---------------------------------------------------------------------------* |
| * FUNCTION |
| * newTraceTag(name_p, no_realloc) |
| * INPUT |
| * name_p == name of the new tag |
| * no_realloc == avoid reallocs? |
| * OUTPUT |
| * none |
| * RETURNS |
| * a pointer to the new trace tag for success, NULL for failure |
| * DESCRIPTION |
| * Adds the trace tag `name' to the trace tag database. |
| *---------------------------------------------------------------------------*/ |
| static utlTraceTag_P newTraceTag(const char *name_p, |
| const bool no_realloc) |
| { |
| utlTraceTag_P tag_p; |
| utlTraceTag_P terminal_tag_p; |
| |
| utlAssert(name_p != NULL); |
| |
| /*--- is this trace tag already defined? ---*/ |
| if (trace_tags_p != NULL) |
| { |
| terminal_tag_p = trace_tags_p + trace_tags_count; |
| for (tag_p = trace_tags_p; tag_p != terminal_tag_p; tag_p++) |
| if (strcmp(name_p, tag_p->name_p) == 0) |
| return tag_p; /* do nothing */ |
| } |
| |
| /*--- existing tags database too small? ---*/ |
| if (trace_tags_count >= trace_tags_size) |
| { |
| utlTraceTag_P new_trace_tags_p; |
| utlTraceTagsSize_T new_trace_tags_size; |
| |
| /*--- avoid realloc? ---*/ |
| if (no_realloc == true) |
| return NULL; |
| |
| /*--- increase size of existing tags database... ---*/ |
| new_trace_tags_size = trace_tags_size + utlTRACE_SYMBOLS_SIZE_INC; |
| if ((new_trace_tags_p = utlRealloc(trace_tags_p, |
| sizeof(utlTraceTag_T) * |
| new_trace_tags_size)) == NULL) |
| return NULL; |
| |
| trace_tags_p = new_trace_tags_p; |
| trace_tags_size = new_trace_tags_size; |
| } |
| |
| tag_p = trace_tags_p + trace_tags_count; |
| |
| /*--- add new trace tag ---*/ |
| tag_p->name_p = name_p; |
| tag_p->state = evaluateTraceState(name_p); |
| tag_p->hits = 0; |
| |
| trace_tags_count++; |
| |
| return tag_p; |
| } |
| |
| /*---------------------------------------------------------------------------* |
| * FUNCTION |
| * findTraceTag(name_p) |
| * INPUT |
| * name_p == name of the trace tag to find |
| * OUTPUT |
| * none |
| * RETURNS |
| * a pointer to a tags database entry for success, NULL for failure |
| * DESCRIPTION |
| * Searchs the trace tags database for the tag specified by `name'. |
| *---------------------------------------------------------------------------*/ |
| static utlTraceTag_P findTraceTag(const char *name_p) |
| { |
| utlTraceTag_P2c tag_p; |
| utlTraceTag_P2c terminal_tag_p; |
| |
| utlAssert(name_p != NULL); |
| |
| if (trace_tags_p == NULL) |
| return NULL; |
| |
| /*--- linear search for a matching trace tags entry ---*/ |
| terminal_tag_p = trace_tags_p + trace_tags_count; |
| for (tag_p = trace_tags_p; tag_p != terminal_tag_p; tag_p++) |
| |
| /*--- found a matching trace tags entry? ---*/ |
| if (strcmp(name_p, tag_p->name_p) == 0) |
| return (utlTraceTag_P)tag_p; |
| |
| /*--- specified trace tag not found ---*/ |
| return NULL; |
| } |
| #endif /* defined(utlDEBUG) || defined(utlTEST) */ |
| |
| /*---------------------------------------------------------------------------* |
| * FUNCTION |
| * utlFormatTracePrefix(buf_p, n, tag_p) |
| * INPUT |
| * buf_p == pointer to a character buffer with room for `n' characters |
| * n == size of buffer pointed to by `buf' (in characters) |
| * tag_p == pointer to a filled utlTraceTag_P structure |
| * OUTPUT |
| * none |
| * RETURNS |
| * nothing |
| * DESCRIPTION |
| * Composes a formatted prefix string for trace messages. |
| * |
| * Generated string: |
| * |
| * <proc name>: TRACE(<tag name>) |
| * |
| *---------------------------------------------------------------------------*/ |
| static void utlFormatTracePrefix( char *buf_p, |
| size_t n |
| #if defined(utlDEBUG) || defined(utlTEST) |
| , const utlTraceTag_P tag_p |
| #endif |
| ) |
| { |
| #if defined(utlDEBUG) || defined(utlTEST) |
| size_t len; |
| |
| utlAssert(buf_p != NULL); |
| utlAssert(n > (size_t)0); |
| |
| memset(buf_p, 0, n); |
| |
| /*--- insert process name (if there's room) ---*/ |
| if (n > (size_t)2) |
| { |
| (void)strncpy(buf_p, proc_name, n - 2); |
| (void)strncat(buf_p, ":", n - 1); |
| } |
| |
| len = strlen(buf_p); |
| |
| /*--- insert keyword and trace tag (if there's room) ---*/ |
| if ((n - len) >= sizeof(" TRACE()")) |
| { |
| |
| /*--- add keyword ---*/ |
| (void)strncpy(buf_p + len, " TRACE(", n - len - 1); |
| |
| len = strlen(buf_p); |
| |
| /*--- add trace-tag name (if specified) ---*/ |
| if ((tag_p != NULL) && |
| (tag_p->name_p != NULL)) (void)strncpy(buf_p + len, tag_p->name_p, n - len - 2); |
| else (void)strncpy(buf_p + len, "", n - len - 2); |
| |
| (void)strcat(buf_p, ")"); |
| } |
| #else |
| (void)strncpy(buf_p, "", n); |
| #endif /* defined(utlDEBUG) || defined(utlTEST) */ |
| } |
| |
| #if defined(utlDEBUG) || defined(utlTEST) |
| |
| /*---------------------------------------------------------------------------* |
| * FUNCTION |
| * utlDoPrintTrace(tag_index, format_p, ...) |
| * INPUT |
| * tag_index == a trace tag index |
| * format_p == pointer to a trace-message format string |
| * OUTPUT |
| * none |
| * RETURNS |
| * nothing |
| * DESCRIPTION |
| * Emits a trace message. |
| *---------------------------------------------------------------------------*/ |
| void utlDoPrintTrace( int tag_index, |
| const char *format_p, |
| ...) |
| { |
| #if defined(utlDEBUG) || defined(utlTEST) |
| va_list va_arg_p; |
| utlTraceTag_P tag_p; |
| char trace_prefix[utlTRACE_MAX_PREFIX_LEN]; |
| |
| utlAssert(format_p != NULL); |
| |
| /*--- if tag was previously found and looks valid... ---*/ |
| if ((tag_index >= 0) && (tag_index < trace_tags_count)) |
| { |
| |
| /*--- acquire read-access ---*/ |
| if (utlAcquireSharedAccess(&trace_semaphore) != utlSUCCESS) |
| return; |
| |
| utlAssert( trace_tags_p != NULL); |
| tag_p = trace_tags_p + tag_index; |
| |
| utlFormatTracePrefix(trace_prefix, utlTRACE_MAX_PREFIX_LEN, tag_p); |
| |
| va_start(va_arg_p, format_p); |
| (void)fprintf(stderr, "%s ", trace_prefix); |
| (void)vfprintf(stderr, format_p, va_arg_p); |
| va_end(va_arg_p); |
| |
| /*--- release read-access ---*/ |
| (void)utlReleaseSharedAccess(&trace_semaphore); |
| |
| } |
| else |
| { |
| va_start(va_arg_p, format_p); |
| (void)vfprintf(stderr, format_p, va_arg_p); |
| va_end(va_arg_p); |
| } |
| #endif /* defined(utlDEBUG) || defined(utlTEST) */ |
| } |
| |
| /*---------------------------------------------------------------------------* |
| * FUNCTION |
| * utlDoTraceOn(tag_index_p, name_p) |
| * INPUT |
| * tag_index_p == pointer to a trace tag, or a NULL pointer |
| * name_p == name of a trace tag |
| * OUTPUT |
| * none |
| * RETURNS |
| * "true" if `tag' should be traced, else "false" is returned. |
| * DESCRIPTION |
| * Returns the trace state for the trace tag specified by `name_p'. |
| * This function assumes that the allocated array pointed to by |
| * `trace_tags_p' is only ever appended to. |
| *---------------------------------------------------------------------------*/ |
| bool utlDoTraceOn( int *tag_index_p, |
| const char *name_p) |
| { |
| static bool new_tag_active = false; |
| |
| utlTraceTag_P tag_p; |
| bool no_realloc; |
| bool rv; |
| |
| utlAssert(tag_index_p != NULL); |
| utlAssert(name_p != NULL); |
| |
| /*--- if semaphore is uninitialized, tracing is off ---*/ |
| if (!utlIsSemaphoreInitialized(&trace_semaphore)) |
| return false; |
| |
| /*--- acquire read-access ---*/ |
| if (utlAcquireSharedAccess(&trace_semaphore) != utlSUCCESS) |
| return false; |
| |
| /*--- if tag was previously found... ---*/ |
| if (*tag_index_p >= 0) |
| { |
| |
| /*--- by definition tracing is turned off for corrupt tags ---*/ |
| if ((trace_tags_p == NULL) || (*tag_index_p >= trace_tags_count)) |
| { |
| |
| /*--- release read-access ---*/ |
| (void)utlReleaseSharedAccess(&trace_semaphore); |
| |
| return false; |
| } |
| |
| tag_p = trace_tags_p + *tag_index_p; |
| tag_p->hits++; |
| rv = tag_p->state == utlTRACING_ENABLED; |
| |
| /*--- release read-access ---*/ |
| if (utlReleaseSharedAccess(&trace_semaphore) != utlSUCCESS) |
| return false; |
| |
| return rv; |
| } |
| |
| /*--- search the trace database for `name' ---*/ |
| tag_p = findTraceTag(name_p); |
| |
| /*--- if `name_p' is already defined in the trace database ---*/ |
| if (tag_p != NULL) |
| { |
| |
| /*--- arrange for the next access to this tag to be much faster ---*/ |
| *tag_index_p = tag_p - trace_tags_p; |
| |
| tag_p->hits++; |
| rv = tag_p->state == utlTRACING_ENABLED; |
| |
| /*--- release read-access ---*/ |
| if (utlReleaseSharedAccess(&trace_semaphore) != utlSUCCESS) |
| return false; |
| |
| return rv; |
| } |
| |
| /*--- release read-access ---*/ |
| if (utlReleaseSharedAccess(&trace_semaphore) != utlSUCCESS) |
| return false; |
| |
| /*--- acquire write-access ---*/ |
| if (utlAcquireExclusiveAccess(&trace_semaphore) != utlSUCCESS) |
| return false; |
| |
| /*--- note if we've recursed ---*/ |
| if (new_tag_active == true) no_realloc = true; |
| else no_realloc = false; |
| |
| /*--- `name_p' does not exist in the trace database, so add it ---*/ |
| new_tag_active = true; |
| tag_p = newTraceTag(name_p, no_realloc); |
| new_tag_active = false; |
| |
| /*--- if `name_p' is now defined in the trace database ---*/ |
| if (tag_p != NULL) |
| { |
| |
| /*--- arrange for the next access to this tag to be faster ---*/ |
| *tag_index_p = tag_p - trace_tags_p; |
| |
| tag_p->hits++; |
| rv = tag_p->state == utlTRACING_ENABLED; |
| |
| /*--- else if while inside newTraceTag() we tried to call utlRealloc() |
| while we were trying to trace a utlRealloc() trace message, |
| we'll determine if tracing is turned on but not add the result to |
| the trace database (since that would lead to infinite recursion). */ |
| } |
| else if (no_realloc == true) |
| { |
| rv = evaluateTraceState(name_p) == utlTRACING_ENABLED; |
| |
| } |
| else |
| rv = false; |
| |
| /*--- release write-access ---*/ |
| if (utlReleaseExclusiveAccess(&trace_semaphore) != utlSUCCESS) |
| return false; |
| |
| return rv; |
| } |
| #endif /* defined(utlDEBUG) || defined(utlTEST) */ |
| |
| #ifdef utlTEST |
| /*---------------------------------------------------------------------------* |
| * FUNCTION |
| * traceTest() |
| * INPUT |
| * none |
| * OUTPUT |
| * none |
| * RETURNS |
| * "true" for pass, "false" for failure |
| *---------------------------------------------------------------------------*/ |
| bool traceTest(void) |
| { |
| |
| char saved_proc_name[20]; |
| utlTraceCriteria_P saved_trace_criterion_p; |
| utlTraceCriterionSize_T saved_trace_criterion_count; |
| utlTraceCriterionSize_T saved_trace_criterion_size; |
| utlTraceTag_P saved_trace_tags_p; |
| utlTraceTagsSize_T saved_trace_tags_count; |
| utlTraceTagsSize_T saved_trace_tags_size; |
| |
| utlTraceTag_P one_p; |
| utlTraceTag_P two_p; |
| utlTraceTag_P three_p; |
| utlTraceTag_P find_one_p; |
| utlTraceTag_P find_two_p; |
| utlTraceTag_P find_three_p; |
| int trace_one_peg; |
| int trace_two_peg; |
| int trace_three_peg; |
| char buf[100]; |
| utlTraceTag_T tag; |
| |
| /*--- save trace context ---*/ |
| strcpy(saved_proc_name, proc_name); |
| saved_trace_criterion_p = trace_criterion_p; |
| saved_trace_criterion_count = trace_criterion_count; |
| saved_trace_criterion_size = trace_criterion_size; |
| saved_trace_tags_p = trace_tags_p; |
| saved_trace_tags_count = trace_tags_count; |
| saved_trace_tags_size = trace_tags_size; |
| |
| /*--- trace setup ---*/ |
| utlSetProcessName("myProcName"); |
| |
| trace_criterion_p = NULL; |
| trace_criterion_count = 0; |
| trace_criterion_size = 0; |
| trace_tags_p = NULL; |
| trace_tags_count = 0; |
| trace_tags_size = 0; |
| |
| |
| /*--- test newTraceTag() ------------------------------------------------*/ |
| |
| if ((one_p = newTraceTag("one", false)) == NULL) |
| { |
| (void)fprintf(stderr, "traceTest: newTraceTag(1) failed\n"); |
| return false; |
| } |
| if ((strcmp(one_p->name_p, "one") != 0) || |
| (one_p->state != utlTRACING_DISABLED) || |
| (one_p->hits != 0) || |
| (trace_tags_count != 1)) |
| { |
| (void)fprintf(stderr, "traceTest: newTraceTag(2) failed\n"); |
| return false; |
| } |
| |
| if ((two_p = newTraceTag("two", false)) == NULL) |
| { |
| (void)fprintf(stderr, "traceTest: newTraceTag(3) failed\n"); |
| return false; |
| } |
| if ((strcmp(two_p->name_p, "two") != 0) || |
| (two_p->state != utlTRACING_DISABLED) || |
| (two_p->hits != 0) || |
| (trace_tags_count != 2)) |
| { |
| (void)fprintf(stderr, "traceTest: newTraceTag(4) failed\n"); |
| return false; |
| } |
| |
| (void)utlAddTraceCriteria("three"); |
| if ((three_p = newTraceTag("three", false)) == NULL) |
| { |
| (void)fprintf(stderr, "traceTest: newTraceTag(5) failed\n"); |
| return false; |
| } |
| if ((strcmp(three_p->name_p, "three") != 0) || |
| (three_p->state != utlTRACING_ENABLED) || |
| (three_p->hits != 0) || |
| (trace_tags_count != 3)) |
| { |
| (void)fprintf(stderr, "traceTest: newTraceTag(6) failed\n"); |
| return false; |
| } |
| |
| |
| /*-- test utlAddTraceCriteria() -----------------------------------------*/ |
| |
| if (utlAddTraceCriteria("five") != utlSUCCESS) |
| { |
| (void)fprintf(stderr, "traceTest: utlAddTraceCriteria(1) failed\n"); |
| return false; |
| } |
| if (trace_criterion_count != 2) |
| { |
| (void)fprintf(stderr, "traceTest: utlAddTraceCriteria(2) failed\n"); |
| return false; |
| } |
| |
| if (utlAddTraceCriteria("one") != utlSUCCESS) |
| { |
| (void)fprintf(stderr, "traceTest: utlAddTraceCriteria(3) failed\n"); |
| return false; |
| } |
| one_p = findTraceTag("one"); |
| if ((one_p->state != utlTRACING_ENABLED) || |
| (trace_criterion_count != 3)) |
| { |
| (void)fprintf(stderr, "traceTest: utlAddTraceCriteria(4) failed\n"); |
| return false; |
| } |
| |
| |
| /*-- test utlDeleteTraceCriteria() --------------------------------------*/ |
| |
| if (utlAddTraceCriteria("delfive") != utlSUCCESS) |
| { |
| (void)fprintf(stderr, "traceTest: utlDeleteTraceCriteria(1) failed\n"); |
| return false; |
| } |
| if (trace_criterion_count != 4) |
| { |
| (void)fprintf(stderr, "traceTest: utlDeleteTraceCriteria(2) failed\n"); |
| return false; |
| } |
| |
| if (utlDeleteTraceCriteria("delfive") != utlSUCCESS) |
| { |
| (void)fprintf(stderr, "traceTest: utlDeleteTraceCriteria(3) failed\n"); |
| return false; |
| } |
| one_p = findTraceTag("one"); |
| if ((one_p->state != utlTRACING_ENABLED) || |
| (trace_criterion_count != 3)) |
| { |
| (void)fprintf(stderr, "traceTest: utlDeleteTraceCriteria(4) failed\n"); |
| return false; |
| } |
| |
| |
| /*--- test findTraceTag() -----------------------------------------------*/ |
| |
| if ((find_one_p = findTraceTag("one")) == NULL) |
| { |
| (void)fprintf(stderr, "traceTest: findTraceTag(1) failed\n"); |
| return false; |
| } |
| if (strcmp(find_one_p->name_p, "one") != 0) |
| { |
| (void)fprintf(stderr, "traceTest: findTraceTag(2) failed\n"); |
| return false; |
| } |
| |
| if ((find_two_p = findTraceTag("two")) == NULL) |
| { |
| (void)fprintf(stderr, "traceTest: findTraceTag(3) failed\n"); |
| return false; |
| } |
| if (strcmp(find_two_p->name_p, "two") != 0) |
| { |
| (void)fprintf(stderr, "traceTest: findTraceTag(4) failed\n"); |
| return false; |
| } |
| |
| if ((find_three_p = findTraceTag("three")) == NULL) |
| { |
| (void)fprintf(stderr, "traceTest: findTraceTag(5) failed\n"); |
| return false; |
| } |
| if (strcmp(find_three_p->name_p, "three") != 0) |
| { |
| (void)fprintf(stderr, "traceTest: findTraceTag(6) failed\n"); |
| return false; |
| } |
| |
| |
| /*--- test utlDoTraceOn() and utlTrace() --------------------------------*/ |
| |
| /* setup for tests */ |
| trace_one_peg = 0; |
| trace_two_peg = 0; |
| trace_three_peg = 0; |
| |
| utlTrace("one", trace_one_peg++; ); |
| utlTrace("one", trace_one_peg++; ); |
| if (trace_one_peg != 2) |
| { |
| (void)fprintf(stderr, "traceTest: utlTrace(1) failed\n"); |
| return false; |
| } |
| |
| utlTrace("two", trace_two_peg++; ); |
| utlTrace("two", trace_two_peg++; ); |
| if (trace_two_peg != 0) |
| { |
| (void)fprintf(stderr, "traceTest: utlTrace(2) failed\n"); |
| return false; |
| } |
| |
| utlTrace("three", trace_three_peg++; ); |
| utlTrace("three", trace_three_peg++; ); |
| if (trace_three_peg != 2) |
| { |
| (void)fprintf(stderr, "traceTest: utlTrace(3) failed\n"); |
| return false; |
| } |
| |
| |
| /*--- test utlDumpTracePegs() -------------------------------------------*/ |
| |
| if (utlDumpTracePegs(stderr, "*") < 0) |
| { |
| (void)fprintf(stderr, "traceTest: utlDumpTracePegs(3) failed\n"); |
| return false; |
| } |
| |
| |
| /*--- test utlFormatTracePrefix() ---------------------------------------*/ |
| |
| tag.name_p = "myTag"; |
| tag.state = utlTRACING_ENABLED; |
| tag.hits = 0; |
| |
| utlFormatTracePrefix(buf, utlNumberOf(buf), &tag); |
| if ((strstr(buf, "TRACE") == NULL) || |
| (strstr(buf, "myTag") == NULL)) |
| { |
| (void)fprintf(stderr, "traceTest: utlFormatTracePrefix(1) failed\n"); |
| return false; |
| } |
| |
| #define FORMAT_TRACE_TEST(i, n, s) { \ |
| utlFormatTracePrefix(buf, n, &tag); \ |
| if (strcmp(buf, s) != 0) { \ |
| (void)fprintf(stderr, "traceTest: utlFormatTracePrefix(%d) failed\n", i); \ |
| return false; \ |
| } \ |
| } |
| |
| FORMAT_TRACE_TEST( 2, 1, ""); |
| FORMAT_TRACE_TEST( 3, 2, ""); |
| FORMAT_TRACE_TEST( 4, 3, "m:"); |
| FORMAT_TRACE_TEST( 5, 4, "my:"); |
| FORMAT_TRACE_TEST( 6, 5, "myP:"); |
| FORMAT_TRACE_TEST( 7, 6, "myPr:"); |
| FORMAT_TRACE_TEST( 8, 7, "myPro:"); |
| FORMAT_TRACE_TEST( 9, 8, "myProc:"); |
| FORMAT_TRACE_TEST(10, 9, "myProcN:"); |
| FORMAT_TRACE_TEST(11, 10, "myProcNa:"); |
| FORMAT_TRACE_TEST(12, 11, "myProcNam:"); |
| FORMAT_TRACE_TEST(13, 12, "myProcName:"); |
| FORMAT_TRACE_TEST(14, 13, "myProcName:"); |
| FORMAT_TRACE_TEST(15, 14, "myProcName:"); |
| FORMAT_TRACE_TEST(16, 15, "myProcName:"); |
| FORMAT_TRACE_TEST(17, 16, "myProcName:"); |
| FORMAT_TRACE_TEST(18, 17, "myProcName:"); |
| FORMAT_TRACE_TEST(19, 18, "myProcName:"); |
| FORMAT_TRACE_TEST(20, 19, "myProcName:"); |
| FORMAT_TRACE_TEST(21, 20, "myProcName: TRACE()"); |
| FORMAT_TRACE_TEST(22, 21, "myProcName: TRACE(m)"); |
| FORMAT_TRACE_TEST(23, 22, "myProcName: TRACE(my)"); |
| FORMAT_TRACE_TEST(24, 23, "myProcName: TRACE(myT)"); |
| FORMAT_TRACE_TEST(25, 24, "myProcName: TRACE(myTa)"); |
| FORMAT_TRACE_TEST(26, 25, "myProcName: TRACE(myTag)"); |
| FORMAT_TRACE_TEST(27, 26, "myProcName: TRACE(myTag)"); |
| |
| |
| /*--- test utlDoPrintTrace() --------------------------------------------*/ |
| |
| utlAddTraceCriteria("print_one"); |
| utlAddTraceCriteria("print_two"); |
| utlAddTraceCriteria("print_three"); |
| utlTrace("print_one", utlPrintTrace(traceTest, "message one\n"); ); |
| utlTrace("print_two", utlPrintTrace(traceTest1, "message two\n"); ); |
| utlTrace("print_three", utlPrintTrace(traceTest2, "message three\n"); ); |
| |
| |
| /*--- cleanup -----------------------------------------------------------*/ |
| |
| /*--- cleanup from previous tests ---*/ |
| if (trace_criterion_p != NULL) |
| { |
| utlTraceCriteria_P criteria_p; |
| utlTraceCriteria_P terminal_criteria_p; |
| |
| terminal_criteria_p = trace_criterion_p + trace_criterion_count; |
| for (criteria_p = trace_criterion_p; criteria_p != terminal_criteria_p; criteria_p++) |
| utlFree(criteria_p->pattern_p); |
| |
| utlFree(trace_criterion_p); |
| } |
| trace_criterion_p = NULL; |
| trace_criterion_count = 0; |
| trace_criterion_size = 0; |
| |
| if (trace_tags_p != NULL) |
| utlFree(trace_tags_p); |
| |
| trace_tags_p = NULL; |
| trace_tags_count = 0; |
| trace_tags_size = 0; |
| |
| /*--- restore trace context ---*/ |
| trace_criterion_p = saved_trace_criterion_p; |
| trace_criterion_count = saved_trace_criterion_count; |
| trace_criterion_size = saved_trace_criterion_size; |
| trace_tags_p = saved_trace_tags_p; |
| trace_tags_count = saved_trace_tags_count; |
| trace_tags_size = saved_trace_tags_size; |
| |
| return true; |
| } |
| #endif /* utlTEST */ |
| |