Setting Ada String Parameters

From OC Systems Wiki!
Jump to: navigation, search

It is often useful to be able to change the value of an Ada string parameter to a subprogram. Here is a description of that process, which varies for PowerAda and GNAT.

Example Program

Here is an example program. The idea is to change the value of the parameter S passed to Pkg.P1.

package Pkg is

  procedure P1(S : in String);

end Pkg;

with Text_Io;

package body Pkg is

  procedure P1(S : in String) is
  begin
    Text_Io.Put_Line("p1 = " & S);
  end P1;

end Pkg;


with Text_Io;
with Pkg;  use Pkg;

procedure Main is
   S1 : String(1..10) := (others => 'x');
begin
   Pkg.P1(S1);
end Main;

PowerAda

PowerAda passes string parameters as three separate values: the character data pointer, the lower bound, and the upper bound. There are two ways to change the value to the string being passed in.

Simple (Destructive) Change

If the passed in string value doesn’t matter anymore, there is a simple (destructive) way to change the string. In this case you just copy the new string data (characters) over the old string data. The new string data must be the same size as the old one or you risk overwriting other data. Here is how you would do that:

 probe thread
 {
    probe "pkg.p1"
    {
       on_entry
       {
          // modifies original passed string (must be careful of length)
          strncpy($s, "abcdefghij", sizeof("abcdefghij"));
       }
    }
 }

Will produce this output:

 p1 = abcdefghij
 

You can get a little fancier if you access the lower and upper bound parameters to determine how much space you have to work with (see the next example below).

Replacing The String

The next example is how you can replace the incoming string value with a completely different one. The idea is to point to different string data and set different lower and upper bounds.

First, use apcgen to find out the name to the string parameter lower and upper bound parameters:

 # generate probe to discover string parameter names
 apcgen -qparams -p pkg.p1 -x main.exe > a.apc
 

In the generated apc in a.apc you will see the hidden parameters for the lower and upper bounds:

   probe extern:"pkg.p1[1]" in MODULE_NAME

   {  // "p1" declared in "/home/swn/aprobe/test/faq/setstrada3/pkg.adb" at line 8
      on_entry
      {
         ap_LogSubprogramEntry;
         log parameter("\"s\" (parameter) = ", $s);
         log parameter("\"string_lb_1_2\" (parameter) = ", $string_lb_1_2);
         log parameter("\"string_ub_1_3\" (parameter) = ", $string_ub_1_3);
      }
      on_exit
      {
         ap_LogSubprogramExit;
         // Only continue if normal exit.
         if (ap_ProbeActionReason != ap_ExitAction) return;
         // Void return value.
      }
   }

So we can see the bounds passed in the parameters: string_lb_1_2 and string_ub_1_3.

We can use the following probe to change the value of the string:

const char new_string[] = "abcdefghijklmnop";

probe thread
{
   probe "pkg.p1"
   {
      on_entry
      {
         // Discovered the "hidden" string bounds using apcgen
         // Set the string data to point to the new string data.
         // We have to use the register name to change the address of the string data.  On AIX $$r3 is first parameter.
         $$r3 = &new_string;
         // Set the lower bound to 1
         $string_lb_1_2 = 1;
         // set the upper bound to the string lentgh
         $string_ub_1_3 = strlen(new_string);
      }
   }
}

Will produce this output:

 p1 = abcdefghijklmnop
 

Larger Test

Here is a larger test that replaces an Ada string in parameter, out parameter, and function return value:

package Pkg is

  subtype Fixed_String is String(1..10);

  procedure P1(S : in String);

  procedure P2(S : in Fixed_String);

  procedure P3(S : out String);

  procedure P4(S : out Fixed_String);

  function F1(I : in Integer) return String;

  function F2(I : in Integer) return Fixed_String;

end Pkg;


with Text_Io;

