triage_version.py: added `--perf` to collect performance data in CSV format (#4833)
* removed old timing tracking code * tools/triage_py/README.md: updated * triage_version.py: fixed output when when stderr and stdout are not empty * triage_version.py: added `--perf` to collect performance data in CSV format * triage_version.py: added TODO about providing additional options * triage_version.py: added `--start` to specify the first tag/commit to execute
This commit is contained in:
parent
5a2c31a41c
commit
4807bffdbf
|
@ -1,23 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# Simple script to generate times-tags.log that contains timing information for a range of revisions
|
|
||||||
# Typically these commands shall be used to get times.txt:
|
|
||||||
# mkdir src
|
|
||||||
# cp lib/* src/ <- fill src/ with some source code
|
|
||||||
# tools/times-tags.sh
|
|
||||||
# gcc -o tools/times-tags tools/times-tags.c
|
|
||||||
# tools/times-tags
|
|
||||||
|
|
||||||
rm times-tags.txt
|
|
||||||
|
|
||||||
for i in $(seq $1 $2);
|
|
||||||
do
|
|
||||||
echo "1.$i"
|
|
||||||
echo "1.$i" >> times-tags.txt
|
|
||||||
git checkout "1.$i" -b "$i"
|
|
||||||
make clean
|
|
||||||
make -j4 > /dev/null
|
|
||||||
/usr/bin/time -a -o times-tags.txt ./cppcheck sources -q 2> /dev/null
|
|
||||||
git checkout main
|
|
||||||
git branch -D "$i"
|
|
||||||
done
|
|
|
@ -1,58 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
# Times script using Visual Studio compiler in Windows
|
|
||||||
#
|
|
||||||
# This script assumes that you have:
|
|
||||||
# Python 3
|
|
||||||
# Visual Studio (script assumes VS2013, manipulate the sed command otherwise)
|
|
||||||
# Cygwin64 for the sed command
|
|
||||||
# Command line svn. TortoiseSVN with that feature selected works.
|
|
||||||
#
|
|
||||||
# Usage:
|
|
||||||
# Open VS command prompt.
|
|
||||||
# cd c:\users\...
|
|
||||||
# svn checkout https://github.com/danmar/cppcheck/trunk cppcheck-svn
|
|
||||||
# cd cppcheck-svn
|
|
||||||
# c:\python34\python.exe times-vs.py rev1:rev2
|
|
||||||
|
|
||||||
|
|
||||||
import subprocess
|
|
||||||
import glob
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
|
|
||||||
if len(sys.argv) != 2:
|
|
||||||
print('revisions not specified')
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
res = re.match(r'([0-9]+):([0-9]+)', sys.argv[1])
|
|
||||||
if res is None:
|
|
||||||
print('invalid format, 11111:22222')
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
rev1 = int(res.group(1))
|
|
||||||
rev2 = int(res.group(2))
|
|
||||||
|
|
||||||
if rev1 > rev2 or rev1 < 10000 or rev2 > 20000 or rev2 - rev1 > 500:
|
|
||||||
print('range, aborting')
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
print('Revisions: ' + str(rev1) + ':' + str(rev2))
|
|
||||||
|
|
||||||
f = open('results.txt', 'wt')
|
|
||||||
f.write('\n')
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
for rev in range(rev1, rev2):
|
|
||||||
subprocess.call(['svn', 'revert', '-R', '.'])
|
|
||||||
subprocess.call(['svn', 'up', '-r' + str(rev)])
|
|
||||||
for vcxproj in glob.glob('*/*.vcxproj'):
|
|
||||||
subprocess.call([r'c:\cygwin64\bin\sed.exe', '-i', 's/140/120/', vcxproj])
|
|
||||||
subprocess.call('msbuild cppcheck.sln /t:build /p:configuration=Release,platform=x64'.split())
|
|
||||||
print('Revision:' + str(rev))
|
|
||||||
p = subprocess.Popen(r'bin\cppcheck.exe src -q --showtime=summary'.split(),
|
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
||||||
comm = p.communicate()
|
|
||||||
f = open('results.txt', 'at')
|
|
||||||
f.write('\nREV ' + str(rev) + '\n')
|
|
||||||
f.write(comm[0].decode('utf-8'))
|
|
||||||
f.close()
|
|
|
@ -1,47 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
static void revncpy(char *dst, const char *src, size_t len)
|
|
||||||
{
|
|
||||||
int n = 0;
|
|
||||||
while (n++<len && *src && *src!=' ' && *src!='\r' && *src!='\n')
|
|
||||||
*dst++ = *src++;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
FILE *f = fopen("times.log", "rt");
|
|
||||||
if (!f)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
char lines[0xffff][64] = {0};
|
|
||||||
|
|
||||||
int n = 0;
|
|
||||||
float mintime=0.0f, maxtime=0.0f;
|
|
||||||
char rev[10] = {0};
|
|
||||||
char line[128] = {0};
|
|
||||||
|
|
||||||
while (fgets(line,sizeof(line),f) && n < (sizeof(lines)/sizeof(*lines))) {
|
|
||||||
if (strncmp(line,"HEAD is now at ", 15) == 0) {
|
|
||||||
if (rev[0])
|
|
||||||
sprintf(lines[n++],"%s\t%.1f\t%.1f", rev, mintime, maxtime);
|
|
||||||
revncpy(rev, line+15, sizeof(rev)-1);
|
|
||||||
mintime = 0.0f;
|
|
||||||
maxtime = 0.0f;
|
|
||||||
}
|
|
||||||
if (strncmp(line,"Overall time:",13)==0) {
|
|
||||||
float time = atof(line+14);
|
|
||||||
if (mintime < 0.1f || time < mintime)
|
|
||||||
mintime = time;
|
|
||||||
if (time > maxtime)
|
|
||||||
maxtime = time;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (n > 0)
|
|
||||||
printf("%s\n", lines[--n]);
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# Simple script to generate times.log that contains timing information for the last <n> revisions
|
|
||||||
# Typically these commands shall be used to get times.txt:
|
|
||||||
mkdir -p src || exit 1
|
|
||||||
cp lib/* src/ || exit 2 # fill src/ with some source code
|
|
||||||
#NOTE: also try with some files other then from cppcheck!
|
|
||||||
|
|
||||||
iterations=4
|
|
||||||
|
|
||||||
# if "old" already exists for some reason, do NOT work on current branch but bail out
|
|
||||||
git checkout -b old || exit
|
|
||||||
|
|
||||||
make clean
|
|
||||||
|
|
||||||
git reset --hard HEAD > times.log
|
|
||||||
|
|
||||||
for i in $(seq 1 50); do
|
|
||||||
git_head=$(git log -1 --format=%h)
|
|
||||||
# if build fails, make clean and try again
|
|
||||||
make SRCDIR=build CXXFLAGS=-O2 -j4 || make clean ; make MATCHCOMPILER=yes CXXFLAGS=-O2 -j4
|
|
||||||
echo "Run number $i"
|
|
||||||
for j in $(seq 1 ${iterations}); do
|
|
||||||
./cppcheck --quiet --showtime=summary --enable=all --inconclusive src 2> /dev/null | tee -a times.log
|
|
||||||
done
|
|
||||||
grep "Overall" times.log | tail -${iterations} | sed s/s// | awk -v "i=$i" -v "iterations=$iterations" -v "git_head=$git_head" '{ sum+=$3} END {print "Run " i", "git_head " Average: " sum/iterations}' | tee -a times.log
|
|
||||||
git reset --hard HEAD^1 | tee -a times.log
|
|
||||||
done
|
|
||||||
|
|
||||||
gcc -o tools/times tools/times.c
|
|
||||||
tools/times
|
|
|
@ -5,8 +5,9 @@ A script to run a code sample against a given set of Cppcheck versions.
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```
|
```
|
||||||
usage: triage_version.py [-h] [--compare] [--verbose] [--debug] [--debug-warnings] [--check-library] [--timeout TIMEOUT]
|
usage: triage_version.py [-h] [--compare] [--verbose] [--debug] [--debug-warnings] [--check-library]
|
||||||
[--compact]
|
[--timeout TIMEOUT] [--compact] [--no-quiet] [--perf] [--start START]
|
||||||
|
[--no-stderr | --no-stdout]
|
||||||
dir infile [repo]
|
dir infile [repo]
|
||||||
|
|
||||||
positional arguments:
|
positional arguments:
|
||||||
|
@ -23,6 +24,11 @@ options:
|
||||||
--check-library passed through to binary if supported
|
--check-library passed through to binary if supported
|
||||||
--timeout TIMEOUT the amount of seconds to wait for the analysis to finish
|
--timeout TIMEOUT the amount of seconds to wait for the analysis to finish
|
||||||
--compact only print versions with changes with --compare
|
--compact only print versions with changes with --compare
|
||||||
|
--no-quiet do not specify -q
|
||||||
|
--perf output duration of execution in seconds (CSV format)
|
||||||
|
--start START specify the start version/commit
|
||||||
|
--no-stderr do not display stdout
|
||||||
|
--no-stdout do not display stderr
|
||||||
```
|
```
|
||||||
|
|
||||||
### Structure of `dir`
|
### Structure of `dir`
|
||||||
|
|
|
@ -3,6 +3,7 @@ import os.path
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import argparse
|
import argparse
|
||||||
|
import time
|
||||||
|
|
||||||
from packaging.version import Version
|
from packaging.version import Version
|
||||||
|
|
||||||
|
@ -18,6 +19,8 @@ parser.add_argument('--check-library', action='store_true', help='passed through
|
||||||
parser.add_argument('--timeout', type=int, default=2, help='the amount of seconds to wait for the analysis to finish')
|
parser.add_argument('--timeout', type=int, default=2, help='the amount of seconds to wait for the analysis to finish')
|
||||||
parser.add_argument('--compact', action='store_true', help='only print versions with changes with --compare')
|
parser.add_argument('--compact', action='store_true', help='only print versions with changes with --compare')
|
||||||
parser.add_argument('--no-quiet', action='store_true', default=False, help='do not specify -q')
|
parser.add_argument('--no-quiet', action='store_true', default=False, help='do not specify -q')
|
||||||
|
parser.add_argument('--perf', action='store_true', default=False, help='output duration of execution in seconds (CSV format)')
|
||||||
|
parser.add_argument('--start', default=None, help='specify the start version/commit')
|
||||||
package_group = parser.add_mutually_exclusive_group()
|
package_group = parser.add_mutually_exclusive_group()
|
||||||
package_group.add_argument('--no-stderr', action='store_true', default=False, help='do not display stdout')
|
package_group.add_argument('--no-stderr', action='store_true', default=False, help='do not display stdout')
|
||||||
package_group.add_argument('--no-stdout', action='store_true', default=False, help='do not display stderr')
|
package_group.add_argument('--no-stdout', action='store_true', default=False, help='do not display stderr')
|
||||||
|
@ -39,6 +42,13 @@ if args.compact:
|
||||||
if not do_compare:
|
if not do_compare:
|
||||||
print('error: --compact requires --compare')
|
print('error: --compact requires --compare')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
if args.perf:
|
||||||
|
if args.compact:
|
||||||
|
print('error: --compact has no effect with --perf')
|
||||||
|
if args.no_stdout:
|
||||||
|
print('error: --no-stdout has no effect with --perf')
|
||||||
|
if args.no_stderr:
|
||||||
|
print('error: --no-stderr has no effect with --perf')
|
||||||
|
|
||||||
directory = args.dir
|
directory = args.dir
|
||||||
input_file = args.infile
|
input_file = args.infile
|
||||||
|
@ -91,7 +101,17 @@ if verbose:
|
||||||
last_ec = None
|
last_ec = None
|
||||||
last_out = None
|
last_out = None
|
||||||
|
|
||||||
|
if args.perf:
|
||||||
|
print('version,time')
|
||||||
|
|
||||||
|
start_entry = args.start
|
||||||
|
|
||||||
for entry in versions:
|
for entry in versions:
|
||||||
|
if start_entry:
|
||||||
|
if start_entry != entry:
|
||||||
|
continue
|
||||||
|
start_entry = None
|
||||||
|
|
||||||
exe_path = os.path.join(directory, entry)
|
exe_path = os.path.join(directory, entry)
|
||||||
exe = os.path.join(exe_path, 'cppcheck')
|
exe = os.path.join(exe_path, 'cppcheck')
|
||||||
|
|
||||||
|
@ -134,16 +154,23 @@ for entry in versions:
|
||||||
else:
|
else:
|
||||||
# TODO: re-add inconclusive: {callstack}: ({severity}{inconclusive:, inconclusive}) {message
|
# TODO: re-add inconclusive: {callstack}: ({severity}{inconclusive:, inconclusive}) {message
|
||||||
cmd.append('--template={callstack}: ({severity}) {message} [{id}]')
|
cmd.append('--template={callstack}: ({severity}) {message} [{id}]')
|
||||||
|
# TODO: how to pass addtional options?
|
||||||
|
if args.perf:
|
||||||
|
cmd.append('--error-exitcode=0')
|
||||||
cmd.append(input_file)
|
cmd.append(input_file)
|
||||||
if verbose:
|
if verbose:
|
||||||
print("running '{}'". format(' '.join(cmd)))
|
print("running '{}'". format(' '.join(cmd)))
|
||||||
|
if args.perf:
|
||||||
|
start = time.time_ns()
|
||||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=exe_path, universal_newlines=True)
|
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=exe_path, universal_newlines=True)
|
||||||
try:
|
try:
|
||||||
comm = p.communicate(timeout=args.timeout)
|
comm = p.communicate(timeout=args.timeout)
|
||||||
|
if args.perf:
|
||||||
|
end = time.time_ns()
|
||||||
out = ''
|
out = ''
|
||||||
if not args.no_stdout:
|
if not args.no_stdout:
|
||||||
out += comm[0]
|
out += comm[0]
|
||||||
if not args.no_stderr and not args.no_stderr:
|
if not args.no_stdout and not args.no_stderr:
|
||||||
out += '\n'
|
out += '\n'
|
||||||
if not args.no_stderr:
|
if not args.no_stderr:
|
||||||
out += comm[1]
|
out += comm[1]
|
||||||
|
@ -156,9 +183,19 @@ for entry in versions:
|
||||||
|
|
||||||
if not do_compare:
|
if not do_compare:
|
||||||
if not use_hashes:
|
if not use_hashes:
|
||||||
print(version)
|
ver_str = version
|
||||||
else:
|
else:
|
||||||
print('{} ({})'.format(entry, version))
|
ver_str = '{} ({})'.format(entry, version)
|
||||||
|
if args.perf:
|
||||||
|
if out == "timeout":
|
||||||
|
data_str = "0.0" # TODO: how to handle these properly?
|
||||||
|
elif not ec == 0:
|
||||||
|
continue # skip errors
|
||||||
|
else:
|
||||||
|
data_str = '{}'.format((end - start) / 1000.0 / 1000.0 / 1000.0)
|
||||||
|
print('"{}",{}'.format(ver_str, data_str))
|
||||||
|
continue
|
||||||
|
print(ver_str)
|
||||||
print(ec)
|
print(ec)
|
||||||
print(out)
|
print(out)
|
||||||
continue
|
continue
|
||||||
|
|
Loading…
Reference in New Issue