Skip to content

Commit 223ae46

Browse files
committed
refs #14599 - added OneShotTimer / moved some ShowTime logic out of Timer
test/cli/test_other.py: added showtime tests with `FileSettings`
1 parent 69d89c5 commit 223ae46

6 files changed

Lines changed: 157 additions & 57 deletions

File tree

cli/cppcheckexecutor.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -267,8 +267,9 @@ int CppCheckExecutor::check(int argc, const char* const argv[])
267267
return EXIT_SUCCESS;
268268
}
269269

270-
TimerResults overallTimerResults;
271-
Timer realTimeClock("Overall time", settings.showtime, &overallTimerResults, Timer::Type::OVERALL);
270+
std::unique_ptr<OneShotTimer> overallTimer;
271+
if (settings.showtime == ShowTime::SUMMARY || settings.showtime == ShowTime::TOP5_SUMMARY)
272+
overallTimer.reset(new OneShotTimer("Overall time", settings.showtime));
272273

273274
settings.loadSummaries();
274275

@@ -277,9 +278,6 @@ int CppCheckExecutor::check(int argc, const char* const argv[])
277278

278279
const int ret = check_wrapper(settings, supprs);
279280

280-
realTimeClock.stop();
281-
overallTimerResults.showResults(settings.showtime, false, true);
282-
283281
return ret;
284282
}
285283

lib/cppcheck.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -923,8 +923,9 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str
923923
if (Settings::terminated())
924924
return mLogger->exitcode();
925925

926-
TimerResults checkTimeResults;
927-
Timer fileTotalTimer{"Check time: " + file.spath(), mSettings.showtime, &checkTimeResults, Timer::Type::FILE};
926+
std::unique_ptr<OneShotTimer> checkTimeTimer;
927+
if (mSettings.showtime == ShowTime::FILE || mSettings.showtime == ShowTime::FILE_TOTAL || mSettings.showtime == ShowTime::TOP5_FILE)
928+
checkTimeTimer.reset(new OneShotTimer("Check time: " + file.spath(), mSettings.showtime));
928929

929930
if (!mSettings.quiet) {
930931
std::string fixedpath = Path::toNativeSeparators(file.spath());
@@ -1296,9 +1297,6 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str
12961297
if (mTimerResults && (mSettings.showtime == ShowTime::FILE || mSettings.showtime == ShowTime::TOP5_FILE))
12971298
mTimerResults->showResults(mSettings.showtime);
12981299

1299-
fileTotalTimer.stop();
1300-
checkTimeResults.showResults(mSettings.showtime, false, true);
1301-
13021300
return mLogger->exitcode();
13031301
}
13041302

