Difference between revisions of "Setting Ada String Parameters"

From OC Systems Wiki!
Jump to: navigation, search
(Created page with " 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. ==E...")
 
m
Line 135: Line 135:
 
  </nowiki>
 
  </nowiki>
  
 +
===Larger Test===
 +
 +
Here is a larger test that replaces an Ada string in parameter, out parameter, and function return value:
 +
 +
<source lang=ada>
 +
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
 +
  begin
 +
    Text_Io.Put_Line("p1 = " & S);
 +
  end P1;
 +
 +
  procedure P2(S : in Fixed_String) is
 +
  begin
 +
    Text_Io.Put_Line("p2 = " & S);
 +
  end P2;
 +
 +
  procedure P3(S : out String) is
 +
  begin
 +
    S := (others => 'v');
 +
  end P3;
 +
 +
  procedure P4(S : out Fixed_String) is
 +
  begin
 +
    S := (others => '.');
 +
  end P4;
 +
 +
  function F1(I : in Integer) return String is
 +
  begin
 +
    return Integer'Image(I);
 +
  end F1;
 +
 +
  function F2(I : in Integer) return Fixed_String is
 +
    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;
 +
 +
</source>
 +
 +
Here is the probe to set the Ada string values:
 +
 +
<source lang=c>
 +
 +
probe thread
 +
{
 +
 +
  probe "pkg.p1"
 +
  {
 +
      on_entry
 +
      {
 +
        // set whole string
 +
        strncpy($s, "abcdefghijklmn", $string_ub_1_3 - $string_lb_1_2 + 1);
 +
      }
 +
  }
 +
 +
  probe "pkg.p2"
 +
  {
 +
      on_entry
 +
      {
 +
        // set whole string
 +
        strncpy($s, "9876543210", 10);
 +
      }
 +
  }
 +
 +
  probe "pkg.p3"
 +
  {
 +
      on_exit
 +
      {
 +
        // set whole string
 +
        // bounds target expressions found with apcgen
 +
        strncpy($s, "abcdefghijklmnop", $string_ub_1_3 - $string_lb_1_2 + 1);
 +
      }
 +
  }
 +
 +
  probe "pkg.p4"
 +
  {
 +
      on_exit
 +
      {
 +
        // set whole string
 +
        strncpy($s, "9876543210", 10);
 +
      }
 +
  }
 +
 +
  probe "pkg.f1"
 +
  {
 +
      on_exit
 +
      {
 +
        // set whole string
 +
        // bounds target expressions found with apcgen
 +
        strncpy($return, "987654321", $string_ub_1_4 - $string_lb_1_3 + 1);
 +
      }
 +
  }
 +
 +
  probe "pkg.f2"
 +
  {
 +
      on_exit
 +
      {
 +
        // set whole string
 +
        strncpy($return, "9876543210", 10);
 +
      }
 +
  }
 +
 +
}
 +
 +
</source>
 +
 +
Here is the output with the probe in place:
 +
 +
<nowiki>
 +
p1 = abcdefghij
 +
p2 = 9876543210
 +
S3 = abcdefghi
 +
S4 = 9876543210
 +
S5 = 987
 +
S6 = 9876543210
 +
</nowiki>
  
 
==Gnat==
 
==Gnat==
Line 164: Line 336:
 
  <nowiki>
 
  <nowiki>
 
  p1 = newvalue
 
  p1 = newvalue
 +
</nowiki>
 +
 +
===Larger Test===
 +
 +
Here is the probe to set the Ada string values:
 +
 +
<source lang=c>
 +
 +
probe thread
 +
{
 +
 +
  probe "pkg.p1"
 +
  {
 +
      on_entry
 +
      {
 +
        // set whole string
 +
        strncpy($s, "abcdefghijklmn", $string_ub_1_3 - $string_lb_1_2 + 1);
 +
      }
 +
  }
 +
 +
  probe "pkg.p2"
 +
  {
 +
      on_entry
 +
      {
 +
        // set whole string
 +
        strncpy($s, "9876543210", 10);
 +
      }
 +
  }
 +
 +
  probe "pkg.p3"
 +
  {
 +
      on_exit
 +
      {
 +
        // set whole string
 +
        // bounds target expressions found with apcgen
 +
        strncpy($s, "abcdefghijklmnop", $string_ub_1_3 - $string_lb_1_2 + 1);
 +
      }
 +
  }
 +
 +
  probe "pkg.p4"
 +
  {
 +
      on_exit
 +
      {
 +
        // set whole string
 +
        strncpy($s, "9876543210", 10);
 +
      }
 +
  }
 +
 +
  probe "pkg.f1"
 +
  {
 +
      on_exit
 +
      {
 +
        // set whole string
 +
        // bounds target expressions found with apcgen
 +
        strncpy($return, "987654321", $string_ub_1_4 - $string_lb_1_3 + 1);
 +
      }
 +
  }
 +
 +
  probe "pkg.f2"
 +
  {
 +
      on_exit
 +
      {
 +
        // set whole string
 +
        strncpy($return, "9876543210", 10);
 +
      }
 +
  }
 +
 +
}
 +
 +
</source>
 +
 +
Here is the output with the probe in place:
 +
 +
<nowiki>
 +
p1 = abcdefghij
 +
p2 = 9876543210
 +
S3 = abcdefghi
 +
S4 = 9876543210
 +
S5 = 987
 +
S6 = 9876543210
 
  </nowiki>
 
  </nowiki>
  

Revision as of 22:02, 23 August 2018

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
       {
          // sets passed string
          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
  begin
    Text_Io.Put_Line("p1 = " & S);
  end P1;

  procedure P2(S : in Fixed_String) is
  begin
    Text_Io.Put_Line("p2 = " & S);
  end P2;

  procedure P3(S : out String) is
  begin
    S := (others => 'v');
  end P3;

  procedure P4(S : out Fixed_String) is
  begin
    S := (others => '.');
  end P4;

  function F1(I : in Integer) return String is
  begin
    return Integer'Image(I);
  end F1;

  function F2(I : in Integer) return Fixed_String is
    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
      {
         // set whole string
         strncpy($s, "abcdefghijklmn", $string_ub_1_3 - $string_lb_1_2 + 1);
      }
   }

   probe "pkg.p2"
   {
      on_entry
      {
         // set whole string
         strncpy($s, "9876543210", 10);
      }
   }

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

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

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

   probe "pkg.f2"
   {
      on_exit
      {
         // set whole string
         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 you 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 = newvalue
 

Larger Test

Here is the probe to set the Ada string values:

probe thread
{

   probe "pkg.p1"
   {
      on_entry
      {
         // set whole string
         strncpy($s, "abcdefghijklmn", $string_ub_1_3 - $string_lb_1_2 + 1);
      }
   }

   probe "pkg.p2"
   {
      on_entry
      {
         // set whole string
         strncpy($s, "9876543210", 10);
      }
   }

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

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

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

   probe "pkg.f2"
   {
      on_exit
      {
         // set whole string
         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