diff options
Diffstat (limited to 'src/trace_viewer/index.ts')
| -rw-r--r-- | src/trace_viewer/index.ts | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/src/trace_viewer/index.ts b/src/trace_viewer/index.ts new file mode 100644 index 00000000..f9b5041b --- /dev/null +++ b/src/trace_viewer/index.ts @@ -0,0 +1,287 @@ +import {remote} from "electron" +import fs = require("fs") +const dialog = remote.dialog +const app = remote.app + +let topCallDiv = document.createElement("div") + +const max_arg_length = 5000 + +abstract class Event { + caller: Call + + protected div: HTMLDivElement | null = null + + public hide(): void { + if (this.div != null) { + this.div.remove() + this.div = null + } + } + + protected abstract showText(text: HTMLParagraphElement): void + + public show(): HTMLDivElement { + let callerDiv: HTMLDivElement = (this.caller != null) ? this.caller.show() : topCallDiv + + if (this.div != null) { + return this.div + } else { + this.div = document.createElement("div") + this.div.className = "tree" + callerDiv.appendChild(this.div) + let text = document.createElement("p") + this.showText(text) + this.div.appendChild(text) + return this.div + } + } +} + +class Load extends Event { + loc: string + val: string + + constructor(loc: string, val: string) { + super() + this.loc = loc + this.val = val + } + + protected showText(text: HTMLParagraphElement): void { + text.className = "load" + text.insertAdjacentText('beforeend', this.loc + " " + this.val) + } +} + +class Read extends Event { + reg: string + value: string + + constructor(reg: string, value: string) { + super() + this.reg = reg + this.value = value + } + + public showText(text: HTMLParagraphElement): void { + text.className = "read" + text.insertAdjacentText('beforeend', this.reg + " " + this.value) + } +} + +class Write extends Event { + reg: string + value: string + + constructor(reg: string, value: string) { + super() + this.reg = reg + this.value = value + } + + public showText(text: HTMLParagraphElement): void { + text.className = "write" + text.insertAdjacentText('beforeend', this.reg + " " + this.value) + } +} + +class Call { + fn: string + arg: string + ret: string + callees: (Call | Event)[] = [] + caller: Call + + private div: HTMLDivElement | null = null + + private toggle: boolean = false + private toggleImg: HTMLImageElement | null = null + + constructor(fn: string, arg: string, ret: string) { + this.fn = fn + this.arg = arg + this.ret = ret + } + + public expand() { + if (this.caller != undefined) { + this.caller.expand() + } + this.showChildren() + } + + public iter(f: (call: Call) => void): void { + f(this) + this.callees.forEach((callee) => { + if (callee instanceof Call) { callee.iter(f) } + }) + + } + + public show(): HTMLDivElement { + let callerDiv: HTMLDivElement = (this.caller != null) ? this.caller.show() : topCallDiv + + if (this.div != null) { + return this.div + } else { + this.div = document.createElement("div") + this.div.className = "tree" + callerDiv.appendChild(this.div) + let text = document.createElement("p") + text.className = "call" + if (this.callees.length > 0) { + this.toggleImg = document.createElement("img") + this.toggleImg.src = "List-add.svg" + this.toggleImg.addEventListener('click', () => { + if (this.toggle) { + this.hideChildren() + } else { + this.showChildren() + } + }) + text.appendChild(this.toggleImg) + } + this.toggle = false + let display_arg = this.arg + if (this.arg.length > max_arg_length) { + display_arg = this.arg.slice(0, max_arg_length) + } + let display_ret = this.ret + if (this.ret.length > max_arg_length) { + display_ret = this.ret.slice(0, max_arg_length) + } + + text.insertAdjacentText('beforeend', this.fn + " " + display_arg + " -> " + display_ret) + this.div.appendChild(text) + return this.div + } + } + + public hide(): void { + if (this.toggle == true) { + this.hideChildren() + } + + if (this.div != null) { + this.div.remove() + this.div = null + } + if (this.toggleImg != null) { + this.toggleImg.remove() + this.toggleImg = null + } + } + + public hideChildren(): void { + this.callees.forEach(call => { + call.hide() + }) + + if (this.toggleImg != null) { + this.toggleImg.src = "List-add.svg" + this.toggle = false + } else { + alert("this.toggleImg was null!") + } + } + + public showChildren(): void { + this.callees.forEach(call => { + call.show() + }); + + if (this.toggleImg != null) { + this.toggleImg.src = "List-remove.svg" + this.toggle = true + } else { + alert("this.toggleImg was null!") + } + } + + public appendChild(child: Call | Write | Read | Load): void { + child.caller = this + + this.callees.push(child) + } +} + +document.addEventListener('DOMContentLoaded', () => { + let rootCall = new Call("ROOT", "", "") + topCallDiv.id = "root" + document.getElementById("container")!.appendChild(topCallDiv) + + let commandInput = document.getElementById("command") as HTMLInputElement + + commandInput.addEventListener("keydown", (event) => { + if(event.keyCode == 13) { + let cmd = commandInput.value.split(" ") + commandInput.value = "" + + if (cmd[0] == "function") { + rootCall.iter((call) => { + if (call.fn == cmd[1]) { call.caller.expand() } + }) + } + } + }) + + let files = dialog.showOpenDialog(remote.getCurrentWindow(), {title: "Select log file", defaultPath: app.getAppPath()}) + + if (files == [] || files == undefined) { + dialog.showErrorBox("Error", "No file selected") + app.exit(1) + } + + fs.readFile(files[0], 'utf-8', (err, data) => { + if (err) { + dialog.showErrorBox("Error", "An error occurred when reading the log: " + err.message) + app.exit(1) + } + + let lines = data.split("\n") + // let indents = lines.map(line => line.search(/[^\s]/) / 2) + lines = lines.map(line => line.trim()) + + let stack : Call[] = [rootCall] + + lines.forEach(line => { + if (line.match(/^Call:/)) { + let words = line.slice(6).split(" ") + let call = new Call(words[0], words.slice(1).join(" "), "") + if (stack.length > 0) { + stack[stack.length - 1].appendChild(call) + } + stack.push(call) + } else if (line.match(/^Return:/)) { + let call = stack.pop() + if (call == undefined) { + alert("Unbalanced return") + app.exit(1) + } else { + call.ret = line.slice(8) + } + } else if (line.match(/^Write:/)) { + let words = line.slice(7).split(" ") + let write = new Write(words[0], words.slice(1).join(" ")) + if (stack.length > 0) { + stack[stack.length - 1].appendChild(write) + } + } else if (line.match(/^Read:/)) { + let words = line.slice(6).split(" ") + let read = new Read(words[0], words.slice(1).join(" ")) + if (stack.length > 0) { + stack[stack.length - 1].appendChild(read) + } + } else if (line.match(/^Load:/)) { + let words = line.slice(6).split(" ") + let load = new Load(words[0], words[1]) + if (stack.length > 0) { + stack[stack.length - 1].appendChild(load) + } + } + }) + + rootCall.show() + }) +})
\ No newline at end of file |
