[PATCH] "next subreturn" runs to end of the current subroutine |
[ Thread Index |
Date Index
| More lists.tuxfamily.org/hatari-devel Archives
]
- Subject: [PATCH] "next subreturn" runs to end of the current subroutine
- From: Eero Tamminen <oak@xxxxxxxxxxxxxx>
- Date: Fri, 8 Jan 2021 01:33:25 +0200
....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, & 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 & 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--