From cf71e0a4550ea03a6923721ac03187296e7d2882 Mon Sep 17 00:00:00 2001 From: Alexey Rodionov Date: Thu, 24 Jan 2019 16:31:51 +0300 Subject: [PATCH] Iroha build time graph per each g++ target Signed-off-by: Alexey Rodionov --- .jenkinsci-new/build.groovy | 2 +- .../builders/x64-linux-build-steps.groovy | 16 ++++- .jenkinsci-new/helpers/analyzeBuildTime.py | 65 +++++++++++++++++++ .../helpers/compilers/clang++-6.0.sh | 2 + .jenkinsci-new/helpers/compilers/clang++-7.sh | 2 + .jenkinsci-new/helpers/compilers/clang++.sh | 2 + .jenkinsci-new/helpers/compilers/g++-5.sh | 2 + .jenkinsci-new/helpers/compilers/g++-7.sh | 2 + .jenkinsci-new/helpers/exportBuildTime.py | 49 ++++++++++++++ .jenkinsci-new/utils/vars.groovy | 10 +-- .jenkinsci/debug-build.groovy | 4 +- .jenkinsci/helpers/analyzeBuildTime.py | 65 +++++++++++++++++++ .jenkinsci/helpers/build.sh | 2 + .jenkinsci/helpers/build_cmake.sh | 2 + .jenkinsci/helpers/exportBuildTime.py | 49 ++++++++++++++ .jenkinsci/helpers/platform_tag.py | 4 ++ .jenkinsci/linux-post-step.groovy | 15 +++++ Jenkinsfile-new | 8 ++- docker/develop/Dockerfile | 2 +- 19 files changed, 291 insertions(+), 12 deletions(-) create mode 100755 .jenkinsci-new/helpers/analyzeBuildTime.py create mode 100755 .jenkinsci-new/helpers/compilers/clang++-6.0.sh create mode 100755 .jenkinsci-new/helpers/compilers/clang++-7.sh create mode 100755 .jenkinsci-new/helpers/compilers/clang++.sh create mode 100755 .jenkinsci-new/helpers/compilers/g++-5.sh create mode 100755 .jenkinsci-new/helpers/compilers/g++-7.sh create mode 100755 .jenkinsci-new/helpers/exportBuildTime.py create mode 100755 .jenkinsci/helpers/analyzeBuildTime.py create mode 100755 .jenkinsci/helpers/build.sh create mode 100755 .jenkinsci/helpers/build_cmake.sh create mode 100755 .jenkinsci/helpers/exportBuildTime.py diff --git a/.jenkinsci-new/build.groovy b/.jenkinsci-new/build.groovy index 8ac859cf60..fb5c0fe61c 100644 --- a/.jenkinsci-new/build.groovy +++ b/.jenkinsci-new/build.groovy @@ -13,7 +13,7 @@ def cmakeConfigure(String buildDir, String cmakeOptions, String sourceTreeDir=". } def cmakeBuild(String buildDir, String cmakeOptions, int parallelism) { - sh "cmake --build ${buildDir} ${cmakeOptions} -- -j${parallelism}" + sh "cmake --build ${buildDir} ${cmakeOptions} -- -j${parallelism} | sponge buildTimeResult.txt" sh "ccache --show-stats" } diff --git a/.jenkinsci-new/builders/x64-linux-build-steps.groovy b/.jenkinsci-new/builders/x64-linux-build-steps.groovy index c829646b3c..407a6ab381 100644 --- a/.jenkinsci-new/builders/x64-linux-build-steps.groovy +++ b/.jenkinsci-new/builders/x64-linux-build-steps.groovy @@ -131,9 +131,23 @@ def buildSteps(int parallelism, List compilerVersions, String build_type, boolea } } -def successPostSteps(scmVars, boolean packagePush, String dockerTag, List environment) { +def successPostSteps(scmVars, boolean packagePush, String dockerTag, List environment, boolean buildTimeGraph) { stage('Linux success PostSteps') { withEnv(environment) { + + // handling build time results + if (buildTimeGraph) { + sh(".jenkinsci-new/helpers/exportBuildTime.py buildTimeResult.txt") + zip archive: true, dir: '', glob: 'buildTimeResult.csv', zipFile: 'buildTimeMeasurement.zip' + archiveArtifacts artifacts: 'buildTimeMeasurement.zip' + + copyArtifacts(projectName: 'develop', filter: 'buildTimeMeasurement.zip', target: 'buildTimeMeasurement-develop'); + unzip zipFile: 'buildTimeMeasurement-develop/buildTimeMeasurement.zip', dir: 'buildTimeMeasurement-develop' + sh ".jenkinsci-new/helpers/analyzeBuildTime.py buildTimeMeasurement-develop/buildTimeResult.csv buildTimeResult.csv" + zip archive: true, dir: '', glob: 'diff.csv', zipFile: 'diff.zip' + archiveArtifacts artifacts: 'diff.zip' + } + if (packagePush) { def artifacts = load ".jenkinsci-new/artifacts.groovy" def utils = load ".jenkinsci-new/utils/utils.groovy" diff --git a/.jenkinsci-new/helpers/analyzeBuildTime.py b/.jenkinsci-new/helpers/analyzeBuildTime.py new file mode 100755 index 0000000000..90e8534a7f --- /dev/null +++ b/.jenkinsci-new/helpers/analyzeBuildTime.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +# +# Analyzes build time per each iroha build target (g++ target) and stores all results to csv-file: +# target, work_type, sys_time, user_time, total, sys_time_diff, user_time_diff, total_diff, sys_time_perc, user_time_perc, total_perc +# + +import csv +from sys import argv +from os import path + +F1, F2 = argv[1], argv[2] # base, compare + +# time difference in seconds (multiply by -1 show difference - faster or lower) +def diff_secs(a, b): + return round( (float(a) - float(b)) * -1 , 2) + +# time difference in percents (multiply by -1 show difference - faster or lower) +def diff_perc(a, b): + if float(a) == 0.0: + return 0.0 + return round( (float(a) - float(b)) / float(a) * 100 * -1, 2) + +if __name__ == '__main__': + if not path.isfile(F1) or not path.isfile(F2): + print("Can't find files!") + exit(1) + + with open(F1) as base, open(F2) as compare: + base_reader, comp_reader = csv.DictReader(base), csv.DictReader(compare) + b, c = { row['target']: row for row in base_reader }, { row['target']: row for row in comp_reader } + + if len(c) == 0: + print("No records found") + exit(1) + + for target in c: + c[target].update( + { 'user_time_diff': 0, 'sys_time_diff': 0, 'total_diff': 0, + 'user_time_perc': 0, 'sys_time_perc': 0, 'total_perc': 0 } + ) + if b[target] is None: + continue + c[target]['sys_time_diff'] = diff_secs(b[target]['sys_time'], c[target]['sys_time']) + c[target]['user_time_diff'] = diff_secs(b[target]['user_time'], c[target]['user_time']) + c[target]['total_diff'] = diff_secs(b[target]['total'], c[target]['total']) + c[target]['sys_time_perc'] = diff_perc(b[target]['sys_time'], c[target]['sys_time']) + c[target]['user_time_perc'] = diff_perc(b[target]['user_time'], c[target]['user_time']) + c[target]['total_perc'] = diff_perc(b[target]['total'], c[target]['total']) + + with open ('diff.csv', 'w+') as csvfile: + fieldnames = ['target', 'work_type', + 'sys_time', 'user_time', 'total', + 'sys_time_diff', 'user_time_diff', 'total_diff', + 'sys_time_perc', 'user_time_perc', 'total_perc' + ] + # fieldnames = sorted(next(iter(c.iteritems()))[1].keys()) + writer = csv.DictWriter(csvfile, fieldnames=fieldnames) + writer.writeheader() + for row in c: + writer.writerow(c[row]) diff --git a/.jenkinsci-new/helpers/compilers/clang++-6.0.sh b/.jenkinsci-new/helpers/compilers/clang++-6.0.sh new file mode 100755 index 0000000000..a3fbd04954 --- /dev/null +++ b/.jenkinsci-new/helpers/compilers/clang++-6.0.sh @@ -0,0 +1,2 @@ +#!/bin/bash +( /usr/bin/time -f "%S\t%U" clang++-6.0 "${@}" 2> >(cat <(echo "clang++-6.0 ${@}") - )) | sponge diff --git a/.jenkinsci-new/helpers/compilers/clang++-7.sh b/.jenkinsci-new/helpers/compilers/clang++-7.sh new file mode 100755 index 0000000000..9c13f754ab --- /dev/null +++ b/.jenkinsci-new/helpers/compilers/clang++-7.sh @@ -0,0 +1,2 @@ +#!/bin/bash +( /usr/bin/time -f "%S\t%U" clang++-7 "${@}" 2> >(cat <(echo "clang++-7 ${@}") - )) | sponge diff --git a/.jenkinsci-new/helpers/compilers/clang++.sh b/.jenkinsci-new/helpers/compilers/clang++.sh new file mode 100755 index 0000000000..2bf02a25a3 --- /dev/null +++ b/.jenkinsci-new/helpers/compilers/clang++.sh @@ -0,0 +1,2 @@ +#!/bin/bash +( /usr/bin/time -f "%S\t%U" clang++ "${@}" 2> >(cat <(echo "clang++ ${@}") - )) | sponge diff --git a/.jenkinsci-new/helpers/compilers/g++-5.sh b/.jenkinsci-new/helpers/compilers/g++-5.sh new file mode 100755 index 0000000000..1bfe74b61b --- /dev/null +++ b/.jenkinsci-new/helpers/compilers/g++-5.sh @@ -0,0 +1,2 @@ +#!/bin/bash +( /usr/bin/time -f "%S\t%U" g++-5 "${@}" 2> >(cat <(echo "g++-5 ${@}") - )) | sponge diff --git a/.jenkinsci-new/helpers/compilers/g++-7.sh b/.jenkinsci-new/helpers/compilers/g++-7.sh new file mode 100755 index 0000000000..856a33bd41 --- /dev/null +++ b/.jenkinsci-new/helpers/compilers/g++-7.sh @@ -0,0 +1,2 @@ +#!/bin/bash +( /usr/bin/time -f "%S\t%U" g++-7 "${@}" 2> >(cat <(echo "g++-7 ${@}") - )) | sponge diff --git a/.jenkinsci-new/helpers/exportBuildTime.py b/.jenkinsci-new/helpers/exportBuildTime.py new file mode 100755 index 0000000000..0987ef539b --- /dev/null +++ b/.jenkinsci-new/helpers/exportBuildTime.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +# +# Compares 2 csv files with build time data, makes new file with difference in seconds and percents +# + +import re +import argparse +import csv + +parser = argparse.ArgumentParser(description='Process time output for compiler') + +parser.add_argument('log_file', type=str, help='input file') + +args = parser.parse_args() +if __name__ == "__main__": + lines = [] + with open(args.log_file, "r") as f: + lines = f.readlines() + i = 0 + units = [] + while i < len(lines): + unit = {} + if "g++" not in lines[i]: + i+=1 + continue + try: + sys_time, user_time = lines[i+1].rstrip().split("\t") + unit['target'] = re.findall(r"-o (\S+) ", lines[i])[0] + unit['work_type'] = 'linking' if "-Wl" in lines[i] else "build" + unit['sys_time'] = float(sys_time) + unit['user_time'] = float(user_time) + unit['total'] = round(unit['sys_time'] + unit['user_time'], 2) + units.append(unit) + i+=2 + except: + i+=1 + continue + + csv_filename = args.log_file.split(".")[0] + ".csv" + with open(csv_filename, 'w') as csvfile: + fieldnames = ['target', 'sys_time', 'user_time', 'total', 'work_type'] + writer = csv.DictWriter(csvfile, fieldnames=fieldnames) + writer.writeheader() + writer.writerows(units) diff --git a/.jenkinsci-new/utils/vars.groovy b/.jenkinsci-new/utils/vars.groovy index 5be64a5463..05fee8f694 100644 --- a/.jenkinsci-new/utils/vars.groovy +++ b/.jenkinsci-new/utils/vars.groovy @@ -9,11 +9,11 @@ // def compilerMapping () { - return ['gcc5': ['cxx_compiler':'g++-5', 'cc_compiler':'gcc-5'], - 'gcc7' : ['cxx_compiler':'g++-7', 'cc_compiler':'gcc-7'], - 'clang6': ['cxx_compiler':'clang++-6.0', 'cc_compiler':'clang-6.0'], - 'clang7': ['cxx_compiler':'clang++-7', 'cc_compiler':'clang-7'], - 'appleclang': ['cxx_compiler':'clang++', 'cc_compiler':'clang'], + return ['gcc5': ['cxx_compiler':"${WORKSPACE}/.jenkinsci-new/helpers/compilers/g++-5.sh", 'cc_compiler':'gcc-5'], + 'gcc7' : ['cxx_compiler':"${WORKSPACE}/.jenkinsci-new/helpers/compilers/g++-7.sh", 'cc_compiler':'gcc-7',], + 'clang6': ['cxx_compiler':"${WORKSPACE}/.jenkinsci-new/helpers/compilers/clang++-6.0.sh", 'cc_compiler':'clang-6.0'], + 'clang7': ['cxx_compiler':"${WORKSPACE}/.jenkinsci-new/helpers/compilers/clang++-7.sh", 'cc_compiler':'clang-7'], + 'appleclang': ['cxx_compiler':"${WORKSPACE}/.jenkinsci-new/helpers/compilers/clang++.sh", 'cc_compiler':'clang'], ] } diff --git a/.jenkinsci/debug-build.groovy b/.jenkinsci/debug-build.groovy index e0a201a6a9..1984d7afb8 100644 --- a/.jenkinsci/debug-build.groovy +++ b/.jenkinsci/debug-build.groovy @@ -66,7 +66,7 @@ def doDebugBuild(coverageEnabled=false) { + " -v /tmp/${GIT_COMMIT}-${BUILD_NUMBER}:/tmp/${GIT_COMMIT}") { def scmVars = checkout scm - def cmakeOptions = "" + def cmakeOptions = "-DCMAKE_CXX_COMPILER=${WORKSPACE}/.jenkinsci/helpers/build.sh" if ( coverageEnabled ) { cmakeOptions += " -DCOVERAGE=ON " } @@ -95,7 +95,7 @@ def doDebugBuild(coverageEnabled=false) { -DIROHA_VERSION=${env.IROHA_VERSION} \ ${cmakeOptions} """ - sh "cmake --build build -- -j${parallelism}" + sh "cmake --build build -- -j${parallelism} | sponge buildTimeResult.txt" sh "ccache --show-stats" if ( coverageEnabled ) { sh "cmake --build build --target coverage.init.info" diff --git a/.jenkinsci/helpers/analyzeBuildTime.py b/.jenkinsci/helpers/analyzeBuildTime.py new file mode 100755 index 0000000000..90e8534a7f --- /dev/null +++ b/.jenkinsci/helpers/analyzeBuildTime.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +# +# Analyzes build time per each iroha build target (g++ target) and stores all results to csv-file: +# target, work_type, sys_time, user_time, total, sys_time_diff, user_time_diff, total_diff, sys_time_perc, user_time_perc, total_perc +# + +import csv +from sys import argv +from os import path + +F1, F2 = argv[1], argv[2] # base, compare + +# time difference in seconds (multiply by -1 show difference - faster or lower) +def diff_secs(a, b): + return round( (float(a) - float(b)) * -1 , 2) + +# time difference in percents (multiply by -1 show difference - faster or lower) +def diff_perc(a, b): + if float(a) == 0.0: + return 0.0 + return round( (float(a) - float(b)) / float(a) * 100 * -1, 2) + +if __name__ == '__main__': + if not path.isfile(F1) or not path.isfile(F2): + print("Can't find files!") + exit(1) + + with open(F1) as base, open(F2) as compare: + base_reader, comp_reader = csv.DictReader(base), csv.DictReader(compare) + b, c = { row['target']: row for row in base_reader }, { row['target']: row for row in comp_reader } + + if len(c) == 0: + print("No records found") + exit(1) + + for target in c: + c[target].update( + { 'user_time_diff': 0, 'sys_time_diff': 0, 'total_diff': 0, + 'user_time_perc': 0, 'sys_time_perc': 0, 'total_perc': 0 } + ) + if b[target] is None: + continue + c[target]['sys_time_diff'] = diff_secs(b[target]['sys_time'], c[target]['sys_time']) + c[target]['user_time_diff'] = diff_secs(b[target]['user_time'], c[target]['user_time']) + c[target]['total_diff'] = diff_secs(b[target]['total'], c[target]['total']) + c[target]['sys_time_perc'] = diff_perc(b[target]['sys_time'], c[target]['sys_time']) + c[target]['user_time_perc'] = diff_perc(b[target]['user_time'], c[target]['user_time']) + c[target]['total_perc'] = diff_perc(b[target]['total'], c[target]['total']) + + with open ('diff.csv', 'w+') as csvfile: + fieldnames = ['target', 'work_type', + 'sys_time', 'user_time', 'total', + 'sys_time_diff', 'user_time_diff', 'total_diff', + 'sys_time_perc', 'user_time_perc', 'total_perc' + ] + # fieldnames = sorted(next(iter(c.iteritems()))[1].keys()) + writer = csv.DictWriter(csvfile, fieldnames=fieldnames) + writer.writeheader() + for row in c: + writer.writerow(c[row]) diff --git a/.jenkinsci/helpers/build.sh b/.jenkinsci/helpers/build.sh new file mode 100755 index 0000000000..623437ab53 --- /dev/null +++ b/.jenkinsci/helpers/build.sh @@ -0,0 +1,2 @@ +#!/bin/bash +( /usr/bin/time -f "%S\t%U" g++ "$@" 2> >(cat <(echo "g++ $@") - )) | sponge diff --git a/.jenkinsci/helpers/build_cmake.sh b/.jenkinsci/helpers/build_cmake.sh new file mode 100755 index 0000000000..623437ab53 --- /dev/null +++ b/.jenkinsci/helpers/build_cmake.sh @@ -0,0 +1,2 @@ +#!/bin/bash +( /usr/bin/time -f "%S\t%U" g++ "$@" 2> >(cat <(echo "g++ $@") - )) | sponge diff --git a/.jenkinsci/helpers/exportBuildTime.py b/.jenkinsci/helpers/exportBuildTime.py new file mode 100755 index 0000000000..0987ef539b --- /dev/null +++ b/.jenkinsci/helpers/exportBuildTime.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +# +# Compares 2 csv files with build time data, makes new file with difference in seconds and percents +# + +import re +import argparse +import csv + +parser = argparse.ArgumentParser(description='Process time output for compiler') + +parser.add_argument('log_file', type=str, help='input file') + +args = parser.parse_args() +if __name__ == "__main__": + lines = [] + with open(args.log_file, "r") as f: + lines = f.readlines() + i = 0 + units = [] + while i < len(lines): + unit = {} + if "g++" not in lines[i]: + i+=1 + continue + try: + sys_time, user_time = lines[i+1].rstrip().split("\t") + unit['target'] = re.findall(r"-o (\S+) ", lines[i])[0] + unit['work_type'] = 'linking' if "-Wl" in lines[i] else "build" + unit['sys_time'] = float(sys_time) + unit['user_time'] = float(user_time) + unit['total'] = round(unit['sys_time'] + unit['user_time'], 2) + units.append(unit) + i+=2 + except: + i+=1 + continue + + csv_filename = args.log_file.split(".")[0] + ".csv" + with open(csv_filename, 'w') as csvfile: + fieldnames = ['target', 'sys_time', 'user_time', 'total', 'work_type'] + writer = csv.DictWriter(csvfile, fieldnames=fieldnames) + writer.writeheader() + writer.writerows(units) diff --git a/.jenkinsci/helpers/platform_tag.py b/.jenkinsci/helpers/platform_tag.py index 593c54c912..f0eb31be7c 100644 --- a/.jenkinsci/helpers/platform_tag.py +++ b/.jenkinsci/helpers/platform_tag.py @@ -1,4 +1,8 @@ #!/usr/env/python +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# import xml.etree.ElementTree as ET import argparse diff --git a/.jenkinsci/linux-post-step.groovy b/.jenkinsci/linux-post-step.groovy index 6ea387fcd9..0533960207 100644 --- a/.jenkinsci/linux-post-step.groovy +++ b/.jenkinsci/linux-post-step.groovy @@ -3,6 +3,7 @@ def linuxPostStep() { try { // stop write core dumps sh "ulimit -c 0" + // handling coredumps (if tests crashed) if (currentBuild.currentResult != "SUCCESS" && params.coredump) { def dumpsFileName = sprintf('coredumps-%1$s.bzip2', @@ -16,6 +17,20 @@ def linuxPostStep() { echo "Build is not SUCCESS! See core dumps at: https://nexus.iroha.tech/repository/artifacts/iroha/coredumps/${dumpsFileName}" } } + + // handling build time results + if (currentBuild.currentResult == "SUCCESS" && !params.fuzzing) { + sh(".jenkinsci/helpers/exportBuildTime.py buildTimeResult.txt") + zip archive: true, dir: '', glob: 'buildTimeResult.csv', zipFile: 'buildTimeMeasurement.zip' + archiveArtifacts artifacts: 'buildTimeMeasurement.zip' + + copyArtifacts(projectName: 'develop', filter: 'buildTimeMeasurement.zip', target: 'buildTimeMeasurement-develop'); + unzip zipFile: 'buildTimeMeasurement-develop/buildTimeMeasurement.zip', dir: 'buildTimeMeasurement-develop' + sh ".jenkinsci/helpers/analyzeBuildTime.py buildTimeMeasurement-develop/buildTimeResult.csv buildTimeResult.csv" + zip archive: true, dir: '', glob: 'diff.csv', zipFile: 'diff.zip' + archiveArtifacts artifacts: 'diff.zip' + } + if (currentBuild.currentResult == "SUCCESS" && GIT_LOCAL_BRANCH ==~ /(master|develop)/) { def artifacts = load ".jenkinsci/artifacts.groovy" def commit = env.GIT_COMMIT diff --git a/Jenkinsfile-new b/Jenkinsfile-new index 99d4521e9f..aff68e99a2 100644 --- a/Jenkinsfile-new +++ b/Jenkinsfile-new @@ -145,7 +145,8 @@ node ('master') { coverage = false coverage_mac = false doxygen = false - + buildTimeGraph = true + build_type = 'Debug' packageBuild = false pushDockerTag = 'not-supposed-to-be-pushed' @@ -262,9 +263,12 @@ node ('master') { parallelism==0 ?x64LinuxWorker.cpusAvailable : parallelism, x64linux_compiler_list, 'Release', specialBranch, false, false , testList, false, false, false, true, false, false, false, environmentList)}] } + if (build_scenario == 'Before merge to trunk') { + buildTimeGraph = false + } x64LinuxPostSteps = new Builder.PostSteps( always: [{x64LinuxBuildScript.alwaysPostSteps(environmentList)}], - success: [{x64LinuxBuildScript.successPostSteps(scmVars, packagePush, pushDockerTag, environmentList)}]) + success: [{x64LinuxBuildScript.successPostSteps(scmVars, packagePush, pushDockerTag, environmentList, buildTimeGraph)}]) } def x64MacBuildSteps def x64MacBuildPostSteps = new Builder.PostSteps() diff --git a/docker/develop/Dockerfile b/docker/develop/Dockerfile index f3e3e7c64b..c6a171e9e0 100644 --- a/docker/develop/Dockerfile +++ b/docker/develop/Dockerfile @@ -9,7 +9,7 @@ ENV IROHA_HOME /opt/iroha ENV IROHA_BUILD /opt/iroha/build RUN apt-get update && \ - apt-get -y --no-install-recommends install apt-utils software-properties-common wget; \ + apt-get -y --no-install-recommends install apt-utils software-properties-common wget time moreutils; \ apt-get -y clean # add repos