[turbolizer] Bytecode sources view
Bug: v8:7327 Change-Id: I0de7ee31762db6b95a631eedffd0f82fa2f0ce3b Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3812034 Reviewed-by: Tobias Tebbi <tebbi@chromium.org> Commit-Queue: Danylo Boiko <danielboyko02@gmail.com> Cr-Commit-Position: refs/heads/main@{#82500}
This commit is contained in:
parent
aa541f1c9c
commit
1c44d07958
@ -76,6 +76,11 @@ ol.linenums {
|
|||||||
-webkit-padding-start: 8px;
|
-webkit-padding-start: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ol.linenums.constants {
|
||||||
|
margin-top: 3px;
|
||||||
|
color: #298f45;
|
||||||
|
}
|
||||||
|
|
||||||
.line-number {
|
.line-number {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
min-width: 3ex;
|
min-width: 3ex;
|
||||||
@ -110,7 +115,7 @@ li.selected .line-number {
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.source-container {
|
.source-container, .bytecode-source-container {
|
||||||
border-bottom: 2px solid #AAAAAA;
|
border-bottom: 2px solid #AAAAAA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import { InstructionsPhase } from "./phases/instructions-phase";
|
|||||||
import { SchedulePhase } from "./phases/schedule-phase";
|
import { SchedulePhase } from "./phases/schedule-phase";
|
||||||
import { SequencePhase } from "./phases/sequence-phase";
|
import { SequencePhase } from "./phases/sequence-phase";
|
||||||
import { BytecodeOrigin } from "./origin";
|
import { BytecodeOrigin } from "./origin";
|
||||||
import { Source } from "./source";
|
import { BytecodeSource, BytecodeSourceData, Source } from "./source";
|
||||||
import { NodeLabel } from "./node-label";
|
import { NodeLabel } from "./node-label";
|
||||||
import { TurboshaftCustomDataPhase } from "./phases/turboshaft-custom-data-phase";
|
import { TurboshaftCustomDataPhase } from "./phases/turboshaft-custom-data-phase";
|
||||||
import { TurboshaftGraphPhase } from "./phases/turboshaft-graph-phase/turboshaft-graph-phase";
|
import { TurboshaftGraphPhase } from "./phases/turboshaft-graph-phase/turboshaft-graph-phase";
|
||||||
@ -23,6 +23,7 @@ export type GenericPhase = GraphPhase | TurboshaftGraphPhase | TurboshaftCustomD
|
|||||||
export class SourceResolver {
|
export class SourceResolver {
|
||||||
nodePositionMap: Array<GenericPosition>;
|
nodePositionMap: Array<GenericPosition>;
|
||||||
sources: Array<Source>;
|
sources: Array<Source>;
|
||||||
|
bytecodeSources: Map<number, BytecodeSource>;
|
||||||
inlinings: Array<InliningPosition>;
|
inlinings: Array<InliningPosition>;
|
||||||
inliningsMap: Map<string, InliningPosition>;
|
inliningsMap: Map<string, InliningPosition>;
|
||||||
positionToNodes: Map<string, Array<string>>;
|
positionToNodes: Map<string, Array<string>>;
|
||||||
@ -37,6 +38,8 @@ export class SourceResolver {
|
|||||||
this.nodePositionMap = new Array<GenericPosition>();
|
this.nodePositionMap = new Array<GenericPosition>();
|
||||||
// Maps source ids to source objects.
|
// Maps source ids to source objects.
|
||||||
this.sources = new Array<Source>();
|
this.sources = new Array<Source>();
|
||||||
|
// Maps bytecode source ids to bytecode source objects.
|
||||||
|
this.bytecodeSources = new Map<number, BytecodeSource>();
|
||||||
// Maps inlining ids to inlining objects.
|
// Maps inlining ids to inlining objects.
|
||||||
this.inlinings = new Array<InliningPosition>();
|
this.inlinings = new Array<InliningPosition>();
|
||||||
// Maps source position keys to inlinings.
|
// Maps source position keys to inlinings.
|
||||||
@ -96,6 +99,22 @@ export class SourceResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setBytecodeSources(bytecodeSourcesJson): void {
|
||||||
|
if (!bytecodeSourcesJson) return;
|
||||||
|
for (const [sourceId, source] of Object.entries<any>(bytecodeSourcesJson)) {
|
||||||
|
const bytecodeSource = source.bytecodeSource;
|
||||||
|
const data = new Array<BytecodeSourceData>();
|
||||||
|
|
||||||
|
for (const bytecode of Object.values<BytecodeSourceData>(bytecodeSource.data)) {
|
||||||
|
data.push(new BytecodeSourceData(bytecode.offset, bytecode.disassembly));
|
||||||
|
}
|
||||||
|
|
||||||
|
const numSourceId = Number(sourceId);
|
||||||
|
this.bytecodeSources.set(numSourceId, new BytecodeSource(source.sourceId, source.functionName,
|
||||||
|
data, bytecodeSource.constantPool));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public setNodePositionMap(mapJson): void {
|
public setNodePositionMap(mapJson): void {
|
||||||
if (!mapJson) return;
|
if (!mapJson) return;
|
||||||
if (typeof mapJson[0] !== "object") {
|
if (typeof mapJson[0] !== "object") {
|
||||||
|
@ -31,3 +31,28 @@ export class Source {
|
|||||||
return `${this.sourceName}:${this.functionName}`;
|
return `${this.sourceName}:${this.functionName}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class BytecodeSource {
|
||||||
|
sourceId: number;
|
||||||
|
functionName: string;
|
||||||
|
data: Array<BytecodeSourceData>;
|
||||||
|
constantPool: Array<string>;
|
||||||
|
|
||||||
|
constructor(sourceId: number, functionName: string, data: Array<BytecodeSourceData>,
|
||||||
|
constantPool: Array<string>) {
|
||||||
|
this.sourceId = sourceId;
|
||||||
|
this.functionName = functionName;
|
||||||
|
this.data = data;
|
||||||
|
this.constantPool = constantPool;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BytecodeSourceData {
|
||||||
|
offset: number;
|
||||||
|
disassembly: string;
|
||||||
|
|
||||||
|
constructor(offset: number, disassembly: string) {
|
||||||
|
this.offset = offset;
|
||||||
|
this.disassembly = disassembly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -7,16 +7,19 @@ import { SourceResolver } from "./source-resolver";
|
|||||||
import { SelectionBroker } from "./selection/selection-broker";
|
import { SelectionBroker } from "./selection/selection-broker";
|
||||||
import { DisassemblyView } from "./views/disassembly-view";
|
import { DisassemblyView } from "./views/disassembly-view";
|
||||||
import { GraphMultiView } from "./graphmultiview";
|
import { GraphMultiView } from "./graphmultiview";
|
||||||
import { CodeMode, CodeView } from "./views/code-view";
|
import { CodeView } from "./views/code-view";
|
||||||
import { Tabs } from "./tabs";
|
import { Tabs } from "./tabs";
|
||||||
import { Resizer } from "./resizer";
|
import { Resizer } from "./resizer";
|
||||||
import { InfoView } from "./views/info-view";
|
import { InfoView } from "./views/info-view";
|
||||||
import { HistoryView } from "./views/history-view";
|
import { HistoryView } from "./views/history-view";
|
||||||
|
import { CodeMode } from "./views/view";
|
||||||
|
import { BytecodeSourceView } from "./views/bytecode-source-view";
|
||||||
|
|
||||||
window.onload = function () {
|
window.onload = function () {
|
||||||
let multiview: GraphMultiView;
|
let multiview: GraphMultiView;
|
||||||
let disassemblyView: DisassemblyView;
|
let disassemblyView: DisassemblyView;
|
||||||
let sourceViews: Array<CodeView> = new Array<CodeView>();
|
let sourceViews: Array<CodeView> = new Array<CodeView>();
|
||||||
|
let bytecodeSourceViews: Array<BytecodeSourceView> = new Array<BytecodeSourceView>();
|
||||||
let historyView: HistoryView;
|
let historyView: HistoryView;
|
||||||
let selectionBroker: SelectionBroker;
|
let selectionBroker: SelectionBroker;
|
||||||
let sourceResolver: SourceResolver;
|
let sourceResolver: SourceResolver;
|
||||||
@ -47,6 +50,7 @@ window.onload = function () {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
sourceViews.forEach(sv => sv.hide());
|
sourceViews.forEach(sv => sv.hide());
|
||||||
|
bytecodeSourceViews.forEach(bsv => bsv.hide());
|
||||||
multiview?.hide();
|
multiview?.hide();
|
||||||
multiview = null;
|
multiview = null;
|
||||||
document.getElementById("ranges").innerHTML = "";
|
document.getElementById("ranges").innerHTML = "";
|
||||||
@ -55,6 +59,7 @@ window.onload = function () {
|
|||||||
disassemblyView?.hide();
|
disassemblyView?.hide();
|
||||||
historyView?.hide();
|
historyView?.hide();
|
||||||
sourceViews = new Array<CodeView>();
|
sourceViews = new Array<CodeView>();
|
||||||
|
bytecodeSourceViews = new Array<BytecodeSourceView>();
|
||||||
sourceResolver = new SourceResolver();
|
sourceResolver = new SourceResolver();
|
||||||
selectionBroker = new SelectionBroker(sourceResolver);
|
selectionBroker = new SelectionBroker(sourceResolver);
|
||||||
|
|
||||||
@ -64,6 +69,7 @@ window.onload = function () {
|
|||||||
sourceResolver.setInlinings(jsonObj.inlinings);
|
sourceResolver.setInlinings(jsonObj.inlinings);
|
||||||
sourceResolver.setSourceLineToBytecodePosition(jsonObj.sourceLineToBytecodePosition);
|
sourceResolver.setSourceLineToBytecodePosition(jsonObj.sourceLineToBytecodePosition);
|
||||||
sourceResolver.setSources(jsonObj.sources, mainFunction);
|
sourceResolver.setSources(jsonObj.sources, mainFunction);
|
||||||
|
sourceResolver.setBytecodeSources(jsonObj.bytecodeSources);
|
||||||
sourceResolver.setNodePositionMap(jsonObj.nodePositions);
|
sourceResolver.setNodePositionMap(jsonObj.nodePositions);
|
||||||
sourceResolver.parsePhases(jsonObj.phases);
|
sourceResolver.parsePhases(jsonObj.phases);
|
||||||
|
|
||||||
@ -83,6 +89,20 @@ window.onload = function () {
|
|||||||
sourceViews.push(sourceView);
|
sourceViews.push(sourceView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sourceResolver.bytecodeSources.size > 0) {
|
||||||
|
const [_, bytecodeContainer] = sourceTabs.addTabAndContent("Bytecode");
|
||||||
|
bytecodeContainer.classList.add("viewpane", "scrollable");
|
||||||
|
|
||||||
|
const bytecodeSources = Array.from(sourceResolver.bytecodeSources.values()).reverse();
|
||||||
|
for (const source of bytecodeSources) {
|
||||||
|
const codeMode = source.sourceId == -1 ? CodeMode.MainSource : CodeMode.InlinedSource;
|
||||||
|
const bytecodeSourceView = new BytecodeSourceView(bytecodeContainer, selectionBroker,
|
||||||
|
source, sourceResolver, codeMode);
|
||||||
|
bytecodeSourceView.show();
|
||||||
|
bytecodeSourceViews.push(bytecodeSourceView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const [disassemblyTab, disassemblyContainer] = disassemblyTabs.addTabAndContent("Disassembly");
|
const [disassemblyTab, disassemblyContainer] = disassemblyTabs.addTabAndContent("Disassembly");
|
||||||
disassemblyContainer.classList.add("viewpane", "scrollable");
|
disassemblyContainer.classList.add("viewpane", "scrollable");
|
||||||
disassemblyTabs.activateTab(disassemblyTab);
|
disassemblyTabs.activateTab(disassemblyTab);
|
||||||
|
110
tools/turbolizer/src/views/bytecode-source-view.ts
Normal file
110
tools/turbolizer/src/views/bytecode-source-view.ts
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
// Copyright 2022 the V8 project authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import { createElement } from "../common/util";
|
||||||
|
import { CodeMode, View } from "./view";
|
||||||
|
import { SelectionBroker } from "../selection/selection-broker";
|
||||||
|
import { BytecodeSource } from "../source";
|
||||||
|
import { SourceResolver } from "../source-resolver";
|
||||||
|
|
||||||
|
export class BytecodeSourceView extends View {
|
||||||
|
broker: SelectionBroker;
|
||||||
|
source: BytecodeSource;
|
||||||
|
sourceResolver: SourceResolver;
|
||||||
|
codeMode: CodeMode;
|
||||||
|
bytecodeOffsetToHtmlElement: Map<number, HTMLElement>;
|
||||||
|
|
||||||
|
constructor(parent: HTMLElement, broker: SelectionBroker, sourceFunction: BytecodeSource,
|
||||||
|
sourceResolver: SourceResolver, codeMode: CodeMode) {
|
||||||
|
super(parent);
|
||||||
|
this.broker = broker;
|
||||||
|
this.source = sourceFunction;
|
||||||
|
this.sourceResolver = sourceResolver;
|
||||||
|
this.codeMode = codeMode;
|
||||||
|
this.bytecodeOffsetToHtmlElement = new Map<number, HTMLElement>();
|
||||||
|
|
||||||
|
this.initializeCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected createViewElement(): HTMLElement {
|
||||||
|
return createElement("div", "bytecode-source-container");
|
||||||
|
}
|
||||||
|
|
||||||
|
private initializeCode(): void {
|
||||||
|
const view = this;
|
||||||
|
const source = this.source;
|
||||||
|
const bytecodeContainer = this.divNode;
|
||||||
|
bytecodeContainer.classList.add(view.getSourceClass());
|
||||||
|
|
||||||
|
const bytecodeHeader = createElement("div", "code-header");
|
||||||
|
bytecodeHeader.setAttribute("id", view.getBytecodeHeaderHtmlElementName());
|
||||||
|
|
||||||
|
const codeFileFunction = createElement("div", "code-file-function", source.functionName);
|
||||||
|
bytecodeHeader.appendChild(codeFileFunction);
|
||||||
|
|
||||||
|
const codeMode = createElement("div", "code-mode", view.codeMode);
|
||||||
|
bytecodeHeader.appendChild(codeMode);
|
||||||
|
|
||||||
|
const clearElement = document.createElement("div");
|
||||||
|
clearElement.style.clear = "both";
|
||||||
|
bytecodeHeader.appendChild(clearElement);
|
||||||
|
bytecodeContainer.appendChild(bytecodeHeader);
|
||||||
|
|
||||||
|
const codePre = createElement("pre", "prettyprint linenums");
|
||||||
|
codePre.setAttribute("id", view.getBytecodeHtmlElementName());
|
||||||
|
bytecodeContainer.appendChild(codePre);
|
||||||
|
|
||||||
|
bytecodeHeader.onclick = () => {
|
||||||
|
codePre.style.display = codePre.style.display === "none" ? "block" : "none";
|
||||||
|
};
|
||||||
|
|
||||||
|
const sourceList = createElement("ol", "linenums");
|
||||||
|
for (const bytecodeSource of view.source.data) {
|
||||||
|
const currentLine = createElement("li", `L${bytecodeSource.offset}`);
|
||||||
|
currentLine.setAttribute("id", `li${bytecodeSource.offset}`);
|
||||||
|
view.insertLineContent(currentLine, bytecodeSource.disassembly);
|
||||||
|
view.insertLineNumber(currentLine, bytecodeSource.offset);
|
||||||
|
view.bytecodeOffsetToHtmlElement.set(bytecodeSource.offset, currentLine);
|
||||||
|
sourceList.appendChild(currentLine);
|
||||||
|
}
|
||||||
|
codePre.appendChild(sourceList);
|
||||||
|
|
||||||
|
if (view.source.constantPool.length === 0) return;
|
||||||
|
|
||||||
|
const constantList = createElement("ol", "linenums constants");
|
||||||
|
const constantListHeader = createElement("li", "");
|
||||||
|
view.insertLineContent(constantListHeader,
|
||||||
|
`Constant pool (size = ${view.source.constantPool.length})`);
|
||||||
|
constantList.appendChild(constantListHeader);
|
||||||
|
|
||||||
|
for (const [idx, constant] of view.source.constantPool.entries()) {
|
||||||
|
const currentLine = createElement("li", `C${idx}`);
|
||||||
|
view.insertLineContent(currentLine, `${idx}: ${constant}`);
|
||||||
|
constantList.appendChild(currentLine);
|
||||||
|
}
|
||||||
|
codePre.appendChild(constantList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getBytecodeHeaderHtmlElementName(): string {
|
||||||
|
return `source-pre-${this.source.sourceId}-header`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getBytecodeHtmlElementName(): string {
|
||||||
|
return `source-pre-${this.source.sourceId}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getSourceClass(): string {
|
||||||
|
return this.codeMode == CodeMode.MainSource ? "main-source" : "inlined-source";
|
||||||
|
}
|
||||||
|
|
||||||
|
private insertLineContent(lineElement: HTMLElement, content: string): void {
|
||||||
|
const lineContentElement = createElement("span", "", content);
|
||||||
|
lineElement.appendChild(lineContentElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
private insertLineNumber(lineElement: HTMLElement, lineNumber: number): void {
|
||||||
|
const lineNumberElement = createElement("div", "line-number", String(lineNumber));
|
||||||
|
lineElement.insertBefore(lineNumberElement, lineElement.firstChild);
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@
|
|||||||
import { Source } from "../source";
|
import { Source } from "../source";
|
||||||
import { GenericPosition, SourceResolver } from "../source-resolver";
|
import { GenericPosition, SourceResolver } from "../source-resolver";
|
||||||
import { SelectionBroker } from "../selection/selection-broker";
|
import { SelectionBroker } from "../selection/selection-broker";
|
||||||
import { View } from "./view";
|
import { CodeMode, View } from "./view";
|
||||||
import { SelectionMap } from "../selection/selection-map";
|
import { SelectionMap } from "../selection/selection-map";
|
||||||
import { ViewElements } from "../common/view-elements";
|
import { ViewElements } from "../common/view-elements";
|
||||||
import { ClearableHandler, SourcePositionSelectionHandler } from "../selection/selection-handler";
|
import { ClearableHandler, SourcePositionSelectionHandler } from "../selection/selection-handler";
|
||||||
@ -19,11 +19,6 @@ declare global {
|
|||||||
const PR: PR;
|
const PR: PR;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum CodeMode {
|
|
||||||
MainSource = "main function",
|
|
||||||
InlinedSource = "inlined function"
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CodeView extends View {
|
export class CodeView extends View {
|
||||||
broker: SelectionBroker;
|
broker: SelectionBroker;
|
||||||
source: Source;
|
source: Source;
|
||||||
|
@ -37,3 +37,8 @@ export abstract class PhaseView extends View {
|
|||||||
super(idOrContainer);
|
super(idOrContainer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum CodeMode {
|
||||||
|
MainSource = "main function",
|
||||||
|
InlinedSource = "inlined function"
|
||||||
|
}
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
"src/selection/selection-handler.ts",
|
"src/selection/selection-handler.ts",
|
||||||
"src/views/view.ts",
|
"src/views/view.ts",
|
||||||
"src/views/code-view.ts",
|
"src/views/code-view.ts",
|
||||||
|
"src/views/bytecode-source-view.ts",
|
||||||
"src/views/graph-view.ts",
|
"src/views/graph-view.ts",
|
||||||
"src/views/history-view.ts",
|
"src/views/history-view.ts",
|
||||||
"src/views/schedule-view.ts",
|
"src/views/schedule-view.ts",
|
||||||
|
Loading…
Reference in New Issue
Block a user