1 /*
2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2015 - Scilab Enterprises - Calixte DENIZET
4 *
5 * Copyright (C) 2012 - 2016 - Scilab Enterprises
6 *
7 * This file is hereby licensed under the terms of the GNU GPL v2.0,
8 * pursuant to article 5.3.4 of the CeCILL v.2.1.
9 * This file was originally licensed under the terms of the CeCILL v2.1,
10 * and continues to be available under such terms.
11 * For more information, see the COPYING file which you should have received
12 * along with this program.
13 *
14 */
15
16 #ifdef _MSC_VER
17 #pragma comment(lib, "../../bin/libxml2.lib")
18 #endif
19
20 #include <libxml/xmlreader.h>
21 #include <libxml/xpath.h>
22
23 #include <algorithm>
24
25 #include "CovHTMLCodePrinter.hxx"
26 #include "CoverModule.hxx"
27 #include "allexp.hxx"
28 #include "alltypes.hxx"
29 #include "allvar.hxx"
30 #include "cover_tools.hxx"
31 #include "coverage_instance.hxx"
32
33 extern "C"
34 {
35 #include "copyfile.h"
36 #include "createdirectory.h"
37 #include "expandPathVariable.h"
38 #include "findfiles.h"
39 #include "freeArrayOfString.h"
40 #include "isdir.h"
41 #include "os_string.h"
42 #include "sci_malloc.h"
43 }
44
45 #ifdef _MSC_VER
46 #define DEFAULT_FILESPEC L"*.*"
47 #else
48 #define DEFAULT_FILESPEC L"*"
49 #endif
50
51 namespace
52 {
expandPathVariable(const std::wstring & p)53 std::wstring expandPathVariable(const std::wstring& p )
54 {
55
56 wchar_t* localPath = expandPathVariableW((wchar_t*)p.c_str());
57 std::wstring l(localPath);
58 FREE(localPath);
59 return l;
60 }
61 }
62
63 namespace coverage
64 {
65
66 CoverModule* CoverModule::instance = nullptr;
67
CoverModule(const std::vector<std::pair<std::wstring,std::wstring>> & paths_mods)68 CoverModule::CoverModule(const std::vector<std::pair<std::wstring, std::wstring>>& paths_mods) : visitor(*this)
69 {
70 getMacros(paths_mods);
71 getBuiltins(paths_mods);
72 ast::CoverageInstance::setCoverage(this);
73 }
74
CoverModule(const std::vector<std::wstring> & moduleNames)75 CoverModule::CoverModule(const std::vector<std::wstring>& moduleNames) : CoverModule(getModule(moduleNames))
76 {
77 ast::CoverageInstance::setCoverage(this);
78 }
79
CoverModule()80 CoverModule::CoverModule() : visitor(*this)
81 {
82 ast::CoverageInstance::setCoverage(this);
83 }
84
~CoverModule()85 CoverModule::~CoverModule()
86 {
87 for (auto& counter : counters)
88 {
89 counter.getExp()->setCoverId(0);
90 }
91 for (auto& p : callCounters)
92 {
93 p.first->DecreaseRef();
94 //p.first->killMe();
95 }
96
97 ast::CoverageInstance::setCoverage(nullptr);
98 }
99
getModule(const std::vector<std::wstring> & moduleNames)100 const std::vector<std::pair<std::wstring, std::wstring>> CoverModule::getModule(const std::vector<std::wstring>& moduleNames)
101 {
102 if (moduleNames.size() == 0)
103 {
104 // nothing to do, return empty
105 return {};
106 }
107
108 if (std::all_of(moduleNames.begin(), moduleNames.end(),
109 [](const std::wstring & p)
110 {
111 return isdirW(expandPathVariable(p).c_str()) == TRUE;
112 }))
113 {
114 // all the provided modulesNames are directories, name them and remove "\macros"
115 std::vector<std::pair<std::wstring, std::wstring>> paths;
116
117 // remove any potential "\macros" suffix
118 for (const auto& name : moduleNames)
119 {
120 const std::wstring remove(std::wstring(DIR_SEPARATORW) + std::wstring(L"macros"));
121 std::wstring localName;
122 size_t n = name.rfind(remove);
123 if (n > 0)
124 {
125 localName = name.substr(0, n) + name.substr(n + remove.length());
126 }
127 else
128 {
129 localName = name;
130 }
131 n = localName.rfind(DIR_SEPARATORW);
132 if (n > 0)
133 {
134 localName = localName.substr(n + 1, localName.length() - n);
135 }
136
137 paths.emplace_back(expandPathVariable(name), std::move(localName));
138 }
139 return paths;
140 }
141
142 // Look for the names as Scilab-related libraries
143 const std::wstring _path = std::wstring(L"SCI") + DIR_SEPARATORW + L"modules" + DIR_SEPARATORW;
144 const std::wstring path(expandPathVariable(_path));
145
146 if (moduleNames.size() == 1 && moduleNames.back() == L"all")
147 {
148 // "all" keyword, parse all the Scilab library files
149 int size = -1;
150 wchar_t** files = findfilesW(path.c_str(), DEFAULT_FILESPEC, &size, FALSE);
151 if (size > 0 && files)
152 {
153 std::vector<std::pair<std::wstring, std::wstring>> paths;
154 for (int i = 0; i < size; ++i)
155 {
156 const std::wstring modulePath = path + files[i];
157 if (isdirW(modulePath.c_str()))
158 {
159 paths.emplace_back(modulePath, files[i]);
160 }
161 }
162 freeArrayOfWideString(files, size);
163 return paths;
164 }
165
166 return {};
167 }
168 else
169 {
170 // a list of Scilab libraries
171 std::vector<std::pair<std::wstring, std::wstring>> paths;
172 for (const auto& name : moduleNames)
173 {
174 paths.emplace_back(path + name, name);
175 }
176 return paths;
177 }
178 }
179
getMacros(const std::vector<std::pair<std::wstring,std::wstring>> & paths_mods)180 void CoverModule::getMacros(const std::vector<std::pair<std::wstring, std::wstring>>& paths_mods)
181 {
182 for (const auto& p : paths_mods)
183 {
184 getMacrosFromDir(p.first, p.second);
185 }
186 }
187
getMacrosFromDir(const std::wstring & path,const std::wstring & module)188 void CoverModule::getMacrosFromDir(const std::wstring& path, const std::wstring& module)
189 {
190 const std::wstring resolvedPath(expandPathVariable(path));
191
192 std::wstring _path = resolvedPath + DIR_SEPARATORW + L"lib";
193 getMacros(_path, module);
194
195 int size = -1;
196 _path = path + DIR_SEPARATORW;
197
198 wchar_t** files = findfilesW(_path.c_str(), DEFAULT_FILESPEC, &size, FALSE);
199 if (size > 0 && files)
200 {
201 for (int i = 0; i < size; ++i)
202 {
203 std::wstring _file = _path + files[i];
204 if (isdirW(_file.c_str()))
205 {
206 getMacrosFromDir(_file, module);
207 }
208 }
209 freeArrayOfWideString(files, size);
210 }
211 }
212
getMacros(const std::wstring & path,const std::wstring & module)213 void CoverModule::getMacros(const std::wstring& path, const std::wstring& module)
214 {
215 std::unordered_set<std::wstring> _macros;
216 std::wstring libPath(expandPathVariable(path));
217
218 char* libFile = wide_string_to_UTF8(libPath.c_str());
219
220 if (getStringFromXPath(libFile, "//scilablib/macro/@name", _macros))
221 {
222 for (const auto& name : _macros)
223 {
224 types::InternalType* pIT = symbol::Context::getInstance()->get(symbol::Symbol(name));
225 if (pIT && pIT->isMacroFile())
226 {
227 types::MacroFile* pMF = static_cast<types::MacroFile*>(pIT);
228 if (types::Macro* macro = pMF->getMacro())
229 {
230 const std::wstring& file = pMF->getPath();
231 std::size_t pos = file.find_last_of(L'.');
232 if (pos != std::string::npos)
233 {
234 instrumentMacro(module, file.substr(0, pos) + L".sci", macro);
235 }
236 else
237 {
238 instrumentMacro(module, file, macro);
239 }
240 }
241 }
242 }
243 }
244 FREE(libFile);
245 }
246
getBuiltins(const std::vector<std::pair<std::wstring,std::wstring>> & paths_mods)247 void CoverModule::getBuiltins(const std::vector<std::pair<std::wstring, std::wstring>>& paths_mods)
248 {
249 for (const auto& p : paths_mods)
250 {
251 std::list<types::Callable*> lst;
252 if (symbol::Context::getInstance()->getFunctionList(lst, p.second))
253 {
254 for (auto pCall : lst)
255 {
256 if (pCall->isFunction())
257 {
258 pCall->IncreaseRef();
259 functions.emplace(p.second, static_cast<types::Function*>(pCall));
260 callCounters.emplace(pCall, CallCounter());
261 }
262 }
263 }
264 }
265 }
266
instrumentMacro(const std::wstring & module,const std::wstring & path,types::Macro * macro)267 void CoverModule::instrumentMacro(const std::wstring& module, const std::wstring& path, types::Macro* macro)
268 {
269 const std::map<symbol::Variable*, types::Macro*>& submacros = macro->getSubMacros();
270 for (const auto& p : submacros)
271 {
272 instrumentSingleMacro(module, path, p.second, true);
273 }
274
275 instrumentSingleMacro(module, path, macro, true);
276 }
277
instrumentSingleMacro(const std::wstring & module,const std::wstring & path,types::Macro * macro,bool instrumentInners)278 void CoverModule::instrumentSingleMacro(const std::wstring& module, const std::wstring& path, types::Macro* macro, bool instrumentInners)
279 {
280 macro->IncreaseRef();
281 visitor.setMacro(macro);
282 macro->getBody()->accept(visitor);
283 macros.emplace(macro, CoverMacroInfo(module, path, visitor.getInstrsCount(), visitor.getBranchesCount(), visitor.getPathsCount()));
284 callCounters.emplace(static_cast<types::Callable*>(macro), CallCounter());
285 functions.emplace(module, static_cast<types::Callable*>(macro));
286
287 if (instrumentInners)
288 {
289 // We make a copy since the call to instrumentSingleMacro will modify visitor.getInnerMacros()
290 const std::vector<types::Macro*> inners = visitor.getInnerMacros();
291 for (auto inner : inners)
292 {
293 instrumentSingleMacro(module, path, inner, true);
294 }
295 }
296 }
297
add(types::Macro * macro,ast::Exp * e)298 void CoverModule::add(types::Macro* macro, ast::Exp* e)
299 {
300 if (e)
301 {
302 counters.emplace_back(macro, e);
303 const std::size_t id = counters.size() + 1;
304 e->setCoverId(id);
305 }
306 }
307
invoke(types::Callable * f)308 void CoverModule::invoke(types::Callable* f)
309 {
310 if (f->isMacroFile())
311 {
312 f = static_cast<types::MacroFile*>(f)->getMacro();
313 }
314 auto i = callCounters.find(f);
315 if (i != callCounters.end())
316 {
317 i->second.inc();
318 }
319 }
320
invoke(const uint64_t id)321 void CoverModule::invoke(const uint64_t id)
322 {
323 counters[id - 2].inc();
324 }
325
getStringFromXPath(char * filePath,const char * xpquery,std::unordered_set<std::wstring> & set)326 bool CoverModule::getStringFromXPath(char* filePath, const char* xpquery, std::unordered_set<std::wstring>& set)
327 {
328 xmlDocPtr doc = xmlReadFile(filePath, "utf-8", XML_PARSE_NOWARNING);
329 if (!doc)
330 {
331 return false;
332 }
333
334 if (!doc->encoding || stricmp((const char*)doc->encoding, "utf-8") != 0)
335 {
336 xmlFreeDoc(doc);
337 return false;
338 }
339
340 xmlXPathContextPtr ctxt = xmlXPathNewContext(doc);
341 xmlXPathObjectPtr xp = xmlXPathEval((const xmlChar*)xpquery, ctxt);
342 xmlNodeSetPtr nodeSet = xp->nodesetval;
343
344 if (nodeSet)
345 {
346 for (unsigned int i = 0; i < nodeSet->nodeNr; ++i)
347 {
348 const char* content = (const char*)xmlNodeGetContent(nodeSet->nodeTab[i]);
349 wchar_t* ws = to_wide_string(content);
350 xmlFree(const_cast<char*>(content));
351 set.emplace(ws);
352 FREE(ws);
353 }
354 }
355
356 xmlXPathFreeObject(xp);
357 xmlXPathFreeContext(ctxt);
358 xmlFreeDoc(doc);
359
360 return nodeSet;
361 }
362
lower_bound(const std::vector<Counter>::const_iterator & first,const std::vector<Counter>::const_iterator & last,types::Macro * value)363 std::vector<Counter>::const_iterator CoverModule::lower_bound(const std::vector<Counter>::const_iterator& first, const std::vector<Counter>::const_iterator& last, types::Macro* value)
364 {
365 // binary search could not be performed as the counters are not sorted, linear lookup is OK
366 for (auto it = first; it < last; it++)
367 {
368 if (it->getMacro() == value)
369 {
370 return it;
371 }
372 }
373
374 return last;
375 }
376
upper_bound(const std::vector<Counter>::const_iterator & first,const std::vector<Counter>::const_iterator & last,types::Macro * value)377 std::vector<Counter>::const_iterator CoverModule::upper_bound(const std::vector<Counter>::const_iterator& first, const std::vector<Counter>::const_iterator& last, types::Macro* value)
378 {
379 // binary search could not be performed as the counters are not sorted, linear lookup is OK
380 for (auto it = lower_bound(first, last, value); it < last; it++)
381 {
382 if (it->getMacro() != value)
383 {
384 return it;
385 }
386 }
387
388 return last;
389 }
390
collect()391 void CoverModule::collect()
392 {
393 if (!counters.empty())
394 {
395 std::vector<Counter>::const_iterator first = counters.begin();
396 std::vector<Counter>::const_iterator last = upper_bound(first, counters.end(), first->getMacro());
397 collect(first, last);
398 while (last != counters.end())
399 {
400 first = last;
401 last = upper_bound(first, counters.end(), first->getMacro());
402 collect(first, last);
403 }
404
405 for (const auto& p : functions)
406 {
407 if (p.second->isFunction())
408 {
409 allCounters[p.first][p.second->getName()] = std::pair<bool, uint64_t>(false, callCounters[p.second].get());
410 }
411 }
412 }
413 }
414
collect(const std::vector<Counter>::const_iterator & first,const std::vector<Counter>::const_iterator & last)415 void CoverModule::collect(const std::vector<Counter>::const_iterator& first, const std::vector<Counter>::const_iterator& last)
416 {
417 types::Macro* current = first->getMacro();
418
419 const std::wstring& name = current->getName();
420 CoverMacroInfo& info = macros.find(current)->second;
421 const uint64_t counter = callCounters[current].get();
422 auto& map1 = results[info.macroModule];
423 auto& map2 = map1[info.macroFilePath];
424 MacroLoc ml(name, current->getBody()->getLocation());
425 auto j = map2.find(ml);
426 if (j == map2.end())
427 {
428 CoverResult& result = map2.emplace(ml, CoverResult(name, info)).first->second;
429 result.setCounter(counter);
430
431 result.populate(first, last);
432 }
433 allCounters[info.macroModule][name] = std::pair<bool, uint64_t>(true, counter);
434 }
435
print()436 void CoverModule::print()
437 {
438 /*std::wcerr << L"Builtin calls" << std::endl << tools::getUnderline(L"Builtin calls") << std::endl;
439 for (const auto & p : callCounters)
440 {
441 if (p.second.get())
442 {
443 std::wcerr << p.first->getName() << L" called " << p.second.get() << L" time" << tools::getPlural(p.second.get()) << L"." << std::endl;
444 }
445 }
446
447 std::wcerr << std::endl;*/
448
449 /* for (const auto & counter : counters)
450 {
451 if (counter.get())
452 {
453
454 const std::wstring & name = counter.getMacro()->getName();
455 std::wcerr << L"Macro " << name << L" visited at " << counter.getExp()->getLocation() << std::endl;
456
457
458 //<< L"Seq not visited:" << std::endl;
459
460 }
461 }
462 */
463 //collect();
464 }
465
toHTML(const std::wstring & outputDir)466 void CoverModule::toHTML(const std::wstring& outputDir)
467 {
468 bool nomodules = false;
469 wchar_t* _outputDir = expandPathVariableW((wchar_t*)outputDir.c_str());
470 createdirectoryW(_outputDir);
471
472 if (results.size() == 1 && results.begin()->first == L"" && results.begin()->second.size() == 1 && results.begin()->second.begin()->first == L"")
473 {
474 for (const auto& p : macros)
475 {
476 writeMacroHTMLReport(p.first, results.begin()->second.begin()->second, _outputDir);
477 }
478 nomodules = true;
479 }
480
481 if (!nomodules)
482 {
483 std::map<std::wstring, std::pair<uint64_t, double>> modulesStats;
484
485 // We make all the reports for the macros
486 for (auto& p1 : results)
487 {
488 const std::wstring& moduleName = p1.first;
489 const std::wstring __outputDir = std::wstring(_outputDir) + DIR_SEPARATORW + moduleName;
490 createdirectoryW((wchar_t*)__outputDir.c_str());
491 uint64_t totalCalls = 0;
492 uint64_t totalInstrs = 0;
493 uint64_t uncoveredInstrs = 0;
494
495 for (auto& p2 : p1.second)
496 {
497 writeMacroHTMLReport(p2.first, moduleName, p2.second, __outputDir);
498 }
499
500 // Now we make the module's reports
501 std::wostringstream out;
502
503 out << L"<html lang=\'en\'>\n"
504 << L"<meta charset=\'UTF-8\'>\n"
505 << L"<head>\n"
506 << L"<link rel=\'icon\' href=\'../favicon.ico\'/>\n"
507 << L"<title>Scilab | Module " << moduleName << L" | Scilab\'s code coverage" << L"</title>\n"
508 << L"<style type=\'text/css\' media=\'all\'>\n"
509 << L"@import url(\'../mod_style.css\');\n"
510 << L"</style>\n"
511 << L"<script type=\'text/javascript\' src=\'../module.js\'></script>"
512 << L"</head>\n"
513 << L"<body>\n";
514
515 bool altern = false;
516 unsigned int tableid = 0;
517 unsigned int buttonid = 0;
518
519 out << L"<h2 class=\'title\'>Macros calls</h2>\n"
520 << L"<table class='module'>\n"
521 << L"<tr><td><div class=\'macros_stats\'>\n"
522 << L"<div class=\'macros_cell\'><table id=\'table" << tableid << L"\'>\n"
523 << L"<tr class=\'col_name\'><td>Name " << CovHTMLCodePrinter::getOrderButton(tableid, buttonid, 0, false) << L"</td><td>File</td><td>Calls " << CovHTMLCodePrinter::getOrderButton(tableid, buttonid + 1, 2, true) << L"</td><td>Covered " << CovHTMLCodePrinter::getOrderButton(tableid, buttonid + 2, 3, false) << L"</td></tr>\n";
524
525 for (const auto& t : getOrderedResults(moduleName))
526 {
527 const CoverResult& res = *std::get<0>(t);
528 const std::wstring& macroFilename = *std::get<1>(t);
529 const std::wstring& macroName = *std::get<2>(t);
530
531 totalInstrs += res.getInfo().instrsCount;
532 uncoveredInstrs += res.getUncInstrs();
533
534 std::size_t pos = macroFilename.find_last_of(L'.');
535 std::wstring filename = macroFilename.substr(0, pos);
536 pos = filename.find_last_of(L"\\/");
537 if (pos != std::string::npos)
538 {
539 filename = filename.substr(pos + 1);
540 }
541 std::wstring filepath = encodeFilename(filename);
542
543 const std::wstring countercls = res.counter == 0 ? L"null_stats" : L"stats";
544 const std::wstring trcls = altern ? L"altern1" : L"altern2";
545 out << L"<tr class=\'" << trcls << L"\'><td><span class=\'cmp\'>" << macroName << L"</span></td>"
546 << L"<td><a class=\'filepath' href=\'" << filepath << L".html\'>" << moduleName << L"/macros/" << filename << L".sci</a></td>"
547 << L"<td class=\'" << countercls << L"\'><span class=\'cmp\'>" << res.counter << L"</span></td>"
548 << L"<td><span class=\'inline_percent\'><span class=\'cmp\'>" << res.getCovInstrsPercent() << L"</span> %</span>";
549 CovHTMLCodePrinter::getDivPercent(out, res.getCovInstrsPercent());
550 out << L"</td></tr>\n";
551
552 altern = !altern;
553 }
554
555 ++tableid;
556 buttonid += 3;
557
558 out << L"</table></div></div></td></tr>\n"
559 << L"</table>\n";
560
561 // Now we print the number of calls for builtins.
562
563 altern = false;
564
565 out << L"<h2 class=\'title\'>Builtins calls</h2>\n"
566 << L"<table class='module'>\n"
567 << L"<tr><td><div class='modulePath'>" << moduleName << L"</div></td></tr>\n"
568 << L"<tr><td><div class=\'builtins_stats\'>\n"
569 << L"<div class=\'builtins_cell\'><table id=\'table" << tableid << L"\'>\n"
570 << L"<tr class=\'col_name\'><td>Name " << CovHTMLCodePrinter::getOrderButton(tableid, buttonid, 0, false) << L"</td><td class=\'col_name\'>Calls " << CovHTMLCodePrinter::getOrderButton(tableid, buttonid + 1, 1, true) << L"</td></tr>\n";
571
572 for (const auto& p : getBuiltinStats(moduleName))
573 {
574 const std::wstring countercls = p.second == 0 ? L"null_stats" : L"stats";
575 const std::wstring trcls = altern ? L"altern1" : L"altern2";
576 out << L"<tr class=\'" << trcls << L"\'><td><span class=\'cmp\'>" << *p.first << L"</span></td><td class=\'" << countercls << L"\'><span class=\'cmp\'>" << p.second << L"</span></td></tr>\n";
577 altern = !altern;
578 totalCalls += p.second;
579 }
580
581 out << L"</table></div>\n"
582 << L"</div></td></tr>\n"
583 << L"</table>\n"
584 << L"</body>\n"
585 << L"</html>\n";
586
587 out.flush();
588 writeFile(out, __outputDir, moduleName + L".html");
589
590 const double percent = totalInstrs ? std::round(100. * (1. - (double)uncoveredInstrs / (double)totalInstrs)) : 100.;
591 modulesStats.emplace(moduleName, std::pair<uint64_t, double>(totalCalls, percent));
592 }
593
594 // Now we must output the results for all the modules
595 const std::wstring __outputDir(_outputDir);
596 std::wostringstream out;
597
598 out << L"<html lang=\'en\'>\n"
599 << L"<meta charset=\'UTF-8\'>\n"
600 << L"<head>\n"
601 << L"<link rel=\'icon\' href=\'favicon.ico\'/>\n"
602 << L"<title>Scilab\'s code coverage" << L"</title>\n"
603 << L"<style type=\'text/css\' media=\'all\'>\n"
604 << L"@import url(\'mod_style.css\');\n"
605 << L"</style>\n"
606 << L"<script type=\'text/javascript\' src=\'module.js\'></script>"
607 << L"</head>\n"
608 << L"<body>\n";
609
610 bool altern = false;
611 unsigned int tableid = 0;
612 unsigned int buttonid = 0;
613
614 out << L"<h2 class=\'title\'>Modules results</h2>\n"
615 << L"<table class='module'>\n"
616 << L"<tr><td><div class=\'macros_stats\'>\n"
617 << L"<div class=\'macros_cell\'><table id=\'table" << tableid << L"\'>\n"
618 << L"<tr class=\'col_name\'><td>Name " << CovHTMLCodePrinter::getOrderButton(tableid, buttonid, 0, false) << L"</td><td>Builtin calls " << CovHTMLCodePrinter::getOrderButton(tableid, buttonid + 1, 1, true) << L"</td><td>Covered " << CovHTMLCodePrinter::getOrderButton(tableid, buttonid + 2, 2, false) << L"</td></tr>\n";
619
620 for (const auto& p : modulesStats)
621 {
622 const std::wstring& moduleName = p.first;
623 const uint64_t builtinCalls = p.second.first;
624 const double percent = p.second.second;
625 const std::wstring countercls = builtinCalls ? L"stats" : L"null_stats";
626 const std::wstring trcls = altern ? L"altern1" : L"altern2";
627 out << L"<tr class=\'" << trcls << L"\'>"
628 << L"<td><span class=\'cmp\'><a class=\'filepath' href=\'" << moduleName << L"/" << moduleName << L".html\'>" << moduleName << L"</a></span></td>"
629 << L"<td class=\'" << countercls << L"\'><span class=\'cmp\'>" << builtinCalls << L"</span></td>"
630 << L"<td><span class=\'inline_percent\'><span class=\'cmp\'>" << percent << L"</span> %</span>";
631
632 CovHTMLCodePrinter::getDivPercent(out, percent);
633 out << L"</tr>\n";
634 altern = !altern;
635 }
636
637 out << L"</table></div>\n"
638 << L"</div></td></tr>\n"
639 << L"</table>\n"
640 << L"</body>\n"
641 << L"</html>\n";
642
643 out.flush();
644 writeFile(out, __outputDir, L"report.html");
645 }
646
647 FREE(_outputDir);
648 copyDataFiles(outputDir);
649 }
650
copyDataFiles(const std::wstring & outputDir)651 void CoverModule::copyDataFiles(const std::wstring& outputDir)
652 {
653 const std::wstring _outputDir = outputDir + DIR_SEPARATORW;
654 const std::wstring _inputDir = std::wstring(L"SCI") + DIR_SEPARATORW + L"modules" + DIR_SEPARATORW + L"coverage" + DIR_SEPARATORW + L"data";
655 copyFile(_inputDir, _outputDir, L"scilab_code.css");
656 copyFile(_inputDir, _outputDir, L"src_style.css");
657 copyFile(_inputDir, _outputDir, L"mod_style.css");
658 copyFile(_inputDir, _outputDir, L"favicon.ico");
659 copyFile(_inputDir, _outputDir, L"module.js");
660 }
661
copyFile(const std::wstring & inDir,const std::wstring & outDir,const std::wstring & filename)662 void CoverModule::copyFile(const std::wstring& inDir, const std::wstring& outDir, const std::wstring& filename)
663 {
664 const std::wstring in = inDir + DIR_SEPARATORW + filename;
665 const std::wstring out = outDir + DIR_SEPARATORW + filename;
666 wchar_t* _input = expandPathVariableW((wchar_t*)in.c_str());
667 wchar_t* _output = expandPathVariableW((wchar_t*)out.c_str());
668 CopyFileFunction(_output, _input);
669 FREE(_input);
670 FREE(_output);
671 }
672
writeFile(const std::wostringstream & out,const std::wstring & outputDir,const std::wstring & filename)673 void CoverModule::writeFile(const std::wostringstream& out, const std::wstring& outputDir, const std::wstring& filename)
674 {
675 const std::string code = scilab::UTF8::toUTF8(out.str().c_str());
676 const std::string _filename = scilab::UTF8::toUTF8(outputDir + DIR_SEPARATORW + filename);
677 std::fstream file(_filename, std::ios::out | std::ios::trunc);
678 file.write(code.c_str(), code.size());
679 file.close();
680 }
681
getOrderedResults(const std::wstring & moduleName) const682 std::set<CoverModule::__Res1, CoverModule::__Compare1> CoverModule::getOrderedResults(const std::wstring& moduleName) const
683 {
684 std::set<__Res1, __Compare1> set;
685 auto i = results.find(moduleName);
686 if (i != results.end())
687 {
688 for (const auto& p : i->second)
689 {
690 const std::wstring& macroFilename = p.first;
691 for (const auto& pp : p.second)
692 {
693 const std::wstring& macroName = pp.first.name;
694 const Location& macroLoc = pp.first.loc;
695 const CoverResult& res = pp.second;
696 set.emplace(__Res1(&res, ¯oFilename, ¯oName, ¯oLoc));
697 }
698 }
699 }
700
701 return set;
702 }
703
getBuiltinStats(const std::wstring & moduleName) const704 std::set<CoverModule::__Res2, CoverModule::__Compare2> CoverModule::getBuiltinStats(const std::wstring& moduleName) const
705 {
706 std::set<__Res2, __Compare2> set;
707 auto i = allCounters.find(moduleName);
708 if (i != allCounters.end())
709 {
710 for (const auto& p : i->second)
711 {
712 if (!p.second.first)
713 {
714 set.emplace(__Res2(&p.first, p.second.second));
715 }
716 }
717 }
718
719 return set;
720 }
721
getFunctionCalls(const std::wstring & moduleName,const bool builtin) const722 std::vector<std::pair<types::Callable*, uint64_t>> CoverModule::getFunctionCalls(const std::wstring& moduleName, const bool builtin) const
723 {
724 struct _Res
725 {
726 types::Callable* const fptr;
727 const uint64_t counter;
728
729 _Res(types::Callable* const _fptr, const uint64_t _counter) : fptr(_fptr), counter(_counter) {}
730 inline bool operator<(const _Res& res) const
731 {
732 return (counter < res.counter) || (counter == res.counter && fptr->getName() < res.fptr->getName());
733 }
734 };
735 std::set<_Res> set;
736 std::vector<std::pair<types::Callable*, uint64_t>> calls;
737 auto range = functions.equal_range(moduleName);
738 for (auto fptr = range.first; fptr != range.second; ++fptr)
739 {
740 if ((builtin && fptr->second->isFunction()) || (!builtin && fptr->second->isMacro()))
741 {
742 auto i = callCounters.find(static_cast<types::Callable*>(fptr->second));
743 if (i != callCounters.end())
744 {
745 set.emplace(fptr->second, i->second.get());
746 }
747 }
748 }
749
750 calls.reserve(set.size());
751 for (const auto r : set)
752 {
753 calls.emplace_back(r.fptr, r.counter);
754 }
755
756 return calls;
757 }
758
getTree(const std::wstring & path)759 ast::Exp* CoverModule::getTree(const std::wstring& path)
760 {
761 if (!path.empty())
762 {
763 std::ifstream src(scilab::UTF8::toUTF8(path), std::ios::in | std::ios::binary | std::ios::ate);
764 if (src.is_open())
765 {
766 src.seekg(0, src.end);
767 int len = src.tellg();
768 src.seekg(0, src.beg);
769 char* buffer = new char[len + 1];
770 buffer[len] = '\0';
771 src.read(buffer, len);
772 src.close();
773
774 wchar_t* _wstr = to_wide_string(buffer);
775 delete[] buffer;
776 Parser parser;
777 parser.parse(_wstr);
778 FREE(_wstr);
779
780 return parser.getTree();
781 }
782 }
783 return nullptr;
784 }
785
getName(const std::wstring & path)786 const std::wstring CoverModule::getName(const std::wstring& path)
787 {
788 std::size_t pos = path.find_last_of(L'.');
789 std::wstring name = path.substr(0, pos);
790 pos = name.find_last_of(L"\\/");
791 if (pos != std::string::npos)
792 {
793 name = name.substr(pos + 1);
794 }
795 return name;
796 }
797
writeMacroHTMLReport(types::Macro * macro,std::map<MacroLoc,CoverResult> & results,const std::wstring & outputDir)798 void CoverModule::writeMacroHTMLReport(types::Macro* macro, std::map<MacroLoc, CoverResult>& results, const std::wstring& outputDir)
799 {
800 std::list<symbol::Variable*>* in = macro->getInputs();
801 std::list<symbol::Variable*>* out = macro->getOutputs();
802 ast::SeqExp& body = *macro->getBody()->clone();
803
804 ast::exps_t& _in = *new ast::exps_t();
805 ast::exps_t& _out = *new ast::exps_t();
806 for (const auto i : *in)
807 {
808 _in.emplace_back(new ast::SimpleVar(Location(), i->getSymbol()));
809 }
810 for (const auto o : *out)
811 {
812 _out.emplace_back(new ast::SimpleVar(Location(), o->getSymbol()));
813 }
814 ast::FunctionDec* fdec = new ast::FunctionDec(Location(), symbol::Symbol(macro->getName()), *new ast::ArrayListVar(Location(), _in), *new ast::ArrayListVar(Location(), _out), body);
815
816 writeMacroHTMLReport(fdec, macro->getName() + L".html", L"", L"", results, outputDir);
817 }
818
writeMacroHTMLReport(ast::Exp * tree,const std::wstring & filename,const std::wstring & path,const std::wstring & moduleName,std::map<MacroLoc,CoverResult> & results,const std::wstring & outputDir)819 void CoverModule::writeMacroHTMLReport(ast::Exp* tree, const std::wstring& filename, const std::wstring& path, const std::wstring& moduleName, std::map<MacroLoc, CoverResult>& results, const std::wstring& outputDir)
820 {
821 std::wostringstream out;
822 std::wstring mod, prev;
823 if (!moduleName.empty())
824 {
825 mod = L" | Module " + moduleName;
826 prev = L"../";
827 }
828 std::wstring pa;
829 if (!path.empty())
830 {
831 pa = L" | " + path;
832 }
833
834 out << L"<html lang=\'en\'>\n"
835 << L"<meta charset=\'UTF-8\'>\n"
836 << L"<head>\n"
837 << L"<link rel=\'icon\' href=\'../favicon.ico\'/>\n"
838 << L"<title>Scilab" << mod << pa << L" | Scilab\'s code coverage" << L"</title>\n"
839 << L"<style type=\'text/css\' media=\'all\'>\n"
840 << L"@import url(\'" << prev << "scilab_code.css\');\n"
841 << L"@import url(\'" << prev << "src_style.css\');\n"
842 << L"</style>\n"
843 << L"<script>\n"
844 << L"function show(did,fid) {\n"
845 << L" x = document.getElementById(did).style;\n"
846 << L" y = document.getElementById(fid);\n"
847 << L" x.visibility = 'visible';\n"
848 << L" x.display = 'block';\n"
849 << L" x.height = 'auto';\n"
850 << L" x.left = y.offsetLeft + 'px';\n"
851 << L" x.top = y.offsetTop + y.offsetHeight + 'px';\n"
852 << L"}\n"
853 << L"function hide(did) {\n"
854 << L" document.getElementById(did).style.visibility = \'hidden\';\n"
855 << L"}\n"
856 << L"</script>\n"
857 << L"</head>\n"
858 << L"<body>\n"
859 << L"<h2 class=\'title\'>Coverage and Profiling report</h2>\n"
860 << L"<table class='sourceFile'>\n";
861
862 if (!path.empty())
863 {
864 out << L"<tr><td><div class='sourcePath'>" << path << L"</div></td></tr>\n";
865 }
866 if (!results.empty())
867 {
868 for (const auto& p : results)
869 {
870 out << L"<tr><td><div class=\'allmacstats\'>\n";
871 CovHTMLCodePrinter::getFunctionStats(out, p.first, p.second);
872 out << L"</div></td></tr\n";
873 }
874 }
875
876 out << L"<tr><td><div class=\'source\'>\n"
877 << L"<table>\n";
878
879 CovHTMLCodePrinter printer(out, results);
880 CodePrinterVisitor visitor(printer);
881 tree->accept(visitor);
882 printer.close();
883 delete tree;
884
885 out << L"</table>\n"
886 << L"</div></td></tr>\n"
887 << L"</table>\n"
888 << L"</body>\n"
889 << L"</html>\n";
890
891 out.flush();
892 writeFile(out, outputDir, filename);
893 }
894
writeMacroHTMLReport(const std::wstring & path,const std::wstring & moduleName,std::map<MacroLoc,CoverResult> & results,const std::wstring & outputDir)895 bool CoverModule::writeMacroHTMLReport(const std::wstring& path, const std::wstring& moduleName, std::map<MacroLoc, CoverResult>& results, const std::wstring& outputDir)
896 {
897 if (ast::Exp* tree = getTree(path))
898 {
899 writeMacroHTMLReport(tree, getName(path) + L".html", path, moduleName, results, outputDir);
900 return true;
901 }
902
903 return false;
904 }
905
encodeFilename(const std::wstring & name)906 std::wstring CoverModule::encodeFilename(const std::wstring& name)
907 {
908 std::wostringstream wos;
909 for (const auto c : name)
910 {
911 URLEncoder::replace(wos, c);
912 }
913 return wos.str();
914 }
915
merge(const std::vector<std::wstring> & paths,const std::wstring & out)916 void CoverModule::merge(const std::vector<std::wstring>& paths, const std::wstring& out)
917 {
918 CoverModule cm;
919 for (const auto path : paths)
920 {
921 cm.load(path);
922 }
923 cm.save(out);
924 }
925
toHTML(const std::wstring & inBin,const std::wstring & outDir)926 void CoverModule::toHTML(const std::wstring& inBin, const std::wstring& outDir)
927 {
928 CoverModule cm;
929 cm.load(inBin);
930 cm.toHTML(outDir);
931 }
932
save(const std::wstring & path) const933 void CoverModule::save(const std::wstring& path) const
934 {
935 if (!path.empty())
936 {
937 std::fstream out(scilab::UTF8::toUTF8(path), std::ios::out | std::ios::binary);
938 if (out.is_open())
939 {
940 toBin(out);
941 out.close();
942 }
943 }
944 }
945
load(const std::wstring & path)946 void CoverModule::load(const std::wstring& path)
947 {
948 if (!path.empty())
949 {
950 std::fstream in(scilab::UTF8::toUTF8(path), std::ios::in | std::ios::binary);
951 if (in.is_open())
952 {
953 fromBin(*this, in);
954 in.close();
955 }
956 }
957 }
958
toBin(std::fstream & out) const959 void CoverModule::toBin(std::fstream& out) const
960 {
961 // Save results
962 CoverModule::write(out, (uint64_t)results.size());
963 for (const auto& p : results)
964 {
965 CoverModule::write(out, p.first);
966 CoverModule::write(out, (uint64_t)p.second.size());
967 for (const auto& pp : p.second)
968 {
969 CoverModule::write(out, pp.first);
970 CoverModule::write(out, (uint64_t)pp.second.size());
971 for (const auto& ppp : pp.second)
972 {
973 CoverModule::write(out, ppp.first.name);
974 CoverModule::write(out, ppp.first.loc);
975 ppp.second.toBin(out);
976 }
977 }
978 }
979
980 // Save allcounters
981 CoverModule::write(out, (uint64_t)allCounters.size());
982 for (const auto& p : allCounters)
983 {
984 CoverModule::write(out, p.first);
985 CoverModule::write(out, (uint64_t)p.second.size());
986 for (const auto& pp : p.second)
987 {
988 CoverModule::write(out, pp.first);
989 CoverModule::write(out, pp.second.first);
990 CoverModule::write(out, pp.second.second);
991 }
992 }
993 }
994
fromBin(std::fstream & in)995 void CoverModule::fromBin(std::fstream& in)
996 {
997 fromBin(*this, in);
998 }
999
fromBin(CoverModule & cm,std::fstream & in)1000 void CoverModule::fromBin(CoverModule& cm, std::fstream& in)
1001 {
1002 // Load results
1003 uint64_t size = CoverModule::readUint64_t(in);
1004 for (uint64_t i = 0; i < size; ++i)
1005 {
1006 const std::wstring moduleName = CoverModule::readWstring(in);
1007 const uint64_t sizee = CoverModule::readUint64_t(in);
1008 auto it = cm.results.find(moduleName);
1009 if (it == cm.results.end())
1010 {
1011 it = cm.results.emplace(moduleName, std::unordered_map<std::wstring, std::map<MacroLoc, CoverResult>>()).first;
1012 }
1013 auto& map = it->second;
1014 for (uint64_t j = 0; j < sizee; ++j)
1015 {
1016 const std::wstring macroFilename = CoverModule::readWstring(in);
1017 const uint64_t sizeee = CoverModule::readUint64_t(in);
1018 auto it = map.find(macroFilename);
1019 if (it == map.end())
1020 {
1021 it = map.emplace(macroFilename, std::map<MacroLoc, CoverResult>()).first;
1022 }
1023 auto& mapp = it->second;
1024 for (uint64_t k = 0; k < sizeee; ++k)
1025 {
1026 const std::wstring macroName = CoverModule::readWstring(in);
1027 const Location macroLoc = CoverModule::readLocation(in);
1028 const MacroLoc ml(macroName, macroLoc);
1029 auto it = mapp.find(ml);
1030 if (it == mapp.end())
1031 {
1032 mapp.emplace(ml, CoverResult::fromBin(in));
1033 }
1034 else
1035 {
1036 it->second.merge(CoverResult::fromBin(in));
1037 }
1038 }
1039 }
1040 }
1041
1042 // Load allcounters
1043 size = CoverModule::readUint64_t(in);
1044 for (uint64_t i = 0; i < size; ++i)
1045 {
1046 const std::wstring moduleName = CoverModule::readWstring(in);
1047 const uint64_t sizee = CoverModule::readUint64_t(in);
1048 auto it = cm.allCounters.find(moduleName);
1049 if (it == cm.allCounters.end())
1050 {
1051 it = cm.allCounters.emplace(moduleName, std::unordered_map<std::wstring, std::pair<bool, uint64_t>>()).first;
1052 }
1053 auto& map = it->second;
1054 for (uint64_t j = 0; j < sizee; ++j)
1055 {
1056 const std::wstring funName = CoverModule::readWstring(in);
1057 const bool ismacro = CoverModule::readBool(in);
1058 const uint64_t counter = CoverModule::readUint64_t(in);
1059 auto it = map.find(funName);
1060 if (it == map.end())
1061 {
1062 map.emplace(funName, std::pair<bool, uint64_t>(ismacro, counter));
1063 }
1064 else
1065 {
1066 auto& p = it->second;
1067 if (p.first == ismacro)
1068 {
1069 p.second += counter;
1070 }
1071 }
1072 }
1073 }
1074 }
1075
1076 } // namespace coverage
1077