[PATCH] "next subreturn" runs to end of the current subroutine

[ Thread Index | Date Index | More lists.tuxfamily.org/hatari-devel Archives ]


....even if it calls further subroutines.

Earlier, "next subreturn" ran only until next RTD/RTR/RTS instruction,
regardless of whether it was in another function, which would mean
user losing context of current function in the debugger.

This is fixed by increasing & decreasing new (CPU & DSP) call depth
variables on subroutine calls and returns, and "next subreturn"
running until return instruction happens on same depth as where user
invoked "next" command.

NOTE: "next exreturn" and "next return" will still run only until
next applicable instruction, regardless of where it's called (see
debugger manual for explanation).
---
 doc/debugger.html             | 49 +++++++++++++++++++++++++++++------
 doc/release-notes.txt         |  2 ++
 src/debug/debugcpu.c          | 44 ++++++++++++++++++++++++++-----
 src/debug/debugcpu.h          |  2 ++
 src/debug/debugdsp.c          | 30 +++++++++++++++++++--
 src/debug/debugdsp.h          |  1 +
 src/debug/vars.c              |  2 ++
 tests/debugger/test-dummies.c |  1 +
 8 files changed, 115 insertions(+), 16 deletions(-)

diff --git a/doc/debugger.html b/doc/debugger.html
index 41c50707..8e9fca8f 100644
--- a/doc/debugger.html
+++ b/doc/debugger.html
@@ -947,7 +947,7 @@ these debugger input files also with the debugger "file" command,
 command line option.
 </p>
 
-<h3>Stepping through code</h3>
+<h3 id="Stepping">Stepping through code</h3>
 
 <p>
 After analyzing the emulation state and/or setting new breakpoints,
@@ -987,13 +987,29 @@ type is encountered, by giving it the instruction type:
 </ul>
 
 <p>
-For example: "n branch", or "dn branch".
+"subreturn" differs from others by running until current subroutine
+ends, even if other subroutines are called before that.  This is
+particularly useful for profiling more complex functions; set
+breakpoint on function start, enable profiling and run until that
+functions returns, to get its full profile. Example: "n subreturn",
+or "dn subreturn".
 </p>
 
-<p>
-(Note: CHK, CHK2, FBCC, FDBCC, &amp; FTRAPCC exception / branch CPU
-instructions aren't supported currently.)
-</p>
+<p>Notes:</p>
+<ul>
+<li>"next subreturn" works only for real subroutines i.e. code called
+    with BSR/JSR and returning with RT[DRS]. In (GCC) optimized compiled
+    C-code, calls to functions in same C / object file may get inlined
+    or just called with JMP.  If that's the case, and making the
+    function non-static doesn't help, move it to another C-file</li>
+<li>"exreturn" and "return" run only until first instruction of given
+    type is encountered.  They cannot implement similar functionality
+    as "subreturn", because it's not possible for "next" command to
+    track CPU / DSP exception invocations, and therefore it cannot
+    do call depth tracking required for this either</li>
+<li>Tracking CPU CHK2 and FPU FBCC, FDBCC &amp; FTRAPCC exception /
+    branch instructions isn't supported currently</li>
+</ul>
 
 
 <h3>Tracing</h3>
@@ -2010,11 +2026,28 @@ Please see <a href="#Profiling">Profiling</a> section for more info.
 profile  on
 c
 [after a while, use AltGr+Pause to get back to debugger]
-profile  counts
+profile  addresses
 </pre>
 Please see <a href="#Profiling">Profiling</a> section for more info.
 </dd>
 
+<dt><em>Profiling specific function and everything it calls</em></dt>
+<dd>Set breakpoint for the function entrypoint and when it is hit,
+enable profiling and continue to the end of the function:
+<pre>
+address myfunction
+c
+[back in debugger at myfunction]
+profile on
+next subreturn
+[back in debugger when function returns]
+</pre>
+Please see <a href="#Profiling">Profiling</a> section on and how
+to save and analyze profiling information after that, and
+<a href="#Stepping">Stepping</a> section for some "next subreturn"
+command limitations.
+</dd>
+
 <dt><em>Seeing program callstack when breakpoint is hit</em></dt>
 <dd><a href="#Caller_information">Profiler caller data</a> includes
 callstack information (with some limitations).
@@ -2038,7 +2071,7 @@ profile  stack
 </dd>
 
 <dt><em>Seeing how program functions/symbols call each other</em></dt>
-<dd><a href="#Profile_data_post-processing">Profile data
+<dd><a href="#Profile_data_post_processing">Profile data
 post-processing</a> can provide execution callgraphs.
 </dd>
 
diff --git a/doc/release-notes.txt b/doc/release-notes.txt
index e80e9437..e7623ecb 100644
--- a/doc/release-notes.txt
+++ b/doc/release-notes.txt
@@ -33,6 +33,8 @@ Emulator:
     implements real VME/SCU register emulation)
 - Debugger:
   - Add "info vme" and VME/SCU reg access tracing support
