Studio:Using printf

From STRIDE Wiki
Jump to navigation Jump to search

In some cases, printf is a quick and easy way to add debug information to your code. When using STRIDE, it is helpful to have the printf output redirected to the trace view window, so that the printf output is in sequence with all the function calls, messages, and trace points. Having printf output sent to the trace views not only consolidates all the debug information into one place — the trace view — it also helps to understand the sequence of events.

STRIDE provides a simple runtime command, srTraceStr, which sends strings to the trace window; however, replacing printf statements with srTraceStr in the source code can require a great deal of effort, which may not be worth it. A much simpler option is to use a macro definition to globally replace printf with srTraceStr. In addition, srTraceStr and printf's argument lists do not match exactly; therefore, parameter mapping is required. srTraceStr's syntax is as follows:

srEXPORT srWORD srSTDCALL srTraceStr( srWORD      wSTID,
                                      srCHAR      *szString,
                                      srLevel_e   eLevel );


szString is the string to be displayed in the trace view. In addition, srTraceStr contains a filtering level for the host trace view. Since printf does not offer any level of trace view filtering, it can be mapped to a single level. In most cases, level 0 is appropriate since there is no need to filter printf statements; usually you want to see them, as they are generally there for a reason.

srTraceStr's other parameter, wSTID, is a bit more complicated. A STID has to be created before srTraceStr can be called. The following two options are available:

  • A STID specific to the printf tracing could be created in the target initialization code. In other words, the “STRIDE printf” would have its own STID, allowing it to be easily filtered out in the trace view if required.
  • Alternately, the STID used for the intercept module could be reused. Since the scope of the “IM STID” is limited to the xxxIM.c file, the “STRIDE printf” would have to be part of this file. The intercept module is automatically generated, and therefore should not be modified; however, the example below demonstrates how the “IM STID” can be reused without having to modify the automatically-generated intercept module code.

The following macro replaces printf with a function called stride_printf :

#define printf stride_printf

Now we have to implement the stride_printf function. Since we replaced printf,we have to address variable arguments. The following example demonstrates a possible implementation:

static __inline srWORD stride_printf(char *s, ...)
{
   srWORD ret;
   va_list args;
   char str[MAX_STRIDE_PRINTF_SIZE];
   va_start(args, s);
   sprintf(str, s, args);
   ret = srTraceStr (wSTID, (srCHAR*) str, srLEVEL_0);
   return ret;
}

In the above example, the intercept module STID (wSTID) was used.

Now that we have made the change, the printf output will go to the STRIDE trace view — however, what if STRIDE is not being used but you still want to see the printf output? This can be done simply by adding a small function which allows switching between the normal printf and stride_printf; by exposing this interface in STRIDE, the output can be controlled regardless of whether or not you are using STRIDE.

void stride_printf_set(stride_printf_flag_t flag)
{
  stride_printf_on = flag;
}

Now the STRIDE printf function has to change slightly to add a “normal” printf:

srWORD stride_printf(char *s, ...)
{
   srWORD ret = srOK;
   va_list args;
   char str[MAX_STRIDE_PRINTF_SIZE];
   va_start(args, s);
   if ((!(stride_printf_on & STRIDE_PRINTF)) ||
         (stride_printf_on & OS_PRINTF))
   {   
      printf(s, args);
   }
   if (stride_printf_on & STRIDE_PRINTF)
   {
      sprintf(str, s, args);
      ret = srTraceStr (wSTID, (srCHAR*) str, srLEVEL_0);
   }   
   return ret;
}

Sample header files

Two header files are required in order to use printf to add debug information to your code: a header file which redefines printf to stride_printf, and a header file containing the stride_printf implementation as an inline function as well as the SCL pragmas for STRIDE.

Redefining printf to stride_printf

This header file should be included in one of the general include files, which in turn is included in all the source files to limit source code modifications.

#ifndef STRIDE_PRINTF_H
#define STRIDE_PRINTF_H
#ifdef __cplusplus
extern "C" {
#endif
#define MAX_STRIDE_PRINTF_SIZE  255
#define OS_PRINTF                 1
#define STRIDE_PRINTF             2
extern srWORD stride_printf(char *s);
/* Redefine printf to stride_printf */
#define printf stride_printf
#ifdef __cplusplus
}
#endif
#endif  /* STRIDE_PRINTF_H */</tt>

Implementing stride_printf as an inline function and SCL pragmas

The stride_printf_scl.h file is included in the STRIDE workspace. It contains the stride_printf implementation as an inline function as well as the SCL pragmas for STRIDE.

#ifndef STRIDE_PRINTF_SCL_H 
#define STRIDE_PRINTF_SCL_H 
#ifdef __cplusplus 
extern "C" { 
#endif 
#include <stdio.h> 
#include <stdarg.h> 
#include "stride_printf.h" 
static unsigned char stride_printf_on = OS_PRINTF; 
extern srWORD   wSTID; 
static __inline void stride_printf_set(unsigned char flag) 
{ 
 stride_printf_on = flag; 
} 
#undef printf 
static __inline srWORD stride_printf(char *s, ...) 
{ 
  srWORD ret = srOK; 
  va_list args; 
  va_start(args, s); 
  if ((!(stride_printf_on & STRIDE_PRINTF)) || 
        (stride_printf_on & OS_PRINTF)) 
  {     
     printf(s, args); 
  } 
  if (stride_printf_on & STRIDE_PRINTF) 
  { 
     char str[MAX_STRIDE_PRINTF_SIZE]; 
     sprintf(str, s, args); 
     ret = srTraceStr (wSTID, (srCHAR*) str, srLEVEL_0); 
  }    
  return ret; 
} 
#ifdef _SCL 
#pragma scl_function(stride_printf_set) 
#endif 
#ifdef __cplusplus 
} 
#endif
#endif  /* STRIDE_PRINTF_SCL_H */