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/* eslint-disable no-restricted-globals */ 29 30module.exports = { versionCheck }; 31 32// Don't execute when required directly instead of being eval'd from 33// lib/internal/v8_prof_processor.js. This way we can test functions 34// from this file in isolation. 35if (module.id === 'internal/v8_prof_polyfill') return; 36 37// Node polyfill 38const fs = require('fs'); 39const cp = require('child_process'); 40const os = { 41 system: function(name, args) { 42 if (process.platform === 'linux' && name === 'nm') { 43 // Filter out vdso and vsyscall entries. 44 const arg = args[args.length - 1]; 45 if (arg === '[vdso]' || 46 arg === '[vsyscall]' || 47 /^[0-9a-f]+-[0-9a-f]+$/.test(arg)) { 48 return ''; 49 } 50 } 51 let out = cp.spawnSync(name, args).stdout.toString(); 52 // Auto c++filt names, but not [iItT] 53 if (process.platform === 'darwin' && name === 'nm') { 54 // nm prints an error along the lines of "Run xcodebuild -license" and 55 // exits when Xcode hasn't been properly installed or when its license 56 // hasn't been accepted yet. Basically any mention of xcodebuild in 57 // the output means the nm command is non-functional. 58 const match = out.match(/(?:^|\n)([^\n]*xcodebuild[^\n]*)(?:\n|$)/); 59 if (match) throw new Error(match[1]); 60 out = macCppfiltNm(out); 61 } 62 return out; 63 } 64}; 65const print = console.log; 66function read(fileName) { 67 return fs.readFileSync(fileName, 'utf8'); 68} 69const quit = process.exit; 70 71// Polyfill "readline()". 72const logFile = arguments[arguments.length - 1]; 73try { 74 fs.accessSync(logFile); 75} catch(e) { 76 console.error('Please provide a valid isolate file as the final argument.'); 77 process.exit(1); 78} 79const fd = fs.openSync(logFile, 'r'); 80const buf = Buffer.allocUnsafe(4096); 81const dec = new (require('string_decoder').StringDecoder)('utf-8'); 82let line = ''; 83 84{ 85 const message = versionCheck(peekline(), process.versions.v8); 86 if (message) console.log(message); 87} 88 89function peekline() { 90 const s = readline(); 91 line = `${s}\n${line}`; 92 return s; 93} 94 95function readline() { 96 while (true) { 97 const lineBreak = line.indexOf('\n'); 98 if (lineBreak !== -1) { 99 const res = line.slice(0, lineBreak); 100 line = line.slice(lineBreak + 1); 101 return res; 102 } 103 const bytes = fs.readSync(fd, buf, 0, buf.length); 104 line += dec.write(buf.slice(0, bytes)); 105 if (line.length === 0) { 106 return ''; 107 } 108 if (bytes === 0) { 109 process.emitWarning(`Profile file ${logFile} is broken`, { 110 code: 'BROKEN_PROFILE_FILE', 111 detail: `${JSON.stringify(line)} at the file end is broken` 112 }); 113 return ''; 114 } 115 } 116} 117 118function versionCheck(firstLine, expected) { 119 // v8-version looks like 120 // "v8-version,$major,$minor,$build,$patch[,$embedder],$candidate" 121 // whereas process.versions.v8 is either "$major.$minor.$build-$embedder" or 122 // "$major.$minor.$build.$patch-$embedder". 123 firstLine = firstLine.split(','); 124 const curVer = expected.split(/[.\-]/); 125 if (firstLine.length !== 6 && firstLine.length !== 7 || 126 firstLine[0] !== 'v8-version') { 127 return 'Unable to read v8-version from log file.'; 128 } 129 // Compare major, minor and build; ignore the patch and candidate fields. 130 for (let i = 0; i < 3; i++) 131 if (curVer[i] !== firstLine[i + 1]) 132 return 'Testing v8 version different from logging version'; 133} 134 135function macCppfiltNm(out) { 136 // Re-grouped copy-paste from `tickprocessor.js` 137 const FUNC_RE = /^([0-9a-fA-F]{8,16} [iItT] )(.*)$/gm; 138 const CLEAN_RE = /^[0-9a-fA-F]{8,16} [iItT] /; 139 let entries = out.match(FUNC_RE); 140 if (entries === null) 141 return out; 142 143 entries = entries.map((entry) => { 144 return entry.replace(CLEAN_RE, '') 145 }); 146 147 let filtered; 148 try { 149 filtered = cp.spawnSync('c++filt', [ '-p' , '-i' ], { 150 input: entries.join('\n') 151 }).stdout.toString(); 152 } catch { 153 return out; 154 } 155 156 let i = 0; 157 filtered = filtered.split('\n'); 158 return out.replace(FUNC_RE, (all, prefix, postfix) => { 159 return prefix + (filtered[i++] || postfix); 160 }); 161} 162