[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;
|
||||
}
|
||||
|
||||
ol.linenums.constants {
|
||||
margin-top: 3px;
|
||||
color: #298f45;
|
||||
}
|
||||
|
||||
.line-number {
|
||||
display: inline-block;
|
||||
min-width: 3ex;
|
||||
@ -110,7 +115,7 @@ li.selected .line-number {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.source-container {
|
||||
.source-container, .bytecode-source-container {
|
||||
border-bottom: 2px solid #AAAAAA;
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ import { InstructionsPhase } from "./phases/instructions-phase";
|
||||
import { SchedulePhase } from "./phases/schedule-phase";
|
||||
import { SequencePhase } from "./phases/sequence-phase";
|
||||
import { BytecodeOrigin } from "./origin";
|
||||
import { Source } from "./source";
|
||||
import { BytecodeSource, BytecodeSourceData, Source } from "./source";
|
||||
import { NodeLabel } from "./node-label";
|
||||
import { TurboshaftCustomDataPhase } from "./phases/turboshaft-custom-data-phase";
|
||||
import { TurboshaftGraphPhase } from "./phases/turboshaft-graph-phase/turboshaft-graph-phase";
|
||||
@ -23,6 +23,7 @@ export type GenericPhase = GraphPhase | TurboshaftGraphPhase | TurboshaftCustomD
|
||||
export class SourceResolver {
|
||||
nodePositionMap: Array<GenericPosition>;
|
||||
sources: Array<Source>;
|
||||
bytecodeSources: Map<number, BytecodeSource>;
|
||||
inlinings: Array<InliningPosition>;
|
||||
inliningsMap: Map<string, InliningPosition>;
|
||||
positionToNodes: Map<string, Array<string>>;
|
||||
@ -37,6 +38,8 @@ export class SourceResolver {
|
||||
this.nodePositionMap = new Array<GenericPosition>();
|
||||
// Maps source ids to source objects.
|
||||
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.
|
||||
this.inlinings = new Array<InliningPosition>();
|
||||
// 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 {
|
||||
if (!mapJson) return;
|
||||
if (typeof mapJson[0] !== "object") {
|
||||
|
@ -31,3 +31,28 @@ export class Source {
|
||||
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 { DisassemblyView } from "./views/disassembly-view";
|
||||
import { GraphMultiView } from "./graphmultiview";
|
||||
import { CodeMode, CodeView } from "./views/code-view";
|
||||
import { CodeView } from "./views/code-view";
|
||||
import { Tabs } from "./tabs";
|
||||
import { Resizer } from "./resizer";
|
||||
import { InfoView } from "./views/info-view";
|
||||
import { HistoryView } from "./views/history-view";
|
||||
import { CodeMode } from "./views/view";
|
||||
import { BytecodeSourceView } from "./views/bytecode-source-view";
|
||||
|
||||
window.onload = function () {
|
||||
let multiview: GraphMultiView;
|
||||
let disassemblyView: DisassemblyView;
|
||||
let sourceViews: Array<CodeView> = new Array<CodeView>();
|
||||
let bytecodeSourceViews: Array<BytecodeSourceView> = new Array<BytecodeSourceView>();
|
||||
let historyView: HistoryView;
|
||||
let selectionBroker: SelectionBroker;
|
||||
let sourceResolver: SourceResolver;
|
||||
@ -47,6 +50,7 @@ window.onload = function () {
|
||||
}
|
||||
try {
|
||||
sourceViews.forEach(sv => sv.hide());
|
||||
bytecodeSourceViews.forEach(bsv => bsv.hide());
|
||||
multiview?.hide();
|
||||
multiview = null;
|
||||
document.getElementById("ranges").innerHTML = "";
|
||||
@ -55,6 +59,7 @@ window.onload = function () {
|
||||
disassemblyView?.hide();
|
||||
historyView?.hide();
|
||||
sourceViews = new Array<CodeView>();
|
||||
bytecodeSourceViews = new Array<BytecodeSourceView>();
|
||||
sourceResolver = new SourceResolver();
|
||||
selectionBroker = new SelectionBroker(sourceResolver);
|
||||
|
||||
@ -64,6 +69,7 @@ window.onload = function () {
|
||||
sourceResolver.setInlinings(jsonObj.inlinings);
|
||||
sourceResolver.setSourceLineToBytecodePosition(jsonObj.sourceLineToBytecodePosition);
|
||||
sourceResolver.setSources(jsonObj.sources, mainFunction);
|
||||
sourceResolver.setBytecodeSources(jsonObj.bytecodeSources);
|
||||
sourceResolver.setNodePositionMap(jsonObj.nodePositions);
|
||||
sourceResolver.parsePhases(jsonObj.phases);
|
||||
|
||||
@ -83,6 +89,20 @@ window.onload = function () {
|
||||
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");
|
||||
disassemblyContainer.classList.add("viewpane", "scrollable");
|
||||
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 { GenericPosition, SourceResolver } from "../source-resolver";
|
||||
import { SelectionBroker } from "../selection/selection-broker";
|
||||
import { View } from "./view";
|
||||
import { CodeMode, View } from "./view";
|
||||
import { SelectionMap } from "../selection/selection-map";
|
||||
import { ViewElements } from "../common/view-elements";
|
||||
import { ClearableHandler, SourcePositionSelectionHandler } from "../selection/selection-handler";
|
||||
@ -19,11 +19,6 @@ declare global {
|
||||
const PR: PR;
|
||||
}
|
||||
|
||||
export enum CodeMode {
|
||||
MainSource = "main function",
|
||||
InlinedSource = "inlined function"
|
||||
}
|
||||
|
||||
export class CodeView extends View {
|
||||
broker: SelectionBroker;
|
||||
source: Source;
|
||||
|
@ -37,3 +37,8 @@ export abstract class PhaseView extends View {
|
||||
super(idOrContainer);
|
||||
}
|
||||
}
|
||||
|
||||
export enum CodeMode {
|
||||
MainSource = "main function",
|
||||
InlinedSource = "inlined function"
|
||||
}
|
||||
|
@ -36,6 +36,7 @@
|
||||
"src/selection/selection-handler.ts",
|
||||
"src/views/view.ts",
|
||||
"src/views/code-view.ts",
|
||||
"src/views/bytecode-source-view.ts",
|
||||
"src/views/graph-view.ts",
|
||||
"src/views/history-view.ts",
|
||||
"src/views/schedule-view.ts",
|
||||
|
Loading…
Reference in New Issue
Block a user