diff --git a/rt/gc/RiGc.v3 b/rt/gc/RiGc.v3 index 0d4f030a0..7bfa5a1d6 100644 --- a/rt/gc/RiGc.v3 +++ b/rt/gc/RiGc.v3 @@ -152,6 +152,27 @@ component RiGc { System.error("GcError", "Invalid object header"); return 0; } + // scan references in a frame and return the size in bytes + def scanStackFrame(sp: Pointer, stackMap: int) -> int { + if ((stackMap & 0x80000) != 0) { + // extended entry + var vmap_p = CiRuntime.GC_EXTMAPS + (RiGc.INT_SIZE * (stackMap & 0x7FFFF)); + return scanExtMap(vmap_p, sp); + } else { + // normal entry + return scanRefMap(stackMap, sp); + } + } + def stackmapSize(stackMap: int) -> int { + if ((stackMap & 0x80000) != 0) { + // extended entry + var vmap_p = CiRuntime.GC_EXTMAPS + (RiGc.INT_SIZE * (stackMap & 0x7FFFF)); + return extmapSize(vmap_p); + } else { + // normal entry + return refmapSize(stackMap); + } + } // scan the references at the given start address, returning the size def scanRefMap(v: int, start: Pointer) -> int { var vmap = v; diff --git a/rt/native/NativeStackScanner.v3 b/rt/native/NativeStackScanner.v3 index 56d644b8f..a585dda5f 100644 --- a/rt/native/NativeStackScanner.v3 +++ b/rt/native/NativeStackScanner.v3 @@ -43,7 +43,8 @@ component NativeStackScanner { } return; } - var frameSize = scanStackFrame(sp, stackmapEntry); + var stackMap = stackmapEntry.load() >>> RiTables.PAGE_SHIFT; + var frameSize = RiGc.scanStackFrame(sp, stackMap); if (RiGc.debug && frameSize != frameWords * RiGc.REF_SIZE) { D.puts("\n\n!GcError: mismatch between source frame size ") .puti(frameWords * RiGc.REF_SIZE) @@ -56,16 +57,4 @@ component NativeStackScanner { ip = t.0; sp = t.1; } } - // scan a single stack frame - def scanStackFrame(sp: Pointer, stackmapEntry: Pointer) -> int { - var stackMap = stackmapEntry.load() >>> RiTables.PAGE_SHIFT; - if ((stackMap & 0x80000) != 0) { - // extended entry - var vmap_p = CiRuntime.GC_EXTMAPS + (RiGc.INT_SIZE * (stackMap & 0x7FFFF)); - return RiGc.scanExtMap(vmap_p, sp); - } else { - // normal entry - return RiGc.scanRefMap(stackMap, sp); - } - } } diff --git a/rt/native/NativeStackWalker.v3 b/rt/native/NativeStackWalker.v3 new file mode 100644 index 000000000..e9470e450 --- /dev/null +++ b/rt/native/NativeStackWalker.v3 @@ -0,0 +1,44 @@ +// Copyright 2019 Google Inc. All rights reserved. +// See LICENSE for details of Apache 2.0 license. + +// Encapsulates logic relating to walking the stack to be exposed to (power user) applications. +component NativeStackWalker { + def iterateFrames(i: Pointer, s: Pointer, callback: (Pointer, Pointer, RiUserCode, int) -> bool) { + var ip = i, sp = s; + while (true) { + // Try looking up a GC stackmap entry first. + var stackmapEntry = RiTables.exactMatch(RiTables.searchTable(CiRuntime.GC_STACKMAP_PAGES, + CiRuntime.GC_STACKMAP_TABLE, ip)); + if (stackmapEntry != Pointer.NULL) { // found a (GC) stackmap entry. + var stackMap = stackmapEntry.load() >>> RiTables.PAGE_SHIFT; + var frameSize = RiGc.stackmapSize(stackMap); + callback(ip, sp, null, frameSize); + var t = RiOs.callerFrame(ip, sp, frameSize / RiGc.REF_SIZE); + ip = t.0; sp = t.1; + continue; + } + + // + /* TODO: try using a source entry if no GC entry. + var sourceEntry = RiTables.findSource(ip); + if (sourceEntry != Pointer.NULL) { // found a source entry. + var frameWords = printSourceEntry(ip, sourceEntry); + var t = RiOs.callerFrame(ip, sp, frameWords); + ip = t.0; sp = t.1; + continue; + } + */ + + // Search for user code. + if (RiRuntime.userCodeList != null) { + var userCode = RiRuntime.findUserCode(ip); + if (userCode == null) return; // no user code found + callback(ip, sp, userCode, 0); + var t = userCode.nextFrame(ip, sp); + ip = t.0; sp = t.1; + continue; + } + return; // unknown frame; assume finished + } + } +} \ No newline at end of file diff --git a/test/rt/x86-64-darwin/stackwalk0.v3 b/test/rt/x86-64-darwin/stackwalk0.v3 new file mode 100644 index 000000000..14d9813a4 --- /dev/null +++ b/test/rt/x86-64-darwin/stackwalk0.v3 @@ -0,0 +1,14 @@ +def main() -> int { + foo(Array.new(4)); + foo(Array.new(8)); + return 33; +} +def foo(x: Array) -> int { + NativeStackWalker.iterateFrames(CiRuntime.callerIp() + -1, CiRuntime.callerSp(), printFrame); + return x.length; +} +def printFrame(ip: Pointer, sp: Pointer, code: RiUserCode, size: int) -> bool { + if (code == null) System.puts("--{virgil frame}--\n"); + else System.puts("--{user frame}--\n"); + return true; +} diff --git a/test/rt/x86-64-darwin/stackwalk0.v3.out b/test/rt/x86-64-darwin/stackwalk0.v3.out new file mode 100644 index 000000000..27b11dbac --- /dev/null +++ b/test/rt/x86-64-darwin/stackwalk0.v3.out @@ -0,0 +1,2 @@ +--{virgil frame}-- +--{virgil frame}-- diff --git a/test/rt/x86-64-linux/stackwalk0.v3 b/test/rt/x86-64-linux/stackwalk0.v3 new file mode 100644 index 000000000..14d9813a4 --- /dev/null +++ b/test/rt/x86-64-linux/stackwalk0.v3 @@ -0,0 +1,14 @@ +def main() -> int { + foo(Array.new(4)); + foo(Array.new(8)); + return 33; +} +def foo(x: Array) -> int { + NativeStackWalker.iterateFrames(CiRuntime.callerIp() + -1, CiRuntime.callerSp(), printFrame); + return x.length; +} +def printFrame(ip: Pointer, sp: Pointer, code: RiUserCode, size: int) -> bool { + if (code == null) System.puts("--{virgil frame}--\n"); + else System.puts("--{user frame}--\n"); + return true; +} diff --git a/test/rt/x86-64-linux/stackwalk0.v3.out b/test/rt/x86-64-linux/stackwalk0.v3.out new file mode 100644 index 000000000..27b11dbac --- /dev/null +++ b/test/rt/x86-64-linux/stackwalk0.v3.out @@ -0,0 +1,2 @@ +--{virgil frame}-- +--{virgil frame}-- diff --git a/test/rt/x86-64-linux/stackwalk1.v3 b/test/rt/x86-64-linux/stackwalk1.v3 new file mode 100644 index 000000000..a8c73ce8d --- /dev/null +++ b/test/rt/x86-64-linux/stackwalk1.v3 @@ -0,0 +1,13 @@ +def main() -> int { + foo(Array.new(4)); + foo(Array.new(8)); + return 33; +} +def foo(x: Array) -> int { + NativeStackWalker.iterateFrames(CiRuntime.callerIp() + -1, CiRuntime.callerSp(), printFrame); + return x.length; +} +def printFrame(ip: Pointer, sp: Pointer, code: RiUserCode, size: int) -> bool { + System.puts("--{user frame}--\n"); + return true; +} diff --git a/test/rt/x86-darwin/stackwalk0.v3 b/test/rt/x86-darwin/stackwalk0.v3 new file mode 100644 index 000000000..14d9813a4 --- /dev/null +++ b/test/rt/x86-darwin/stackwalk0.v3 @@ -0,0 +1,14 @@ +def main() -> int { + foo(Array.new(4)); + foo(Array.new(8)); + return 33; +} +def foo(x: Array) -> int { + NativeStackWalker.iterateFrames(CiRuntime.callerIp() + -1, CiRuntime.callerSp(), printFrame); + return x.length; +} +def printFrame(ip: Pointer, sp: Pointer, code: RiUserCode, size: int) -> bool { + if (code == null) System.puts("--{virgil frame}--\n"); + else System.puts("--{user frame}--\n"); + return true; +} diff --git a/test/rt/x86-darwin/stackwalk0.v3.out b/test/rt/x86-darwin/stackwalk0.v3.out new file mode 100644 index 000000000..27b11dbac --- /dev/null +++ b/test/rt/x86-darwin/stackwalk0.v3.out @@ -0,0 +1,2 @@ +--{virgil frame}-- +--{virgil frame}-- diff --git a/test/rt/x86-linux/stackwalk0.v3 b/test/rt/x86-linux/stackwalk0.v3 new file mode 100644 index 000000000..14d9813a4 --- /dev/null +++ b/test/rt/x86-linux/stackwalk0.v3 @@ -0,0 +1,14 @@ +def main() -> int { + foo(Array.new(4)); + foo(Array.new(8)); + return 33; +} +def foo(x: Array) -> int { + NativeStackWalker.iterateFrames(CiRuntime.callerIp() + -1, CiRuntime.callerSp(), printFrame); + return x.length; +} +def printFrame(ip: Pointer, sp: Pointer, code: RiUserCode, size: int) -> bool { + if (code == null) System.puts("--{virgil frame}--\n"); + else System.puts("--{user frame}--\n"); + return true; +} diff --git a/test/rt/x86-linux/stackwalk0.v3.out b/test/rt/x86-linux/stackwalk0.v3.out new file mode 100644 index 000000000..27b11dbac --- /dev/null +++ b/test/rt/x86-linux/stackwalk0.v3.out @@ -0,0 +1,2 @@ +--{virgil frame}-- +--{virgil frame}--