LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

Is it possible to read from code Function stack trace or not ?

Solved!
Go to solution

Hi all,

 

in case of some error I would need to log where exaclly error is generated e.g.  in which function and with what parameters(like looking at stack trace in LabWindows/CVI).

Does anyone know how to read stack function names and its parameters ?

 

 

Best regards,

Milan.

0 Kudos
Message 1 of 9
(4,197 Views)

you could start with using the macro __FUNCTION__ (and possible also __LINE__ and __FILE__)

0 Kudos
Message 2 of 9
(4,196 Views)

Hi Wolfgang,

 

1. do you have any peace of the code because I am kind of beginner ?

2. does __FUNCTION__ return current function name(which is obvious) or function name of calling function ? 

3. do you see any way of getting values of function parameters ?

 

Best regards,

Milan.

0 Kudos
Message 3 of 9
(4,190 Views)
Solution
Accepted by topic author MilanV

Hi Milan,

 

First of all you can try looking at this sample code I posted to the community that describes error checking functions that make use of __FILE__, __FUNCTION__ and __LINE__ macros to inform the user of an error.

 

The only way I can think of for logging down function parameters in case of errors is using variable argument functions: in this case all parameters to the function can be accessed via some macros so that inside the function you can write some line of code to log all of them to the debug output window or a file or whatever you want. You can start looking here, here and here for some reference and some sample code. While I have used variable argument functions, using them for error logging scope it's just a conceptual framework: I haven't really tried to do that but I suppose it can be done this way.



Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
0 Kudos
Message 4 of 9
(4,183 Views)

Hi Roberto,

 

thanks !

lets analyze the answer together:

 

1. maybe it would be usefull if the complete LabWindows/CVI project would be included in attachment ? because as far I try to understand code, the line:

void ErrorMessage (int err, int terr, int line, char *function, char *file)

do just nice printing on screen and the function:

void CVICALLBACK WarnMessage (void *callbackData)

get somehow through callbackData information about function name and line. How callbackData knows the line and function infos ? and does it means that after executing some code and getting some error the program will jump to the callback function and newer turn back to the code position where error originally happend ?

 

2. You wrote about __FILE__, __FUNCTION__ but I do not see them in your code, where they are defined ?

 

3. I had a problem to create variable arguments list of type "char*" is this forbidden for some reson ?(all given examples are with int or double type)

 

   int i_arg_count = 1; 

   char* c_param;

 

   va_start(ap,i_arg_count);
 
    for (j=1; j <= i_arg_count; j++)
       c_param = va_arg(ap, char*); // here for c_param I get some wrong data !!!
    
    va_end(ap);

 

do I use "va_arg" correctlly ?

 

Best regards,

MilanV

0 Kudos
Message 5 of 9
(4,107 Views)

ErrorMessage function is to be used while inside a function that can be interrupted with a MessagePopup, e.g. some control callback where the user is operating on the user interface or saving data or doing some interruptible piece of code. The use is like this one:

 

int CVICALLBACK ShowOnePanel (int panel, int control, int event, void *callbackData, int eventData1, int eventData2)

{
  int line, error = 0;

  if (event != EVENT_COMMIT) return 0;

  errChk (tmpH = LoadPanel (0, "MyFile,uir", PANEL));
  DisplayPanel (tmpH);

Error:
  if (error < 0)
    ErrorMessage (error, ERRGEN, line, __FUNCTION__, __FILE__);
  return 0;
}

 In this code, if LoadPanel returns an error, errChk macro lets the program jump to Error label where ErrorMessage displays a properly formatted error message.

 

 

On the other hand, when you cannot use a popup to inform the operator, you can prepare a memory structure with all relevant informations and display the message in another moment, like in this example:

 

