diff --git a/.CI/Jenkinsfile b/.CI/Jenkinsfile index e24fc78..7b5d16e 100644 --- a/.CI/Jenkinsfile +++ b/.CI/Jenkinsfile @@ -469,6 +469,11 @@ pipeline { GITBRANCHES_WASM_JIT = 'wasm-jit-me wasm-jit-cs wasm-jit' GITBRANCHES_C_PLUS_RUST = 'c-plus-rust' GITBRANCHES_SPECIAL = 'master newInst-newBackend' + // The C+Rust job ran a few times before it was added to this stage, so + // it is past the run count all-reports.py takes for a first report and + // gets no history without being named here. Drop it once the branch has + // one; the run says so in its log. + NEWBRANCHES = '--new-branch=c-plus-rust' PYTHONIOENCODING = 'utf-8' IDA_EMAIL = credentials('IDA email') // A secret file holding one pgpass line; libpq reads the password from @@ -488,7 +493,7 @@ pipeline { ''' sh './clean-empty-omcversion-dates.py' - sh "./all-reports.py --email --omcgitdir=OpenModelica ${env.GITBRANCHES} ${env.GITBRANCHES_FMI} ${env.GITBRANCHES_NEWINST} ${env.GITBRANCHES_DAE} ${env.GITBRANCHES_NEWBACKEND_DAE} ${env.GITBRANCHES_CPP} ${env.GITBRANCHES_WASM_JIT} ${env.GITBRANCHES_C_PLUS_RUST} conversion heavy_tests generateSymbolicJacobian gbode cvode ida" + sh "./all-reports.py --email ${env.NEWBRANCHES} --omcgitdir=OpenModelica ${env.GITBRANCHES} ${env.GITBRANCHES_FMI} ${env.GITBRANCHES_NEWINST} ${env.GITBRANCHES_DAE} ${env.GITBRANCHES_NEWBACKEND_DAE} ${env.GITBRANCHES_CPP} ${env.GITBRANCHES_WASM_JIT} ${env.GITBRANCHES_C_PLUS_RUST} conversion heavy_tests generateSymbolicJacobian gbode cvode ida" sh "./all-plots.py ${env.GITBRANCHES} ${env.GITBRANCHES_FMI} ${env.GITBRANCHES_NEWINST} ${env.GITBRANCHES_DAE} ${env.GITBRANCHES_NEWBACKEND_DAE} ${env.GITBRANCHES_CPP} ${env.GITBRANCHES_WASM_JIT} ${env.GITBRANCHES_C_PLUS_RUST} conversion heavy_tests generateSymbolicJacobian gbode cvode ida" // Recorded and announced only once they are up: a report the database diff --git a/all-plots.py b/all-plots.py index f801dcf..4669981 100755 --- a/all-plots.py +++ b/all-plots.py @@ -1,42 +1,20 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -import sys, argparse, subprocess, os -import simplejson as json +import argparse, os import shared, resultsdb -import re, time, math +import time, datetime import collections, multiprocessing -from omcommon import friendlyStr import matplotlib as mpl mpl.use('svg') # Disables the Tk dependency / DISPLAY dependency import matplotlib.pyplot as plt -import matplotlib.dates as mdates -import matplotlib.cbook as cbook -from matplotlib.ticker import MaxNLocator -from matplotlib.font_manager import FontProperties - -import time, datetime -from omcommon import friendlyStr, multiple_replace def defaultJobs(): # The cpus this process may use, not the ones the machine has. count = getattr(os, "process_cpu_count", os.cpu_count)() return max(1, count or 1) -def dateStr(dint): - return str(datetime.datetime.fromtimestamp(dint).strftime('%Y-%m-%d %H:%M:%S')) - -def getTagOrVersion(v): - v = v.replace("OpenModelica ","").replace("OMCompiler ","") - m = re.search("[+]g([0-9a-f]{7})$", v) - if m: - return m.group(1) - return v - -def libraryLink(branch, libname): - return '%s' % (baseurl,branch,libname,libname,libname) - def plotLibrary(fnameprefix, branch, libname, xs, total, frontend,backend,simcode,template,compile,simulate,verify): f, ax = plt.subplots(1) lw = 0.5 @@ -49,16 +27,6 @@ def plotLibrary(fnameprefix, branch, libname, xs, total, frontend,backend,simcod plt.plot(xs, simulate, label='simulate (%d)' % simulate[-1], linewidth=lw) if not (min(verify)==0 and max(verify)==0): plt.plot(xs, verify, label='verify (%d)' % verify[-1], linewidth=lw) - if False: - ticksize = 5 - if len(total)<=10: - ticksize = 1 - if len(total)>=200: - ticksize = 25 - elif len(total)>=100: - ticksize = 10 - #yint = range(0, ticksize, math.ceil(max(total))+10-(math.ceil(max(total))%10)) - #plt.yticks(yint) ax.set_ylim(ymin=0) ax.set_xlim(xmin=xs[0], xmax=xs[-1]) @@ -93,18 +61,8 @@ def plotJobs(db, cursor, branches, fnameprefix): """One job per library; a generator, so a branch is queried while the plots of the previous one are still being rendered.""" for branch in branches: - try: - one = (branch,) if db.tableExists(branch) else None - if one == None: - print("No such table '%s'; specify it using --branch=XXX when running test.py" % branch) - # ignore this table and continue - continue - else: - v = one[0] - except: - # raise Exception("No such table '%s'; specify it using --branch=XXX" % branch) + if not db.tableExists(branch): print("No such table '%s'; specify it using --branch=XXX when running test.py" % branch) - # ignore this table and continue continue db.createDateIndex(branch) @@ -162,38 +120,3 @@ def main(): if __name__ == '__main__': main() - -""" -for branch in branches: - cursor.execute('''CREATE TABLE if not exists [datelookup_%s] - (date integer NOT NULL, runDate integer NOT NULL, libname text NOT NULL, branch text NOT NULL)''' % branch) - - cursor.execute('''CREATE INDEX IF NOT EXISTS idx_%s_date ON %s(date)''' % (branch,branch)) - cursor.execute('''CREATE INDEX IF NOT EXISTS idx_omcversion_date ON omcversion(date)''') - cursor.execute('''CREATE INDEX IF NOT EXISTS idx_libversion_date ON libversion(date)''') - - cursor.execute('''SELECT DISTINCT V.date as date,L.libname as libname - FROM [omcversion] as V - CROSS JOIN (SELECT DISTINCT libname FROM [%s]) AS L - LEFT JOIN [datelookup_%s] as D ON D.date=V.date AND D.branch=? AND D.libname=L.libname - WHERE D.runDate IS NULL - ORDER BY V.date ASC -''' % (branch,branch), (branch,)) - entries = cursor.fetchall() - print(len(entries)) - progress = 0 - start = time.time() - cursor.execute('''DROP INDEX IF EXISTS idx_datelookup_%s_date''' % branch) - for (date,libname) in entries: - cursor.execute("SELECT date FROM [%s] WHERE libname=? AND date <= ? ORDER BY date DESC LIMIT 1" % branch, (libname,date)) - (runDate,) = cursor.fetchone() or (0,) - cursor.execute("INSERT INTO [datelookup_%s] VALUES (?,?,?,?)" % branch, (date,runDate,libname,branch)) - progress += 1 - if progress % 100 == 0: - end = time.time() - print("%d total insertions, %0.2g" % (progress,end-start)) - start = end - cursor.execute('''CREATE INDEX IF NOT EXISTS idx_datelookup_%s_date ON datelookup_%s(date)''' % (branch,branch)) - conn.commit() -conn.commit() -""" diff --git a/all-reports.py b/all-reports.py index 4b203cf..c03abea 100755 --- a/all-reports.py +++ b/all-reports.py @@ -3,7 +3,7 @@ import urllib.request, urllib.error, urllib.parse import codecs -import sys, argparse, subprocess, os, time +import argparse, subprocess, os, time, datetime import simplejson as json import shared, resultsdb import re @@ -19,6 +19,10 @@ parser.add_argument('--email', default=False, action='store_true') parser.add_argument('--pending', default="pending-reports.json", help="Where to leave this run's reports for publish-reports.py") +parser.add_argument('--new-branch', default=[], action='append', metavar='BRANCH', + help="Start a history for this branch from the runs there are, for a job " + "that ran before it was added to the report stage. Give it once per " + "branch, and drop it once the branch has a history.") resultsdb.addArgument(parser) args = parser.parse_args() @@ -33,24 +37,15 @@ omcgitdir = args.omcgitdir doemail = args.email pendingfile = args.pending +newbranches = set(shared.resultTable(branch) for branch in args.new_branch) if not os.path.exists(omcgitdir): raise Exception("Could not find OpenModelica.git directory, set it with --omcgitdir. Tried: %s" % omcgitdir) -dates = {} -dates_str = {} -fields = ["exectime", "frontend", "backend", "simcode", "templates", "compile", "simulate", "verify"] -entryhead = "BranchTotalFrontendBackendSimCodeTemplatesCompilationSimulationVerification\n" - timeMinPhase = 4 # Need to have completed code generation to report performance regressions timeRel = 1.7 # Minimum 1.7x time is registered as a performance regression timeAbs = 10 # Ignore performance regressions for times <10s... -libs = {} - -import time, datetime -from omcommon import friendlyStr, multiple_replace - db = resultsdb.connect(args.db) db.createHistoryTable() cursor = db.cursor() @@ -60,7 +55,10 @@ def dateStr(dint): def getTagOrVersion(v): v = v.replace("OpenModelica ","").replace("OMCompiler ","") - v = re.sub(r"-rust$", "", v) + # A cmake build appends "-cmake" to the describe string it reports (the Rust + # omc "-rust"); git resolves neither, and the report then has no commit table + # and nobody is mailed about it. + v = re.sub(r"-(cmake|rust)$", "", v) m = re.search("[+]g([0-9a-f]{7}[0-9a-f]*)$", v) if m: return m.group(1) @@ -96,6 +94,10 @@ def modelLink(libname, modelname, extension, text): # file rather than a broken deployment or something else answering; # - the database holds at most FIRSTREPORT runs of the branch, so the report # about to be generated is its first and none can have been lost. +# +# A job added to the report stage after the test stage fails the run count and +# cannot recover: nothing is written, so nothing is published, so the index stays +# missing while the count grows. --new-branch lifts that condition, and only it. FIRSTREPORT = 2 entryRe = re.compile(r'^

[^/"]+)">[^<]* ' @@ -196,6 +198,8 @@ def historyOf(branch, nruns): else: published = readPublishedIndex(branch) stored = storedEntries(branch) + if branch in newbranches and (published is not None or stored): + print("--new-branch=%s can be dropped; the branch has a history now" % branch) if published is None: if stored: print("Rebuilding the index of %s from the %d reports in the database" @@ -204,12 +208,13 @@ def historyOf(branch, nruns): if not historyRootIsServed(): print("Neither the database nor the history root knows about %s; leaving it alone" % branch) return None - if nruns > FIRSTREPORT: + if nruns > FIRSTREPORT and branch not in newbranches: print("%s has no index and no reports in the database although it has %d runs; " "leaving it alone rather than publishing a history with only the newest " - "report in it" % (branch, nruns)) + "report in it. Pass --new-branch=%s to report on it from its %d runs" + % (branch, nruns, branch, nruns)) return None - print("Starting a new history for %s" % branch) + print("Starting a new history for %s from its %d runs" % (branch, nruns)) return ([], [], None) (entries, preamble) = parseIndex(published) known = set((e[0], e[1]) for e in stored) @@ -228,19 +233,8 @@ def historyOf(branch, nruns): missing_branches = [] emails_to_send = {} for branch in branches: - try: - one = (branch,) if db.tableExists(branch) else None - if one == None: - print("No such table '%s'; specify it using --branch=XXX when running test.py" % branch) - # ignore this table and continue - missing_branches.append(branch) - continue - else: - v = one[0] - except: - #raise Exception("No such table '%s'; specify it using --branch=XXX" % branch) + if not db.tableExists(branch): print("No such table '%s'; specify it using --branch=XXX when running test.py" % branch) - # ignore this table and continue missing_branches.append(branch) continue