lib/timer.cpp

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ namespace {
3838

3939
// TODO: this does not include any file context when SHOWTIME_FILE thus rendering it useless - should we include the logging with the progress logging?
4040
// that could also get rid of the broader locking
41-
void TimerResults::showResults(ShowTime mode, bool metrics, bool format) const
41+
void TimerResults::showResults(ShowTime mode, bool metrics) const
4242
{
4343
if (mode == ShowTime::NONE)
4444
return;
@@ -60,11 +60,7 @@ void TimerResults::showResults(ShowTime mode, bool metrics, bool format) const
6060
const double sec = iter->second.getSeconds().count();
6161
const double secAverage = sec / static_cast<double>(iter->second.mNumberOfResults);
6262
if ((mode != ShowTime::TOP5_FILE && mode != ShowTime::TOP5_SUMMARY) || (ordinal<=5)) {
63-
std::cout << iter->first << ": ";
64-
if (format)
65-
std::cout << TimerResultsData::durationToString(iter->second.mDuration);
66-
else
67-
std::cout << sec << "s";
63+
std::cout << iter->first << ": " << sec << "s";
6864
if (metrics)
6965
std::cout << " (avg. " << secAverage << "s - " << iter->second.mNumberOfResults << " result(s))";
7066
std::cout << std::endl;
@@ -87,10 +83,9 @@ void TimerResults::reset()
8783
mResults.clear();
8884
}
8985

90-
Timer::Timer(std::string str, ShowTime showtimeMode, TimerResultsIntf* timerResults, Type type)
86+
Timer::Timer(std::string str, ShowTime showtimeMode, TimerResultsIntf* timerResults)
9187
: mName(std::move(str))
9288
, mMode(showtimeMode)
93-
, mType(type)
9489
, mStart(Clock::now())
9590
, mResults(timerResults)
9691
{}
@@ -104,14 +99,6 @@ void Timer::stop()
10499
{
105100
if (mMode == ShowTime::NONE)
106101
return;
107-
if (mType == Type::OVERALL && mMode != ShowTime::TOP5_SUMMARY && mMode != ShowTime::SUMMARY) {
108-
mMode = ShowTime::NONE;
109-
return;
110-
}
111-
if (mType == Type::FILE && mMode != ShowTime::TOP5_FILE && mMode != ShowTime::FILE && mMode != ShowTime::FILE_TOTAL) {
112-
mMode = ShowTime::NONE;
113-
return;
114-
}
115102
if (mStart != TimePoint{}) {
116103
if (!mResults) {
117104
assert(false);
@@ -124,7 +111,7 @@ void Timer::stop()
124111
mMode = ShowTime::NONE; // prevent multiple stops
125112
}
126113

127-
std::string TimerResultsData::durationToString(std::chrono::milliseconds duration)
114+
static std::string durationToString(std::chrono::milliseconds duration)
128115
{
129116
// Extract hours
130117
auto hours = std::chrono::duration_cast<std::chrono::hours>(duration);
@@ -148,3 +135,24 @@ std::string TimerResultsData::durationToString(std::chrono::milliseconds duratio
148135
secondsStr.resize(pos + 4); // keep three decimal
149136
return (ellapsedTime + secondsStr + "s");
150137
}
138+
139+
OneShotTimer::OneShotTimer(std::string name, ShowTime showtime)
140+
{
141+
if (showtime == ShowTime::NONE)
142+
return;
143+
144+
class MyResults : public TimerResultsIntf
145+
{
146+
private:
147+
void addResults(const std::string &name, std::chrono::milliseconds duration) override
148+
{
149+
std::lock_guard<std::mutex> l(stdCoutLock);
150+
151+
// TODO: do not use std::cout directly
152+
std::cout << name << ": " << durationToString(duration) << std::endl;
153+
}
154+
};
155+
156+
mResults.reset(new MyResults);
157+
mTimer.reset(new Timer(std::move(name), showtime, mResults.get()));
158+
}

lib/timer.h

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <chrono>
2626
#include <cstdint>
2727
#include <map>
28+
#include <memory>
2829
#include <mutex>
2930
#include <string>
3031
#include <utility>
@@ -52,15 +53,13 @@ struct TimerResultsData {
5253
std::chrono::duration<double> getSeconds() const {
5354
return std::chrono::duration_cast<std::chrono::duration<double>>(mDuration);
5455
}
55-
56-
static std::string durationToString(std::chrono::milliseconds duration);
5756
};
5857

5958
class CPPCHECKLIB WARN_UNUSED TimerResults : public TimerResultsIntf {
6059
public:
6160
TimerResults() = default;
6261

63-
void showResults(ShowTime mode, bool metrics = true, bool format = false) const;
62+
void showResults(ShowTime mode, bool metrics = true) const;
6463
void addResults(const std::string& str, std::chrono::milliseconds duration) override;
6564

6665
void reset();
@@ -75,13 +74,7 @@ class CPPCHECKLIB Timer {
7574
using Clock = std::chrono::high_resolution_clock;
7675
using TimePoint = std::chrono::time_point<Clock>;
7776

78-
enum class Type : std::uint8_t {
79-
FILE,
80-
OVERALL,
81-
OTHER
82-
};
83-
84-
Timer(std::string str, ShowTime showtimeMode, TimerResultsIntf* timerResults = nullptr, Type type = Type::OTHER);
77+
Timer(std::string str, ShowTime showtimeMode, TimerResultsIntf* timerResults = nullptr);
8578
~Timer();
8679

8780
Timer(const Timer&) = delete;
@@ -98,10 +91,18 @@ class CPPCHECKLIB Timer {
9891
private:
9992
const std::string mName;
10093
ShowTime mMode{};
101-
Type mType{};
10294
TimePoint mStart;
10395
TimerResultsIntf* mResults{};
10496
};
10597

98+
class CPPCHECKLIB OneShotTimer
99+
{
100+
public:
101+
OneShotTimer(std::string name, ShowTime showtime);
102+
private:
103+
std::unique_ptr<TimerResultsIntf> mResults;
104+
std::unique_ptr<Timer> mTimer;
105+
};
106+
106107
//---------------------------------------------------------------------------
107108
#endif // timerH

test/cli/other_test.py

Lines changed: 97 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import subprocess
1010
import shutil
1111

12-
from testutils import cppcheck, assert_cppcheck, cppcheck_ex, __lookup_cppcheck_exe
12+
from testutils import cppcheck, assert_cppcheck, cppcheck_ex, __lookup_cppcheck_exe, create_compile_commands
1313
from xml.etree import ElementTree
1414

1515

@@ -956,10 +956,9 @@ def test_unused_function_include(tmpdir):
956956

957957
# TODO: test with clang-tidy
958958
# TODO: test with --addon
959-
# TODO: test with FileSettings
960959
# TODO: test with multiple files
961-
def __test_showtime(tmp_path, showtime, exp_res, exp_last, extra_args=None):
962-
test_file = tmp_path / 'test.cpp'
960+
def __test_showtime(tmp_path, showtime, exp_res, exp_last, use_compdb, extra_args=None):
961+
test_file = tmp_path / 'test.cpp' # the use of C++ is intentional
963962
with open(test_file, 'wt') as f:
964963
f.write(
965964
"""
@@ -972,10 +971,17 @@ def __test_showtime(tmp_path, showtime, exp_res, exp_last, extra_args=None):
972971
args = [
973972
f'--showtime={showtime}',
974973
'--quiet',
975-
'--inline-suppr',
976-
str(test_file)
974+
'--inline-suppr'
977975
]
978976

977+
if use_compdb:
978+
compdb_file = tmp_path / 'compile_commands.json'
979+
create_compile_commands(compdb_file, [test_file])
980+
981+
args.append(f'--project={compdb_file}')
982+
else:
983+
args.append(str(test_file))
984+
979985
if extra_args:
980986
args += extra_args
981987

@@ -994,50 +1000,122 @@ def __test_showtime(tmp_path, showtime, exp_res, exp_last, extra_args=None):
9941000
assert stderr == ''
9951001

9961002

1003+
def __test_showtime_top5_file(tmp_path, use_compdb):
1004+
__test_showtime(tmp_path, 'top5_file', 5, 'Check time: ', use_compdb)
1005+
1006+
9971007
def test_showtime_top5_file(tmp_path):
998-
__test_showtime(tmp_path, 'top5_file', 5, 'Check time: ')
1008+
__test_showtime_top5_file(tmp_path, False)
1009+
1010+
1011+
def test_showtime_top5_file_compdb(tmp_path):
1012+
__test_showtime_top5_file(tmp_path, True)
1013+
1014+
1015+
# TODO: remove extra args when --executor=process works
1016+
def __test_showtime_top5_summary(tmp_path, use_compdb):
1017+
__test_showtime(tmp_path, 'top5_summary', 5, 'Overall time: ', use_compdb, ['-j1'])
9991018

10001019

1001-
# TODO: remove extra args when --executor=process works works
10021020
def test_showtime_top5_summary(tmp_path):
1003-
__test_showtime(tmp_path, 'top5_summary', 5, 'Overall time: ', ['-j1'])
1021+
__test_showtime_top5_summary(tmp_path, False)
1022+
10041023

1024+
def test_showtime_top5_summary_compdb(tmp_path):
1025+
__test_showtime_top5_summary(tmp_path, True)
10051026

1006-
# TODO: remove when --executor=process works works
1027+
1028+
# TODO: remove when --executor=process works
10071029
def test_showtime_top5_summary_j_thread(tmp_path):
1008-
__test_showtime(tmp_path, 'top5_summary', 5, 'Overall time: ', ['-j2', '--executor=thread'])
1030+
__test_showtime(tmp_path, 'top5_summary', 5, 'Overall time: ', False, ['-j2', '--executor=thread'])
1031+
1032+
1033+
# TODO: remove when --executor=process works
1034+
def test_showtime_top5_summary_compdb_j_thread(tmp_path):
1035+
__test_showtime(tmp_path, 'top5_summary', 5, 'Overall time: ', True, ['-j2', '--executor=thread'])
10091036

10101037

10111038
# TODO: remove override when fixed
10121039
@pytest.mark.skipif(sys.platform == 'win32', reason="requires ProcessExecutor")
10131040
@pytest.mark.xfail(strict=True) # TODO: need to transfer the timer results to parent process - see #4452
10141041
def test_showtime_top5_summary_j_process(tmp_path):
1015-
__test_showtime(tmp_path, 'top5_summary', 5, 'Overall time: ', ['-j2', '--executor=process'])
1042+
__test_showtime(tmp_path, 'top5_summary', 5, 'Overall time: ', False, ['-j2', '--executor=process'])
1043+
1044+
1045+
# TODO: remove override when fixed
1046+
@pytest.mark.skipif(sys.platform == 'win32', reason="requires ProcessExecutor")
1047+
@pytest.mark.xfail(strict=True) # TODO: need to transfer the timer results to parent process - see #4452
1048+
def test_showtime_top5_summary_compdb_j_process(tmp_path):
1049+
__test_showtime(tmp_path, 'top5_summary', 5, 'Overall time: ', True, ['-j2', '--executor=process'])
1050+
1051+
1052+
def __test_showtime_file(tmp_path, use_compdb):
1053+
exp_res = 79
1054+
# project analysis does not call Preprocessor::getConfig()
1055+
if use_compdb:
1056+
exp_res -= 1
1057+
__test_showtime(tmp_path, 'file', exp_res, 'Check time: ', use_compdb)
10161058

10171059

10181060
def test_showtime_file(tmp_path):
1019-
__test_showtime(tmp_path, 'file', 79, 'Check time: ')
1061+
__test_showtime_file(tmp_path, False)
1062+
1063+
1064+
def test_showtime_file_compdb(tmp_path):
1065+
__test_showtime_file(tmp_path, True)
1066+
1067+
1068+
# TODO: remove extra args when --executor=process works
1069+
def __test_showtime_summary(tmp_path, use_compdb):
1070+
exp_res = 79
1071+
# project analysis does not call Preprocessor::getConfig()
1072+
if use_compdb:
1073+
exp_res -= 1
1074+
__test_showtime(tmp_path, 'summary', exp_res, 'Overall time: ', use_compdb, ['-j1'])
10201075

10211076

1022-
# TODO: remove extra args when --executor=process works works
10231077
def test_showtime_summary(tmp_path):
1024-
__test_showtime(tmp_path, 'summary', 79, 'Overall time: ', ['-j1'])
1078+
__test_showtime_summary(tmp_path, False,)
1079+
10251080

1081+
def test_showtime_summary_compdb(tmp_path):
1082+
__test_showtime_summary(tmp_path, True)
10261083

1027-
# TODO: remove when --executor=process works works
1084+
1085+
# TODO: remove when --executor=process works
10281086
def test_showtime_summary_j_thread(tmp_path):
1029-
__test_showtime(tmp_path, 'summary', 79, 'Overall time: ', ['-j2', '--executor=thread'])
1087+
__test_showtime(tmp_path, 'summary', 79, 'Overall time: ', False, ['-j2', '--executor=thread'])
1088+
1089+
1090+
# TODO: remove when --executor=process works
1091+
def test_showtime_summary_compdb_j_thread(tmp_path):
1092+
__test_showtime(tmp_path, 'summary', 78, 'Overall time: ', True, ['-j2', '--executor=thread'])
10301093

10311094

10321095
# TODO: remove override when fixed
10331096
@pytest.mark.skipif(sys.platform == 'win32', reason="requires ProcessExecutor")
10341097
@pytest.mark.xfail(strict=True) # TODO: need to transfer the timer results to parent process - see #4452
10351098
def test_showtime_summary_j_process(tmp_path):
1036-
__test_showtime(tmp_path, 'summary', 79, 'Overall time: ', ['-j2', '--executor=process'])
1099+
__test_showtime(tmp_path, 'summary', 79, 'Overall time: ', False, ['-j2', '--executor=process'])
1100+
1101+
1102+
# TODO: remove override when fixed
1103+
@pytest.mark.skipif(sys.platform == 'win32', reason="requires ProcessExecutor")
1104+
@pytest.mark.xfail(strict=True) # TODO: need to transfer the timer results to parent process - see #4452
1105+
def test_showtime_summary_compdb_j_process(tmp_path):
1106+
__test_showtime(tmp_path, 'summary', 78, 'Overall time: ', True, ['-j2', '--executor=process'])
1107+
1108+
1109+
def __test_showtime_file_total(tmp_path, use_compdb):
1110+
__test_showtime(tmp_path, 'file-total', 0, 'Check time: ', use_compdb)
10371111

10381112

10391113
def test_showtime_file_total(tmp_path):
1040-
__test_showtime(tmp_path, 'file-total', 0, 'Check time: ')
1114+
__test_showtime_file_total(tmp_path, False)
1115+
1116+
1117+
def test_showtime_file_total_compdb(tmp_path):
1118+
__test_showtime_file_total(tmp_path, True)
10411119

10421120

10431121
def test_showtime_unique(tmp_path):

test/cli/testutils.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import subprocess
66
import time
77
import tempfile
8+
import json
89

910
# Create Cppcheck project file
1011
import sys
@@ -45,6 +46,22 @@ def create_gui_project_file(project_file, root_path=None, import_project=None, p
4546
f.write(cppcheck_xml)
4647

4748

49+
def create_compile_commands(compdb_file, files):
50+
compdb = []
51+
# TODO: bail out on empty list
52+
for f in files:
53+
compdb.append(
54+
{
55+
"file": str(f),
56+
"directory": os.path.dirname(f),
57+
"command": "gcc -c {}".format(os.path.basename(f))
58+
}
59+
)
60+
compdb_j = json.dumps(compdb)
61+
with open(compdb_file, 'wt') as f:
62+
f.write(compdb_j)
63+
64+
4865
def __lookup_cppcheck_exe():
4966
# path the script is located in
5067
script_path = os.path.dirname(os.path.realpath(__file__))

0 commit comments

Comments
 (0)