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 */ 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 = { // eslint-disable-line no-unused-vars 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; // eslint-disable-line no-unused-vars 71function read(fileName) { // eslint-disable-line no-unused-vars 72 return fs.readFileSync(fileName, 'utf8'); 73} 74const quit = process.exit; // eslint-disable-line no-unused-vars 75 76// Polyfill "readline()". 77const logFile = arguments[arguments.length - 1]; // eslint-disable-line no-undef 78try { 79 fs.accessSync(logFile); 80} catch { 81 console.error('Please provide a valid isolate file as the final argument.'); 82 process.exit(1); 83} 84const fd = fs.openSync(logFile, 'r'); 85const buf = Buffer.allocUnsafe(4096); 86const dec = new (require('string_decoder').StringDecoder)('utf-8'); 87let line = ''; 88 89{ 90 const message = versionCheck(peekline(), process.versions.v8); 91 if (message) console.log(message); 92} 93 94function peekline() { 95 const s = readline(); 96 line = `${s}\n${line}`; 97 return s; 98} 99 100function readline() { 101 while (true) { 102 const lineBreak = line.indexOf('\n'); 103 if (lineBreak !== -1) { 104 const res = line.slice(0, lineBreak); 105 line = line.slice(lineBreak + 1); 106 return res; 107 } 108 const bytes = fs.readSync(fd, buf, 0, buf.length); 109 line += dec.write(buf.slice(0, bytes)); 110 if (line.length === 0) { 111 return ''; 112 } 113 if (bytes === 0) { 114 process.emitWarning(`Profile file ${logFile} is broken`, { 115 code: 'BROKEN_PROFILE_FILE', 116 detail: `${JSON.stringify(line)} at the file end is broken` 117 }); 118 return ''; 119 } 120 } 121} 122 123function versionCheck(firstLine, expected) { 124 // v8-version looks like 125 // "v8-version,$major,$minor,$build,$patch[,$embedder],$candidate" 126 // whereas process.versions.v8 is either "$major.$minor.$build-$embedder" or 127 // "$major.$minor.$build.$patch-$embedder". 128 firstLine = firstLine.split(','); 129 const curVer = expected.split(/[.-]/); 130 if ((firstLine.length !== 6 && firstLine.length !== 7) || 131 firstLine[0] !== 'v8-version') { 132 return 'Unable to read v8-version from log file.'; 133 } 134 // Compare major, minor and build; ignore the patch and candidate fields. 135 for (let i = 0; i < 3; i++) 136 if (curVer[i] !== firstLine[i + 1]) 137 return 'Testing v8 version different from logging version'; 138} 139 140function macCppfiltNm(out) { 141 // Re-grouped copy-paste from `tickprocessor.js` 142 const FUNC_RE = /^([0-9a-fA-F]{8,16} [iItT] )(.*)$/gm; 143 const CLEAN_RE = /^[0-9a-fA-F]{8,16} [iItT] /; 144 let entries = out.match(FUNC_RE); 145 if (entries === null) 146 return out; 147 148 entries = entries.map((entry) => entry.replace(CLEAN_RE, '')); 149 150 let filtered; 151 try { 152 filtered = cp.spawnSync('c++filt', [ '-p', '-i' ], { 153 input: entries.join('\n') 154 }).stdout.toString(); 155 } catch { 156 return out; 157 } 158 159 let i = 0; 160 filtered = filtered.split('\n'); 161 return out.replace(FUNC_RE, (all, prefix, postfix) => { 162 return prefix + (filtered[i++] || postfix); 163 }); 164} 165