99#include  " utils/FileSystemUtils.h" 
1010#include  " utils/KleeUtils.h" 
1111#include  " utils/LogUtils.h" 
12+ #include  " utils/stats/CSVReader.h" 
1213
1314#include  " loguru.h" 
1415
1516#include  < fstream> 
1617#include  < utility> 
18+ #include  < utils/stats/TestsGenerationStats.h> 
1719
1820using  namespace  tests ; 
1921
22+ namespace  {
23+     void  clearUnusedData (const  fs::path &kleeDir) {
24+         fs::remove (kleeDir / " assembly.ll" 
25+         fs::remove (kleeDir / " run.istats" 
26+     }
27+ 
28+     StatsUtils::KleeStats parseKleeStatsReport (const  std::string &kleeStatsReport) {
29+         std::stringstream ss (kleeStatsReport);
30+         StatsUtils::CSVTable parsedCSV = StatsUtils::readCSV (ss, ' ,' 
31+         std::map<std::string, std::chrono::milliseconds> timeStats;
32+         std::vector<std::string> keys = {" Time(s)" " TSolver(s)" " TResolve(s)" 
33+         std::vector<std::chrono::milliseconds> timeValues;
34+         for  (const  auto  &key: keys) {
35+             if  (!CollectionUtils::containsKey (parsedCSV, key)) {
36+                 LOG_S (WARNING) << StringUtils::stringFormat (" Key %s not found in klee-stats report" 
37+             }
38+             std::chrono::milliseconds totalTime ((int )(1000  * std::stof (parsedCSV[key].back ())));
39+             timeValues.emplace_back (totalTime);
40+         }
41+         return  StatsUtils::KleeStats (timeValues[0 ], timeValues[1 ], timeValues[2 ]);
42+     }
43+ 
44+     StatsUtils::KleeStats writeKleeStats (const  fs::path &kleeOut) {
45+         ShellExecTask::ExecutionParameters kleeStatsParams (" klee-stats" 
46+                                                            {" --utbot-config" string (),
47+                                                             " --table-format=readable-csv" 
48+         auto [out, status, _] = ShellExecTask::runShellCommandTask (kleeStatsParams);
49+         if  (status != 0 ) {
50+             LOG_S (ERROR) << " klee-stats call failed:" 
51+             LOG_S (ERROR) << out;
52+         } else  {
53+             LOG_S (DEBUG) << " klee-stats report:" 
54+             LOG_S (DEBUG) << ' \n ' 
55+         }
56+         return  parseKleeStatsReport (out);
57+     }
58+ }
59+ 
2060KleeRunner::KleeRunner (utbot::ProjectContext projectContext,
2161                       utbot::SettingsContext settingsContext,
2262                       fs::path serverBuildDir)
@@ -31,7 +71,8 @@ void KleeRunner::runKlee(const std::vector<tests::TestMethod> &testMethods,
3171                         const  std::shared_ptr<LineInfo> &lineInfo,
3272                         TestsWriter *testsWriter,
3373                         bool  isBatched,
34-                          bool  interactiveMode) {
74+                          bool  interactiveMode,
75+                          StatsUtils::TestsGenerationStatsFileMap &generationStats) {
3576    LOG_SCOPE_FUNCTION (DEBUG);
3677
3778    fs::path kleeOutDir = Paths::getKleeOutDir (projectTmpPath);
@@ -74,120 +115,97 @@ void KleeRunner::runKlee(const std::vector<tests::TestMethod> &testMethods,
74115            LOG_S (MAX) << logStream.str ();
75116        }
76117        if  (interactiveMode) {
77-             if  (!batch.empty ()) {
78-                 processBatchWithInteractive (batch, tests, ktests);
79-             }
118+             processBatchWithInteractive (batch, tests, ktests);
80119        } else  {
81-           for  (auto  const  &testMethod : batch) {
82-               MethodKtests ktestChunk;
83-               processBatchWithoutInteractive (ktestChunk, testMethod, tests);
84-               ExecUtils::throwIfCancelled ();
85-               ktests.push_back (ktestChunk);
86-           }
120+             processBatchWithoutInteractive (batch, tests, ktests);
87121        }
122+         auto  kleeStats = writeKleeStats (Paths::kleeOutDirForFilePath (projectContext, projectTmpPath, filePath));
88123        generator->parseKTestsToFinalCode (tests, methodNameToReturnTypeMap, ktests, lineInfo,
89124                                          settingsContext.verbose );
125+         generationStats.addFileStats (kleeStats, tests);
90126    };
91127
92128    testsWriter->writeTestsWithProgress (testsMap, " Running klee" testDirPath ,
93129                                        std::move (writeFunctor));
94130}
95131
96- namespace  {
97-     void  clearUnusedData (const  fs::path &kleeDir) {
98-         fs::remove (kleeDir / " assembly.ll" 
99-         fs::remove (kleeDir / " run.istats" 
100-     }
101- 
102-     void  writeKleeStats (const  fs::path &kleeOut) {
103-         ShellExecTask::ExecutionParameters kleeStatsParams (" klee-stats" 
104-                                                            { " --utbot-config" string () });
105-         auto  [out, status, _] = ShellExecTask::runShellCommandTask (kleeStatsParams);
106-         if  (status != 0 ) {
107-             LOG_S (ERROR) << " klee-stats call failed:" 
108-             LOG_S (ERROR) << out;
109-         } else  {
110-             LOG_S (DEBUG) << " klee-stats report:" 
111-             LOG_S (DEBUG) << ' \n ' 
112-         }
113-     }
114- }
115- 
116132static  void  processMethod (MethodKtests &ktestChunk,
117133                          tests::Tests &tests,
118134                          const  fs::path &kleeOut,
119135                          const  tests::TestMethod &method) {
120-     if  (fs::exists (kleeOut)) {
121-         clearUnusedData (kleeOut);
122-         bool  hasTimeout = false ;
123-         bool  hasError = false ;
124-         for  (auto  const  &entry : fs::directory_iterator (kleeOut)) {
125-             auto  const  &path = entry.path ();
126-             if  (Paths::isKtestJson (path)) {
127-                 if  (Paths::hasEarly (path)) {
128-                     hasTimeout = true ;
129-                 } else  if  (Paths::hasInternalError (path)) {
130-                     hasError = true ;
131-                 } else  {
132-                     std::unique_ptr<TestCase, decltype (&TestCase_free)> ktestData{
133-                         TC_fromFile (path.c_str ()), TestCase_free
134-                     };
135-                     if  (ktestData == nullptr ) {
136-                         LOG_S (WARNING) << " Unable to open .ktestjson file" 
137-                         continue ;
138-                     }
139-                     UTBotKTest::Status status = Paths::hasError (path) ? UTBotKTest::Status::FAILED
140-                                                                       : UTBotKTest::Status::SUCCESS;
141-                     std::vector<ConcretizedObject> kTestObjects (
142-                         ktestData->objects , ktestData->objects  + ktestData->n_objects );
143- 
144-                     std::vector<UTBotKTestObject> objects = CollectionUtils::transform (
145-                         kTestObjects , [](const  ConcretizedObject &kTestObject ) {
146-                             return  UTBotKTestObject{ kTestObject  };
147-                         });
148- 
149-                     ktestChunk[method].emplace_back (objects, status);
136+     if  (!fs::exists (kleeOut)) {
137+         return ;
138+     }
139+ 
140+     clearUnusedData (kleeOut);
141+     bool  hasTimeout = false ;
142+     bool  hasError = false ;
143+     for  (auto  const  &entry : fs::directory_iterator (kleeOut)) {
144+         auto  const  &path = entry.path ();
145+         if  (Paths::isKtestJson (path)) {
146+             if  (Paths::hasEarly (path)) {
147+                 hasTimeout = true ;
148+             } else  if  (Paths::hasInternalError (path)) {
149+                 hasError = true ;
150+             } else  {
151+                 std::unique_ptr<TestCase, decltype (&TestCase_free)> ktestData{
152+                     TC_fromFile (path.c_str ()), TestCase_free
153+                 };
154+                 if  (ktestData == nullptr ) {
155+                     LOG_S (WARNING) << " Unable to open .ktestjson file" 
156+                     continue ;
150157                }
151-             }
152-         }
153-         if  (hasTimeout) {
154-             std::string message = StringUtils::stringFormat (
155-                 " Some tests for function '%s' were skipped, as execution of function is " 
156-                 " out of timeout." 
157-                 method.methodName );
158-             tests.commentBlocks .emplace_back (std::move (message));
159-         }
160-         if  (hasError) {
161-             std::string message = StringUtils::stringFormat (
162-                 " Some tests for function '%s' were skipped, as execution of function leads " 
163-                 " KLEE to the internal error. See console log for more details." 
164-                 method.methodName );
165-             tests.commentBlocks .emplace_back (std::move (message));
166-         }
158+                 UTBotKTest::Status status = Paths::hasError (path) ? UTBotKTest::Status::FAILED
159+                                                                   : UTBotKTest::Status::SUCCESS;
160+                 std::vector<ConcretizedObject> kTestObjects (
161+                     ktestData->objects , ktestData->objects  + ktestData->n_objects );
167162
168-         writeKleeStats (kleeOut);
163+                 std::vector<UTBotKTestObject> objects = CollectionUtils::transform (
164+                     kTestObjects , [](const  ConcretizedObject &kTestObject ) {
165+                         return  UTBotKTestObject{ kTestObject  };
166+                     });
169167
170-         if  (!CollectionUtils::containsKey (ktestChunk, method) || ktestChunk.at (method).empty ()) {
171-             tests.commentBlocks .emplace_back (StringUtils::stringFormat (
172-                 " Tests for %s were not generated. Maybe the function is too complex." 
173-                 method.methodName ));
168+                 ktestChunk[method].emplace_back (objects, status);
169+             }
174170        }
175171    }
172+     if  (hasTimeout) {
173+         std::string message = StringUtils::stringFormat (
174+             " Some tests for function '%s' were skipped, as execution of function is " 
175+             " out of timeout." 
176+             method.methodName );
177+         tests.commentBlocks .emplace_back (std::move (message));
178+     }
179+     if  (hasError) {
180+         std::string message = StringUtils::stringFormat (
181+             " Some tests for function '%s' were skipped, as execution of function leads " 
182+             " KLEE to the internal error. See console log for more details." 
183+             method.methodName );
184+         tests.commentBlocks .emplace_back (std::move (message));
185+     }
186+ 
187+     if  (!CollectionUtils::containsKey (ktestChunk, method) || ktestChunk.at (method).empty ()) {
188+         tests.commentBlocks .emplace_back (StringUtils::stringFormat (
189+             " Tests for %s were not generated. Maybe the function is too complex." 
190+             method.methodName ));
191+     }
176192}
177193
178- void  KleeRunner::processBatchWithoutInteractive (MethodKtests &ktestChunk ,
179-                                                 const  TestMethod &testMethod ,
180-                                                 Tests &tests ) {
181-     if  (!tests.isFilePresentedInArtifact ) {
194+ void  KleeRunner::processBatchWithoutInteractive (const  std::vector<tests::TestMethod> &testMethods ,
195+                                                 tests::Tests &tests ,
196+                                                 std::vector<tests::MethodKtests> &ktests ) {
197+     if  (!tests.isFilePresentedInArtifact  || testMethods. empty () ) {
182198        return ;
183199    }
184-     if  (testMethod.sourceFilePath  != tests.sourceFilePath ) {
185-         std::string message = StringUtils::stringFormat (
186-                 " While generating tests for source file: %s tried to generate tests for method %s " 
187-                 " from another source file: %s. This can cause invalid generation.\n " 
188-                 tests.sourceFilePath , testMethod.methodName , testMethod.sourceFilePath );
189-         LOG_S (WARNING) << message;
190-     }
200+ 
201+     for  (const  auto  &testMethod : testMethods) {
202+         if  (testMethod.sourceFilePath  != tests.sourceFilePath ) {
203+             std::string message = StringUtils::stringFormat (
204+                     " While generating tests for source file: %s tried to generate tests for method %s " 
205+                     " from another source file: %s. This can cause invalid generation.\n " 
206+                     tests.sourceFilePath , testMethod.methodName , testMethod.sourceFilePath );
207+             LOG_S (WARNING) << message;
208+         }
191209
192210    std::string entryPoint = KleeUtils::entryPointFunction (tests, testMethod.methodName , true );
193211    std::string entryPointFlag = StringUtils::stringFormat (" --entry-point=%s" 
@@ -219,25 +237,28 @@ void KleeRunner::processBatchWithoutInteractive(MethodKtests &ktestChunk,
219237    argvData.emplace_back (" --sym-stdin" 
220238    argvData.emplace_back (std::to_string (types::Type::symStdinSize));
221239
222-     {
223-         std::vector<char  *> cargv, cenvp;
224-         std::vector<std::string> tmp;
225-         ExecUtils::toCArgumentsPtr (argvData, tmp, cargv, cenvp, false );
226-         LOG_S (DEBUG) << " Klee command :: " StringUtils::joinWith (argvData, "  " 
227-         MEASURE_FUNCTION_EXECUTION_TIME
240+          {
241+              std::vector<char  *> cargv, cenvp;
242+              std::vector<std::string> tmp;
243+              ExecUtils::toCArgumentsPtr (argvData, tmp, cargv, cenvp, false );
244+              LOG_S (DEBUG) << " Klee command :: " StringUtils::joinWith (argvData, "  " 
245+              MEASURE_FUNCTION_EXECUTION_TIME
228246
229-         RunKleeTask task (cargv.size (), cargv.data (), settingsContext.timeoutPerFunction );
230-         ExecUtils::ExecutionResult result __attribute__ ((unused)) = task.run ();
231-         ExecUtils::throwIfCancelled ();
247+              RunKleeTask task (cargv.size (), cargv.data (), settingsContext.timeoutPerFunction );
248+              ExecUtils::ExecutionResult result __attribute__ ((unused)) = task.run ();
249+              ExecUtils::throwIfCancelled ();
232250
233-         processMethod (ktestChunk, tests, kleeOut, testMethod);
251+             MethodKtests ktestChunk;
252+             processMethod (ktestChunk, tests, kleeOut, testMethod);
253+             ktests.push_back (ktestChunk);
254+         }
234255    }
235256}
236257
237258void  KleeRunner::processBatchWithInteractive (const  std::vector<tests::TestMethod> &testMethods,
238259                                             tests::Tests &tests,
239260                                             std::vector<tests::MethodKtests> &ktests) {
240-     if  (!tests.isFilePresentedInArtifact ) {
261+     if  (!tests.isFilePresentedInArtifact  || testMethods. empty () ) {
241262        return ;
242263    }
243264
0 commit comments