Skip to content

Commit efc1a98

Browse files
Vasili SkurydzinV8 LUCI CQ
authored andcommitted
Use BigInts in processor.mjs and related code to avoid unsafe ints in
calculations Bug: v8:13440 Change-Id: Ie03b831b511a49fb475b9f303ef8662189bdaf3d Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4017455 Reviewed-by: Camillo Bruni <[email protected]> Commit-Queue: Camillo Bruni <[email protected]> Cr-Commit-Position: refs/heads/main@{#84698}
1 parent 122836d commit efc1a98

6 files changed

Lines changed: 126 additions & 37 deletions

File tree

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2020 the V8 project authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// Flags: --logfile='+' --log --log-maps --log-ic --log-code
6+
// Flags: --log-function-events --no-predictable
7+
8+
import { Processor } from "../../../tools/system-analyzer/processor.mjs";
9+
10+
// log code start
11+
function doWork() {
12+
let array = [];
13+
for (let i = 0; i < 500; i++) {
14+
doWorkStep(i, array);
15+
}
16+
let sum = 0;
17+
for (let i = 0; i < 500; i++) {
18+
sum += array[i]["property" + i];
19+
}
20+
return sum;
21+
}
22+
23+
function doWorkStep(i, array) {
24+
const obj = {
25+
["property" + i]: i,
26+
};
27+
array.push(obj);
28+
obj.custom1 = 1;
29+
obj.custom2 = 2;
30+
}
31+
32+
const result = doWork();
33+
// log code end
34+
35+
const logString = d8.log.getAndStop();
36+
assertTrue(logString.length > 0);
37+
const useBigInts = true;
38+
const processor = new Processor(useBigInts);
39+
await processor.processChunk(logString);
40+
await processor.finalize();
41+
42+
const maps = processor.mapTimeline;
43+
const ics = processor.icTimeline;
44+
const scripts = processor.scripts;
45+
46+
(function testResults() {
47+
assertEquals(result, 124750);
48+
assertTrue(maps.length > 0);
49+
assertTrue(ics.length > 0);
50+
assertTrue(scripts.length > 0);
51+
})();
52+
53+
(function testIcKeys() {
54+
const keys = new Set();
55+
ics.forEach(ic => keys.add(ic.key));
56+
assertTrue(keys.has("custom1"));
57+
assertTrue(keys.has("custom2"));
58+
assertTrue(keys.has("push"));
59+
})();

tools/codemap.mjs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ export class CodeMap {
6565
*/
6666
pages_ = new Set();
6767

68+
constructor(useBigInt=false) {
69+
this.useBigInt = useBigInt;
70+
this.kPageSize = useBigInt ? BigInt(kPageSize) : kPageSize;
71+
this.kOne = useBigInt ? 1n : 1;
72+
this.kZero = useBigInt ? 0n : 0;
73+
}
6874

6975
/**
7076
* Adds a code entry that might overlap with static code (e.g. for builtins).
@@ -73,7 +79,7 @@ export class CodeMap {
7379
* @param {CodeEntry} codeEntry Code entry object.
7480
*/
7581
addAnyCode(start, codeEntry) {
76-
const pageAddr = (start / kPageSize) | 0;
82+
const pageAddr = (start / this.kPageSize) | this.kZero;
7783
if (!this.pages_.has(pageAddr)) return this.addCode(start, codeEntry);
7884
// We might have loaded static code (builtins, bytecode handlers)
7985
// and we get more information later in v8.log with code-creation events.
@@ -147,8 +153,8 @@ export class CodeMap {
147153
* @private
148154
*/
149155
markPages_(start, end) {
150-
for (let addr = start; addr <= end; addr += kPageSize) {
151-
this.pages_.add((addr / kPageSize) | 0);
156+
for (let addr = start; addr <= end; addr += this.kPageSize) {
157+
this.pages_.add((addr / this.kPageSize) | this.kZero);
152158
}
153159
}
154160

@@ -157,13 +163,13 @@ export class CodeMap {
157163
*/
158164
deleteAllCoveredNodes_(tree, start, end) {
159165
const to_delete = [];
160-
let addr = end - 1;
166+
let addr = end - this.kOne;
161167
while (addr >= start) {
162168
const node = tree.findGreatestLessThan(addr);
163169
if (node === null) break;
164170
const start2 = node.key, end2 = start2 + node.value.size;
165171
if (start2 < end && start < end2) to_delete.push(start2);
166-
addr = start2 - 1;
172+
addr = start2 - this.kOne;
167173
}
168174
for (let i = 0, l = to_delete.length; i < l; ++i) tree.remove(to_delete[i]);
169175
}
@@ -191,7 +197,7 @@ export class CodeMap {
191197
* @param {number} addr Address.
192198
*/
193199
findAddress(addr) {
194-
const pageAddr = (addr / kPageSize) | 0;
200+
const pageAddr = (addr / this.kPageSize) | this.kZero;
195201
if (this.pages_.has(pageAddr)) {
196202
// Static code entries can contain "holes" of unnamed code.
197203
// In this case, the whole library is assigned to this address.

tools/logreader.mjs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@
3535
export function parseString(field) { return field };
3636
export const parseVarArgs = 'parse-var-args';
3737

38+
// Checks fields for numbers that are not safe integers. Returns true if any are
39+
// found.
40+
function containsUnsafeInts(fields) {
41+
for (let i = 0; i < fields.length; i++) {
42+
let field = fields[i];
43+
if ('number' == typeof(field) && !Number.isSafeInteger(field)) return true;
44+
}
45+
return false;
46+
}
47+
3848
/**
3949
* Base class for processing log files.
4050
*
@@ -44,7 +54,7 @@ export const parseVarArgs = 'parse-var-args';
4454
* @constructor
4555
*/
4656
export class LogReader {
47-
constructor(timedRange=false, pairwiseTimedRange=false) {
57+
constructor(timedRange=false, pairwiseTimedRange=false, useBigInt=false) {
4858
this.dispatchTable_ = new Map();
4959
this.timedRange_ = timedRange;
5060
this.pairwiseTimedRange_ = pairwiseTimedRange;
@@ -54,6 +64,11 @@ export class LogReader {
5464
// Variables for tracking of 'current-time' log entries:
5565
this.hasSeenTimerMarker_ = false;
5666
this.logLinesSinceLastTimerMarker_ = [];
67+
// Flag to parse all numeric fields as BigInt to avoid arithmetic errors
68+
// caused by memory addresses being greater than MAX_SAFE_INTEGER
69+
this.useBigInt = useBigInt;
70+
this.parseFrame = useBigInt ? BigInt : parseInt;
71+
this.hasSeenUnsafeIntegers = false;
5772
}
5873

5974
/**
@@ -180,11 +195,11 @@ export class LogReader {
180195
const firstChar = frame[0];
181196
if (firstChar === '+' || firstChar === '-') {
182197
// An offset from the previous frame.
183-
prevFrame += parseInt(frame, 16);
198+
prevFrame += this.parseFrame(frame);
184199
fullStack.push(prevFrame);
185200
// Filter out possible 'overflow' string.
186201
} else if (firstChar !== 'o') {
187-
fullStack.push(parseInt(frame, 16));
202+
fullStack.push(this.parseFrame(frame));
188203
} else {
189204
console.error(`Dropping unknown tick frame: ${frame}`);
190205
}
@@ -216,6 +231,12 @@ export class LogReader {
216231
parsedFields[i] = parser(fields[1 + i]);
217232
}
218233
}
234+
if (!this.useBigInt) {
235+
if (!this.hasSeenUnsafeIntegers && containsUnsafeInts(parsedFields)) {
236+
console.warn(`Log line containts unsafe integers: ${fields}`);
237+
this.hasSeenUnsafeIntegers = true;
238+
}
239+
}
219240
// Run the processor.
220241
await dispatch.processor(...parsedFields);
221242
}

tools/profile.mjs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -305,14 +305,18 @@ const kProfileOperationTick = 2;
305305
* @constructor
306306
*/
307307
export class Profile {
308-
codeMap_ = new CodeMap();
309308
topDownTree_ = new CallTree();
310309
bottomUpTree_ = new CallTree();
311310
c_entries_ = {__proto__:null};
312311
scripts_ = [];
313312
urlToScript_ = new Map();
314313
warnings = new Set();
315314

315+
constructor(useBigInt=false) {
316+
this.useBigInt = useBigInt;
317+
this.codeMap_ = new CodeMap(useBigInt);
318+
}
319+
316320
serializeVMSymbols() {
317321
let result = this.codeMap_.getAllStaticEntriesWithAddresses();
318322
result.concat(this.codeMap_.getAllLibraryEntriesWithAddresses())
@@ -513,7 +517,7 @@ export class Profile {
513517
// it is safe to put them in a single code map.
514518
let func = this.codeMap_.findDynamicEntryByStartAddress(funcAddr);
515519
if (func === null) {
516-
func = new FunctionEntry(name);
520+
func = new FunctionEntry(name, this.useBigInt);
517521
this.codeMap_.addCode(funcAddr, func);
518522
} else if (func.name !== name) {
519523
// Function object has been overwritten with a new one.
@@ -961,8 +965,8 @@ class FunctionEntry extends CodeEntry {
961965
/** @type {Set<DynamicCodeEntry>} */
962966
_codeEntries = new Set();
963967

964-
constructor(name) {
965-
super(0, name);
968+
constructor(name, useBigInt=false) {
969+
super(useBigInt ? 0n : 0, name);
966970
const index = name.lastIndexOf(' ');
967971
this.functionName = 1 <= index ? name.substring(0, index) : '<anonymous>';
968972
}

tools/system-analyzer/log/tick.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export class TickLogEntry extends LogEntry {
4242
return 'Idle';
4343
}
4444
const topOfStack = processedStack[0];
45-
if (typeof topOfStack === 'number') {
45+
if (typeof topOfStack === 'number' || typeof topOfStack === 'bigint') {
4646
// TODO(cbruni): Handle VmStack and native ticks better.
4747
return 'Other';
4848
}

tools/system-analyzer/processor.mjs

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ class AsyncConsumer {
4747
}
4848

4949
export class Processor extends LogReader {
50-
_profile = new Profile();
5150
_codeTimeline = new Timeline();
5251
_deoptTimeline = new Timeline();
5352
_icTimeline = new Timeline();
@@ -70,12 +69,16 @@ export class Processor extends LogReader {
7069

7170
MAJOR_VERSION = 7;
7271
MINOR_VERSION = 6;
73-
constructor() {
74-
super();
72+
constructor(useBigInt = false) {
73+
super(false, false, useBigInt);
74+
this.useBigInt = useBigInt;
75+
this.kZero = useBigInt ? 0n : 0;
76+
this.parseAddress = useBigInt ? BigInt : parseInt;
7577
this._chunkConsumer =
7678
new AsyncConsumer((chunk) => this._processChunk(chunk));
79+
this._profile = new Profile(useBigInt);
7780
const propertyICParser = [
78-
parseInt, parseInt, parseInt, parseInt, parseString, parseString,
81+
this.parseAddress, parseInt, parseInt, parseInt, parseString, parseString,
7982
parseString, parseString, parseString, parseString
8083
];
8184
this.setDispatchTable({
@@ -88,14 +91,14 @@ export class Processor extends LogReader {
8891
processor: this.processV8Version,
8992
},
9093
'shared-library': {
91-
parsers: [parseString, parseInt, parseInt, parseInt],
94+
parsers: [parseString, this.parseAddress, this.parseAddress, parseInt],
9295
processor: this.processSharedLibrary.bind(this),
9396
isAsync: true,
9497
},
9598
'code-creation': {
9699
parsers: [
97-
parseString, parseInt, parseInt, parseInt, parseInt, parseString,
98-
parseVarArgs
100+
parseString, parseInt, parseInt, this.parseAddress, this.parseAddress,
101+
parseString, parseVarArgs
99102
],
100103
processor: this.processCodeCreation
101104
},
@@ -111,17 +114,13 @@ export class Processor extends LogReader {
111114
'code-delete': {parsers: [parseInt], processor: this.processCodeDelete},
112115
'code-source-info': {
113116
parsers: [
114-
parseInt, parseInt, parseInt, parseInt, parseString, parseString,
115-
parseString
117+
this.parseAddress, parseInt, parseInt, parseInt, parseString,
118+
parseString, parseString
116119
],
117120
processor: this.processCodeSourceInfo
118121
},
119122
'code-disassemble': {
120-
parsers: [
121-
parseInt,
122-
parseString,
123-
parseString,
124-
],
123+
parsers: [this.parseAddress, parseString, parseString],
125124
processor: this.processCodeDisassemble
126125
},
127126
'feedback-vector': {
@@ -138,8 +137,10 @@ export class Processor extends LogReader {
138137
'sfi-move':
139138
{parsers: [parseInt, parseInt], processor: this.processFunctionMove},
140139
'tick': {
141-
parsers:
142-
[parseInt, parseInt, parseInt, parseInt, parseInt, parseVarArgs],
140+
parsers: [
141+
this.parseAddress, parseInt, parseInt, this.parseAddress, parseInt,
142+
parseVarArgs
143+
],
143144
processor: this.processTick
144145
},
145146
'active-runtime-timer': undefined,
@@ -157,8 +158,8 @@ export class Processor extends LogReader {
157158
{parsers: [parseInt, parseString], processor: this.processMapCreate},
158159
'map': {
159160
parsers: [
160-
parseString, parseInt, parseString, parseString, parseInt, parseInt,
161-
parseInt, parseString, parseString
161+
parseString, parseInt, parseString, parseString, this.parseAddress,
162+
parseInt, parseInt, parseString, parseString
162163
],
163164
processor: this.processMap
164165
},
@@ -310,11 +311,9 @@ export class Processor extends LogReader {
310311
// Many events rely on having a script around, creating fake entries for
311312
// shared libraries.
312313
this._profile.addScriptSource(-1, name, '');
313-
314314
if (this._cppEntriesProvider == undefined) {
315315
await this._setupCppEntriesProvider();
316316
}
317-
318317
await this._cppEntriesProvider.parseVmSymbols(
319318
name, startAddr, endAddr, aslrSlide, (fName, fStart, fEnd) => {
320319
const entry = this._profile.addStaticCode(fName, fStart, fEnd);
@@ -352,7 +351,7 @@ export class Processor extends LogReader {
352351
let profilerEntry;
353352
let stateName = '';
354353
if (maybe_func.length) {
355-
const funcAddr = parseInt(maybe_func[0]);
354+
const funcAddr = this.parseAddress(maybe_func[0]);
356355
stateName = maybe_func[1] ?? '';
357356
const state = Profile.parseState(maybe_func[1]);
358357
profilerEntry = this._profile.addFuncCode(
@@ -439,13 +438,13 @@ export class Processor extends LogReader {
439438
// that a callback calls itself. Instead we use tos_or_external_callback,
440439
// as simply resetting PC will produce unaccounted ticks.
441440
pc = tos_or_external_callback;
442-
tos_or_external_callback = 0;
441+
tos_or_external_callback = this.kZero;
443442
} else if (tos_or_external_callback) {
444443
// Find out, if top of stack was pointing inside a JS function
445444
// meaning that we have encountered a frameless invocation.
446445
const funcEntry = this._profile.findEntry(tos_or_external_callback);
447446
if (!funcEntry?.isJSFunction?.()) {
448-
tos_or_external_callback = 0;
447+
tos_or_external_callback = this.kZero;
449448
}
450449
}
451450
const entryStack = this._profile.recordTick(

0 commit comments

Comments
 (0)