diff --git a/CMakeLists.txt b/CMakeLists.txt
index 95f44d9753..cfc00885ea 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -934,6 +934,8 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/MIPS/MIPSInt.h
Core/MIPS/MIPSIntVFPU.cpp
Core/MIPS/MIPSIntVFPU.h
+ Core/MIPS/MIPSStackWalk.cpp
+ Core/MIPS/MIPSStackWalk.h
Core/MIPS/MIPSTables.cpp
Core/MIPS/MIPSTables.h
Core/MIPS/MIPSVFPUUtils.cpp
diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt
index 90dbf87d39..dad4011183 100644
--- a/Core/CMakeLists.txt
+++ b/Core/CMakeLists.txt
@@ -15,6 +15,7 @@ set(SRCS
MIPS/MIPSDisVFPU.cpp
MIPS/MIPSInt.cpp
MIPS/MIPSIntVFPU.cpp
+ MIPS/MIPSStackWalk.cpp
MIPS/MIPSTables.cpp
MIPS/MIPSVFPUUtils.cpp
MIPS/JitCommon/JitCommon.cpp
diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj
index 60a98ee845..604058db6f 100644
--- a/Core/Core.vcxproj
+++ b/Core/Core.vcxproj
@@ -336,6 +336,7 @@
+
@@ -487,6 +488,7 @@
+
diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters
index e96aaf9a55..3d79f6093d 100644
--- a/Core/Core.vcxproj.filters
+++ b/Core/Core.vcxproj.filters
@@ -454,6 +454,9 @@
HW
+
+ MIPS
+
@@ -846,6 +849,9 @@
HW
+
+ MIPS
+
diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp
index 6ebc42ca29..96b43d03a3 100644
--- a/Core/HLE/sceKernelThread.cpp
+++ b/Core/HLE/sceKernelThread.cpp
@@ -3539,6 +3539,8 @@ std::vector GetThreadsInfo()
info.name[KERNELOBJECT_MAX_NAME_LENGTH] = 0;
info.status = t->nt.status;
info.entrypoint = t->nt.entrypoint;
+ info.initialStack = t->nt.initialStack;
+ info.stackSize = (u32)t->nt.stackSize;
info.priority = t->nt.currentPriority;
info.waitType = t->nt.waitType;
if(*iter == currentThread)
diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h
index 964812df1a..e931f959f4 100644
--- a/Core/HLE/sceKernelThread.h
+++ b/Core/HLE/sceKernelThread.h
@@ -313,8 +313,10 @@ struct DebugThreadInfo
SceUID id;
char name[KERNELOBJECT_MAX_NAME_LENGTH+1];
u32 status;
- int curPC;
- int entrypoint;
+ u32 curPC;
+ u32 entrypoint;
+ u32 initialStack;
+ int stackSize;
int priority;
WaitType waitType;
bool isCurrent;
diff --git a/Core/MIPS/MIPSStackWalk.cpp b/Core/MIPS/MIPSStackWalk.cpp
new file mode 100644
index 0000000000..a2241e8b2a
--- /dev/null
+++ b/Core/MIPS/MIPSStackWalk.cpp
@@ -0,0 +1,173 @@
+// Copyright (c) 2012- PPSSPP Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0 or later versions.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official git repository and contact information can be found at
+// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+
+#include "Core/MemMap.h"
+#include "Core/Debugger/SymbolMap.h"
+#include "Core/MIPS/MIPSCodeUtils.h"
+#include "Core/MIPS/MIPSStackWalk.h"
+
+#define _RS ((op >> 21) & 0x1F)
+#define _RT ((op >> 16) & 0x1F)
+#define _RD ((op >> 11) & 0x1F)
+#define _IMM16 ((signed short)(op & 0xFFFF))
+
+#define MIPSTABLE_IMM_MASK 0xFC000000
+#define MIPSTABLE_SPECIAL_MASK 0xFC00003F
+
+namespace MIPSStackWalk {
+ using namespace MIPSCodeUtils;
+
+ // In the worst case, we scan this far above the pc for a entry.
+ const int MAX_FUNC_SIZE = 32768 * 4;
+ // After this we assume we're stuck.
+ const size_t MAX_DEPTH = 1024;
+
+ static u32 GuessEntry(u32 pc) {
+ SymbolInfo info;
+ if (symbolMap.GetSymbolInfo(&info, pc)) {
+ return info.address;
+ }
+ return INVALIDTARGET;
+ }
+
+ bool IsSWInstr(u32 op) {
+ return (op & MIPSTABLE_IMM_MASK) == 0xAC000000;
+ }
+
+ bool IsAddImmInstr(u32 op) {
+ return (op & MIPSTABLE_IMM_MASK) == 0x20000000 || (op & MIPSTABLE_IMM_MASK) == 0x24000000;
+ }
+
+ bool IsMovRegsInstr(u32 op) {
+ // TODO: There are more options here. Let's assume addu for now.
+ if ((op & MIPSTABLE_SPECIAL_MASK) == 0x00000021) {
+ return _RS == 0 || _RT == 0;
+ }
+ return false;
+ }
+
+ bool ScanForAllocaSignature(u32 pc) {
+ // In God Eater Burst, for example, after 0880E750, there's what looks like an alloca().
+ // It's surrounded by "mov fp, sp" and "mov sp, fp", which is unlikely to be used for other reasons.
+
+ // It ought to be pretty close.
+ u32 stop = pc - 32 * 4;
+ for (; Memory::IsValidAddress(pc) && pc >= stop; pc -= 4) {
+ u32 op = Memory::Read_Instruction(pc);
+
+ // We're looking for a "mov fp, sp" close by a "addiu sp, sp, -N".
+ if (IsMovRegsInstr(op) && _RD == MIPS_REG_FP && (_RS == MIPS_REG_SP || _RT == MIPS_REG_SP)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool ScanForEntry(StackFrame &frame, u32 entry, u32 &ra) {
+ // TODO: Check if found entry is in the same symbol? Might be wrong sometimes...
+
+ int ra_offset = -1;
+ u32 stop = entry == INVALIDTARGET ? 0 : entry;
+ for (u32 pc = frame.pc; Memory::IsValidAddress(pc) && pc >= stop; pc -= 4) {
+ u32 op = Memory::Read_Instruction(pc);
+
+ // Here's where they store the ra address.
+ if (IsSWInstr(op) && _RT == MIPS_REG_RA && _RS == MIPS_REG_SP) {
+ ra_offset = _IMM16;
+ }
+
+ if (IsAddImmInstr(op) && _RT == MIPS_REG_SP && _RS == MIPS_REG_SP) {
+ // A positive imm either means alloca() or we went too far.
+ if (_IMM16 > 0) {
+ // TODO: Maybe check for any alloca() signature and bail?
+ continue;
+ }
+ if (ScanForAllocaSignature(pc)) {
+ continue;
+ }
+
+ frame.entry = pc;
+ frame.stackSize = -_IMM16;
+ if (ra_offset != -1) {
+ ra = Memory::Read_U32(frame.sp + ra_offset);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool DetermineFrameInfo(StackFrame &frame, u32 possibleEntry, u32 threadEntry, u32 &ra) {
+ if (ScanForEntry(frame, possibleEntry, ra)) {
+ // Awesome, found one that looks right.
+ return true;
+ } else if (ra != INVALIDTARGET && possibleEntry != INVALIDTARGET) {
+ // Let's just assume it's a leaf.
+ frame.entry = possibleEntry;
+ frame.stackSize = 0;
+ return true;
+ }
+
+ // Okay, we failed to get one. Our possibleEntry could be wrong, it often is.
+ // Let's just scan upward.
+ u32 newPossibleEntry = frame.pc > threadEntry ? threadEntry : frame.pc - MAX_FUNC_SIZE;
+ if (ScanForEntry(frame, newPossibleEntry, ra)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ std::vector Walk(u32 pc, u32 ra, u32 sp, u32 threadEntry, u32 threadStackTop) {
+ std::vector frames;
+ StackFrame current;
+ current.pc = pc;
+ current.sp = sp;
+ current.entry = INVALIDTARGET;
+ current.stackSize = -1;
+
+ u32 prevEntry = INVALIDTARGET;
+ while (pc != threadEntry) {
+ u32 possibleEntry = GuessEntry(current.pc);
+ if (DetermineFrameInfo(current, possibleEntry, threadEntry, ra)) {
+ frames.push_back(current);
+ if (current.entry == threadEntry || GuessEntry(current.entry) == threadEntry) {
+ break;
+ }
+ if (current.entry == prevEntry || frames.size() >= MAX_DEPTH) {
+ // Recursion, means we're screwed. Let's just give up.
+ break;
+ }
+ prevEntry = current.entry;
+
+ current.pc = ra;
+ current.sp += current.stackSize;
+ ra = INVALIDTARGET;
+ current.entry = INVALIDTARGET;
+ current.stackSize = -1;
+ } else {
+ // Well, we got as far as we could.
+ current.entry = possibleEntry;
+ current.stackSize = 0;
+ frames.push_back(current);
+ break;
+ }
+ }
+
+ return frames;
+ }
+};
\ No newline at end of file
diff --git a/Core/MIPS/MIPSStackWalk.h b/Core/MIPS/MIPSStackWalk.h
new file mode 100644
index 0000000000..78b3e1dc95
--- /dev/null
+++ b/Core/MIPS/MIPSStackWalk.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2012- PPSSPP Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0 or later versions.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official git repository and contact information can be found at
+// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+
+#pragma once
+
+#include
+#include "Common/CommonTypes.h"
+
+namespace MIPSStackWalk {
+ struct StackFrame {
+ // Beginning of function symbol (may be estimated.)
+ u32 entry;
+ // Next position within function.
+ u32 pc;
+ // Value of SP inside this function (assuming no alloca()...)
+ u32 sp;
+ // Size of stack in bytes.
+ int stackSize;
+ };
+
+ std::vector Walk(u32 pc, u32 ra, u32 sp, u32 threadEntry, u32 threadStackTop);
+};
\ No newline at end of file
diff --git a/android/jni/Android.mk b/android/jni/Android.mk
index 9f99b804fb..fd2c2ebad7 100644
--- a/android/jni/Android.mk
+++ b/android/jni/Android.mk
@@ -282,6 +282,7 @@ LOCAL_SRC_FILES := \
$(SRC)/Core/MIPS/MIPSDisVFPU.cpp \
$(SRC)/Core/MIPS/MIPSInt.cpp.arm \
$(SRC)/Core/MIPS/MIPSIntVFPU.cpp.arm \
+ $(SRC)/Core/MIPS/MIPSStackWalk.cpp \
$(SRC)/Core/MIPS/MIPSTables.cpp.arm \
$(SRC)/Core/MIPS/MIPSVFPUUtils.cpp.arm \
$(SRC)/Core/MIPS/MIPSCodeUtils.cpp.arm \