diff options
author | Monte Copeland <copelanm@us.ibm.com> | 2011-11-16 09:02:17 -0600 |
---|---|---|
committer | MIKE J. JONES <mjjones@us.ibm.com> | 2011-11-21 12:32:19 -0600 |
commit | d4b2086e646ac2444539bac0750af82e5e0b5d7c (patch) | |
tree | cf754de98935df10870b5aa96b2ec9cb270e8c8e /src | |
parent | 11c80c5abcf203e5a65098ea047fd6d2a6e607cc (diff) | |
download | talos-hostboot-d4b2086e646ac2444539bac0750af82e5e0b5d7c.tar.gz talos-hostboot-d4b2086e646ac2444539bac0750af82e5e0b5d7c.zip |
collectTrace to allow partial trace buffer collection
Change-Id: I06ce6df416f38c4619281180ea8515c90f8f2fab
Reviewed-on: http://gfw160.austin.ibm.com:8080/gerrit/498
Reviewed-by: A. Patrick Williams III <iawillia@us.ibm.com>
Tested-by: Jenkins Server
Diffstat (limited to 'src')
-rwxr-xr-x | src/build/debug/Hostboot/Trace.pm | 2 | ||||
-rw-r--r-- | src/include/ctype.h | 45 | ||||
-rwxr-xr-x | src/include/string_ext.h | 45 | ||||
-rw-r--r-- | src/include/usr/errl/errlentry.H | 23 | ||||
-rw-r--r-- | src/include/usr/trace/trace.H | 63 | ||||
-rw-r--r-- | src/include/util/align.H | 18 | ||||
-rw-r--r-- | src/lib/ctype.C | 31 | ||||
-rw-r--r-- | src/lib/makefile | 2 | ||||
-rw-r--r-- | src/lib/string_ext.C | 35 | ||||
-rw-r--r-- | src/makefile | 5 | ||||
-rw-r--r-- | src/usr/errl/errlentry.C | 78 | ||||
-rw-r--r-- | src/usr/errl/test/errltest.H | 272 | ||||
-rw-r--r-- | src/usr/trace/trace.C | 485 |
13 files changed, 753 insertions, 351 deletions
diff --git a/src/build/debug/Hostboot/Trace.pm b/src/build/debug/Hostboot/Trace.pm index adc043984..347e88e76 100755 --- a/src/build/debug/Hostboot/Trace.pm +++ b/src/build/debug/Hostboot/Trace.pm @@ -70,7 +70,7 @@ sub main my $buffAddr = ::read64($symAddr); $symAddr += DESC_ARRAY_ENTRY_ADDR_SIZE; - if ((not defined $traceBuffers) or ($traceBuffers =~ m/$compName/)) + if ((not defined $traceBuffers) or (uc($traceBuffers) =~ m/$compName/)) { $foundBuffer = 1; print $fh (::readData($buffAddr, TRAC_DEFAULT_BUFFER_SIZE)); diff --git a/src/include/ctype.h b/src/include/ctype.h new file mode 100644 index 000000000..46e89c2ab --- /dev/null +++ b/src/include/ctype.h @@ -0,0 +1,45 @@ +// IBM_PROLOG_BEGIN_TAG +// This is an automatically generated prolog. +// +// $Source: src/include/ctype.h $ +// +// IBM CONFIDENTIAL +// +// COPYRIGHT International Business Machines Corp. 2011 +// +// p1 +// +// Object Code Only (OCO) source materials +// Licensed Internal Code Source Materials +// IBM HostBoot Licensed Internal Code +// +// The source code for this program is not published or other- +// wise divested of its trade secrets, irrespective of what has +// been deposited with the U.S. Copyright Office. +// +// Origin: 30 +// +// IBM_PROLOG_END +#ifndef __CTYPE_H +#define __CTYPE_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @brief Converts lowercase letter to uppercase + * + * If no such conversion is possible then the input is returned unchanged + * + * @param[in] Input letter + * @return int. Uppercase letter + */ +int toupper(int); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/src/include/string_ext.h b/src/include/string_ext.h new file mode 100755 index 000000000..a09e8782e --- /dev/null +++ b/src/include/string_ext.h @@ -0,0 +1,45 @@ +// IBM_PROLOG_BEGIN_TAG +// This is an automatically generated prolog. +// +// $Source: src/include/string_ext.h $ +// +// IBM CONFIDENTIAL +// +// COPYRIGHT International Business Machines Corp. 2011 +// +// p1 +// +// Object Code Only (OCO) source materials +// Licensed Internal Code Source Materials +// IBM HostBoot Licensed Internal Code +// +// The source code for this program is not published or other- +// wise divested of its trade secrets, irrespective of what has +// been deposited with the U.S. Copyright Office. +// +// Origin: 30 +// +// IBM_PROLOG_END +#ifndef __STRING_EXT_H +#define __STRING_EXT_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @brief Converts lowercase string to uppercase + * + * Any characters that cannot be converted are left unchanged + * + * @param[in] s Pointer to c-string that is converted to uppercase + * @return char *. Pointer to beginning of string (same as 's' parameter) + */ +char* strupr(char* s); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/src/include/usr/errl/errlentry.H b/src/include/usr/errl/errlentry.H index 5046cd1c6..b3e856996 100644 --- a/src/include/usr/errl/errlentry.H +++ b/src/include/usr/errl/errlentry.H @@ -329,19 +329,26 @@ public: /** * @brief Collect component trace - * The given component's trace is collected (if possible) - * and added to the log's data sections. The amount of data - * added is the smallest of the log's available space (up to - * 1024 bytes of trace) or the given input max. + * The trace buffer named is collected and added to the error + * log. The amount of traces is controlled by the i_max parameter. + * When zero, or left to default, the full trace buffer is copied. + * Otherwise, i_max size must be big enough to hold a trace buffer + * header (40 bytes) plus some trace data. For example, a trace + * entry with n bytes of data is n+28 bytes in size. * - * @param[in] i_Name Component Name - * @param[in] i_Max Upper limit of trace to capture. + * Note that component names given in hbotcompid.H do not necessarily + * map to the names of trace buffers created by that component. + * Trace buffer names are case insensitive. + * + * @param[in] i_name Trace buffer name + * @param[in] i_max Size of trace to capture. * * @return A Boolean indication of success. False likely means - * the component name input is not found. + * the trace buffer name given is not found. However, check the ERRL + * trace buffer for the cause of the failure. */ bool collectTrace(const char i_name[], - const uint32_t i_max = 0); + const uint64_t i_max = 0); diff --git a/src/include/usr/trace/trace.H b/src/include/usr/trace/trace.H index c5d0a0d2a..6450a42a1 100644 --- a/src/include/usr/trace/trace.H +++ b/src/include/usr/trace/trace.H @@ -206,29 +206,36 @@ public: /** - * @brief Retrieve full trace buffer for component i_comp. + * @brief Retrieve the trace buffer named by i_pName * * Caller must allocate memory for the output buffer. Caller may * first query the size of the buffer by calling with the desired - * component/trace buffer name and with o_data null and with i_bufferSize - * zero. The value returned will be the buffer size. Caller then - * allocates the buffer and calls again. + * buffer name and with o_data null and i_bufferSize + * zero. The value returned will be the full buffer size. Caller + * allocates the buffer and calls again. + * + * The buffer provided can be less than the full size of the desired + * buffer. In that case, this function will copy as many of the most + * recent traces into the output buffer as will fit. The buffer must + * be big enough to hold a trace buffer header (40 bytes). * - * @param [in] i_pComp pointer to name string - * @param [out] o_data pointer to where data will be stored - * @param [in] i_bufferSize size of buffer in bytes + * i_bufferSize may be larger that the desired trace buffer. + * + * @param [in] i_pName name of trace buffer + * @param [out] o_data pointer to output buffer + * @param [in] i_bufferSize size of output buffer in bytes * * @return Count of bytes copied, or if given null parameters, - * the size of the buffer, or else zero for error, perhaps the - * component name/trace buffer name is not found. + * the size of the buffer. Returns zero for error, perhaps the + * component name/trace buffer name is not found, or perhaps + * the size of the provided buffer is unreasonable. */ - uint64_t getBuffer( const char * i_pComp, + uint64_t getBuffer( const char * i_pName, void * o_data, uint64_t i_bufferSize ); - protected: /** @@ -283,34 +290,20 @@ private: /** - * @brief Retrieve partial trace buffer for component i_comp + * @brief Retrieve trace descriptor for input component name. + * If an exact match for the name is not found, then return nul. + * Internally, Trace keeps buffer names in upper case, and i_pName + * will be converted internally to upper case for the search. * - * This function assumes memory has already been allocated for - * the trace buffer (size io_size). This function will copy - * in up to io_size in bytes to the buffer and set io_size - * to the exact size that is copied in. - * - * TODO - Not Supported Yet + * @param [in] i_pName Buffer name to search. * - * @param [in] i_td_ptr Trace descriptor of buffer to retrieve. - * @param [out] o_data Pre-allocated pointer to where data will be stored. - * @param [in,out] io_size Size of trace data to retrieve (input) - * Actual size of trace data stored (output) - * - * @return Non-zero return code on error + * @return trace descriptor for the name, or nul if not found. */ - int32_t getBufferPartial(const trace_desc_t * i_td_ptr, - void *o_data, - uint32_t *io_size); + trace_desc_t * findTdByName( const char *i_pName ); + + + - /** - * @brief Retrieve trace descriptor for input component name - * - * @param [in] i_comp Component name to retrieve trace descriptor for. - * - * @return Valid trace descriptor on success, NULL on failure. - */ - trace_desc_t * getTd(const char *i_comp); /** * @brief Reset all trace buffers diff --git a/src/include/util/align.H b/src/include/util/align.H index 55073256d..8ba0e06b9 100644 --- a/src/include/util/align.H +++ b/src/include/util/align.H @@ -25,14 +25,20 @@ #include <limits.h> -// Return a number >= input that is aligned on a 4-byte boundary -#define ALIGN_4(u) ((u + 0x3ull) & ~0x3ull) +// Return a number >= input that is aligned up to the next 4-byte boundary +#define ALIGN_4(u) (((u) + 0x3ull) & ~0x3ull) -// Return a number >= input that is aligned on a 8-byte bounday -#define ALIGN_8(u) ((u + 0x7ull) & ~0x7ull) +// Return a number <= input that is rounded down to nearest 4-byte boundary +#define ALIGN_DOWN_4(u) ((u) & ~3ull) -// Return a number >= input that is aligned on a page boundary -#define ALIGN_PAGE(u) ((u + (PAGESIZE-1)) & ~(PAGESIZE-1)) +// Return a number >= input that is aligned up to the next 8-byte bounday +#define ALIGN_8(u) (((u) + 0x7ull) & ~0x7ull) + +// Return a number <= input that is rounded down to nearest 8-byte boundary +#define ALIGN_DOWN_8(u) ((u) & ~7ull) + +// Return a number >= input that is aligned up to the next page boundary +#define ALIGN_PAGE(u) (((u) + (PAGESIZE-1)) & ~(PAGESIZE-1)) // Return a number <= input that is aligned on a page boundary #define ALIGN_PAGE_DOWN(u) ((u) - (u)%PAGESIZE) diff --git a/src/lib/ctype.C b/src/lib/ctype.C new file mode 100644 index 000000000..44e3dabeb --- /dev/null +++ b/src/lib/ctype.C @@ -0,0 +1,31 @@ +// IBM_PROLOG_BEGIN_TAG +// This is an automatically generated prolog. +// +// $Source: src/lib/ctype.C $ +// +// IBM CONFIDENTIAL +// +// COPYRIGHT International Business Machines Corp. 2011 +// +// p1 +// +// Object Code Only (OCO) source materials +// Licensed Internal Code Source Materials +// IBM HostBoot Licensed Internal Code +// +// The source code for this program is not published or other- +// wise divested of its trade secrets, irrespective of what has +// been deposited with the U.S. Copyright Office. +// +// Origin: 30 +// +// IBM_PROLOG_END +int toupper(int ch) +{ + if(( ch >= 'a') && (ch <= 'z' )) + { + ch &= ~0x20; + } + return ch; +} + diff --git a/src/lib/makefile b/src/lib/makefile index 59a12b350..7997189f8 100644 --- a/src/lib/makefile +++ b/src/lib/makefile @@ -22,7 +22,7 @@ # IBM_PROLOG_END ROOTPATH = ../.. -OBJS = string.o stdlib.o assert.o stdio.o math.o +OBJS = string.o string_ext.o stdlib.o ctype.o assert.o stdio.o math.o OBJS += syscall_stub.o syscall_task.o syscall_msg.o OBJS += syscall_mmio.o syscall_time.o sync.o syscall_misc.o OBJS += syscall_mm.o cxxtest_data.o diff --git a/src/lib/string_ext.C b/src/lib/string_ext.C new file mode 100644 index 000000000..dfca65b68 --- /dev/null +++ b/src/lib/string_ext.C @@ -0,0 +1,35 @@ +// IBM_PROLOG_BEGIN_TAG +// This is an automatically generated prolog. +// +// $Source: src/lib/string_ext.C $ +// +// IBM CONFIDENTIAL +// +// COPYRIGHT International Business Machines Corp. 2011 +// +// p1 +// +// Object Code Only (OCO) source materials +// Licensed Internal Code Source Materials +// IBM HostBoot Licensed Internal Code +// +// The source code for this program is not published or other- +// wise divested of its trade secrets, irrespective of what has +// been deposited with the U.S. Copyright Office. +// +// Origin: 30 +// +// IBM_PROLOG_END +#include <ctype.h> + +extern "C" char* strupr(char* s) +{ + char *k = s; + while(*s != '\0') + { + *s = toupper(*s); + s++; + } + return k; +} + diff --git a/src/makefile b/src/makefile index 56c0c8224..fd59896b2 100644 --- a/src/makefile +++ b/src/makefile @@ -27,8 +27,9 @@ SUBDIRS = kernel.d lib.d libc++.d sys.d usr.d build.d IMGS = hbicore hbicore_test EXTRA_LIDS = dslid -BASE_OBJECTS = console.o spinlock.o string.o stdlib.o assert.o stdio.o \ - builtins.o vfs_init.o heapmgr.o pagemgr.o math.o barrier.o +BASE_OBJECTS = console.o spinlock.o string.o string_ext.o stdlib.o ctype.o \ + assert.o stdio.o builtins.o vfs_init.o heapmgr.o pagemgr.o \ + math.o barrier.o DIRECT_BOOT_OBJECTS = start.o kernel.o taskmgr.o cpumgr.o syscall.o \ scheduler.o exception.o vmmmgr.o timemgr.o \ diff --git a/src/usr/errl/errlentry.C b/src/usr/errl/errlentry.C index ee688dc86..553bdab8f 100644 --- a/src/usr/errl/errlentry.C +++ b/src/usr/errl/errlentry.C @@ -135,55 +135,75 @@ void ErrlEntry::appendToFFDC(ErrlUD * i_pErrlUD, /////////////////////////////////////////////////////////////////////////////// // Return a Boolean indication of success. -bool ErrlEntry::collectTrace(const char i_name[], const uint32_t i_max) +bool ErrlEntry::collectTrace(const char i_name[], const uint64_t i_max) { - bool l_rc = false; + bool l_rc = false; // assume a problem. + char * l_pBuffer = NULL; + uint64_t l_cbOutput = 0; + uint64_t l_cbBuffer = 0; do { - // By passing nil arguments, obtain the size of the buffer. + // By passing nil arguments 2 and 3, obtain the size of the buffer. + // Besides getting buffer size, it validates i_name. uint64_t l_cbFull = TRACE::Trace::getTheInstance().getBuffer( i_name, NULL, 0 ); if( 0 == l_cbFull ) { + // Problem, likely unknown trace buffer name. TRACFCOMP( g_trac_errl, - "ErrlEntry::collectTrace(): getBuffer(%s) rets zero.", i_name ); + "ErrlEntry::collectTrace(): getBuffer(%s) rets zero.",i_name); break; } - if( 0 == i_max ) + if(( 0 == i_max ) || ( i_max >= l_cbFull )) { - // Full trace buffer desired. Allocate the buffer. - char l_traceBuffer[ l_cbFull ]; - - // Get the data into the buffer. - TRACE::Trace::getTheInstance().getBuffer( i_name, - l_traceBuffer, - l_cbFull ); - - // Save the trace buffer as a UD section on this. - ErrlUD * l_udSection = new ErrlUD( l_traceBuffer, - l_cbFull, - ERRL_COMP_ID, - ERRL_UDV_DEFAULT_VER_1, - ERRL_UDT_TRACE ); - - // Add the trace section to the vector of sections - // for this error log. - iv_SectionVector.push_back( l_udSection ); - - l_rc = true; + // Full trace buffer desired + l_cbBuffer = l_cbFull; + } + else + { + // Partial buffer desired + l_cbBuffer = i_max; + } + + // allocate the buffer + l_pBuffer = new char[ l_cbBuffer ]; + + // Get the data into the buffer. + l_cbOutput = + TRACE::Trace::getTheInstance().getBuffer( i_name, + l_pBuffer, + l_cbBuffer ); + + if( 0 == l_cbOutput ) + { + // Problem. + TRACFCOMP( g_trac_errl, + "ErrlEntry::collectTrace(): getBuffer(%s,%ld) rets zero.", + i_name, + l_cbBuffer ); break; } - // else partial buffer desired... future sprint - TRACFCOMP( g_trac_errl, - "ErrlEntry::collectTrace(): partial buffer not impl'd" ); - break; + // Save the trace buffer as a UD section on this. + ErrlUD * l_udSection = new ErrlUD( l_pBuffer, + l_cbOutput, + ERRL_COMP_ID, + ERRL_UDV_DEFAULT_VER_1, + ERRL_UDT_TRACE ); + + // Add the trace section to the vector of sections + // for this error log. + iv_SectionVector.push_back( l_udSection ); + + l_rc = true; } while(0); + delete[] l_pBuffer; + return l_rc; } diff --git a/src/usr/errl/test/errltest.H b/src/usr/errl/test/errltest.H index 94218aace..817bcee89 100644 --- a/src/usr/errl/test/errltest.H +++ b/src/usr/errl/test/errltest.H @@ -32,6 +32,7 @@ #include <cxxtest/TestSuite.H> #include <errl/errlmanager.H> #include <errl/errlentry.H> +#include <trace/trace.H> #include <hbotcompid.H> #define TEST_REASON_CODE (ERRL_COMP_ID | 0x0F) @@ -87,126 +88,203 @@ public: uint64_t l_userData2 = TWO_UINT16_ONE_UINT32_TO_UINT64(l_16bit_1, l_16bit_2, l_32bit_2); // yields 0x8000900390000003 - // Create an error log - errlHndl_t l_err = new ERRORLOG::ErrlEntry( - ERRORLOG::ERRL_SEV_INFORMATIONAL, - TEST_MOD_ID, - TEST_REASON_CODE, - l_userData1, - l_userData2); - // Make sure log is created - if (l_err == NULL) + do { - TS_FAIL("testErrl1: createErrlLog() outputs NULL pointer!"); - } + // Create an error log + errlHndl_t l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_INFORMATIONAL, + TEST_MOD_ID, + TEST_REASON_CODE, + l_userData1, + l_userData2); - // These addFFDC() calls return a pointer to class ERRORLOG::ErrlFFDC - // but errlffdc.H is not publicly includable to give me the definition - // for it. addFFDC() should return a Boolean indication of success. - const char * pch = "martha washington"; - pffdc = l_err->addFFDC( ERRL_COMP_ID, pch, strlen( pch ), 1, 2 ); - if ( NULL == pffdc ) - { - TS_FAIL("testErrl1: addFFDC() output NULL pointer"); - } + // Make sure log is created + if (l_err == NULL) + { + TS_FAIL("testErrl1: createErrlLog() outputs NULL pointer!"); + break; + } - // really short user data - pch = "A"; - pffdc = l_err->addFFDC( DEVFW_COMP_ID, pch, strlen( pch ), 3, 4 ); - if ( NULL == pffdc ) - { - TS_FAIL("testErrl1: addFFDC() output NULL pointer"); - } - pch = "george washington"; - pffdc = l_err->addFFDC( DEVFW_COMP_ID, pch, strlen( pch ), 3, 4 ); - if ( NULL == pffdc ) - { - TS_FAIL("testErrl1: addFFDC() output NULL pointer"); - } + // These addFFDC() calls return a pointer to class ERRORLOG::ErrlFFDC + // but errlffdc.H is not publicly includable to give me the definition + // for it. addFFDC() should return a Boolean indication of success. - pch = "dwight eisenhour"; - pffdc = l_err->addFFDC( SCOM_COMP_ID, pch, strlen( pch ), 5, 6 ); - if ( NULL == pffdc ) - { - TS_FAIL("testErrl1: addFFDC() output NULL pointer"); - } + const char * pch = "martha washington"; + pffdc = l_err->addFFDC( ERRL_COMP_ID, pch, strlen( pch ), 1, 2 ); + if ( NULL == pffdc ) + { + TS_FAIL("testErrl1: addFFDC() output NULL pointer"); + break; + } - pch = "ronald "; - pffdc = l_err->addFFDC( ERRL_COMP_ID, pch, strlen( pch ), 7, 8 ); - if ( NULL == pffdc ) - { - TS_FAIL("testErrl1: addFFDC() output NULL pointer"); - } + // really short user data + pch = "A"; + pffdc = l_err->addFFDC( DEVFW_COMP_ID, pch, strlen( pch ), 3, 4 ); + if ( NULL == pffdc ) + { + TS_FAIL("testErrl1: addFFDC() output NULL pointer"); + break; + } - // Append data to something already added. - pch = "reagan"; - l_err->appendToFFDC( pffdc, pch, strlen(pch) ); + pch = "george washington"; + pffdc = l_err->addFFDC( DEVFW_COMP_ID, pch, strlen( pch ), 3, 4 ); + if ( NULL == pffdc ) + { + TS_FAIL("testErrl1: addFFDC() output NULL pointer"); + break; + } - // Collect trace - fOK = l_err->collectTrace( "INITSVC" ); - if( !fOK ) - { - TS_FAIL( "collectTrace(INITSVC) rets false." ); - } + pch = "dwight eisenhour"; + pffdc = l_err->addFFDC( SCOM_COMP_ID, pch, strlen( pch ), 5, 6 ); + if ( NULL == pffdc ) + { + TS_FAIL("testErrl1: addFFDC() output NULL pointer"); + break; + } + + pch = "ronald "; + pffdc = l_err->addFFDC( ERRL_COMP_ID, pch, strlen( pch ), 7, 8 ); + if ( NULL == pffdc ) + { + TS_FAIL("testErrl1: addFFDC() output NULL pointer"); + break; + } + + // Append data to something already added. + pch = "reagan"; + l_err->appendToFFDC( pffdc, pch, strlen(pch) ); + + // Collect trace + fOK = l_err->collectTrace( "INITSVC" ); + if( !fOK ) + { + TS_FAIL( "collectTrace(INITSVC) rets false." ); + break; + } + + // Assuming trace buffers are 0x800 in size, and you're going + // after a trace buffer that has wrapped, then a size almost as + // big as the buffer will exercise the wrapping code in + // trace::getBuffer() + fOK = l_err->collectTrace( "XSCOM" , 0x7D0 ); + if( !fOK ) + { + TS_FAIL( "collectTrace(XSCOM) rets false." ); + break; + } + + fOK = l_err->collectTrace( "UNKNOWN" ); + if( fOK ) + { + TS_FAIL( "collectTrace(UNKNOWN) rets true" ); + break; + } + + fOK = l_err->collectTrace( "TARG", sizeof(trace_buf_head_t)-2); + if( fOK ) + { + // expect an error, buffer not big enough + TS_FAIL( "collectTrace(TARG,38) rets true" ); + break; + } + + fOK = l_err->collectTrace( "TARG", sizeof( trace_buf_head_t )); + if( !fOK ) + { + // Buffer is big enough for the header only. It is + // supposed to work, although not terribly useful. + TS_FAIL( "collectTrace(TARG,40) rets false" ); + break; + } + + // sizeof( trace_buf_head_t ) + n bytes such that a single trace + // entry cannot fit into. + uint64_t l_cb = sizeof(trace_buf_head_t) + (sizeof(trace_bin_entry_t)/2); + fOK = l_err->collectTrace( "TARG", l_cb ); + if( !fOK ) + { + // cb is big enough for the header only, but no + // room for any entries. + TS_FAIL( "collectTrace(TARG,l_cb) rets false", l_cb ); + break; + } + + // Normal buffer sizes are 0x800 (2048), so passing + // something bigger is not expected to be an error. + // TODO: maybe after story "user selectable trace buffer sizes" + // TRAC_DEFAULT_BUFFER_SIZE (0x800) will be publicly available. + fOK = l_err->collectTrace( "TARG" , 4003 ); + if( !fOK ) + { + TS_FAIL( "collectTrace(TARG,4003) rets false" ); + break; + } + + + // Add null data. + pffdc = l_err->addFFDC( ERRL_COMP_ID, NULL, 0, 9, 10 ); + if ( NULL != pffdc ) + { + TS_FAIL("testErrl1: addFFDC() returned non null"); + break; + } + + // Verify log data + if (l_err->sev() != ERRORLOG::ERRL_SEV_INFORMATIONAL) + { + TS_FAIL("testErrl1: createErrlLog() returns incorrect severity!"); + break; + } + + if (l_err->reasonCode() != TEST_REASON_CODE) + { + TS_FAIL("testErrl1: createErrlLog() returns incorrect reason code!"); + break; + } + + if (l_err->eventType() != ERRORLOG::ERRL_ETYPE_NOT_APPLICABLE) + { + TS_FAIL("testErrl1: createErrlLog() returns incorrect event type!"); + break; + } + + if (l_err->subSys() != ERRORLOG::EPUB_FIRMWARE_SUBSYS ) + { + TS_FAIL("testErrl1: createErrlLog() returns incorrect sub system!"); + break; + } + + if (l_err->srcType() != ERRORLOG::SRC_ERR_INFO) + { + TS_FAIL("testErrl1: createErrlLog() returns incorrect SRC type!"); + break; + } + + if (l_err->termState() != ERRORLOG::TERM_STATE_UNKNOWN) + { + TS_FAIL("testErrl1: termState() returns incorrect term state!"); + break; + } - fOK = l_err->collectTrace( "XSCOM" ); - if( !fOK ) - { - TS_FAIL( "collectTrace(XSCOM) rets false." ); - } - fOK = l_err->collectTrace( "UNK" ); - if( fOK ) - { - TS_FAIL( "collectTrace(UNK) rets true" ); - } - // Add null data. - pffdc = l_err->addFFDC( ERRL_COMP_ID, NULL, 0, 9, 10 ); - if ( NULL != pffdc ) - { - TS_FAIL("testErrl1: addFFDC() returned non null"); - } - // Verify log data - else if (l_err->sev() != ERRORLOG::ERRL_SEV_INFORMATIONAL) - { - TS_FAIL("testErrl1: createErrlLog() returns incorrect severity!"); - } - else if (l_err->reasonCode() != TEST_REASON_CODE) - { - TS_FAIL("testErrl1: createErrlLog() returns incorrect reason code!"); - } - else if (l_err->eventType() != ERRORLOG::ERRL_ETYPE_NOT_APPLICABLE) - { - TS_FAIL("testErrl1: createErrlLog() returns incorrect event type!"); - } - else if (l_err->subSys() != ERRORLOG::EPUB_FIRMWARE_SUBSYS ) - { - TS_FAIL("testErrl1: createErrlLog() returns incorrect sub system!"); - } - else if (l_err->srcType() != ERRORLOG::SRC_ERR_INFO) - { - TS_FAIL("testErrl1: createErrlLog() returns incorrect SRC type!"); - } - else if (l_err->termState() != ERRORLOG::TERM_STATE_UNKNOWN) - { - TS_FAIL("testErrl1: termState() returns incorrect term state!"); - } - else - { // Commit error log with different component ID. errlCommit(l_err, FSI_COMP_ID); + // Make sure error log has been deleted by manager if (l_err != NULL) { TS_FAIL("testErrl1: commitErrLog() did not delete error!"); + break; } + } + while(0); } /** diff --git a/src/usr/trace/trace.C b/src/usr/trace/trace.C index 447fad968..221270c48 100644 --- a/src/usr/trace/trace.C +++ b/src/usr/trace/trace.C @@ -20,13 +20,19 @@ // Origin: 30 // // IBM_PROLOG_END + +/** + * @file trace.C + * + * @brief Implementation of class Trace + */ + + /* TODO * - Add support in for debug trace enable/disable * - FORMAT_PRINTF support * - %s support * - Multiple buffer support - * - Prolog - * * */ @@ -43,11 +49,14 @@ #include <stdlib.h> #include <sys/task.h> #include <sys/sync.h> -#include <string.h> +#include <string_ext.h> #include <util/align.H> #include <trace/trace.H> + + + /******************************************************************************/ // Namespace /******************************************************************************/ @@ -156,6 +165,9 @@ void Trace::initBuffer(trace_desc_t **o_td, const char* i_comp, strcpy(l_comp, i_comp); } + // make string upper case + strupr(l_comp); + // CRITICAL REGION START mutex_lock(&iv_trac_mutex); @@ -191,7 +203,7 @@ void Trace::initBuffer(trace_desc_t **o_td, const char* i_comp, // TODO can't handle i_size yet - everything is coded // around TRAC_DEFAULT_BUFFER_SIZE l_td = static_cast<char *>(malloc(TRAC_DEFAULT_BUFFER_SIZE)); - + g_desc_array[i].td_entry = reinterpret_cast<trace_desc_t *>(l_td); @@ -331,10 +343,15 @@ void Trace::_trace_adal_write_all(trace_desc_t *io_td, uint32_t num_4byte_args = 0; //fsp-trace counts 8-byte args as 2 4-byte args const char* _fmt = i_fmt; - // Save a copy + // Save a copy of input args because calling + // va_arg() on a va_list is a one-shot. va_list l_args; va_copy (l_args, i_args); + + // Sum the sizes of the items in i_args in order to know how big to + // allocate the entry. + for (size_t i = 0; i <= strlen(_fmt); i++) { if ('%' == _fmt[i]) @@ -365,7 +382,8 @@ void Trace::_trace_adal_write_all(trace_desc_t *io_td, //printk("Trace: STRING %s: strlen %d num_args %d l_data_size %d\n", // l_str, static_cast<uint32_t>(l_strLen), // num_args, l_data_size); - //printk("Trace: l_str_map 0x%16llX\n", static_cast<long long>(l_str_map)); + //printk("Trace: l_str_map 0x%16llX\n", + // static_cast<long long>(l_str_map)); } else if ('c' == _fmt[i]) { @@ -412,6 +430,9 @@ void Trace::_trace_adal_write_all(trace_desc_t *io_td, } } + va_end( i_args ); + + if((num_4byte_args <= TRAC_MAX_ARGS) && (io_td != NULL)) { // Fill in the entry structure @@ -427,7 +448,9 @@ void Trace::_trace_adal_write_all(trace_desc_t *io_td, // Time stamp convertTime(&l_entry.stamp); - // Calculate total space needed + // Calculate total space needed for the entry, which is a + // combination of the data size from above, the entry + // headers, and an overall length field. l_entry_size = l_data_size; l_entry_size += sizeof(trace_entry_stamp_t); l_entry_size += sizeof(trace_entry_head_t); @@ -445,7 +468,9 @@ void Trace::_trace_adal_write_all(trace_desc_t *io_td, memset(l_buffer, 0, l_data_size); char * l_ptr = static_cast<char *> (l_buffer); - // Now copy the arguments to the buffer + // Now copy the arguments to the buffer. + + for (size_t i = 0; i < num_args; i++) { uint32_t l_strLen = 0; @@ -654,12 +679,16 @@ void Trace::writeData(trace_desc_t *io_td, if(i_size > (io_td->size-sizeof(trace_buf_head_t))) { + // unreasonable size, caller is asking to write something + // that is very nearly the size of the entire buffer break; } if((io_td->next_free + l_total_size) > io_td->size) { + // Does not fit entirely, write what fits, and wrap the buffer. + // Get the pointer to current location in buffer l_buf_ptr = reinterpret_cast<char *>(io_td) + io_td->next_free; // Figure out the alignment @@ -731,277 +760,389 @@ void Trace::convertTime(trace_entry_stamp_t *o_entry) } + + /******************************************************************************/ -// getTd +// findTdByName /******************************************************************************/ -trace_desc_t * Trace::getTd(const char *i_comp) +trace_desc_t * Trace::findTdByName(const char *i_pName) { - /*------------------------------------------------------------------------*/ - /* Local Variables */ - /*------------------------------------------------------------------------*/ - uint32_t i=0; trace_desc_t * l_td = NULL; - char l_comp[COMP_NAME_SIZE] = {'\0'}; + char l_comp[COMP_NAME_SIZE]; - /*------------------------------------------------------------------------*/ - /* Code */ - /*------------------------------------------------------------------------*/ - if (strlen(i_comp) != 0) + uint64_t i = strlen(i_pName); + if ( i ) { - // Limit component name to 15 characters. - if (strlen(i_comp) > (COMP_NAME_SIZE -1)) + if ( i > (COMP_NAME_SIZE -1)) { - memcpy(l_comp, i_comp, COMP_NAME_SIZE - 1); + // Limit component name. + memcpy(l_comp, i_pName, COMP_NAME_SIZE - 1); + l_comp[ COMP_NAME_SIZE - 1 ] = 0; } else { - strcpy(l_comp, i_comp); + strcpy( l_comp, i_pName ); } - // Search all allocated component buffers + // Use upper case. + strupr( l_comp ); + + // Search the buffers array for(i=0; (i < (TRAC_MAX_NUM_BUFFERS - 1)) && (strlen(g_desc_array[i].comp) != 0); i++) { - if(!strcmp(l_comp, g_desc_array[i].comp)) + if(0 == strcmp(l_comp, g_desc_array[i].comp)) { - // Found the component buffer + // Return this one. l_td = g_desc_array[i].td_entry; break; } } - if (((TRAC_MAX_NUM_BUFFERS - 1) == i) && - (strlen(g_desc_array[i].comp) != 0)) - - { - // Must be the default buffer - l_td = g_desc_array[i].td_entry; - } } - return(l_td); + return l_td; } /*****************************************************************************/ -// getBuffer() called by ErrlEntry.CollectTrace() -// Return how many bytes copied, or if given a null pointer or zero buffer -// size, then return the size of the buffer. +// getBuffer() called by ErrlEntry.collectTrace() +// Return how many bytes copied to output buffer. +// If given a null pointer or zero buffer then return the full size +// of the buffer. // // Otherwise return zero on error; perhaps the component name/trace buffer -// name is not found. +// name is not found, or maybe the size of buffer given is too small to even +// hold a trace buffer header. uint64_t Trace::getBuffer( const char * i_pComp, void * o_data, - uint64_t i_bufferSize ) + uint64_t i_size ) { - int64_t l_rc = 0; - trace_desc_t * l_pDescriptor = NULL; + const char * l_pchEntry = NULL; // use this to walk the entries + const char * l_pchEntryEOL = NULL; // end of list of entries + const char * l_pchTraceBuffer = NULL; // source buffer, including header + const char * l_pchTraceData = NULL; // source data, just past header + const char * l_pchTraceEOB = NULL; // end of source buffer + trace_buf_head_t * l_pCallerHeader = NULL; // output buffer, including header + trace_desc_t * l_pDescriptor = NULL; + uint64_t l_cbWrap = 0; + uint64_t l_rc = 0; do { - l_pDescriptor = getTd( i_pComp ); + l_pDescriptor = findTdByName( i_pComp ); if( NULL == l_pDescriptor ) { + // trace buffer name not found break; } - if( ( NULL == o_data ) || ( 0 == i_bufferSize )) + if( (o_data == NULL) || (i_size == 0 )) { // return how big is the buffer. - l_rc = TRAC_DEFAULT_BUFFER_SIZE; + l_rc = l_pDescriptor->size; break; } - // Not to exceed buffer size. - uint64_t l_copyCount = i_bufferSize; - if( i_bufferSize > TRAC_DEFAULT_BUFFER_SIZE ) + // Round size down to nearest 4-byte boundary. + i_size = ALIGN_DOWN_4(i_size); + + + if( i_size < sizeof(trace_buf_head_t)) { - l_copyCount = TRAC_DEFAULT_BUFFER_SIZE; + // Need at least enough space for the header. + // printk("trace_get_buffer_partial: i_size too small"); + break; } - // Get the lock - mutex_lock(&iv_trac_mutex); - // Copy buffer to caller's space - memcpy( o_data, l_pDescriptor, (size_t)l_copyCount ); + // Caller's destination buffer starts with a trace_buf_head_t. + l_pCallerHeader = static_cast<trace_buf_head_t*>(o_data); - mutex_unlock(&iv_trac_mutex); - l_rc = l_copyCount; - } - while( 0 ); - return l_rc; -} + if( i_size >= l_pDescriptor->size ) + { + // Caller's buffer is big enough to hold the whole buffer. + uint64_t l_copyCount = l_pDescriptor->size; + // Get the lock + mutex_lock(&iv_trac_mutex); + // If the buffer is not full, then the unused + // portion is just zeroes. Avoid copying the zeroes. + if( 0 == l_pDescriptor->times_wrap ) + { + // Buffer has never wrapped, so copy the + // data up to the next-free offset. + l_copyCount = l_pDescriptor->next_free; + } + // Copy source buffer to caller's destination buffer + memcpy( o_data, l_pDescriptor, l_copyCount ); -#if 0 -/******************************************************************************/ -// getBufferPartial - TODO -/******************************************************************************/ -// TODO -int32_t Trace::getBufferPartial(const trace_desc_t *i_td_ptr, - void *o_data, - uint32_t *io_size) -{ - /*------------------------------------------------------------------------*/ - /* Local Variables */ - /*------------------------------------------------------------------------*/ - int32_t l_rc = 0; - char *l_full_buf = NULL; - trace_desc_t *l_head = NULL; - uint32_t l_part_size = 0; + mutex_unlock(&iv_trac_mutex); - /*------------------------------------------------------------------------*/ - /* Code */ - /*------------------------------------------------------------------------*/ + // Update the header in the output buffer. + l_pCallerHeader->size = l_copyCount; - do - { - - if((i_td_ptr == NULL) || (o_data == NULL) || (io_size == NULL)) - { - printk("trace_get_buffer_partial: Invalid parameter passed by caller"); - l_rc = TRAC_INVALID_PARM; - if(io_size != NULL) - { - *io_size = 0; - } + l_rc = l_copyCount; break; } - if(*io_size < sizeof(trace_buf_head_t)) - { - // Need to at least have enough space for the header - printk("trace_get_buffer_partial: *io_size to small"); - l_rc = TRAC_MEM_BUFF_TO_SMALL; - *io_size = 0; - break; - } - // First get the full buffer - l_rc = tx_byte_allocate(&tpmd_trac_debug_byte_pool, - (void **)&l_full_buf, - TPMD_TRACE_BUFFER_SIZE, - TX_NO_WAIT); - if(l_rc != TX_SUCCESS) - { - printk("trace_get_buffer_partial: Failure allocating memory for temp buffer"); - *io_size = 0; - l_rc = TRAC_MEM_ALLOC_FAIL; - break; - } - l_rc = trace_get_buffer(i_td_ptr, - l_full_buf); - if(l_rc != 0) - { - printk("trace_get_buffer_partial: Failure in call to TRAC_get_buffer()"); - *io_size = 0; - break; - } + // Input buffer size is smaller than source buffer size. - // Now that we have full buffer, adjust it to be requested size - memset(o_data,0,(size_t)*io_size); + mutex_lock(&iv_trac_mutex); - if(*io_size > TPMD_TRACE_BUFFER_SIZE) + if((i_size >= l_pDescriptor->next_free) && (0 == l_pDescriptor->times_wrap)) { - // It fits - *io_size = TPMD_TRACE_BUFFER_SIZE; - memcpy(o_data,l_full_buf,(size_t)*io_size); - break; - } + // The source buffer has not wrapped, + // and what is there fits into caller's buffer. + l_rc = l_pDescriptor->next_free; + memcpy( o_data, l_pDescriptor, l_rc ); - l_head = (trace_desc_t *)l_full_buf; - memcpy(o_data,l_full_buf,(size_t)(l_head->hdr_len)); - l_head = (trace_desc_t *)o_data; - l_head->size = *io_size; + mutex_unlock(&iv_trac_mutex); - if((l_head->next_free == l_head->hdr_len) && (l_head->times_wrap == 0)) - { - // No data in buffer so just return what we have + // Update the header in the output buffer. + l_pCallerHeader->size = l_rc; break; } - if(l_head->next_free > *io_size) + + // Otherwise, walk the entries backwards because the word + // just prior to any entry is the length of the previous entry. + // Subtract this length from the current entry pointer to + // point to the previous entry. Wrap around as required. + + + // Trace descriptor points to base of source trace buffer. + l_pchTraceBuffer = reinterpret_cast<const char*>(l_pDescriptor); + + // Source trace data resides just past the header. + l_pchTraceData = reinterpret_cast<const char *>(l_pDescriptor+1); + + // EOB (end of buffer) of source trace buffer + l_pchTraceEOB = l_pchTraceBuffer + l_pDescriptor->size; + + // useful when calculating locations of wrapped data + l_cbWrap = l_pDescriptor->size - sizeof(trace_buf_head_t); + + // This is how much trace data caller's buffer can hold. + int l_cbToFill = i_size - sizeof(trace_buf_head_t); + + + + // Start at next_free, which is not an actual entry. + // It is where the next entry write will go when it comes. + // It also marks the end of the list (EOL). + l_pchEntryEOL = l_pchTraceBuffer + l_pDescriptor->next_free; + + + // Walk backwards through the entries, looking for a point + // such that when walking the source entries from that + // point forward, those entries will fit into the + // destination buffer. Because of the cases handled above, + // this walking will not loop around back to where we started + // within the source buffer. Otherwise there would have to be + // tests made for wrapping and sensing when l_pchEntry passes + // l_pchEntryEOL. Note that trace entry structures and payload + // data may be wrapped anywhere on a 4-byte bound. + + + // Start here and work backwards. + l_pchEntry = l_pchEntryEOL; + + + do { - // We can't even fit in first part of buffer - // Make sure data size is larger than header length - // Otherwise, we will be accessing beyond memory - if(*io_size < l_head->hdr_len) + + if(( l_pchEntry == l_pchTraceData ) && (0 == l_pDescriptor->times_wrap)) { - l_rc = TRAC_DATA_SIZE_LESS_THAN_HEADER_SIZE; + // Exit from this do loop with l_pchEntry the starting point. + // Probably not going to happen, because the non-wrap short + // buffer case was handled above. + // massert( 0 ); break; } - l_part_size = *io_size - l_head->hdr_len; - memcpy((UCHAR *)o_data+l_head->hdr_len, - l_full_buf+l_head->next_free-l_part_size, - (size_t)l_part_size); - // Set pointer at beginning because this will be a - // "just wrapped" buffer. - l_head->next_free = l_head->hdr_len; + // Determine the size of the entry prior to l_pchEntry. Normally, + // this length is found in the 4-byte word just before the start of any + // entry. However, trace code may wrap any given trace entry + // anywhere on a 4-byte word. + + // massert( l_pchEntry >= l_pchTraceData ); + // massert( l_pchEntry < l_pchTraceEOB ); + // massert( 0 == (((uint64_t)(l_pchEntry)) & 3) ); + + // Length of previous entry is in prior 32-bit word. + const char * l_pchPreviousLength = l_pchEntry - sizeof(uint32_t); + + if( l_pchPreviousLength < l_pchTraceData ) + { + // I am at the start of the source data. Apply wrap byte count + // to find length up at the end of the buffer. + l_pchPreviousLength += l_cbWrap; + + // Source buffer must have wrapped. + // massert( l_pDescriptor->times_wrap ); + } + + // Dereference and get the length of previous entry. + const uint32_t * l_p32; + l_p32 = reinterpret_cast<const uint32_t*>(l_pchPreviousLength); + int l_cbPrevious = *l_p32; + + + + if(( l_cbToFill - l_cbPrevious ) < 0 ) + { + // This one is too much. l_pchEntry is the starting point. + // This is the regular exit point from this loop. + break; + } + + + // Given the length of the previous one, + // assign a new value to l_pchEntry + l_pchEntry -= l_cbPrevious; - // Buffer is now wrapped because we copied max data into it. - if(!l_head->times_wrap) + if( l_pchEntry < l_pchTraceData ) { - l_head->times_wrap = 1; + // Wrap. + l_pchEntry += l_cbWrap; } + + l_cbToFill -= l_cbPrevious; + // massert( l_cbToFill >= 0 ); } - else + while( 1 ); + + + + // Having walked backwards, l_pchEntry is the starting point, + // All the entries forward of this point are supposed to fit + // into caller's data buffer. + + // Count how many copied from source to destination buffer. + int l_entriesCopied = 0; + int l_bytesCopied = sizeof( trace_buf_head_t ); + + // Set up destination header. + memcpy( l_pCallerHeader, l_pDescriptor, sizeof(trace_buf_head_t)); + + // Caller's destination area for trace entry data, just past the + // buffer header. + char * l_pchDest = reinterpret_cast<char*>(l_pCallerHeader+1); + + + while( l_pchEntry != l_pchEntryEOL ) { - // First part of buffer fits fine - memcpy((UCHAR *)o_data+l_head->hdr_len, - l_full_buf+l_head->hdr_len, - (size_t)(l_head->next_free - l_head->hdr_len)); + const trace_bin_entry_t * l_pEntry; + // Calculate how many bytes make up this entry. Value + // goes into l_cbEntry; + int l_cbEntry; - // If it's wrapped then pick up some more data - if(l_head->times_wrap) + if( (l_pchEntry + sizeof(trace_bin_entry_t)) > l_pchTraceEOB ) { - // Figure out how much room we have left - l_part_size = *io_size - l_head->next_free; + // This entry wraps. Copy this split-up + // trace_bin_entry_t to the callers destination buffer + // (save a malloc) then reference entry->head.length. + + // Copy this much from the end of the source trace buffer. + int l_cb = l_pchTraceEOB - l_pchEntry; + memcpy( l_pchDest, l_pchEntry, l_cb ); - memcpy((UCHAR *)o_data+l_head->next_free, - l_full_buf+TPMD_TRACE_BUFFER_SIZE-l_part_size, - (size_t)l_part_size); + // Copy the rest from the start of data in the trace buffer. + int l_cbTheRest = sizeof( trace_bin_entry_t ) - l_cb; + memcpy( l_pchDest+l_cb, l_pchTraceData, l_cbTheRest ); + // I just copied one of these into callers destination buffer. + l_pEntry = reinterpret_cast<trace_bin_entry_t*>(l_pchDest); } else { - // No more data to get, make buffer look as small - // as possible - // add '+4' to avoid the need to mark it as wrapped - // (if the last byte of the buffer is filled - // next_free has to pointer to the first byte) + // Otherwise, point the entry into the source buffer. + l_pEntry = reinterpret_cast<const trace_bin_entry_t*>(l_pchEntry); + } - l_head->size = l_head->next_free + 4; + // Compute length of this entry. entry->head.length is the actual + // length of the trace data, and has to rounded up to next 4-byte + // boundary. The extra uint32 is where the size is stored. + l_cbEntry = ALIGN_4(l_pEntry->head.length) + + sizeof( trace_bin_entry_t ) + + sizeof( uint32_t ); + + if( (l_pchEntry + l_cbEntry) > l_pchTraceEOB ) + { + // It wraps. Copy this split-up entry to the + // callers buffer. + int l_cb = l_pchTraceEOB - l_pchEntry; + memcpy( l_pchDest, l_pchEntry, l_cb ); + + // Copy the rest + int l_cbTheRest = l_cbEntry - l_cb; + memcpy( l_pchDest + l_cb, l_pchTraceData, l_cbTheRest ); + + // Assign l_pchEntry to next entry + l_pchEntry = l_pchTraceData + l_cbTheRest; } + else + { + // Copy to destination buffer in one go. + memcpy( l_pchDest, l_pchEntry, l_cbEntry ); + + // Assign l_pchEntry to next entry + l_pchEntry += l_cbEntry; + } + + l_bytesCopied += l_cbEntry; + // massert( 0 == ( l_bytesCopied & 3 )); + + // massert( l_pchEntry >= l_pchTraceData ); + // massert( l_pchEntry < l_pchTraceEOB ); + // massert( 0 == (((uint64_t)(l_pchEntry)) & 3) ); + + + // Increment new data destination pointer. + l_pchDest += l_cbEntry; + // massert( l_pchDest <= ((char*)l_pCallerHeader) + i_size ); + + // This will eventually go into destination header. + l_entriesCopied++; } - *io_size = l_head->size; + // Done looking at source buffer stuff. + mutex_unlock(&iv_trac_mutex); - }while(0); + // Finish the caller's trace buffer header. + l_pCallerHeader->times_wrap = 0; + l_pCallerHeader->te_count = l_entriesCopied; + l_pCallerHeader->next_free = l_bytesCopied; + l_pCallerHeader->size = l_bytesCopied; - if(l_full_buf != NULL) - { - tx_byte_release(l_full_buf); + // Return how many bytes written to output buffer. + l_rc = l_bytesCopied; } + while(0); - return(l_rc); + return l_rc; } -#endif + + + + /******************************************************************************/ // resetBuf - TODO |