Skip to content

Commit 547b368

Browse files
committed
manual dump: fix performance issue
When looking for dump trigger indications in a C/C++ source, we were so far reading the source file contents to an unbounded string, which is very inefficient for large files. Use the GNATCOLL.Mmap API instead to avoid the costly string copies/allocations. This was noticed when instrumenting gnatcov with the manual dump-trigger, and notably when trying to look for a manual dump indication in the `clang-wrapper.cc` file. The preprocessed version of a C++ file including parts of the standard library can be quite large, which incurred this performance slow down.
1 parent 42c3745 commit 547b368

File tree

1 file changed

+71
-35
lines changed

1 file changed

+71
-35
lines changed

tools/gnatcov/instrument-c.adb

+71-35
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
------------------------------------------------------------------------------
1818

1919
with Ada.Characters.Handling;
20-
with Ada.Characters.Latin_1;
2120
with Ada.Containers; use Ada.Containers;
2221
with Ada.Directories; use Ada.Directories;
2322
with Ada.Text_IO; use Ada.Text_IO;
@@ -30,6 +29,7 @@ with GNAT.OS_Lib; use GNAT.OS_Lib;
3029
with GNAT.Regpat; use GNAT.Regpat;
3130

3231
with GNATCOLL.VFS;
32+
with GNATCOLL.Mmap;
3333

3434
with Interfaces; use Interfaces;
3535
with Interfaces.C; use Interfaces.C;
@@ -3851,16 +3851,15 @@ package body Instrument.C is
38513851
declare
38523852
Options : Analysis_Options;
38533853
PP_Filename : Unbounded_String;
3854-
File : Ada.Text_IO.File_Type;
38553854
Dummy_Main : Compilation_Unit_Part;
38563855
Dump_Pat : constant Pattern_Matcher :=
3857-
Compile ("^[\t ]*\/\* GNATCOV_DUMP_BUFFERS \*\/[ \t]*");
3856+
Compile
3857+
("^[\t ]*\/\* GNATCOV_DUMP_BUFFERS \*\/[ \t]*",
3858+
Flags => Multiple_Lines);
38583859
Matches : Match_Array (0 .. 1);
38593860
Dump_Procedure : constant String :=
38603861
Dump_Procedure_Symbol
38613862
(Main => Dummy_Main, Manual => True, Prj_Name => +Prj.Prj_Name);
3862-
Contents : Unbounded_String :=
3863-
+("extern void " & Dump_Procedure & " (void);");
38643863
begin
38653864
-- Preprocess the source, keeping the comment to look for the manual
38663865
-- dump indication later.
@@ -3887,47 +3886,84 @@ package body Instrument.C is
38873886
end loop;
38883887
end;
38893888

3890-
-- Look for the manual dump indication in the preprocessed file
3889+
-- Look for the manual dump indication in the preprocessed file. Use
3890+
-- the GNATCOLL.Mmap API to map the file contents in memory, as we
3891+
-- may need to rewrite it to the source file, with the manual dump
3892+
-- indication replaced by an actual call to the dump buffers
3893+
-- function.
38913894

3892-
Ada.Text_IO.Open
3893-
(File => File,
3894-
Mode => In_File,
3895-
Name => (+PP_Filename));
3895+
declare
3896+
use GNATCOLL.Mmap;
3897+
File : Mapped_File := Open_Read (To_String (PP_Filename));
3898+
Region : Mapped_Region := Read (File);
3899+
Raw_Str : constant Str_Access := Data (Region);
3900+
Raw_Str_Last : constant Natural := Last (Region);
3901+
Str : String renames Raw_Str (1 .. Raw_Str_Last);
3902+
3903+
Tmp_Filename : constant String := +PP_Filename & ".tmp";
3904+
Output_File : Ada.Text_IO.File_Type;
3905+
-- Temporary file containing the new version of the original file,
3906+
-- with inserted calls to dump buffers. The original file is then
3907+
-- overwritten by this temporary file.
3908+
3909+
Index : Positive := 1;
3910+
-- Starting index, or last index of the previous match in the
3911+
-- original file.
38963912

3897-
while not Ada.Text_IO.End_Of_File (File) loop
3898-
declare
3899-
Line : constant String := Get_Line (File);
3900-
begin
3901-
Match (Dump_Pat, Line, Matches);
3913+
begin
3914+
Has_Manual_Indication := False;
3915+
while Index in Str'Range loop
3916+
Match (Dump_Pat, Str (Index .. Str'Last), Matches);
3917+
exit when Matches (0) = No_Match;
39023918

3903-
if Matches (0) /= No_Match then
3904-
Contents := Contents & Dump_Procedure & "();";
3919+
-- Open the output file if this is the first match we find,
3920+
-- then forward the source code that appear before the match
3921+
-- unchanged.
3922+
3923+
if not Has_Manual_Indication then
3924+
Create (Output_File, Out_File, Tmp_Filename);
39053925
Has_Manual_Indication := True;
3906-
else
3907-
Contents := Contents & Line;
39083926
end if;
3927+
Put (Output_File, Str (Index .. Matches (0).First));
39093928

3910-
Contents := Contents & Ada.Characters.Latin_1.LF;
3911-
end;
3912-
end loop;
3929+
-- Replace the match with the call to the dump procedure
3930+
3931+
Put (Output_File, Dump_Procedure & "();");
3932+
Index := Matches (0).Last + 1;
3933+
end loop;
39133934

3914-
Ada.Text_IO.Close (File);
3935+
-- If we had a manual indication, and thus wrote a modified source
3936+
-- file, overwrite the original source file with it.
39153937

3916-
if Has_Manual_Indication then
3917-
-- Content now holds the text of the original file with calls to
3918-
-- the manual dump procedure where the indications and its extern
3919-
-- declaration were. Replace the original content of the file with
3920-
-- Content.
3938+
if Has_Manual_Indication then
3939+
declare
3940+
Tmp_File : constant Virtual_File := Create (+Tmp_Filename);
3941+
Success : Boolean;
3942+
begin
3943+
-- Flush the rest of the file contents
39213944

3922-
Ada.Text_IO.Open
3923-
(File => File,
3924-
Mode => Out_File,
3925-
Name => (+PP_Filename));
3945+
Ada.Text_IO.Put (Output_File, Str (Index .. Str'Last));
3946+
Ada.Text_IO.Close (Output_File);
39263947

3927-
Ada.Text_IO.Put_Line (File, (+Contents));
3948+
Free (Region);
3949+
Close (File);
39283950

3929-
Ada.Text_IO.Close (File);
3930-
end if;
3951+
-- Overwrite the original file with its newer version
3952+
3953+
Tmp_File.Rename
3954+
(Full_Name => Create (+(+PP_Filename)),
3955+
Success => Success);
3956+
if not Success then
3957+
Outputs.Fatal_Error
3958+
("Failed to replace manual dump indication for Source "
3959+
& (+Source.File.Full_Name));
3960+
end if;
3961+
end;
3962+
else
3963+
Free (Region);
3964+
Close (File);
3965+
end if;
3966+
end;
39313967
end;
39323968
end Replace_Manual_Dump_Indication;
39333969

0 commit comments

Comments
 (0)