From: shylesh kumar Date: Wed, 22 Aug 2018 01:02:32 +0000 (-0400) Subject: code coverage from source X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=5a3c02530ff2ba8b844d159999089a97996260e7;p=ceph.git code coverage from source Signed-off-by: shylesh kumar --- diff --git a/qa/suites/rados/downstream/coverage.test/cover-suit.yaml b/qa/suites/rados/downstream/coverage.test/cover-suit.yaml new file mode 100644 index 00000000000..1f3b04b30c5 --- /dev/null +++ b/qa/suites/rados/downstream/coverage.test/cover-suit.yaml @@ -0,0 +1,14 @@ +roles: +- [mon.0, osd.6, osd.7, osd.8, client.0] +- [mgr.0,osd.0, osd.1, osd.2, osd.3, osd.4, osd.5] + +tasks: +- make_coverage: +- ceph: + coverage: true + conf: + osd: + osd max object name len : 400 + osd max object namespace len : 64 +- collect_coverage: + diff --git a/qa/suites/rados/downstream/coverage/% b/qa/suites/rados/downstream/coverage/% new file mode 100644 index 00000000000..e69de29bb2d diff --git a/qa/suites/rados/downstream/coverage/ceph.yaml b/qa/suites/rados/downstream/coverage/ceph.yaml new file mode 100644 index 00000000000..e7cc441fa01 --- /dev/null +++ b/qa/suites/rados/downstream/coverage/ceph.yaml @@ -0,0 +1,11 @@ +tasks: +- make_coverage: +- ceph: + conf: + osd: + osd max object name len : 400 + osd max object namespace len : 64 +- full_sequential_finally: + - collect_coverage: + + diff --git a/qa/suites/rados/downstream/coverage/tasks/radosbench.yaml b/qa/suites/rados/downstream/coverage/tasks/radosbench.yaml new file mode 100644 index 00000000000..9f889fe09a5 --- /dev/null +++ b/qa/suites/rados/downstream/coverage/tasks/radosbench.yaml @@ -0,0 +1,17 @@ +roles: +- [mon.0, osd.6, osd.7, osd.8, client.0] +- [mgr.0,osd.0, osd.1, osd.2, osd.3, osd.4, osd.5] + +overrides: + ceph: + conf: + mon: + osd default pool size: 3 + +tasks: +- radosbench: + clients: [client.0] + time: 720 + type: rand + objectsize: 1048576 + diff --git a/qa/suites/rados/downstream/coverage/tasks/radosgw-admin.yaml b/qa/suites/rados/downstream/coverage/tasks/radosgw-admin.yaml new file mode 100644 index 00000000000..6fb65897d1c --- /dev/null +++ b/qa/suites/rados/downstream/coverage/tasks/radosgw-admin.yaml @@ -0,0 +1,18 @@ +roles: +- [mon.a, osd.0] +- [mgr.x, client.0, osd.1, osd.2, osd.3] + +overrides: + ceph: + conf: + client: + debug ms: 1 + rgw gc obj min wait: 15 + osd: + debug ms: 1 + debug objclass : 20 + +tasks: +- rgw: + client.0: +- radosgw-admin: diff --git a/qa/suites/rados/downstream/coverage/tasks/rbd_fio_perf_4k.yaml b/qa/suites/rados/downstream/coverage/tasks/rbd_fio_perf_4k.yaml new file mode 100644 index 00000000000..7aa3d937bcd --- /dev/null +++ b/qa/suites/rados/downstream/coverage/tasks/rbd_fio_perf_4k.yaml @@ -0,0 +1,19 @@ +roles: +- [mon.0, osd.6, osd.7, osd.8, client.0] +- [mgr.0,osd.0, osd.1, osd.2, osd.3, osd.4, osd.5] + +overrides: + ceph: + conf: + mon: + osd default pool size: 3 + +tasks: +- rbd_fio: + client.0: + fio-io-size: 80% + formats: [2] + features: [[layering],[layering,exclusive-lock,object-map]] + io-engine: rbd + rw: randrw + runtime: 600 diff --git a/qa/suites/rados/downstream/coverage/tasks/readwrite.yaml b/qa/suites/rados/downstream/coverage/tasks/readwrite.yaml new file mode 100644 index 00000000000..05dfa337c28 --- /dev/null +++ b/qa/suites/rados/downstream/coverage/tasks/readwrite.yaml @@ -0,0 +1,19 @@ +roles: +- [mon.0, osd.6, osd.7, osd.8, client.0] +- [mgr.0,osd.0, osd.1, osd.2, osd.3, osd.4, osd.5] + +overrides: + ceph: + crush_tunables: optimal + conf: + mon: + mon osd initial require min compat client: luminous +tasks: +- rados: + clients: [client.0] + ops: 4000 + objects: 500 + op_weights: + read: 45 + write: 45 + delete: 10 diff --git a/qa/tasks/collect_coverage.py b/qa/tasks/collect_coverage.py new file mode 100644 index 00000000000..fea0dd32946 --- /dev/null +++ b/qa/tasks/collect_coverage.py @@ -0,0 +1,151 @@ +''' +Compiling ceph from source on each node +for code coverage runs +''' +import contextlib +import logging +import os +import re +import shlex +import subprocess + +from subprocess import Popen +from cStringIO import StringIO +from teuthology import misc as teuthology +from teuthology.parallel import parallel +from teuthology.orchestra import run, remote +from teuthology.config import config as teuth_config + +log = logging.getLogger(__name__) +COVERAGE_LOGS="/a/code_coverage_logs/" +run_dir="" + +def aggregate_info(ctx, tdir, curator, ilist, run_dir): + ''' + Aggregate the results from all the nodes to generate + a single .info file + ''' + + coverdir="/tmp/coverage" + JID_PREFIX=os.path.basename(ctx.archive) + cmd = "mkdir -p {}".format(coverdir) + r=curator.run(args=cmd) + dst="" + + mergefile=coverdir+"/coverage_merged.info" + + cmd="lcov --rc lcov_branch_coverage=1 " + for ent in ilist: + addstr=" -a "+tdir+ent + tstr=" -t "+ent.split(".")[0] + cmd = cmd + addstr+tstr + cmd = cmd +" -o "+mergefile + log.info(cmd) + + r=curator.run(args=cmd) + + ''' + rename file with JID_PREFIX + ''' + new_mergefile=coverdir+"/"+JID_PREFIX+"_coverage_merged.info" + cmd = "mv "+mergefile+" "+new_mergefile + log.info("renaming {src} to {dst}".format(src=mergefile,\ + dst=new_mergefile)) + log.info(cmd) + r=curator.run(args=cmd) + + ''' + transfer file to teuthology master node + ''' + assert(os.path.exists(run_dir)) + dst=curator.get_file(new_mergefile, False, run_dir) + assert(os.path.exists(dst)) + log.info("xfrd file {} to {}".format(new_mergefile, dst)) + + + ''' + cmd="genhtml -o {archive_dir} {coverfile}".format( \ + archive_dir=coverdir, coverfile=mergefile) + log.info(cmd) + r=curator.run(args=cmd) + teuthology.pull_directory(curator, coverdir, ctx.archive) + ''' + +def _gather_data(ctx, remote, rpath, curator): + tdir="/tmp/cov/"+ctx.archive + lpath="" + if remote != curator: + if not os.path.exists(tdir): + os.makedirs(tdir) + lpath=remote.get_file(rpath, False, tdir) + assert(os.path.exists(lpath)) + log.info("Transfered file {} to {} ".format(rpath, lpath)) + #pass the file to curator + # direct copying from remote->remote not working + fn=os.path.basename(rpath) + src=tdir+"/{fn}".format(fn=fn) + + with file(src, 'rb') as f: + teuthology.sudo_write_file( + remote=curator, + path=rpath, + data=f, + ) + remote.run( + args=[ + 'sudo', + 'chmod', + 'a=rx', + '--', + rpath, + ] + ) + + + +def gather_data(ctx, config, remote, srcdir, curator): + this_infofile="{hostname}.info".format(hostname=remote.shortname) + ''' Generate .info file on respective nodes and copy it to + local node + ''' + cmd="sudo bash -c '(cd /sourcebuild/ceph && \ + exec lcov --rc lcov_branch_coverage=1 \ + --capture -d . --output-file {nodename})'"\ + .format(nodename=this_infofile) + log.info(cmd) + r=remote.run(args=cmd) + + _gather_data(ctx, remote, srcdir+this_infofile, curator) + +@contextlib.contextmanager +def task(ctx, config): + """ + generate reports on respective nodes but fetch all of them to local + so that we can merge the reports + """ + global run_dir + srcdir="/sourcebuild/ceph/" + dstdir="{ctxdir}/".format(ctxdir=ctx.archive) + run_dir=COVERAGE_LOGS+os.path.basename(os.path.dirname(ctx.archive))+"/" + + + if os.path.exists(dstdir): + print 'coverage directory present' + else: + os.makedirs(dstdir) + curator=ctx.cluster.remotes.keys()[0] + + with parallel() as ptask: + for remote, roles in ctx.cluster.remotes.iteritems(): + ptask.spawn(gather_data, ctx, config, remote, srcdir, curator) + ilist=[] + for remote, roles in ctx.cluster.remotes.iteritems(): + node=remote.shortname+".info" + ilist.append(node) + + aggregate_info(ctx, srcdir, curator, ilist, run_dir) + yield + + log.info("DONE WITH COLLECTION") + + diff --git a/qa/tasks/make_coverage.py b/qa/tasks/make_coverage.py new file mode 100644 index 00000000000..1efaff9e285 --- /dev/null +++ b/qa/tasks/make_coverage.py @@ -0,0 +1,240 @@ +''' +Compiling ceph from source on each node +for code coverage runs +''' +import contextlib +import logging +import os +import re +import shlex +import subprocess + +from cStringIO import StringIO +from teuthology import misc as teuthology +from teuthology.parallel import parallel +from teuthology.orchestra import run +from teuthology.config import config as teuth_config + +log = logging.getLogger(__name__) +COVERAGE_LOGS = "/a/code_coverage_logs/" + + +def get_sourcedir(ctx, config, remote, rdir): + ''' + get source code directory along with gcno files + on the machine where we want to generate report + ''' + ldir = "/tmp/build/{}/".format(os.path.basename(os.path.dirname(ctx.archive))) + if not os.path.exists(ldir): + os.makedirs(ldir) +#o teuthology.pull_directory(remote, rdir, ldir) + cmd="scp -r {rhost}:{rpath} {lpath}".format(\ + rhost=remote.name, rpath=rdir, \ + lpath=ldir) + log.info(cmd) + + p=subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE) + p_stdout=p.communicate()[0] + log.info(p_stdout) + + +def copy_compile_utils(ctx, config, remote): + FILES=['update-cmakelists', 'update-docmake'] + destdir = '/usr/bin' + for filename in FILES: + log.info('Shipping %r...', filename) + src = os.path.join(os.path.dirname(__file__), "util/"+filename) + dst = os.path.join(destdir, filename) + with file(src, 'rb') as f: + teuthology.sudo_write_file( + remote=remote, + path=dst, + data=f, + ) + remote.run( + args=[ + 'sudo', + 'chmod', + 'a=rx', + '--', + dst, + ] + ) + + +def copy_utils(ctx, config, remote): + FILES = ['daemon-helper', 'adjust-ulimits'] + destdir = '/usr/bin' + for filename in FILES: + log.info('Shipping %r...', filename) + src = os.path.join(os.path.dirname(teuthology.__file__), "task/install/"+filename) + dst = os.path.join(destdir, filename) + with file(src, 'rb') as f: + teuthology.sudo_write_file( + remote=remote, + path=dst, + data=f, + ) + f.seek(0) + remote.run( + args=[ + 'sudo', + 'chmod', + 'a=rx', + '--', + dst, + ] + ) + + +def get_dependencies(ctx, config, remote): + #1. enable repos + #2. get dependencies + cmd='sudo subscription-manager --force register \ + --serverurl=subscription.rhsm.stage.redhat.com:443/subscription \ + --baseurl=https://cdn.stage.redhat.com --username=qa@redhat.com \ + --password=redhatqa --auto-attach' + r=remote.run(args=cmd) + cmd='sudo subscription-manager repos --enable=rhel-7-server-extras-rpms' + r=remote.run(args=cmd) + cmd='sudo subscription-manager repos --enable=rhel-7-server-optional-rpms' + r=remote.run(args=cmd) + cmd='sudo sed -i -- \'s/enabled=0/enabled=1/g\' /etc/yum.repos.d/epel.repo' + r=remote.run(args=cmd) + cmd='sudo yum repolist' + r=remote.run(args=cmd) + + cmd = "sudo bash -c '(cd /sourcebuild/ceph && exec git checkout luminous)'" + r=remote.run(args=cmd) + cmd="sudo bash -c '(cd /sourcebuild/ceph && exec git submodule update --init --recursive)'" + r=remote.run(args=cmd) + cmd='sudo sed -i -- \'s/yum install subscription-manager/yum install \ + -y subscription-manager/g\' /sourcebuild/ceph/install-deps.sh' + r=remote.run(args=cmd) + cmd="sudo bash -c '(cd /sourcebuild/ceph && exec ./install-deps.sh)'" + r=remote.run(args=cmd) + cmd="sudo bash -c '(sudo yum -y install lcov)'" + r=remote.run(args=cmd) + + copy_compile_utils(ctx, config, remote) + + #edit CMakefiles.txt and do_cmake.sh according to gcov need + cmd="sudo update-cmakelists " + "/sourcebuild/ceph/CMakeLists.txt" + r=remote.run(args=cmd) + #1. set INSTALL_PREFIX=/usr in do_cmake + cmd="sudo update-docmake " + "/sourcebuild/ceph/do_cmake.sh" + r=remote.run(args=cmd) + + #cmd="sudo bash -c '(cd /sourcebuild/ceph && source scl_source enable devtoolset-7 && exec ./do_cmake.sh)'" + cmd="sudo bash -c '(cd /sourcebuild/ceph && exec ./do_cmake.sh)'" + r=remote.run(args=cmd) + cmd="sudo bash -c '(cd /sourcebuild/ceph/build && exec make -j4)'" + r=remote.run(args=cmd) + cmd="sudo bash -c '(cd /sourcebuild/ceph/build && exec make install)'" + r=remote.run(args=cmd) + #cmd="sudo bash -c '(cp -ar /usr/local/lib64/python2.7/site-packages/* /usr/lib64/python2.7/site-packages/)'" + #r=remote.run(args=cmd) + cmd="sudo mkdir /etc/ceph" + r=remote.run(args=cmd) + cmd="sudo mkdir /var/log/ceph" + r=remote.run(args=cmd) + cmd="sudo mkdir /var/lib/ceph" + r=remote.run(args=cmd) + + ''' copy adjust-ulimits, daemon-helper scripts ''' + copy_utils(ctx, config, remote) + + #update permissions for testdir so that + # teuthology can delete archive/coverage/gcda files + test_dir=teuthology.get_testdir(ctx) + cmd="sudo chmod -R 0777 {tdir}".format(tdir=test_dir) + r=remote.run(args=cmd) + + #before CEPH task + #- ceph: + #conf: + # osd: + # osd max object name len : 400 + # osd max object namespace len : 64 + #TODO + #gather gcda files + #aggreagate + #publish report + + +def start_compile(ctx, config, remote, builddir): + #1. edit cmakefile for gcov entry + #2.compile + pass + + +def compile_with_gcov(ctx, config, grepo, remote, builddir): + ''' please use variables once task working''' + try: + r = remote.run(args=['sudo', 'mkdir', '/sourcebuild'], \ + ) + except: + pass + + r = remote.run (args=['sudo', 'git','config',\ + '--global','http.sslVerify', 'false'] + ) +# r = remote.run(args=['sudo', 'git', 'clone',\ +# 'https://gitlab.cee.redhat.com/ceph/ceph.git','/sourcebuild/ceph'], timeout=3600,\ +# check_status=False, wait=True) + + r = remote.run(args=['sudo', 'git', 'clone',\ + 'https://github.com/ceph/ceph.git','/sourcebuild/ceph'], timeout=3600,\ + check_status=False, wait=True) + + get_dependencies(ctx, config, remote) + #start_compile() + + + + +@contextlib.contextmanager +def task(ctx, config): + """ + This will replace Install task for the tests. + fetch source from a git repo on all the nodes and compile + them locally. this is because we need to preserve .gcno files + for gcov report generation purpose. + + ex: + - tasks: + make-coverage: + ceph: + radosbench: + """ + ''' Hard coding for POC purpose''' + + grepo="https://gitlab.cee.redhat.com/ceph/ceph.git" + builddir="/sourcebuild/ceph/" + + with parallel() as ptask: + for remote, roles in ctx.cluster.remotes.iteritems(): + ptask.spawn(compile_with_gcov, ctx, config, grepo, remote, builddir) + + + ''' + Transfer source dir which has gcno files and source + only from one node + ''' + + log.info("Transferring the source directory") + remote=next(iter(ctx.cluster.remotes)) + get_sourcedir(ctx, config, remote, builddir) + + '''create dir with run name in COVERAGE_LOGS directory''' + rundir=COVERAGE_LOGS+os.path.basename(os.path.dirname(ctx.archive)) + log.info("rundir = {}".format(rundir)) + try: + os.mkdir(rundir) + except: + log.error("Failed to create rundir") + + yield + #TODO: cleanup still pending + + diff --git a/qa/tasks/util/update-cmakelists b/qa/tasks/util/update-cmakelists new file mode 100755 index 00000000000..57751efa6da --- /dev/null +++ b/qa/tasks/util/update-cmakelists @@ -0,0 +1,14 @@ +#!/bin/bash +dst=$1 + +sudo sed -i '/option.WITH_TSAN/ i\option(WITH_GCOV "build with gcov" ON)\ +if(WITH_GCOV)\ + find_program(HAVE_GCOV gcov)\ + if(NOT HAVE_GCOV)\ + message(FATAL_ERROR "Coverage Enabled but gcov Not Found")\ + endif(NOT HAVE_GCOV)\ + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage -O0")\ + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage")\ + list(APPEND EXTRALIBS gcov)\ +endif(WITH_GCOV)\n +' $1 diff --git a/qa/tasks/util/update-docmake b/qa/tasks/util/update-docmake new file mode 100755 index 00000000000..07c4ad57150 --- /dev/null +++ b/qa/tasks/util/update-docmake @@ -0,0 +1,3 @@ +#!/bin/bash +dest=$1 +sed -i '/^cmake/ s/cmake/cmake\ -DCMAKE_INSTALL_PREFIX=\/usr/' $1