Browse Source

Merge pull request #28988 from overleaf/bg-add-file-info-to-performance-logs

add latexmk fdb file info to performance logs

GitOrigin-RevId: 3cc5709cd10fd55c2cd8aff7754fb7868aacdf0c
Brian Gough 10 months ago
parent
commit
58094ebcd6

+ 30 - 1
services/clsi/app/js/CompileManager.js

@@ -24,7 +24,8 @@ const {
   downloadOutputDotSynctexFromCompileCache,
 } = require('./CLSICacheHandler')
 const StatsManager = require('./StatsManager')
-const { enableLatexMkMetrics } = require('./LatexMetrics')
+const SafeReader = require('./SafeReader')
+const { enableLatexMkMetrics, addLatexFdbMetrics } = require('./LatexMetrics')
 const { callbackifyMultiResult } = require('@overleaf/promise-utils')
 
 const COMPILE_TIME_BUCKETS = [
@@ -339,6 +340,19 @@ async function doCompile(request, stats, timings) {
 
   // Record compile performance for a subset of users
   if (recordPerformanceMetrics) {
+    // Add fdb metrics if available
+    try {
+      const fdbFileContent = await _readFdbFile(compileDir)
+      if (fdbFileContent) {
+        addLatexFdbMetrics(fdbFileContent, stats)
+      }
+    } catch (err) {
+      // ignore errors reading fdb file
+      logger.warn(
+        { err, projectId, userId },
+        'error reading fdb file for performance metrics'
+      )
+    }
     logger.info(
       {
         userId: request.user_id,
@@ -390,6 +404,21 @@ async function _saveOutputFiles({
   return { outputFiles, allEntries, buildId }
 }
 
+// Set a maximum size for reading output.fdb_latexmk files
+// This limit is chosen to prevent excessive memory usage and ensure performance,
+// as fdb files are typically much smaller and only metrics are extracted from them.
+const MAX_FDB_FILE_SIZE = 1024 * 1024 // 1 MB
+
+async function _readFdbFile(compileDir) {
+  const fdbFile = Path.join(compileDir, 'output.fdb_latexmk')
+  const { result } = await SafeReader.promises.readFile(
+    fdbFile,
+    MAX_FDB_FILE_SIZE,
+    'utf8'
+  )
+  return result
+}
+
 async function stopCompile(projectId, userId) {
   const compileName = getCompileName(projectId, userId)
   await LatexRunner.promises.killLatex(compileName)

+ 138 - 1
services/clsi/app/js/LatexMetrics.js

@@ -132,4 +132,141 @@ function enableLatexMkMetrics(stats) {
   })
 }
 
-module.exports = { enableLatexMkMetrics, addLatexMkMetrics }
+/**
+ * Parses the content of a latexmk .fdb file to extract and record metrics
+ * about the files used during compilation.
+ *
+ * It categorizes files into 'system' (paths starting with '/') and 'user'
+ * files. For each category, it aggregates the count and total size of files
+ * grouped by their extension. The results are added to the provided stats
+ * object under `stats.latexmk['fdb-file-types']`.
+ *
+ * Each file is counted only once, even if it appears multiple times in the
+ * .fdb content. Files without a valid extension are counted as 'other'.
+ *
+ * @param {string | null | undefined} fdbContent - The content of the .fdb_latexmk file.
+ * @param {object} stats - The statistics object to be populated. The metrics will be added to `stats.latexmk`.
+ * @returns {void}
+ */
+function addLatexFdbMetrics(fdbContent, stats) {
+  if (!fdbContent) {
+    return
+  }
+  const { systemFileTypes, userFileTypes } = parseFdbContent(fdbContent)
+
+  if (
+    Object.keys(systemFileTypes).length === 0 &&
+    Object.keys(userFileTypes).length === 0
+  ) {
+    return
+  }
+
+  const userSummary = summarizeFileTypes(userFileTypes)
+  const systemSummary = summarizeFileTypes(systemFileTypes)
+
+  stats.latexmk['fdb-file-types'] = {
+    total: {
+      systemFileCount: systemSummary.total.count,
+      systemFileSize: systemSummary.total.size,
+      imageFileCount: userSummary.image.count,
+      imageFileSize: userSummary.image.size,
+      textFileCount: userSummary.text.count,
+      textFileSize: userSummary.text.size,
+      fontFileCount: userSummary.font.count,
+      fontFileSize: userSummary.font.size,
+      otherFileCount: userSummary.other.count,
+      otherFileSize: userSummary.other.size,
+    },
+    system: convertToArray(systemFileTypes),
+    user: convertToArray(userFileTypes),
+  }
+}
+
+function parseFdbContent(fdbContent) {
+  const systemFileTypes = {}
+  const userFileTypes = {}
+  const seenFiles = new Set()
+  // Extract the file path and size from lines like:
+  //  FILENAME TIMESTAMP SIZE CHECKSUM ...
+  //  "main.tex" 1760016467 6147 9da336eb1132ecb1d61cd1f5a70cfa62 ""
+  // The timestamp can be an integer or a float
+  const lineRegex = /^\s*"([^"]+)"\s+\d+(?:\.\d*)?\s+(\d+)/gm
+
+  for (const match of fdbContent.matchAll(lineRegex)) {
+    // strip leading /compile/ from paths
+    const filePath = match[1].replace(/^\/compile\//, '')
+    if (seenFiles.has(filePath)) {
+      continue
+    }
+    const fileSize = parseInt(match[2], 10)
+    const isSystemFile = filePath.startsWith('/')
+    const extMatch = filePath.match(/\.([a-z]{1,4})$/i)
+    const ext = extMatch ? extMatch[1].toLowerCase() : 'other'
+    const fileTypes = isSystemFile ? systemFileTypes : userFileTypes
+    if (!fileTypes[ext]) {
+      fileTypes[ext] = { count: 0, size: 0 }
+    }
+    fileTypes[ext].count++
+    fileTypes[ext].size += fileSize
+    seenFiles.add(filePath)
+  }
+  return { systemFileTypes, userFileTypes }
+}
+
+function getFileTypeCategory(ext) {
+  switch (ext) {
+    case 'png':
+    case 'jpg':
+    case 'jpeg':
+    case 'tif':
+    case 'tiff':
+    case 'bmp':
+    case 'gif':
+    case 'svg':
+    case 'eps':
+      return 'image'
+    case 'tex':
+    case 'sty':
+    case 'cls':
+    case 'bib':
+      return 'text'
+    case 'ttf':
+    case 'otf':
+    case 'pfb':
+      return 'font'
+    default:
+      return 'other'
+  }
+}
+
+function summarizeFileTypes(fileTypes) {
+  const summary = {
+    image: { count: 0, size: 0 },
+    text: { count: 0, size: 0 },
+    font: { count: 0, size: 0 },
+    other: { count: 0, size: 0 },
+    total: { count: 0, size: 0 },
+  }
+
+  for (const [ext, info] of Object.entries(fileTypes)) {
+    const category = getFileTypeCategory(ext)
+    summary[category].count += info.count
+    summary[category].size += info.size
+    summary.total.count += info.count
+    summary.total.size += info.size
+  }
+
+  return summary
+}
+
+function convertToArray(object) {
+  return Object.entries(object)
+    .map(([ext, value]) => ({
+      ext,
+      count: value.count,
+      size: value.size,
+    }))
+    .sort((a, b) => b.size - a.size) // sort by size descending
+}
+
+module.exports = { enableLatexMkMetrics, addLatexMkMetrics, addLatexFdbMetrics }

+ 5 - 0
services/clsi/app/js/SafeReader.js

@@ -14,6 +14,7 @@
 let SafeReader
 const fs = require('node:fs')
 const logger = require('@overleaf/logger')
+const { promisifyMultiResult } = require('@overleaf/promise-utils')
 
 module.exports = SafeReader = {
   // safely read up to size bytes from a file and return result as a
@@ -60,3 +61,7 @@ module.exports = SafeReader = {
     })
   },
 }
+
+module.exports.promises = {
+  readFile: promisifyMultiResult(SafeReader.readFile, ['result', 'bytesRead']),
+}

+ 112 - 0
services/clsi/test/unit/js/LatexMetricsTests.js

@@ -0,0 +1,112 @@
+const fs = require('node:fs')
+const path = require('node:path')
+const { expect } = require('chai')
+const { addLatexFdbMetrics } = require('../../../app/js/LatexMetrics')
+
+describe('LatexMetrics', function () {
+  describe('addLatexFdbMetrics', function () {
+    beforeEach(function () {
+      this.stats = {}
+      Object.defineProperty(this.stats, 'latexmk', {
+        value: {},
+        enumerable: false,
+      })
+    })
+
+    it('should do nothing if fdbContent is null or empty', function () {
+      addLatexFdbMetrics(null, this.stats)
+      expect(this.stats.latexmk).to.deep.equal({})
+      addLatexFdbMetrics('', this.stats)
+      expect(this.stats.latexmk).to.deep.equal({})
+    })
+
+    it('should parse v3 fdb content and add to stats', function () {
+      const fdbContent = fs.readFileSync(
+        path.join(__dirname, 'fixtures', 'v3.fdb_latexmk'),
+        'utf8'
+      )
+      addLatexFdbMetrics(fdbContent, this.stats)
+      expect(this.stats.latexmk['fdb-file-types']).to.deep.equal({
+        system: [
+          { ext: 'fmt', count: 1, size: 3847283 },
+          { ext: 'map', count: 2, size: 1644257 },
+          { ext: 'pfb', count: 12, size: 404691 },
+          { ext: 'sty', count: 19, size: 209868 },
+          { ext: 'mkii', count: 1, size: 71627 },
+          { ext: 'def', count: 1, size: 55368 },
+          { ext: 'cnf', count: 2, size: 32268 },
+          { ext: 'bst', count: 1, size: 24635 },
+          { ext: 'tfm', count: 16, size: 20608 },
+          { ext: 'cls', count: 1, size: 20496 },
+          { ext: 'clo', count: 1, size: 8967 },
+          { ext: 'cfg', count: 2, size: 4241 },
+          { ext: 'fd', count: 2, size: 4089 },
+        ],
+        user: [
+          { ext: 'png', count: 2, size: 3886031 },
+          { ext: 'tex', count: 1, size: 6147 },
+          { ext: 'aux', count: 1, size: 1080 },
+          { ext: 'bib', count: 1, size: 230 },
+          { ext: 'bbl', count: 1, size: 203 },
+        ],
+        total: {
+          fontFileCount: 0,
+          fontFileSize: 0,
+          imageFileCount: 2,
+          imageFileSize: 3886031,
+          otherFileCount: 2,
+          otherFileSize: 1283,
+          systemFileCount: 61,
+          systemFileSize: 6348398,
+          textFileCount: 2,
+          textFileSize: 6377,
+        },
+      })
+    })
+
+    it('should parse v4 fdb content and add to stats', function () {
+      const fdbContent = fs.readFileSync(
+        path.join(__dirname, 'fixtures', 'v4.fdb_latexmk'),
+        'utf8'
+      )
+      addLatexFdbMetrics(fdbContent, this.stats)
+      expect(this.stats.latexmk['fdb-file-types']).to.deep.equal({
+        system: [
+          { ext: 'fmt', count: 1, size: 8172536 },
+          { ext: 'map', count: 2, size: 4652176 },
+          { ext: 'pfb', count: 13, size: 542949 },
+          { ext: 'sty', count: 9, size: 100959 },
+          { ext: 'mkii', count: 1, size: 71627 },
+          { ext: 'def', count: 2, size: 49388 },
+          { ext: 'cnf', count: 2, size: 41037 },
+          { ext: 'bst', count: 1, size: 24635 },
+          { ext: 'tfm', count: 17, size: 22144 },
+          { ext: 'cls', count: 1, size: 20144 },
+          { ext: 'clo', count: 1, size: 8448 },
+          { ext: 'enc', count: 1, size: 2900 },
+          { ext: 'fd', count: 1, size: 2470 },
+          { ext: 'cfg', count: 2, size: 1902 },
+        ],
+        user: [
+          { ext: 'png', count: 2, size: 3886031 },
+          { ext: 'tex', count: 1, size: 6147 },
+          { ext: 'aux', count: 1, size: 1382 },
+          { ext: 'bib', count: 1, size: 230 },
+          { ext: 'bbl', count: 1, size: 203 },
+        ],
+        total: {
+          fontFileCount: 0,
+          fontFileSize: 0,
+          imageFileCount: 2,
+          imageFileSize: 3886031,
+          otherFileCount: 2,
+          otherFileSize: 1585,
+          systemFileCount: 54,
+          systemFileSize: 13713315,
+          textFileCount: 2,
+          textFileSize: 6377,
+        },
+      })
+    })
+  })
+})

+ 81 - 0
services/clsi/test/unit/js/fixtures/v3.fdb_latexmk

@@ -0,0 +1,81 @@
+# Fdb version 3
+["bibtex output"] 1760016711 "output.aux" "output.bbl" "output" 1760016711
+  "/usr/local/texlive/2014/texmf-dist/bibtex/bst/base/apalike.bst" 1292289607 24635 604dafcc79f521a11ea73e506e1fbd98 ""
+  "output.aux" 1760016711 1080 1120b2c4bfc950ce767f3b27e61c9eab ""
+  "output.bcf" 0 -1 0 ""
+  "references.bib" 1760016710 230 a9ffc0446ebf8fd522116ce4ffea9c00 ""
+  (generated)
+  "output.bbl"
+["pdflatex"] 1760016711 "main.tex" "/compile/output.pdf" "output" 1760016711
+  "/compile/output.aux" 1760016711 1080 1120b2c4bfc950ce767f3b27e61c9eab ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/map/fontname/texfonts.map" 1272929888 3287 e6b82fe08f5336d4d5ebc73fb1152e87 ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/tfm/public/cm/cmbx10.tfm" 1136768653 1328 c834bbb027764024c09d3d2bf908b5f0 ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/tfm/public/cm/cmbx12.tfm" 1136768653 1324 c910af8c371558dc20f2d7822f66fe64 ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/tfm/public/cm/cmbx9.tfm" 1136768653 1328 5442e22a7072966dbaf88ca900acf3f0 ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/tfm/public/cm/cmmi12.tfm" 1136768653 1524 4414a8315f39513458b80dfc63bff03a ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/tfm/public/cm/cmmi6.tfm" 1136768653 1512 f21f83efb36853c0b70002322c1ab3ad ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/tfm/public/cm/cmmi8.tfm" 1136768653 1520 eccf95517727cb11801f4f1aee3a21b4 ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/tfm/public/cm/cmr12.tfm" 1136768653 1288 655e228510b4c2a1abe905c368440826 ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/tfm/public/cm/cmr17.tfm" 1136768653 1292 296a67155bdbfc32aa9c636f21e91433 ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/tfm/public/cm/cmr6.tfm" 1136768653 1300 b62933e007d01cfd073f79b963c01526 ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/tfm/public/cm/cmr8.tfm" 1136768653 1292 21c1c5bfeaebccffdb478fd231a0997d ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/tfm/public/cm/cmr9.tfm" 1136768653 1292 6b21b9c2c7bebb38aa2273f7ca0fb3af ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/tfm/public/cm/cmsy10.tfm" 1136768653 1124 6c73e740cf17375f03eec0ee63599741 ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/tfm/public/cm/cmsy6.tfm" 1136768653 1116 933a60c408fc0a863a92debe84b2d294 ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/tfm/public/cm/cmsy8.tfm" 1136768653 1120 8b7d695260f3cff42e636090a8002094 ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/tfm/public/cm/cmti10.tfm" 1136768653 1480 aa8e34af0eb6a2941b776984cf1dfdc4 ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/tfm/public/cm/cmtt10.tfm" 1136768653 768 1321e9409b4137d6fb428ac9dc956269 ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/type1/public/amsfonts/cm/cmbx10.pfb" 1248133631 34811 78b52f49e893bcba91bd7581cdc144c0 ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/type1/public/amsfonts/cm/cmbx12.pfb" 1248133631 32080 340ef9bf63678554ee606688e7b5339d ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/type1/public/amsfonts/cm/cmbx9.pfb" 1248133631 32298 c6d25bb16d1eac01ebdc6d7084126a1e ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi10.pfb" 1248133631 36299 5f9df58c2139e7edcf37c8fca4bd384d ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb" 1248133631 35752 024fb6c41858982481f6968b5fc26508 ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/type1/public/amsfonts/cm/cmr12.pfb" 1248133631 32722 d7379af29a190c3f453aba36302ff5a9 ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/type1/public/amsfonts/cm/cmr17.pfb" 1248133631 32362 179c33bbf43f19adbb3825bb4e36e57a ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/type1/public/amsfonts/cm/cmr7.pfb" 1248133631 32762 224316ccc9ad3ca0423a14971cfa7fc1 ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/type1/public/amsfonts/cm/cmr9.pfb" 1248133631 33993 9b89b85fd2d9df0482bd47194d1d3bf3 ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy10.pfb" 1248133631 32569 5e5ddc8df908dea60932f3c484a54c0d ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/type1/public/amsfonts/cm/cmti10.pfb" 1248133631 37944 359e864bd06cde3b1cf57bb20757fb06 ""
+  "/usr/local/texlive/2014/texmf-dist/fonts/type1/public/amsfonts/cm/cmtt10.pfb" 1248133631 31099 c85edf1dd5b9e826d67c9c7293b6786c ""
+  "/usr/local/texlive/2014/texmf-dist/tex/context/base/supp-pdf.mkii" 1337017135 71627 94eb9990bed73c364d7f53f960cc8c5b ""
+  "/usr/local/texlive/2014/texmf-dist/tex/generic/ifxetex/ifxetex.sty" 1284331290 1458 43ab4710dc82f3edeabecd0d099626b2 ""
+  "/usr/local/texlive/2014/texmf-dist/tex/generic/oberdiek/etexcmds.sty" 1335995445 7612 c47308d923ec19888707b0f1792b326a ""
+  "/usr/local/texlive/2014/texmf-dist/tex/generic/oberdiek/ifluatex.sty" 1303254447 7324 11d14f318d865f420e692d4e6c9c18c3 ""
+  "/usr/local/texlive/2014/texmf-dist/tex/generic/oberdiek/ifpdf.sty" 1303254447 7140 ece2cc23d9f20e1f53975ac167f42d3e ""
+  "/usr/local/texlive/2014/texmf-dist/tex/generic/oberdiek/ifvtex.sty" 1335995445 6797 68c89f65e01894df882dd523d3fc0a8f ""
+  "/usr/local/texlive/2014/texmf-dist/tex/generic/oberdiek/infwarerr.sty" 1335995445 8253 3bdedc8409aa5d290a2339be6f09af03 ""
+  "/usr/local/texlive/2014/texmf-dist/tex/generic/oberdiek/kvdefinekeys.sty" 1335995445 5152 387d9200f396b498d5fd679ae44ed898 ""
+  "/usr/local/texlive/2014/texmf-dist/tex/generic/oberdiek/kvsetkeys.sty" 1335995445 14040 8de9f47fabc4ca3bd69b6d795e32751c ""
+  "/usr/local/texlive/2014/texmf-dist/tex/generic/oberdiek/ltxcmds.sty" 1335995445 18425 775b341047ce304520cc7c11ca41392e ""
+  "/usr/local/texlive/2014/texmf-dist/tex/generic/oberdiek/pdftexcmds.sty" 1335995445 19987 01cb2f3c1d21e5f05711b7fd50b17f2a ""
+  "/usr/local/texlive/2014/texmf-dist/tex/latex/base/article.cls" 1415144112 20496 25b54d5a19c40eb011a68431975d83fe ""
+  "/usr/local/texlive/2014/texmf-dist/tex/latex/base/omlcmr.fd" 1415144112 2045 283939c750107d9977e7c396d3f2e00d ""
+  "/usr/local/texlive/2014/texmf-dist/tex/latex/base/omscmr.fd" 1415144112 2044 0c5bdb386a9131ba14472afc2c48653d ""
+  "/usr/local/texlive/2014/texmf-dist/tex/latex/base/size10.clo" 1415144112 8967 2708205432f6a3ece9206e1f8d1214fb ""
+  "/usr/local/texlive/2014/texmf-dist/tex/latex/geometry/geometry.sty" 1284422013 40502 e003406220954b0716679d7928aedd8a ""
+  "/usr/local/texlive/2014/texmf-dist/tex/latex/graphics/graphics.sty" 1415923035 14189 66438d186cddf9823e0cb45858daca8c ""
+  "/usr/local/texlive/2014/texmf-dist/tex/latex/graphics/graphicx.sty" 1415923035 7911 1cba3b04e7ab1f8ace99b431fca372ac ""
+  "/usr/local/texlive/2014/texmf-dist/tex/latex/graphics/keyval.sty" 1415923035 2317 f0bb266f9d8a4fb51671d02627254c73 ""
+  "/usr/local/texlive/2014/texmf-dist/tex/latex/graphics/trig.sty" 1156702488 3153 c50e6bd54d2dd3933fc52bcf369bec4a ""
+  "/usr/local/texlive/2014/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg" 1279039959 678 4792914a8f45be57bb98413425e4c7af ""
+  "/usr/local/texlive/2014/texmf-dist/tex/latex/latexconfig/graphics.cfg" 1278958963 3563 d35e897cae3b8c6848f6677b73370b54 ""
+  "/usr/local/texlive/2014/texmf-dist/tex/latex/oberdiek/epstopdf-base.sty" 1303254447 12029 04d7fdf76e0464c23b5aa3a727952d7c ""
+  "/usr/local/texlive/2014/texmf-dist/tex/latex/oberdiek/grfext.sty" 1335995445 7075 bd0c34fbf1ae8fd1debd2a554e41b2d5 ""
+  "/usr/local/texlive/2014/texmf-dist/tex/latex/oberdiek/kvoptions.sty" 1335995445 22417 c74ff4af6a1aa2b65d1924020edbbe11 ""
+  "/usr/local/texlive/2014/texmf-dist/tex/latex/pdftex-def/pdftex.def" 1306616590 55368 3c8a0d99822330f2dfabc0dfb09ce897 ""
+  "/usr/local/texlive/2014/texmf-dist/tex/latex/placeins/placeins.sty" 1137110565 4087 636308456f60d2b31cbf97867db5708d ""
+  "/usr/local/texlive/2014/texmf-dist/web2c/texmf.cnf" 1427202111 31813 278f393fac9aa1fe43accdd16bd9ad2b ""
+  "/usr/local/texlive/2014/texmf-var/fonts/map/pdftex/updmap/pdftex.map" 1427192307 1640970 6e9d9b9d0fb86fadb6e255a8bafce781 ""
+  "/usr/local/texlive/2014/texmf-var/web2c/pdftex/pdflatex.fmt" 1427192373 3847283 624656363f426539ed06bf4eb69090e1 ""
+  "/usr/local/texlive/2014/texmf.cnf" 1414518529 455 5b996dcaa0eb4ef14a83b026bc0a008c ""
+  "duck.png" 1760016710 1810070 a0706053cc7bbc4a9ba9f3143a935fa9 ""
+  "ducklings.png" 1760016710 2075961 c3f28212a57be365eeaaeb6dcf19e87c ""
+  "main.tex" 1760016710 6147 9da336eb1132ecb1d61cd1f5a70cfa62 ""
+  "output.aux" 1760016711 1080 1120b2c4bfc950ce767f3b27e61c9eab ""
+  "output.bbl" 1760016711 203 3852f6dfe7c9bace75cac514ada6c2e1 "bibtex output"
+  (generated)
+  "/compile/output.log"
+  "output.aux"
+  "output.pdf"
+  "output.log"
+  "/compile/output.pdf"

+ 73 - 0
services/clsi/test/unit/js/fixtures/v4.fdb_latexmk

@@ -0,0 +1,73 @@
+# Fdb version 4
+["bibtex output"] 1760016358 "output.aux" "output.bbl" "output" 1760016467 0
+  "./references.bib" 1760016467 230 a9ffc0446ebf8fd522116ce4ffea9c00 ""
+  "/usr/local/texlive/2023/texmf-dist/bibtex/bst/base/apalike.bst" 1292289607 24635 604dafcc79f521a11ea73e506e1fbd98 ""
+  "output.aux" 1760016467 1382 704134ab1925e12d496665950786c3bf "pdflatex"
+  (generated)
+  "output.bbl"
+  "output.blg"
+  (rewritten before read)
+["pdflatex"] 1760016467 "main.tex" "output.pdf" "output" 1760016467 0
+  "/usr/local/texlive/2023/texmf-dist/fonts/enc/dvips/cm-super/cm-super-ts1.enc" 1136849721 2900 1537cc8184ad1792082cd229ecc269f4 ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/map/fontname/texfonts.map" 1577235249 3524 cb3e574dea2d1052e39280babc910dc8 ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/tfm/jknappen/ec/tcrm1000.tfm" 1136768653 1536 e07581a4bb3136ece9eeb4c3ffab8233 ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmbx10.tfm" 1136768653 1328 c834bbb027764024c09d3d2bf908b5f0 ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmbx12.tfm" 1136768653 1324 c910af8c371558dc20f2d7822f66fe64 ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmbx9.tfm" 1136768653 1328 5442e22a7072966dbaf88ca900acf3f0 ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmmi12.tfm" 1136768653 1524 4414a8315f39513458b80dfc63bff03a ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmmi6.tfm" 1136768653 1512 f21f83efb36853c0b70002322c1ab3ad ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmmi8.tfm" 1136768653 1520 eccf95517727cb11801f4f1aee3a21b4 ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmr12.tfm" 1136768653 1288 655e228510b4c2a1abe905c368440826 ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmr17.tfm" 1136768653 1292 296a67155bdbfc32aa9c636f21e91433 ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmr6.tfm" 1136768653 1300 b62933e007d01cfd073f79b963c01526 ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmr8.tfm" 1136768653 1292 21c1c5bfeaebccffdb478fd231a0997d ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmr9.tfm" 1136768653 1292 6b21b9c2c7bebb38aa2273f7ca0fb3af ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmsy10.tfm" 1136768653 1124 6c73e740cf17375f03eec0ee63599741 ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmsy6.tfm" 1136768653 1116 933a60c408fc0a863a92debe84b2d294 ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmsy8.tfm" 1136768653 1120 8b7d695260f3cff42e636090a8002094 ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmti10.tfm" 1136768653 1480 aa8e34af0eb6a2941b776984cf1dfdc4 ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmtt10.tfm" 1136768653 768 1321e9409b4137d6fb428ac9dc956269 ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmbx10.pfb" 1248133631 34811 78b52f49e893bcba91bd7581cdc144c0 ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmbx12.pfb" 1248133631 32080 340ef9bf63678554ee606688e7b5339d ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmbx9.pfb" 1248133631 32298 c6d25bb16d1eac01ebdc6d7084126a1e ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi10.pfb" 1248133631 36299 5f9df58c2139e7edcf37c8fca4bd384d ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb" 1248133631 35752 024fb6c41858982481f6968b5fc26508 ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmr12.pfb" 1248133631 32722 d7379af29a190c3f453aba36302ff5a9 ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmr17.pfb" 1248133631 32362 179c33bbf43f19adbb3825bb4e36e57a ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmr7.pfb" 1248133631 32762 224316ccc9ad3ca0423a14971cfa7fc1 ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmr9.pfb" 1248133631 33993 9b89b85fd2d9df0482bd47194d1d3bf3 ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy10.pfb" 1248133631 32569 5e5ddc8df908dea60932f3c484a54c0d ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmti10.pfb" 1248133631 37944 359e864bd06cde3b1cf57bb20757fb06 ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmtt10.pfb" 1248133631 31099 c85edf1dd5b9e826d67c9c7293b6786c ""
+  "/usr/local/texlive/2023/texmf-dist/fonts/type1/public/cm-super/sfrm1000.pfb" 1215737283 138258 6525c253f16cededa14c7fd0da7f67b2 ""
+  "/usr/local/texlive/2023/texmf-dist/tex/context/base/mkii/supp-pdf.mkii" 1461363279 71627 94eb9990bed73c364d7f53f960cc8c5b ""
+  "/usr/local/texlive/2023/texmf-dist/tex/generic/iftex/iftex.sty" 1644112042 7237 bdd120a32c8fdb4b433cf9ca2e7cd98a ""
+  "/usr/local/texlive/2023/texmf-dist/tex/generic/iftex/ifvtex.sty" 1572645307 1057 525c2192b5febbd8c1f662c9468335bb ""
+  "/usr/local/texlive/2023/texmf-dist/tex/latex/base/article.cls" 1686341992 20144 fbcf51733730d8f1a62143512ff5b1fe ""
+  "/usr/local/texlive/2023/texmf-dist/tex/latex/base/omlcmr.fd" 1686341992 2470 6599b13c7da0906c887a7e3cfca75f4e ""
+  "/usr/local/texlive/2023/texmf-dist/tex/latex/base/size10.clo" 1686341992 8448 74078c4a887f2cdcd57c3ec111622740 ""
+  "/usr/local/texlive/2023/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty" 1579991033 13886 d1306dcf79a944f6988e688c1785f9ce ""
+  "/usr/local/texlive/2023/texmf-dist/tex/latex/geometry/geometry.sty" 1578002852 41601 9cf6c5257b1bc7af01a58859749dd37a ""
+  "/usr/local/texlive/2023/texmf-dist/tex/latex/graphics-cfg/graphics.cfg" 1465944070 1224 978390e9c2234eab29404bc21b268d1e ""
+  "/usr/local/texlive/2023/texmf-dist/tex/latex/graphics-def/pdftex.def" 1663965824 19448 1e988b341dda20961a6b931bcde55519 ""
+  "/usr/local/texlive/2023/texmf-dist/tex/latex/graphics/graphics.sty" 1654720880 18387 8f900a490197ebaf93c02ae9476d4b09 ""
+  "/usr/local/texlive/2023/texmf-dist/tex/latex/graphics/graphicx.sty" 1654720880 8010 a8d949cbdbc5c983593827c9eec252e1 ""
+  "/usr/local/texlive/2023/texmf-dist/tex/latex/graphics/keyval.sty" 1654720880 2671 7e67d78d9b88c845599a85b2d41f2e39 ""
+  "/usr/local/texlive/2023/texmf-dist/tex/latex/graphics/trig.sty" 1654720880 4023 293ea1c16429fc0c4cf605f4da1791a9 ""
+  "/usr/local/texlive/2023/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def" 1681935207 29940 9473d58112bc8a88f5505b823a053dde ""
+  "/usr/local/texlive/2023/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg" 1279039959 678 4792914a8f45be57bb98413425e4c7af ""
+  "/usr/local/texlive/2023/texmf-dist/tex/latex/placeins/placeins.sty" 1137110565 4087 636308456f60d2b31cbf97867db5708d ""
+  "/usr/local/texlive/2023/texmf-dist/web2c/texmf.cnf" 1681165822 40967 d940221f8762fd5848760382d97d1226 ""
+  "/usr/local/texlive/2023/texmf-var/fonts/map/pdftex/updmap/pdftex.map" 1691423502 4648652 0564963f60f2444ccc86ab709ed5cd6d ""
+  "/usr/local/texlive/2023/texmf-var/web2c/pdftex/pdflatex.fmt" 1693258675 8172536 79a4224afd68f9742750f1cc18a81fb8 ""
+  "/usr/local/texlive/2023/texmf.cnf" 1686916785 70 5459fb59cf354c6f7234715ba9bfff91 ""
+  "duck.png" 1760016467 1810070 a0706053cc7bbc4a9ba9f3143a935fa9 ""
+  "ducklings.png" 1760016467 2075961 c3f28212a57be365eeaaeb6dcf19e87c ""
+  "main.tex" 1760016467 6147 9da336eb1132ecb1d61cd1f5a70cfa62 ""
+  "output.aux" 1760016467 1382 704134ab1925e12d496665950786c3bf "pdflatex"
+  "output.bbl" 1760016359 203 3852f6dfe7c9bace75cac514ada6c2e1 "bibtex output"
+  (generated)
+  "output.aux"
+  "output.log"
+  "output.pdf"
+  (rewritten before read)