Logging variables

From OC Systems Wiki!
Revision as of 00:22, 16 August 2019 by Swn (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Logging variables from applications with debug information is simple. In general, it is easy to log a variable using the log() probe directive and the variable's "target name." The variable must be logged from a probe in a scope where the variable is visible or you must specify the variable scope in the target expression.

From a probed function you can log parameters, local variables, function-static variables, file-static variables and globals within the same file using the target expression for the variable.

You can log global and static variables from any scope using the target expression modifiers -file or -unit (see examples below).

In most case, the target expression is just the variable name preceded by a dollar sign, for example:

$var
$structvar.field
$structptr->field

For global and file-static variables you may need to use target expression modifiers to locate the variable scope, for example:

$(global-var, "-file file.c")
$(file-static, "-file file.c")
$(powerada-global-var, "-unit sec/pkg")
$(gnat-global-var, "-file pkg.adb")

You can use the apcgen tool generate probes for logging functions, lines, and parameters. Version 4.4.6d of apcgen supports logging locals and globals.

If you don't have debug information in the application, you can still log parameters using positional notation ($1, $2, $3, ...). You can even cast the positional parameters to local types you declare in the probe.

C Example

For this C program:

int global = 1234;

static int file_local = 5678;

void p1(int param)
{
  static int func_static = 9876;

  int func_local = param * 10;

  {
     int block_local = param * 100;

     printf("b = %d\n", block_local);
     global += block_local;
  }
}

The following probe:

probe thread
{
   on_entry
   {
      // This probe not in any scope so we have to use the "-file" modifier to specify the scope. 
      log ("\"global\" = ", $(global, "-file main.c"));
      log ("\"file_local\" = ", $(file_local, "-file main.c"));
   }

   probe "p1"
   {
      // all target expressions are in the scope containing the variable so no modifiers needed.
      on_entry
      {
         log ("\"global\" = ", $global);
         log ("\"file_local\" = ", $file_local);
         log ("\"func_static\" = ", $func_static);
         log ("\"param\" = ", $param);
      }
      on_line(17)
      {
         log ("\"func_local\" = ", $func_local);
         log ("\"block_local\" = ", $block_local);
      }
   }
}

Produces the following output:

"global" = 1234
"file_local" = 5678
"global" = 1234
"file_local" = 5678
"func_static" = 9876
"param" = 1234
"func_local" = 12340
"block_local" = 123400


C++ Example

For this C++ program:

class Rectangle {
    int width, height;
    static int count = 0;
  public:
    void set_values (int,int);
    int area (void);
};

Class static data is not part of the object; it is a global and is referenced using a qualified name, in any function probe like:

probe thread
{
  probe "MyFunction"
  {
    on_entry
    {
      log($("Rectangle::count"));
    }
  }
}

If you're unsure of the full name of a static data item you can use:

  apinfo -d myprog.exe

A class object is always called $this within a method. You can access class member data in a class method like:

probe thread
{
  probe "Rectangle::area"
  {
    on_entry
    {
      log($this->width);  // log one data member
      log(*$this);        // log the whole object
    }
  }
}

If you're unsure of the full method name in class "Class", you can use

  apcgen -L <dll-or-exe> | grep "Class::"

to list all the methods.

Static class methods do not have a $this argument. The arguments of a static class member can be logged like C parameters above.

Ada Example

For an Ada program:

package Pkg is

  function P1(Param : in Integer) return Integer;

  PkgGlobal : Integer := 53;

end Pkg;

with Text_Io;

package body Pkg is

  PkgLocal : Integer := 27;

  function P1(Param : in Integer) return Integer is
    PLocal : Integer := Param * 5;
  begin
    Text_Io.Put_Line("PkgGlobal = " & Integer'Image(PkgGlobal));
    Text_Io.Put_Line("PkgLocal = " & Integer'Image(PkgLocal));
    Text_Io.Put_Line("Param = " & Integer'Image(Param));
    Text_Io.Put_Line("PLocal = " & Integer'Image(PLocal));
    declare
       PBlock : Integer := Param * 3;
    begin
       Text_Io.Put_Line("PBlock = " & Integer'Image(PBlock));
       PLocal := PLocal + PBlock;
    end;
    return PLocal;
  end P1;

The following probe:

probe thread
{
   probe "pkg.p1"
   {
      on_entry
      {
#ifdef _AIX
         // Must specify the PowerAda unit name to access global
         log ("\"PkgGlobal\" = ", $(PkgGlobal, "-unit lib/pkg"));
#else
         log ("\"PkgGlobal\" = ", $PkgGlobal);
#endif
         log ("\"PkgLocal\" = ", $PkgLocal);
         log ("\"Param\" = ", $Param);
      }
      on_line(18)
      {
         log ("\"PLocal\" = ", $PLocal);
         log ("\"PBlock\" = ", $PBlock);
      }
      on_exit
      {
         log ("\"Return\" = ", $return);
      }
   }
}

Produces the following output:

"PkgGlobal" = 53
"PkgLocal" = 27
"Param" = 1234
"PLocal" = 6170
"PBlock" = 3702
"Return" = 9872