From b32d67879ed01cc6bab7710af72db9f3ec8797dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Sj=C3=B6lund?= Date: Tue, 8 Sep 2026 09:00:09 +0200 Subject: [PATCH] Report on c-plus-rust's history and regressions all-reports.py generated nothing for `c-plus-rust`: no history report and no regression email. Two independent reasons. The job was added to the report stage (7a8677f) after it had already run four times, so it failed the FIRSTREPORT guard: no `00_history.html` on the server and no rows in `[history]`, but more runs than a first report can account for, so the branch was left alone. Nothing is written then, so nothing is published, so the index stays missing while the run count only grows - it cannot recover on its own. `--new-branch=BRANCH` says the history is missing because there has never been one and starts it from the runs there are; the run prints when the flag can be dropped again. It is also the only job built with cmake, and omc_git_revision.cmake appends `-cmake` to the version omc reports: | branch | omcversion | git log | | ----------- | -------------------------------------- | -------- | | master | OMCompiler v1.28.0-dev.652+g6a7b108fff | ok | | wasm-jit | v1.28.0-dev-651-g69ffe69097-rust | ok | | c-plus-rust | v1.28.0-dev-649-g64e0c28497-cmake | exit 128 | git resolves neither the revision nor the range, so every report fell into the `CalledProcessError` branch: no commit table, no 3rdParty diff, and `emails_current` never grew past openmodelicabuilds. Strip `-cmake` as `-rust` already was. Checked against a copy of the branch's rows from omdb: the skip reproduces without the flag, and with it the three reports and their seven recipients come out. While here, drop dead code: all-plots.py's stale copy of getTagOrVersion (unused, and the same function fixed here), its unused dateStr and libraryLink, an `if False:` block, the commented-out datelookup script at the end and nine unused imports; all-reports.py's unused globals and its second set of imports; and the tableExists dance in both, whose bare `except:` reported a broken database connection as a missing table. Assisted-by: Claude Opus 5 --- .CI/Jenkinsfile | 7 ++++- all-plots.py | 83 ++----------------------------------------------- all-reports.py | 48 +++++++++++++--------------- 3 files changed, 30 insertions(+), 108 deletions(-) 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