Setting Ada String Parameters
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.
Contents
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