int SomeFunction (...)
{
  int   line, error = 0;
  errStruct *xe = NULL;

  // some line of code

  while (TRUE) {
    // some line of code

    error = some_function_returning_an_error;
    if (error < 0) {
      // prepare to warn the operator 
xe = malloc (sizeor (errStruct)); xe.err = error; // Error code xe.terr = ERRGEN; // Type of error xe.line = line; // Line the error raised strcpy (xe.func, __FUNCTION__); // Function where the error raised strcpy (xe.file, __FILE__); // Source file where the error raised
// issue a warning in the main thread
PostDeferredCall (WarnMessage, (void *)&xe);

error = 0; // Clear error indicator
 } // the rest of the code
ProcessSystemEvents ();
 } // Stopping active tasks / operations; cleaning environment and so on return 0; }

In this case, the next time the system processes events after the error has arised, WarnMessage function will be executed and will prompt the operator with error informations and will dispose of the memory allocate with error informations.

 

 

Now, to permit using __FUNCTION__ macro on CVI from 8.5 on you need to enable C)) function within the IDE adding this macro to the code:

// Enable C99 fucntions in CVI
#pragma iso_9899_1999;

On previous CVI releases this is not necessary (but I seem to remember that __FUNCTION__ macro is present only starting from CVI7 - to be confirmed).

 

 

I have used variable argument functions with strings without errors: this is an abstract from a function that logs parameters on a string to display them on a textbox:

 

// Types of parameters ("stdarg.h")
#define	INTPRM		1
#define	DBLPRM		2
#define CHRPRM		3


Logging function:

  // Logging parameters
  strcpy (msg, "Parameters");
  while ((type = *parms++) != 0) {
    switch (type) {
      case INTPRM: sprintf (msg, "%s,%d", msg, va_arg (ap, int)); break;
      case DBLPRM: sprintf (msg, "%s,%.1f", msg, va_arg (ap, double)); break;
      case CHRPRM: sprintf (msg, "%s,%s", msg, va_arg (ap, char *)); break;
    }
  }

 

Hope these informations can help you in your job.

 



Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
0 Kudos
Message 6 of 9
(4,099 Views)

Hi Roberto,

 

thanks for tipps, some expected "function stack" support obvious CVI does snot offer.

I tried to make function with variable arguments but i failed again 😞 can you please check what is here wrong ?

 

/* in .h file */

void mv_to_string(int iParamCount, ...);

 

/* in .c file */

void mv_to_string(int iParamCount, ...)
{
  char msg[500];
  int type = 0;
  va_list listPointer;
 
  va_start(listPointer,iParamCount);
 
 // Logging parameters
  strcpy (msg, "Parameters");
  while ((type = *listPointer++) != 0)
  {
    switch (type)
    {
      case INTPRM: sprintf (msg, "%s,%d", msg, va_arg (listPointer, int)); break;
      case DBLPRM: sprintf (msg, "%s,%.1f", msg, va_arg (listPointer, double)); break;
      case CHRPRM: sprintf (msg, "%s,%s", msg, va_arg (listPointer, char *)); break;
    }
  }
 
  va_end(listPointer);
}

 

 

/* somwhere in some third file */

mv_to_string(3, 1234, 0.54321, "blach");

 

inside the last function call "msg" are not having the values of arguments. Can you guess why ?

 

 

Best regards,

Milan V.

0 Kudos
Message 7 of 9
(4,076 Views)

Well, I was using this function in a bit different way, passing an array of parameter types before the variable arguments list. The function is like this one:

 

void LogParms (int *parms, ...)
{
	char	type, msg[512];
	va_list	ap;

	// Initialize variable parameters handling
	va_start (ap, parms);

// Log parameters strcpy (msg, "Parameters"); while ((type = *parms++) != 0) { switch (type) { case INTPRM: sprintf (msg, "%s,%d", msg, va_arg (ap, int)); break; case DBLPRM: sprintf (msg, "%s,%.1f", msg, va_arg (ap, double)); break; case CHRPRM: sprintf (msg, "%s,%s", msg, va_arg (ap, char *)); break; } } va_end (ap); return; }

 

To call the function I must use the following:

parms[0] = INTPRM; parms[1] = DBLPRM; parms[2] = CHRPRM; parms[s] = 0;
LogParms (parms, 10, 23.4, "Test");

 Always end parms[] list with a 0 value. parms[] must be global to all modules where you are calling the function or where the fucntion is.

 



Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
Message 8 of 9
(4,065 Views)

... why not giving a kudos? why not 🙂

0 Kudos
Message 9 of 9
(4,054 Views)