Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save zhangjiequan/c9c97c70e554d58bd7bdda84389f4771 to your computer and use it in GitHub Desktop.
Save zhangjiequan/c9c97c70e554d58bd7bdda84389f4771 to your computer and use it in GitHub Desktop.
Dump Lua Stack using C/C# In Unity Tolua#
int luaopen_fp64(lua_State* L)
{
luaL_newmetatable(L, "fp64");
luaL_setfuncs(L, lib_fp64_meta, 0);
luaL_newlib(L, lib_fp64);
stackDump(L); //加这句,dump出当前Lua虚拟栈
pushfp64(L, fp64_pi);
stackDump(L); //加这句,dump出当前Lua虚拟栈
lua_setfield(L, -2, "pi");
pushfp64(L, fp64_one);
//Call_StackDumper_Example.cs
protected void OpenFP64()
{
luaState.OpenLibs(LuaDLL.luaopen_fp64);
StackDumper.StackDump(luaState);//加这句,dump出当前Lua虚拟栈
luaState.LuaSetField(-3, "fp64");
StackDumper.StackDump(luaState);//加这句,dump出当前Lua虚拟栈
}
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#include <string.h>
#include "stackdumper.h"
#include <stdbool.h>
#include <inttypes.h>
bool _enable = true;
void callLuaPrint(lua_State *L, const char *str)
{
if (!_enable)
{
return;
}
lua_getglobal(L, "print");
lua_pushfstring(L, str);
int result = lua_pcall(L, 1, 0, 0);
if (result != 0)
{
int stackSize = lua_gettop(L);
const char *errMsg = lua_tostring(L, -1);
char finalErrMsg[8192];
snprintf(finalErrMsg, sizeof(finalErrMsg), "callLuaPrint result: %d, stackSize: %d, error: %s\n", result, stackSize, errMsg);
luaL_error(L, finalErrMsg);
}
}
void callLuaPrintFmt(lua_State *L, const char *fmt, ...)
{
lua_getglobal(L, "print");
va_list argp;
va_start(argp, fmt);
lua_pushvfstring(L, fmt, argp);
va_end(argp);
int result = lua_pcall(L, 1, 0, 0);
if (result != 0)
{
int stackSize = lua_gettop(L);
const char *errMsg = lua_tostring(L, -1);
char finalErrMsg[8192];
snprintf(finalErrMsg, sizeof(finalErrMsg), "callLuaPrintFmt result: %d, stackSize: %d, error: %s\n", result, stackSize, errMsg);
luaL_error(L, finalErrMsg);
}
}
const char *callLuaTostring(lua_State *L, int idx)
{
lua_pushvalue(L, idx);
lua_getglobal(L, "tostring");
lua_insert(L, -2);
int result = lua_pcall(L, 1, 1, 0);
if (result != 0)
{
int stackSize = lua_gettop(L);
const char *errMsg = lua_tostring(L, -1);
char finalErrMsg[8192];
snprintf(finalErrMsg, sizeof(finalErrMsg), "callLuaTostring result: %d, stackSize: %d, error: %s\n", result, stackSize, errMsg);
luaL_error(L, finalErrMsg);
return "";
}
const char *msg = lua_tostring(L, -1); /* get result */
lua_pop(L, 1); /* pop result */
return msg;
}
void stackDump(lua_State *L)
{
if (!_enable)
{
return;
}
int top = lua_gettop(L);
callLuaPrintFmt(L, "<<<stackDump start, count %d", top);
int i;
for (i = 1; i <= top; i++)
{ /* repeat for each level */
int t = lua_type(L, i);
switch (t)
{
case LUA_TNIL: /* nil */
callLuaPrintFmt(L, "LUA_TNIL: nil");
break;
case LUA_TBOOLEAN: /* boolean */
callLuaPrintFmt(L, "LUA_TBOOLEAN: `%s`", lua_toboolean(L, i) ? "true" : "false");
break;
case LUA_TLIGHTUSERDATA: /* light userdata */
callLuaPrintFmt(L, "LUA_TLIGHTUSERDATA: @%p, tostring: %s", lua_touserdata(L, i), callLuaTostring(L, i));
break;
case LUA_TNUMBER: /* number */
callLuaPrintFmt(L, "LUA_TNUMBER: %g", lua_tonumber(L, i));
break;
case LUA_TSTRING: /* string */
callLuaPrintFmt(L, "LUA_TSTRING: \"%s\"", lua_tostring(L, i));
break;
case LUA_TTABLE: /* number */
{
char *dumpedTableStr = mallocDumpedTableStr(L, i);
callLuaPrintFmt(L, "LUA_TTABLE: tostring: %s\n%s", callLuaTostring(L, i), dumpedTableStr);
free(dumpedTableStr);
break;
}
case LUA_TFUNCTION: /* function */
callLuaPrintFmt(L, "LUA_TFUNCTION: @%p, tostring: %s", lua_touserdata(L, i), callLuaTostring(L, i));
break;
case LUA_TUSERDATA: /* userdata */
callLuaPrintFmt(L, "LUA_TUSERDATA: @%p, tostring: %s", lua_touserdata(L, i), callLuaTostring(L, i));
break;
case LUA_TTHREAD: /* thread */
callLuaPrintFmt(L, "LUA_TTHREAD: @%p, tostring: %s", lua_touserdata(L, i), callLuaTostring(L, i));
break;
default: /* other values */
// callLuaPrintFmt(L, "LUA_other_values(type): type: %s, tostring: %s", lua_typename(L, t), lua_tostring(L, i));
callLuaPrintFmt(L, "LUA_other_values(type): type: %s, tostring: %s", lua_typename(L, t), callLuaTostring(L, i));
break;
}
}
callLuaPrintFmt(L, "stackDump end>>>");
}
char *_malloc_dump_lua_table_internal(lua_State *L, int index, int indent);
char *_malloc_concatenate_strings_free_first(char *str1, const char *str2)
{
size_t len1 = strlen(str1);
size_t len2 = strlen(str2);
char *result = (char *)malloc(len1 + len2 + 1);
strcpy(result, str1);
strcat(result, str2);
free(str1);
return result;
}
char *_malloc_concatenate_spaced_strings_free_first(char *str1, const char *str2)
{
char *spacedStr1 = _malloc_concatenate_strings_free_first(str1, " ");
char *result = _malloc_concatenate_strings_free_first(spacedStr1, str2);
return result;
}
char *mallocDumpedTableStr(lua_State *L, int index)
{
lua_pushvalue(L, index);
if (lua_istable(L, -1))
{
char *result = _malloc_dump_lua_table_internal(L, lua_gettop(L), 0);
result = _malloc_concatenate_strings_free_first(result, "\n");
lua_pop(L, 1);
return result;
}
else
{
int t = lua_type(L, -1);
char *result = (char *)malloc(8192);
snprintf(result, sizeof(result), "getDumpedTableStr is not table, type: %s, tostring: %s\n", lua_typename(L, t), callLuaTostring(L, -1));
lua_pop(L, 1);
return result;
}
}
void dumpTable(lua_State *L, int index)
{
if (!_enable)
{
return;
}
lua_pushvalue(L, index);
if (lua_istable(L, -1))
{
char *result = _malloc_dump_lua_table_internal(L, lua_gettop(L), 0);
callLuaPrintFmt(L, "<<<dumpTable start \n%s\ndumpTable end>>>", result);
free(result);
}
else
{
int t = lua_type(L, -1);
callLuaPrintFmt(L, "<<<dumpTable start \ndump_lua_table is not table, type: %s, tostring: %s\ndumpTable end>>>", lua_typename(L, t), callLuaTostring(L, -1));
}
lua_pop(L, 1);
}
char *_malloc_dump_lua_table_internal(lua_State *L, int index, int indent)
{
if (indent > 6)
{
char *result = strdup("...\n");
return result;
}
char *result = strdup("{\n");
lua_pushnil(L); // Push a nil value to start the iteration
while (lua_next(L, index) != 0)
{
// Print indentation
for (int i = 0; i < indent + 1; i++)
{
result = _malloc_concatenate_strings_free_first(result, " ");
}
if (lua_isnumber(L, -2))
{
char key[8192];
snprintf(key, sizeof(key), "[%" PRId64 "] = ", lua_tointeger(L, -2));
result = _malloc_concatenate_spaced_strings_free_first(result, key);
}
else
{
const char *key = lua_tostring(L, -2);
result = _malloc_concatenate_spaced_strings_free_first(result, key ? key : "nil");
result = _malloc_concatenate_strings_free_first(result, " = ");
}
// Print the value
if (lua_istable(L, -1))
{
char *nestedTable = _malloc_dump_lua_table_internal(L, lua_gettop(L), indent + 1); // Recursively dump nested tables
result = _malloc_concatenate_strings_free_first(result, nestedTable);
free(nestedTable);
for (int i = 0; i < indent + 1; i++)
{
result = _malloc_concatenate_strings_free_first(result, " ");
}
result = _malloc_concatenate_strings_free_first(result, "\n");
}
else if (lua_isstring(L, -1))
{
const char *value = lua_tostring(L, -1);
result = _malloc_concatenate_spaced_strings_free_first(result, value ? value : "nil");
result = _malloc_concatenate_strings_free_first(result, "\n");
}
else if (lua_isboolean(L, -1))
{
result = _malloc_concatenate_strings_free_first(result, lua_toboolean(L, -1) ? "true\n" : "false\n");
}
else if (lua_isnumber(L, -1))
{
char value[8192];
snprintf(value, sizeof(value), "%g\n", lua_tonumber(L, -1));
result = _malloc_concatenate_strings_free_first(result, value);
}
else
{
const char *typeName = lua_typename(L, lua_type(L, -1));
result = _malloc_concatenate_spaced_strings_free_first(result, typeName ? typeName : "nil");
result = _malloc_concatenate_strings_free_first(result, "\n");
}
// Pop the value, but keep the key for the next iteration
lua_pop(L, 1);
}
for (int i = 0; i < indent; i++)
{
result = _malloc_concatenate_strings_free_first(result, " ");
}
result = _malloc_concatenate_strings_free_first(result, "}");
return result;
}
#ifdef __cplusplus
}
#endif
using UnityEngine;
using System.Collections;
using LuaInterface;
public static class StackDumper
{
private static readonly bool Enable = true;
static public void CallLuaPrint(LuaState L, string str)
{
if (!Enable)
{
return;
}
L.LuaGetGlobal("print");
L.LuaPushString(str);
int result = L.LuaPCall(1, 0, 0);
if (result != 0)
{
int stackSize = L.LuaGetTop();
string errMsg = L.LuaToString(-1);
string finalErrMsg = string.Format("CallLuaPrint# result: {0}, stackSize: {1}, error: {2}\n", result, stackSize, errMsg);
throw new LuaException(finalErrMsg);
}
}
static public string CallLuaTostring(LuaState L, int idx)
{
if (!Enable)
{
return "";
}
L.LuaPushValue(idx);
L.LuaGetGlobal("tostring");
L.LuaInsert(-2);
int result = L.LuaPCall(1, 1, 0);
if (result != 0)
{
int stackSize = L.LuaGetTop();
string errMsg = L.LuaToString(-1);
string finalErrMsg = string.Format("CallLuaTostring# result: {0}, stackSize: {1}, error: {2}\n", result, stackSize, errMsg);
throw new LuaException(finalErrMsg);
}
string msg = L.LuaToString(-1); /* get result */
L.LuaPop(1); /* pop result */
return msg;
}
static public void StackDump(LuaState L)
{
if (!Enable)
{
return;
}
int top = L.LuaGetTop();
CallLuaPrint(L, string.Format("<<<stackDump # start, count {0}", top));
for (int i = 1; i <= top; i++)
{ /* repeat for each level */
LuaTypes t = L.LuaType(i);
string msg;
switch (t)
{
case LuaTypes.LUA_TNIL: /* nil */
msg = string.Format("LUA_TNIL: nil");
break;
case LuaTypes.LUA_TBOOLEAN: /* boolean */
msg = string.Format("LUA_TBOOLEAN: `{0}`", L.LuaToBoolean(i) ? "true" : "false");
break;
case LuaTypes.LUA_TLIGHTUSERDATA: /* light userdata */
msg = string.Format("LUA_TLIGHTUSERDATA: @{0}, tostring: {1}", L.LuaToUserData(i), CallLuaTostring(L, i));
break;
case LuaTypes.LUA_TNUMBER: /* number */
msg = string.Format("LUA_TNUMBER: {0}", L.LuaToNumber(i));
break;
case LuaTypes.LUA_TSTRING: /* string */
msg = string.Format("LUA_TSTRING: \"{0}\"", L.LuaToString(i));
break;
case LuaTypes.LUA_TTABLE: /* table */
{
msg = string.Format("LUA_TTABLE: tostring: {0}\n{1}", CallLuaTostring(L, i), GetDumpedTableStr(L, i));
break;
}
case LuaTypes.LUA_TFUNCTION: /* function */
msg = string.Format("LUA_TFUNCTION: @{0}, tostring: {1}", L.LuaToUserData(i), CallLuaTostring(L, i));
break;
case LuaTypes.LUA_TUSERDATA: /* userdata */
msg = string.Format("LUA_TUSERDATA: @{0}, tostring: {1}", L.LuaToUserData(i), CallLuaTostring(L, i));
break;
case LuaTypes.LUA_TTHREAD: /* thread */
msg = string.Format("LUA_TTHREAD: @{0}, tostring: {1}", L.LuaToUserData(i), CallLuaTostring(L, i));
break;
case LuaTypes.LUA_TNONE: /* none */
msg = string.Format("LUA_TNONE: type: {0}, tostring: {1}", L.LuaTypeName(t), CallLuaTostring(L, i));
break;
default: /* other values */
msg = string.Format("LUA_other_values: type: {0}, tostring: {1}", L.LuaTypeName(t), CallLuaTostring(L, i));
break;
}
CallLuaPrint(L, msg);
}
CallLuaPrint(L, "stackDump # end>>>");
}
static public string GetDumpedTableStr(LuaState L, int index)
{
if (!Enable)
{
return "";
}
L.LuaPushValue(index);
if (L.lua_istable(-1))
{
string result = GetDumpLuaTableInternal(L, L.LuaGetTop(), 0);
result += "\n";
L.LuaPop(1);
return result;
}
else
{
LuaTypes t = L.LuaType(-1);
string result = string.Format("getDumpedTableStr # is not table, type: {0}, tostring: {1}\n", L.LuaTypeName(t), CallLuaTostring(L, -1));
L.LuaPop(1);
return result;
}
}
static public void DumpTable(LuaState L, int index)
{
if (!Enable)
{
return;
}
L.LuaPushValue(index);
if (L.lua_istable(-1))
{
string result = GetDumpLuaTableInternal(L, L.LuaGetTop(), 0);
CallLuaPrint(L, string.Format("<<<dumpTable start \n{0}\ndumpTable end>>>", result));
}
else
{
LuaTypes t = L.LuaType(-1);
CallLuaPrint(L, string.Format("<<<dumpTable start \ndump_lua_table is not table, type: {0}, tostring: {1}\ndumpTable end>>>", L.LuaTypeName(t), CallLuaTostring(L, -1)));
}
L.LuaPop(1);
}
static private string GetDumpLuaTableInternal(LuaState L, int index, int indent)
{
if (!Enable)
{
return "";
}
if (indent > 6)
{
return "...\n";
}
string result = "{\n";
L.LuaPushNil(); // Push a nil value to start the iteration
while (L.LuaNext(index))
{
// Print indentation
for (int i = 0; i < indent + 1; i++)
{
result += " ";
}
if (L.LuaIsNumber(-2))
{
string key = string.Format("[{0}] = ", L.LuaToInteger(-2));
result += key;
}
else
{
string key = L.LuaToString(-2);
result += key ?? "nil";
result += " = ";
}
// Print the value
if (L.lua_istable(-1))
{
string nestedTable = GetDumpLuaTableInternal(L, L.LuaGetTop(), indent + 1); // Recursively dump nested tables
result += nestedTable;
for (int i = 0; i < indent + 1; i++)
{
result += " ";
}
result += "\n";
}
else if (L.LuaIsString(-1))
{
string value = L.LuaToString(-1);
result += value ?? "nil";
result += "\n";
}
else if (L.lua_isboolean(-1))
{
result += L.LuaToBoolean(-1) ? "true\n" : "false\n";
}
else if (L.LuaIsNumber(-1))
{
double value = L.LuaToNumber(-1);
result += value.ToString() + "\n";
}
else
{
string typeName = L.LuaTypeName(L.LuaType(-1));
result += typeName ?? "nil";
result += "\n";
}
// Pop the value, but keep the key for the next iteration
L.LuaPop(1);
}
for (int i = 0; i < indent; i++)
{
result += " ";
}
result += "}";
return result;
}
}
#ifndef STACKDUMPER_H
#define STACKDUMPER_H
#ifdef __cplusplus
extern "C" {
#endif
#include "lua.h"
#include "lauxlib.h"
void callLuaPrint(lua_State *L, const char *str);
void callLuaPrintFmt(lua_State *L, const char *fmt, ...);
const char *callLuaTostring(lua_State *L, int idx);
void dumpTable(lua_State *L, int index);
char *mallocDumpedTableStr(lua_State *L, int index);
void stackDump(lua_State *L);
#ifdef __cplusplus
}
#endif
#endif //#ifndef STACKDUMPER_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment