summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--changelog.md2
-rw-r--r--compiler/options.nim1
-rw-r--r--compiler/vm.nim7
-rw-r--r--compiler/vmops.nim3
-rw-r--r--lib/std/vmutils.nim11
-rw-r--r--tests/stdlib/tvmutils.nim31
6 files changed, 53 insertions, 2 deletions
diff --git a/changelog.md b/changelog.md
index 570aff542..6672f7538 100644
--- a/changelog.md
+++ b/changelog.md
@@ -387,6 +387,8 @@
 
 - Added `--declaredlocs` to show symbol declaration location in messages.
 
+- You can now enable/disable VM tracing in user code via `vmutils.vmTrace`.
+
 - Deprecated `TaintedString` and `--taintmode`.
 
 - Deprecated `--nilseqs` which is now a noop.
diff --git a/compiler/options.nim b/compiler/options.nim
index ffb0f4501..135ecf1de 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -330,6 +330,7 @@ type
     warnCounter*: int
     errorMax*: int
     maxLoopIterationsVM*: int ## VM: max iterations of all loops
+    isVmTrace*: bool
     configVars*: StringTableRef
     symbols*: StringTableRef ## We need to use a StringTableRef here as defined
                              ## symbols are always guaranteed to be style
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 837a1c366..9541af701 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -548,9 +548,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         "pc", $pc, "opcode", alignLeft($c.code[pc].opcode, 15),
         "ra", regDescr("ra", ra), "rb", regDescr("rb", instr.regB),
         "rc", regDescr("rc", instr.regC)]
-
+    if c.config.isVmTrace:
+      # unlike nimVMDebug, this doesn't require re-compiling nim and is controlled by user code
+      let info = c.debug[pc]
+      # other useful variables: c.loopIterations
+      echo "$# [$#] $#" % [c.config$info, $instr.opcode, c.config.sourceLine(info)]
     c.profiler.enter(c, tos)
-
     case instr.opcode
     of opcEof: return regs[ra]
     of opcRet:
diff --git a/compiler/vmops.nim b/compiler/vmops.nim
index 85729fd59..67a196b8f 100644
--- a/compiler/vmops.nim
+++ b/compiler/vmops.nim
@@ -249,6 +249,9 @@ proc registerAdditionalOps*(c: PCtx) =
                   "isExported() requires a symbol. '" & $n & "' is of kind '" & $n.kind & "'", n.info)
     setResult(a, sfExported in n.sym.flags)
 
+  registerCallback c, "stdlib.vmutils.vmTrace", proc (a: VmArgs) =
+    c.config.isVmTrace = getBool(a, 0)
+
   proc hashVmImpl(a: VmArgs) =
     var res = hashes.hash(a.getString(0), a.getInt(1).int, a.getInt(2).int)
     if c.config.backend == backendJs:
diff --git a/lib/std/vmutils.nim b/lib/std/vmutils.nim
new file mode 100644
index 000000000..e16912a3c
--- /dev/null
+++ b/lib/std/vmutils.nim
@@ -0,0 +1,11 @@
+##[
+Experimental API, subject to change.
+]##
+
+proc vmTrace*(on: bool) {.compileTime.} =
+  runnableExamples:
+    static: vmTrace(true)
+    proc fn =
+      var a = 1
+      vmTrace(false)
+    static: fn()
diff --git a/tests/stdlib/tvmutils.nim b/tests/stdlib/tvmutils.nim
new file mode 100644
index 000000000..f43557ad8
--- /dev/null
+++ b/tests/stdlib/tvmutils.nim
@@ -0,0 +1,31 @@
+discard """
+  joinable: false
+  nimout: '''
+0
+1
+2
+tvmutils.nim(28, 13) [opcLdImmInt]     if i == 4:
+tvmutils.nim(28, 10) [opcEqInt]     if i == 4:
+tvmutils.nim(28, 10) [opcFJmp]     if i == 4:
+tvmutils.nim(28, 13) [opcLdImmInt]     if i == 4:
+tvmutils.nim(28, 10) [opcEqInt]     if i == 4:
+tvmutils.nim(28, 10) [opcFJmp]     if i == 4:
+tvmutils.nim(29, 7) [opcLdConst]       vmTrace(false)
+tvmutils.nim(29, 15) [opcLdImmInt]       vmTrace(false)
+tvmutils.nim(29, 14) [opcIndCall]       vmTrace(false)
+5
+6
+'''
+"""
+# line 20 (only showing a subset of nimout to avoid making the test rigid)
+import std/vmutils
+
+proc main() =
+  for i in 0..<7:
+    echo i
+    if i == 2:
+      vmTrace(true)
+    if i == 4:
+      vmTrace(false)
+
+static: main()