fixed callbackGetRemote(); started on profiler

This commit is contained in:
Steven Hugg 2019-03-03 10:32:25 -06:00
parent ce019b5632
commit ab1500ccb6
8 changed files with 197 additions and 44 deletions

View File

@ -98,6 +98,9 @@ export interface Platform {
showHelp?(tool:string, ident?:string) : void;
resize?() : void;
startProfiling?() : ProfilerOutput;
getRasterScanline?() : number;
debugSymbols? : DebugSymbols;
}
@ -122,6 +125,17 @@ export interface EmuRecorder {
recordFrame(state : EmuState);
}
export interface ProfilerScanline {
start,end : number; // start/end frameindex
}
export interface ProfilerFrame {
iptab : Uint32Array; // array of IPs
lines : ProfilerScanline[];
}
export interface ProfilerOutput {
frame : ProfilerFrame;
}
/////
export abstract class BasePlatform {
@ -194,10 +208,31 @@ export abstract class BaseDebugPlatform extends BasePlatform {
this.advance(novideo);
this.postFrame();
}
startProfiling() : ProfilerOutput {
var frame = null;
var output = {frame:null};
var i = 0;
var lastsl = 9999;
var start = 0;
(this as any).runEval((c:CpuState) => {
var sl = (this as any).getRasterScanline();
if (sl != lastsl) {
if (frame) frame.lines.push({start:start,end:i});
if (sl < lastsl) {
output.frame = frame;
frame = {iptab:new Uint32Array(14672), lines:[]};
i = 0;
}
start = i+1;
lastsl = sl;
}
frame.iptab[i++] = c.EPC || c.PC;
return false; // profile forever
});
return output;
}
}
//////
////// 6502
export function getToolForFilename_6502(fn:string) : string {
@ -933,7 +968,7 @@ export function dumpStackToString(platform:Platform, mem:Uint8Array|number[], st
var opcode = read(addr + jsrofs); // might be out of bounds
if (opcode == jsrop) { // JSR
s += "\n$" + hex(sp) + ": ";
s += hex(addr,4) + " " + lookupSymbol(platform, addr);
s += hex(addr,4) + " " + lookupSymbol(platform, addr, true);
sp++;
nraw = 0;
} else {
@ -946,20 +981,18 @@ export function dumpStackToString(platform:Platform, mem:Uint8Array|number[], st
return s+"\n";
}
export function lookupSymbol(platform:Platform, addr:number) {
export function lookupSymbol(platform:Platform, addr:number, extra:boolean) {
var start = addr;
var foundsym;
var addr2symbol = platform.debugSymbols && platform.debugSymbols.addr2symbol;
while (addr2symbol && addr >= 0) {
var sym = addr2symbol[addr];
if (sym && sym.startsWith('_')) { // return first C symbol we find
return addr2symbol[addr] + " + " + (start-addr);
} else if (sym && !foundsym) { // cache first non-C symbol found
foundsym = sym;
if (sym) { // return first symbol we find
var sym = addr2symbol[addr];
return extra ? (sym + " + " + (start-addr)) : sym;
}
addr--;
}
return foundsym || "";
return "";
}
///// Basic Platforms
@ -975,7 +1008,7 @@ export abstract class BasicZ80ScanlinePlatform extends BaseZ80Platform {
cpuCyclesPerLine : number;
currentScanline : number;
startLineTstates : number;
cpu;
membus : MemoryBus;
iobus : MemoryBus;
@ -995,12 +1028,12 @@ export abstract class BasicZ80ScanlinePlatform extends BaseZ80Platform {
abstract getKeyboardMap();
abstract startScanline(sl : number);
abstract drawScanline(sl : number);
constructor(mainElement : HTMLElement) {
super();
this.mainElement = mainElement;
}
start() {
this.cpuCyclesPerLine = Math.round(this.cpuFrequency / 60 / this.numTotalScanlines);
this.ram = this.newRAM();
@ -1074,4 +1107,3 @@ export abstract class BasicZ80ScanlinePlatform extends BaseZ80Platform {
this.cpu.setTstates(0);
}
}

View File

@ -1,6 +1,6 @@
"use strict";
import { Platform, Base6502Platform, BaseMAMEPlatform, getOpcodeMetadata_6502, cpuStateToLongString_6502, getToolForFilename_6502, dumpStackToString } from "../baseplatform";
import { Platform, Base6502Platform, BaseMAMEPlatform, getOpcodeMetadata_6502, cpuStateToLongString_6502, getToolForFilename_6502, dumpStackToString, ProfilerOutput } from "../baseplatform";
import { PLATFORMS, RAM, newAddressDecoder, padBytes, noise, setKeyboardFromMap, AnimationTimer, RasterVideo, Keys, makeKeycodeMap, dumpRAM, KeyFlags } from "../emu";
import { hex, lpad, lzgmini } from "../util";
import { CodeAnalyzer_nes } from "../analysis";
@ -131,9 +131,7 @@ const _JSNESPlatform = function(mainElement) {
nes.cpu._emulate = nes.cpu.emulate;
nes.cpu.emulate = () => {
var cycles = nes.cpu._emulate();
//if (self.debugCondition && !self.debugBreakState && self.debugClock < 100) console.log(self.debugClock, nes.cpu.REG_PC);
this.evalDebugCondition();
// TODO: doesn't stop on breakpoint
return cycles;
}
timer = new AnimationTimer(60, this.nextFrame.bind(this));
@ -227,7 +225,35 @@ const _JSNESPlatform = function(mainElement) {
runToVsync() {
var frame0 = frameindex;
this.runEval(function(c) { return frameindex>frame0; });
this.runEval((c) => { return frameindex>frame0; });
}
getRasterScanline() : number {
return nes.ppu.scanline;
}
startProfiling() : ProfilerOutput {
var frame0 = frameindex;
var frame = null;
var output = {frame:null};
var i = 0;
var lastsl = 9999;
var start = 0;
this.runEval((c) => {
var sl = this.getRasterScanline();
if (sl != lastsl) {
if (frame) frame.lines.push({start:start,end:i});
if (sl < lastsl) {
output.frame = frame;
frame = {iptab:new Uint32Array(14672), lines:[]};
i = 0;
}
start = i+1;
lastsl = sl;
}
frame.iptab[i++] = c.EPC || c.PC;
return false; //frameindex>frame0; // TODO
});
return output;
}
getCPUState() {

View File

@ -112,6 +112,9 @@ class VCSPlatform extends BasePlatform {
var ypos = row-39;
return {x:xpos, y:ypos};
}
getRasterScanline() : number {
return this.getRasterPosition().y;
}
// TODO: Clock changes this on event, so it may not be current
isRunning() {
@ -286,14 +289,14 @@ class VCSPlatform extends BasePlatform {
disassemble(pc:number, read:(addr:number)=>number) : DisasmLine {
return disassemble6502(pc, read(pc), read(pc+1), read(pc+2));
}
showHelp(tool:string, ident:string) {
if (tool == 'bataribasic')
window.open("help/bataribasic/manual.html", "_help");
else
window.open("https://alienbill.com/2600/101/docs/stella.html", "_help"); // TODO
}
};
// TODO: mixin for Base6502Platform?

View File

@ -209,20 +209,18 @@ export class CodeProject {
webpath += ".a"; // legacy stuff
// try to GET file, use file ext to determine text/binary
this.callbackGetRemote( webpath, (data:FileData) => {
if (data instanceof ArrayBuffer)
data = new Uint8Array(data); // convert to typed array
console.log("GET",webpath,data.length,'bytes');
this.filedata[path] = data; // do not update store, just cache
addResult(path, data);
loadNext();
}, isProbablyBinary(path) ? 'arraybuffer' : 'text')
.fail( (err:XMLHttpRequest) => {
console.log("Could not load preset file", path, err.status);
// only cache result if status is 404 (not found)
if (err.status && err.status == 404)
if (data == null) {
console.log("Could not load preset file", path);
this.filedata[path] = null; // mark cache entry as invalid
} else {
if (data instanceof ArrayBuffer)
data = new Uint8Array(data); // convert to typed array
console.log("GET",webpath,data.length,'bytes');
this.filedata[path] = data; // do not update store, just cache
addResult(path, data);
}
loadNext();
});
}, isProbablyBinary(path) ? 'arraybuffer' : 'text');
} else {
// not gonna find it, keep going
loadNext();

View File

@ -100,7 +100,12 @@ function getWithBinary(url:string, success:(text:FileData)=>void, datatype:'text
oReq.open("GET", url, true);
oReq.responseType = datatype;
oReq.onload = function (oEvent) {
success(oReq.response);
if (oReq.status == 200)
success(oReq.response);
else if (oReq.status == 404)
success(null);
else
throw "Error " + oReq.status + " loading " + url;
}
oReq.send(null);
}
@ -210,6 +215,11 @@ function refreshWindowList() {
return new Views.MemoryMapView();
});
}
if (platform.startProfiling && platform.runEval && platform.getRasterScanline) {
addWindowItem("#profiler", "Profiler", function() {
return new Views.ProfileView();
});
}
}
// can pass integer or string id

View File

@ -4,7 +4,7 @@ import $ = require("jquery");
//import CodeMirror = require("codemirror");
import { CodeProject } from "./project";
import { SourceFile, WorkerError, Segment } from "./workertypes";
import { Platform, EmuState } from "./baseplatform";
import { Platform, EmuState, ProfilerOutput, lookupSymbol } from "./baseplatform";
import { hex, lpad, rpad } from "./util";
import { CodeAnalyzer } from "./analysis";
import { platform, platform_id, compparams, current_project, lastDebugState, projectWindows } from "./ui";
@ -610,7 +610,7 @@ export class MemoryView implements ProjectView {
if (compparams && this.dumplines)
this.scrollToAddress(compparams.data_start);
}
scrollToAddress(addr : number) {
this.memorylist.scrollToItem(this.findMemoryWindowLine(addr));
}
@ -774,7 +774,7 @@ export class BinaryFileView implements ProjectView {
refresh() {
}
getPath() { return this.path; }
}
@ -789,7 +789,7 @@ export class MemoryMapView implements ProjectView {
this.refresh();
return this.maindiv[0];
}
// TODO: overlapping segments (e.g. ROM + LC)
addSegment(seg : Segment) {
var offset = $('<div class="col-md-1 segment-offset"/>');
@ -831,5 +831,84 @@ export class MemoryMapView implements ProjectView {
}
}
}
}
///
export class ProfileView implements ProjectView {
profilelist;
prof : ProfilerOutput;
maindiv : HTMLElement;
symcache : {};
createDiv(parent : HTMLElement) {
var div = document.createElement('div');
div.setAttribute("class", "memdump");
parent.appendChild(div);
this.showMemoryWindow(parent, div);
return this.maindiv = div;
}
showMemoryWindow(workspace:HTMLElement, parent:HTMLElement) {
this.profilelist = new VirtualList({
w: $(workspace).width(),
h: $(workspace).height(),
itemHeight: getVisibleEditorLineHeight(),
totalRows: 262,
generatorFn: (row : number) => {
var s = this.getProfileLineAt(row);
var linediv = document.createElement("div");
linediv.appendChild(document.createTextNode(s));
return linediv;
}
});
$(parent).append(this.profilelist.container);
this.symcache = {};
this.tick();
}
getProfileLineAt(row : number) : string {
var s = lpad(row+': ',5);
if (!this.prof) return s;
var f = this.prof.frame;
if (!f) return s;
var l = f.lines[row];
if (!l) return s;
var lastsym = '';
for (var i=l.start; i<=l.end; i++) {
var pc = f.iptab[i];
var sym = this.symcache[pc];
if (!sym) {
sym = lookupSymbol(platform, pc, false);
this.symcache[pc] = sym;
}
if (sym != lastsym) {
s += sym + ' ';
lastsym = sym;
}
}
return s;
}
refresh() {
this.tick();
}
tick() {
if (this.profilelist) {
$(this.maindiv).find('[data-index]').each( (i,e) => {
var div = $(e);
var row = parseInt(div.attr('data-index'));
var oldtext = div.text();
var newtext = this.getProfileLineAt(row);
if (oldtext != newtext)
div.text(newtext);
});
}
// TODO: better way to keep it profiling? also, it clears the buffer
if (platform.isRunning()) {
this.prof = platform.startProfiling();
}
}
}

View File

@ -934,8 +934,10 @@ function linkLD65(step:BuildStep) {
var toks = s.split(" ");
if (toks[0] == 'al') {
let ident = toks[2].substr(1);
let ofs = parseInt(toks[1], 16);
symbolmap[ident] = ofs;
if (ident.length != 5 || !ident.startsWith('L')) { // no line numbers
let ofs = parseInt(toks[1], 16);
symbolmap[ident] = ofs;
}
}
}
// build segment map
@ -1226,7 +1228,7 @@ function linkSDLDZ80(step:BuildStep)
var symbolmap = {};
for (var s of noiout.split("\n")) {
var toks = s.split(" ");
if (toks[0] == 'DEF' && !toks[1].startsWith("A$main$")) {
if (toks[0] == 'DEF' && !toks[1].startsWith("A$")) {
symbolmap[toks[1]] = parseInt(toks[2], 16);
}
}

View File

@ -76,7 +76,10 @@ describe('Store', function() {
var platform = {};
var project = new prj.CodeProject(worker, test_platform_id, platform, store);
var remote = [];
project.callbackGetRemote = function(path) { remote.push(path); return {fail:function(failfn){failfn({status:404})}} };
project.callbackGetRemote = function(path, success, datatype) {
remote.push(path);
success();
};
project.loadFiles(['local/test','test'], function(err, result) {
assert.equal(null, err);
assert.deepEqual(["presets/_TEST/test"], remote);
@ -85,7 +88,7 @@ describe('Store', function() {
});
});
});
it('Should build linked project', function(done) {
localStorage.clear();
localItems['__migrated__TEST'] = 'true';
@ -93,7 +96,7 @@ describe('Store', function() {
var expectmsgs = [
true,
{ preload: 'dasm', platform: '_TEST' },
{
{
buildsteps: [
{ path: "test.a", platform: "_TEST", tool: "dasm", mainfile:true, files:["test.a"] },
],