package body Pkg is

  procedure P1(S : in String) is
    -- bounds are padded in with string
  begin
    Text_Io.Put_Line("p1 = " & S);
  end P1;

  procedure P2(S : in Fixed_String) is
    -- fixed length, no bounds
  begin
    Text_Io.Put_Line("p2 = " & S);
  end P2;

  procedure P3(S : out String) is
    -- bounds are passed in with string
  begin
    S := (others => 'v');
  end P3;

  procedure P4(S : out Fixed_String) is
     -- fixed length, no bounds
  begin
    S := (others => '.');
  end P4;

  function F1(I : in Integer) return String is
    -- bounds are passed out
  begin
    return Integer'Image(I);
  end F1;

  function F2(I : in Integer) return Fixed_String is
    -- fixed length, no bounds
    Temp: String := Integer'Image(I);
    Result : Fixed_String := (others => ' ');
  begin
    if Temp'Length > Result'Length then
       Result := Temp(Temp'First .. Temp'First + Result'Length -1);
    else
       Result(Result'First .. Result'First + Temp'Length - 1) := Temp;
    end if;
    return Result;
  end F2;

end Pkg;


with Text_Io;
with Pkg;  use Pkg;

procedure Main is
   S1 : String(1..10) := (others => 'x');
   S2 : Fixed_String := "1234567890";
   S3 : String := "123456789";
   S4 : Fixed_String;
   S6 : Fixed_String;
begin
   Pkg.P1(S1);
   Pkg.P2(S2);
   Pkg.P3(S3);
   Text_Io.Put_Line("S3 = " & S3);
   Pkg.P4(S4);
   Text_Io.Put_Line("S4 = " & S4);
   declare
      S5 : String := Pkg.F1(10);
   begin
      Text_Io.Put_Line("S5 = " & S5);
   end;
   S6 := Pkg.F2(77);
   Text_Io.Put_Line("S6 = " & S6);
end Main;

Here is the probe to set the Ada string values:

probe thread
{

   probe "pkg.p1"
   {
      on_entry
      {
         // change original string data (using dynamic bounds)
         // bounds target expressions found with apcgen
         strncpy($s, "abcdefghijklmn", $string_ub_1_3 - $string_lb_1_2 + 1);
      }
   }

   probe "pkg.p2"
   {
      on_entry
      {
         // change original string data (fixed bounds)
         strncpy($s, "9876543210", 10);
      }
   }

   probe "pkg.p3"
   {
      on_exit
      {
         // change string data
         // bounds target expressions found with apcgen
         strncpy($s, "abcdefghijklmnop", $string_ub_1_3 - $string_lb_1_2 + 1);
      }
   }

   probe "pkg.p4"
   {
      on_exit
      {
         // change string data
         strncpy($s, "9876543210", 10);
      }
   }

   probe "pkg.f1"
   {
      on_exit
      {
         // change string data
         // bounds target expressions found with apcgen
         strncpy($return, "987654321", $string_ub_1_4 - $string_lb_1_3 + 1);
      }
   }

   probe "pkg.f2"
   {
      on_exit
      {
         // change string data (fixed bounds)
         strncpy($return, "9876543210", 10);
      }
   }

}

Here is the output with the probe in place:

 
 p1 = abcdefghij
 p2 = 9876543210
 S3 = abcdefghi
 S4 = 9876543210
 S5 = 987 
 S6 = 9876543210
 

Gnat

Gnat passes Ada strings as a couple of records: the first contains the pointer to the string data (characters) and a pointer to the bounds record, the second record contains the lower and upper bounds. Aprobe includes some macros for manipulating GNAT string parameters so you don't ahve to worry about the underlying layout. Include $APROBE/include/gnatstrings.h in your probe apc.

Replacing The String

Here is a probe to replace a Gnat Ada string parameter with a new one:

#include "gnatstrings.h"

probe thread
{
   probe "pkg.p1"
   {
      on_entry
      {
         ap_SetGnatUCString($s, "new value");
      }
   }
}

Will produce this output:

 p1 = new value
 

Larger Test

Here is the probe to set the Ada string values:

#include "gnatstrings.h"

// Set the value of a constrained string
#define ap_SetGnatCStringValue(X,STR) \
   strncpy((char *)(X).P_ARRAY, STR, (X).P_BOUNDS->UB0 - (X).P_BOUNDS->LB0 + 1)


probe thread
{

   probe "pkg.p1"
   {
      on_entry
      {
         // change original string
         ap_SetGnatUCString($s, "abcdefghijklmn");
      }
   }

   probe "pkg.p2"
   {
      on_entry
      {
         // change original string data
         // can't use target expression, use parameter register, warning OK
         ap_Param(1) = "9876543210";
      }
   }

   probe "pkg.p3"
   {
      on_exit
      {
         // set new string data in constrained string
         ap_SetGnatCStringValue($s, "abcdefghijklmn");
      }
   }

   probe "pkg.p4"
   {
      on_exit
      {
         // set new data
         strncpy($s, "9876543210", 10);
      }
   }

   probe "pkg.f1"
   {
      on_exit
      {
         // return new string
         ap_SetGnatUCString($return, "987");
      }
   }

   probe "pkg.f2"
   {
      on_exit
      {
         // set new string data
         strncpy($return, "9876543210", 10);
      }
   }

}

Here is the output with the probe in place:

 
 p1 = abcdefghij
 p2 = 9876543210
 S3 = abcdefghij
 S4 = 9876543210
 S5 = 9876543210
 S6 = 9876543210