1// Copyright 2012 the V8 project authors. All rights reserved. 2// Redistribution and use in source and binary forms, with or without 3// modification, are permitted provided that the following conditions are 4// met: 5// 6// * Redistributions of source code must retain the above copyright 7// notice, this list of conditions and the following disclaimer. 8// * Redistributions in binary form must reproduce the above 9// copyright notice, this list of conditions and the following 10// disclaimer in the documentation and/or other materials provided 11// with the distribution. 12// * Neither the name of Google Inc. nor the names of its 13// contributors may be used to endorse or promote products derived 14// from this software without specific prior written permission. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28'use strict'; 29 30/* eslint-disable node-core/prefer-primordials, no-restricted-globals */ 31/* global console */ 32 33module.exports = { versionCheck }; 34 35// Don't execute when required directly instead of being eval'd from 36// lib/internal/v8_prof_processor.js. This way we can test functions 37// from this file in isolation. 38if (module.id === 'internal/v8_prof_polyfill') return; 39 40// Node polyfill 41const fs = require('fs'); 42const cp = require('child_process'); 43const { Buffer } = require('buffer'); 44const os = { 45 system: function(name, args) { 46 if (process.platform === 'linux' && name === 'nm') { 47 // Filter out vdso and vsyscall entries. 48 const arg = args[args.length - 1]; 49 if (arg === '[vdso]' || 50 arg === '[vsyscall]' || 51 /^[0-9a-f]+-[0-9a-f]+$/.test(arg)) { 52 return ''; 53 } 54 } 55 let out = cp.spawnSync(name, args).stdout.toString(); 56 // Auto c++filt names, but not [iItT] 57 if (process.platform === 'darwin' && name === 'nm') { 58 // `nm` prints an error along the lines of "Run xcodebuild -license" and 59 // exits when Xcode hasn't been properly installed or when its license 60 // hasn't been accepted yet. Basically any mention of xcodebuild in 61 // the output means the nm command is non-functional. 62 const match = out.match(/(?:^|\n)([^\n]*xcodebuild[^\n]*)(?:\n|$)/); 63 // eslint-disable-next-line no-restricted-syntax 64 if (match) throw new Error(match[1]); 65 out = macCppfiltNm(out); 66 } 67 return out; 68 }, 69}; 70const print = console.log; 71function read(fileName) { 72 return fs.readFileSync(fileName, 'utf8'); 73} 74const quit = process.exit; 75// Polyfill "readline()". 76const logFile = globalThis.arguments[globalThis.arguments.length - 1]; 77try { 78 fs.accessSync(logFile); 79} catch { 80 console.error('Please provide a valid isolate file as the final argument.'); 81 process.exit(1); 82} 83const fd = fs.openSync(logFile, 'r'); 84const buf = Buffer.allocUnsafe(4096); 85const dec = new (require('string_decoder').StringDecoder)('utf-8'); 86let line = ''; 87 88{ 89 const message = versionCheck(peekline(), process.versions.v8); 90 if (message) console.log(message); 91} 92 93function peekline() { 94 const s = readline(); 95 line = `${s}\n${line}`; 96 return s; 97} 98 99function readline() { 100 while (true) { 101 const lineBreak = line.indexOf('\n'); 102 if (lineBreak !== -1) { 103 const res = line.slice(0, lineBreak); 104 line = line.slice(lineBreak + 1); 105 return res; 106 } 107 const bytes = fs.readSync(fd, buf, 0, buf.length); 108 line += dec.write(buf.slice(0, bytes)); 109 if (line.length === 0) { 110 return ''; 111 } 112 if (bytes === 0) { 113 process.emitWarning(`Profile file ${logFile} is broken`, { 114 code: 'BROKEN_PROFILE_FILE', 115 detail: `${JSON.stringify(line)} at the file end is broken`, 116 }); 117 return ''; 118 } 119 } 120} 121 122function versionCheck(firstLine, expected) { 123 // v8-version looks like 124 // "v8-version,$major,$minor,$build,$patch[,$embedder],$candidate" 125 // whereas process.versions.v8 is either "$major.$minor.$build-$embedder" or 126 // "$major.$minor.$build.$patch-$embedder". 127 firstLine = firstLine.split(','); 128 const curVer = expected.split(/[.-]/); 129 if ((firstLine.length !== 6 && firstLine.length !== 7) || 130 firstLine[0] !== 'v8-version') { 131 return 'Unable to read v8-version from log file.'; 132 } 133 // Compare major, minor and build; ignore the patch and candidate fields. 134 for (let i = 0; i < 3; i++) 135 if (curVer[i] !== firstLine[i + 1]) 136 return 'Testing v8 version different from logging version'; 137} 138 139function macCppfiltNm(out) { 140 // Re-grouped copy-paste from `tickprocessor.js` 141 const FUNC_RE = /^([0-9a-fA-F]{8,16} [iItT] )(.*)$/gm; 142 const CLEAN_RE = /^[0-9a-fA-F]{8,16} [iItT] /; 143 let entries = out.match(FUNC_RE); 144 if (entries === null) 145 return out; 146 147 entries = entries.map((entry) => entry.replace(CLEAN_RE, '')); 148 149 let filtered; 150 try { 151 filtered = cp.spawnSync('c++filt', [ '-p', '-i' ], { 152 input: entries.join('\n'), 153 }).stdout.toString(); 154 } catch { 155 return out; 156 } 157 158 let i = 0; 159 filtered = filtered.split('\n'); 160 return out.replace(FUNC_RE, (all, prefix, postfix) => { 161 return prefix + (filtered[i++] || postfix); 162 }); 163} 164 165Object.assign(globalThis, { 166 os, 167 print, 168 read, 169 quit, 170 readline, 171}); 172