+  - "next subreturn" will run until current function returns,
+    even if function calls other functions before that
 
 
  Version 2.3.1 (2020-12-27)
diff --git a/src/debug/debugcpu.c b/src/debug/debugcpu.c
index 96da1788..f18ff9cd 100644
--- a/src/debug/debugcpu.c
+++ b/src/debug/debugcpu.c
@@ -731,16 +731,28 @@ static char *DebugCpu_MatchNext(const char *text, int state)
 	return DebugUI_MatchHelper(ntypes, ARRAY_SIZE(ntypes), text, state);
 }
 
+/**
+ * Variable + debugger variable function for tracking
+ * subroutine call depth for "next" breakpoint
+ */
+static int CpuCallDepth;
+Uint32 DebugCpu_CallDepth(void)
+{
+	return CpuCallDepth;
+}
+#define CALL_START_DEPTH 1024
+
 /**
  * Command: Step CPU, but proceed through subroutines
  * Does this by temporary conditional breakpoint
  */
 static int DebugCpu_Next(int nArgc, char *psArgv[])
 {
-	char command[40];
+	char command[64];
 	if (nArgc > 1)
 	{
 		int optype;
+		bool depthcheck = false;
 		if(strcmp(psArgv[1], "branch") == 0)
 			optype = CALL_BRANCH;
 		else if(strcmp(psArgv[1], "exception") == 0)
@@ -750,7 +762,10 @@ static int DebugCpu_Next(int nArgc, char *psArgv[])
 		else if(strcmp(psArgv[1], "subcall") == 0)
 			optype = CALL_SUBROUTINE;
 		else if (strcmp(psArgv[1], "subreturn") == 0)
+		{
 			optype = CALL_SUBRETURN;
+			depthcheck = true;
+		}
 		else if (strcmp(psArgv[1], "return") == 0)
 			optype = CALL_SUBRETURN | CALL_EXCRETURN;
 		else
@@ -758,7 +773,18 @@ static int DebugCpu_Next(int nArgc, char *psArgv[])
 			fprintf(stderr, "Unrecognized opcode type given!\n");
 			return DEBUGGER_CMDDONE;
 		}
-		sprintf(command, "CpuOpcodeType & $%x > 0 :once :quiet\n", optype);
+		/* CpuOpCodeType increases call depth on subroutine calls,
+		 * and decreases depth on return from them, so it must be
+		 * first check to get called on every relevant instruction.
+		 */
+		if (depthcheck)
+		{
+			CpuCallDepth = CALL_START_DEPTH;
+			sprintf(command, "CpuOpcodeType & $%x > 0  &&  CpuCallDepth < $%x  :once :quiet\n",
+				optype, CALL_START_DEPTH);
+		}
+		else
+			sprintf(command, "CpuOpcodeType & $%x > 0 :once :quiet\n", optype);
 	}
 	else
 	{
@@ -805,16 +831,21 @@ Uint32 DebugCpu_OpcodeType(void)
 	if (opcode == 0x4e74 ||			/* RTD */
 	    opcode == 0x4e75 ||			/* RTS */
 	    opcode == 0x4e77)			/* RTR */
+	{
+		CpuCallDepth--;
 		return CALL_SUBRETURN;
-
+	}
 	if (opcode == 0x4e73)			/* RTE */
+	{
 		return CALL_EXCRETURN;
-
+	}
 	/* NOTE: BSR needs to be matched before BRA/BCC! */
 	if ((opcode & 0xff00) == 0x6100 ||	/* BSR */
 	    (opcode & 0xffc0) == 0x4e80)	/* JSR */
+	{
+		CpuCallDepth++;
 		return CALL_SUBROUTINE;
-
+	}
 	/* TODO: ftrapcc, chk2? */
 	if (opcode == 0x4e72 ||			/* STOP */
 	    opcode == 0x4afc ||			/* ILLEGAL */
@@ -822,8 +853,9 @@ Uint32 DebugCpu_OpcodeType(void)
 	    (opcode & 0xfff0) == 0x4e40 ||	/* TRAP */
 	    (opcode & 0xf1c0) == 0x4180 ||	/* CHK */
 	    (opcode & 0xfff8) == 0x4848)	/* BKPT */
+	{
 		return CALL_EXCEPTION;
-
+	}
 	/* TODO: fbcc, fdbcc */
 	if ((opcode & 0xf000) == 0x6000 ||	/* BRA / BCC */
 	    (opcode & 0xffc0) == 0x4ec0 ||	/* JMP */
diff --git a/src/debug/debugcpu.h b/src/debug/debugcpu.h
index 872432fe..89be812e 100644
--- a/src/debug/debugcpu.h
+++ b/src/debug/debugcpu.h
@@ -12,6 +12,8 @@
 
 extern void DebugCpu_Check(void);
 extern void DebugCpu_SetDebugging(void);
+
+extern Uint32 DebugCpu_CallDepth(void);
 extern Uint32 DebugCpu_InstrCount(void);
 extern Uint32 DebugCpu_OpcodeType(void);
 
diff --git a/src/debug/debugdsp.c b/src/debug/debugdsp.c
index bc6bf67c..05147813 100644
--- a/src/debug/debugdsp.c
+++ b/src/debug/debugdsp.c
@@ -326,16 +326,28 @@ static char *DebugDsp_MatchNext(const char *text, int state)
 	return DebugUI_MatchHelper(ntypes, ARRAY_SIZE(ntypes), text, state);
 }
 
+/**
+ * Variable + debugger variable function for tracking
+ * subroutine call depth for "dspnext" breakpoint
+ */
+static int DspCallDepth;
+Uint32 DebugDsp_CallDepth(void)
+{
+	return DspCallDepth;
+}
+#define CALL_START_DEPTH 1024
+
 /**
  * Command: Step DSP, but proceed through subroutines
  * Does this by temporary conditional breakpoint
  */
 static int DebugDsp_Next(int nArgc, char *psArgv[])
 {
-	char command[40];
+	char command[64];
 	if (nArgc > 1)
 	{
 		int optype;
+		bool depthcheck = false;
 		if(strcmp(psArgv[1], "branch") == 0)
 			optype = CALL_BRANCH;
 		else if(strcmp(psArgv[1], "exreturn") == 0)
@@ -343,7 +355,10 @@ static int DebugDsp_Next(int nArgc, char *psArgv[])
 		else if(strcmp(psArgv[1], "subcall") == 0)
 			optype = CALL_SUBROUTINE;
 		else if (strcmp(psArgv[1], "subreturn") == 0)
+		{
 			optype = CALL_SUBRETURN;
+			depthcheck = true;
+		}
 		else if (strcmp(psArgv[1], "return") == 0)
 			optype = CALL_SUBRETURN | CALL_EXCRETURN;
 		else
@@ -351,7 +366,16 @@ static int DebugDsp_Next(int nArgc, char *psArgv[])
 			fprintf(stderr, "Unrecognized opcode type given!\n");
 			return DEBUGGER_CMDDONE;
 		}
-		sprintf(command, "DspOpcodeType & $%x > 0 :once :quiet\n", optype);
+		/* DspOpCodeType increases call depth on subroutine calls, and
+		 * decreases depth on return from them, so it must be first check
+		 * to get called on every relevant instruction.
+		 */
+		DspCallDepth = CALL_START_DEPTH;
+		if (depthcheck)
+			sprintf(command, "DspOpcodeType & $%x > 0  &&  DspCallDepth < $%x  :once :quiet\n",
+				optype, CALL_START_DEPTH);
+		else
+			sprintf(command, "DspOpcodeType & $%x > 0 :once :quiet\n", optype);
 	}
 	else
 	{
@@ -390,6 +414,7 @@ Uint32 DebugDsp_OpcodeType(void)
 
 	/* subroutine returns */
 	if (opcode == 0xC) {	/* (just) RTS */
+		DspCallDepth--;
 		return CALL_SUBRETURN;
 	}
 	if (
@@ -407,6 +432,7 @@ Uint32 DebugDsp_OpcodeType(void)
 	    (opcode & 0xFFC0A0) == 0xB00A0 ||	/* JSSET 00001011 00aaaaaa 1S1bbbbb */
 	    (opcode & 0xFFC0A0) == 0xB80A0 ||	/* JSSET 00001011 10pppppp 1S1bbbbb */
 	    (opcode & 0xFFC0E0) == 0xBC020) {	/* JSSET 00001011 11DDDDDD 001bbbbb */
+		DspCallDepth++;
 		return CALL_SUBROUTINE;
 	}
 	/* exception handler returns */
diff --git a/src/debug/debugdsp.h b/src/debug/debugdsp.h
index 12168b91..7f76385f 100644
--- a/src/debug/debugdsp.h
+++ b/src/debug/debugdsp.h
@@ -16,6 +16,7 @@ extern void DebugDsp_SetDebugging(void);
 #define DebugDsp_SetDebugging()
 #endif /* !ENABLE_DSP_EMU */
 extern void DebugDsp_Check(void);
+extern Uint32 DebugDsp_CallDepth(void);
 extern Uint32 DebugDsp_InstrCount(void);
 extern Uint32 DebugDsp_OpcodeType(void);
 extern int DebugDsp_DisAsm(int nArgc, char *psArgs[]);
diff --git a/src/debug/vars.c b/src/debug/vars.c
index 091ad661..31095ee6 100644
--- a/src/debug/vars.c
+++ b/src/debug/vars.c
@@ -158,11 +158,13 @@ static const var_addr_t hatari_vars[] = {
 	{ "Basepage", (Uint32*)DebugInfo_GetBASEPAGE, VALUE_TYPE_FUNCTION32, 0, "invalid before Desktop is up" },
 	{ "BiosOpcode", (Uint32*)GetBiosOpcode, VALUE_TYPE_FUNCTION32, 16, "$FFFF when not on BIOS trap" },
 	{ "BSS", (Uint32*)DebugInfo_GetBSS, VALUE_TYPE_FUNCTION32, 0, "invalid before Desktop is up" },
+	{ "CpuCallDepth", (Uint32*)DebugCpu_CallDepth, VALUE_TYPE_FUNCTION32, 0, "CPU subroutine call depth" },
 	{ "CpuInstr", (Uint32*)DebugCpu_InstrCount, VALUE_TYPE_FUNCTION32, 0, "CPU instructions count" },
 	{ "CpuOpcodeType", (Uint32*)DebugCpu_OpcodeType, VALUE_TYPE_FUNCTION32, 0, "internal CPU instruction type" },
 	{ "CycleCounter", (Uint32*)GetCycleCounter, VALUE_TYPE_FUNCTION32, 0, "global cycles counter (lower 32 bits)" },
 	{ "DATA", (Uint32*)DebugInfo_GetDATA, VALUE_TYPE_FUNCTION32, 0, "invalid before Desktop is up" },
 #if ENABLE_DSP_EMU
+	{ "DspCallDepth", (Uint32*)DebugDsp_CallDepth, VALUE_TYPE_FUNCTION32, 0, "DSP subroutine call depth" },
 	{ "DspInstr", (Uint32*)DebugDsp_InstrCount, VALUE_TYPE_FUNCTION32, 0, "DSP instructions count" },
 	{ "DspOpcodeType", (Uint32*)DebugDsp_OpcodeType, VALUE_TYPE_FUNCTION32, 0, "internal DSP instruction type" },
 #endif
diff --git a/tests/debugger/test-dummies.c b/tests/debugger/test-dummies.c
index 4b6f9bc6..b2843f21 100644
--- a/tests/debugger/test-dummies.c
+++ b/tests/debugger/test-dummies.c
@@ -126,6 +126,7 @@ info_func_t DebugInfo_GetInfoFunc(const char *name) {
 #ifdef ENABLE_DSP_EMU
 #include "debugdsp.h"
 void DebugDsp_InitSession(void) { }
+Uint32 DebugDsp_CallDepth(void) { return 0; }
 Uint32 DebugDsp_InstrCount(void) { return 0; }
 Uint32 DebugDsp_OpcodeType(void) { return 0; }
 #endif
-- 
2.20.1


--------------6FE778D10B6AC4CAA2EC08D8--



Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/