7 #define ENABLE_LUAIMPL_DEPENDENT 9 #ifdef ENABLE_LUAIMPL_DEPENDENT 26 static_assert(
sizeof(ExtraSpace) <= LUA_EXTRASPACE,
27 "lua extra space is not enough");
29 inline ExtraSpace &extra(lua_State *L)
31 void *extra = lua_getextraspace(L);
32 return *
static_cast<ExtraSpace *
>(extra);
35 #ifdef ENABLE_LUAIMPL_DEPENDENT 38 inline Proto *toproto(lua_State *L,
int i)
40 return getproto(L->top + (i));
44 void validLines(Proto *f, F callback)
47 for (
int pc = 0; pc < n; pc++) {
48 int line = getfuncline(f, pc);
51 for (
int i = 0; i < f->sizep; i++) {
52 validLines(f->p[i], callback);
57 void forAllValidLines(lua_State *L, F callback)
59 Proto *f = toproto(L, -1);
60 validLines(f, callback);
69 m_L(L), m_debugEnable(debugEnable), m_instLimit(instLimit), m_heapSize(heapSize)
83 info.chunkName = name;
86 std::string srcStr(src, size);
87 std::regex re(
"([^\\r\\n]*)\\r?\\n?");
89 std::sregex_iterator iter(srcStr.cbegin(), srcStr.cend(), re);
90 std::sregex_iterator end;
92 std::regex tabspace(
"\\t");
93 for (; iter != end; ++iter) {
95 auto result = std::regex_replace(iter->str(1), tabspace,
" ");
96 info.srcLines.emplace_back(result);
100 info.validLines.resize(info.srcLines.size(), 0);
101 if (!lua_isfunction(L, -1)) {
102 throw std::logic_error(
"stack top must be chunk");
104 #ifdef ENABLE_LUAIMPL_DEPENDENT 105 impldep::forAllValidLines(L, [&info](
int line) {
107 if (line >= 0 && static_cast<size_t>(line) < info.validLines.size()) {
108 info.validLines[line] = 1;
114 info.breakPoints.resize(info.srcLines.size(), 0);
116 m_debugInfo.emplace(name, std::move(info));
127 int mask = m_debugEnable ?
128 (LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT) :
130 lua_sethook(L, hookRaw, mask, m_instLimit);
133 m_debugState = autoBreak ?
134 DebugState::BREAK_LINE_ANY : DebugState::CONT;
135 int base = lua_gettop(L) - narg;
136 lua_pushcfunction(L, msghandler);
138 int ret = lua_pcall(L, narg, nret, base);
141 throw LuaError(
"Call global function failed", L);
150 int LuaDebugger::msghandler(lua_State *L)
152 const char *
msg = lua_tostring(L, 1);
153 luaL_traceback(L, L, msg, 1);
156 int origTop = lua_gettop(L);
158 if (dbg->m_debugEnable) {
166 lua_settop(L, origTop);
171 void LuaDebugger::hookRaw(lua_State *L, lua_Debug *ar)
173 extra(L).dbg->hook(ar);
177 void LuaDebugger::hook(lua_Debug *ar)
188 void LuaDebugger::hookNonDebug(lua_Debug *ar)
193 luaL_error(L,
"Instruction count exceeded");
215 const CmdEntry CmdList[] = {
218 L
"help [<cmd>...]", L
"コマンドリスト、コマンド詳細説明表示",
219 L
"コマンド一覧を表示します。引数にコマンド名を指定すると詳細な説明を表示します。" 223 L
"conf", L
"Lua バージョンとコンパイル時設定表示",
224 L
"Luaのバージョンとコンパイル時設定を表示します。" 228 L
"mem [-gc]", L
"メモリ使用状況表示",
229 L
"現在のメモリ使用量を表示します。-gc をつけると full GC を実行します。" 233 L
"bt", L
"バックトレース(コールスタック)表示",
234 L
"バックトレース(関数呼び出し履歴)を表示します。" 238 L
"fr [<frame_no>]", L
"カレントフレームの変更、カレントフレームの情報表示",
239 L
"コールスタックのフレーム番号を指定してそのフレームに移動します。" 240 "番号を指定しない場合、現在のフレームの詳細な情報を表示します。" 244 L
"eval <lua_script>", L
"Lua スクリプトの実行",
245 L
"引数を連結して Lua スクリプトとしてカレントフレーム上にいるかのように実行します。" 249 L
"watch [<lua_script> | -d <number>]", L
"ウォッチ式の登録",
250 L
"ブレークする度に自動で評価されるスクリプトを登録します。" 254 L
"src [-f <file>] [<line>] [-n <numlines>] [-all]", L
"ソースファイルの表示",
255 L
"デバッガがロードしているソースファイルの情報を表示します。" 256 L
"ブレーク可能ポイントや設置済みのブレークポイントも一緒に表示します。" 260 L
"c", L
"続行(continue)",
265 L
"s", L
"ステップイン(step)",
266 L
"新たな行に到達するまで実行します。関数呼び出しがあった場合、その中に入ります。" 270 L
"n", L
"ステップオーバー(next)",
271 L
"新たな行に到達するまで実行します。関数呼び出しがあった場合、それが終わるまで実行します。" 280 L
"bp [-d] [-f <filename>] [<line>...]", L
"ブレークポイントの設置/削除/表示",
281 L
"ブレークポイントを設定します。引数を指定しない場合、ブレークポイント一覧を表示します。" 286 const CmdEntry *searchCommandList(
const std::wstring &
cmd)
288 for (
const auto &entry : CmdList) {
289 if (cmd == entry.cmd) {
296 std::vector<std::wstring> readLine()
298 HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
303 BOOL ret = ::ReadConsole(hStdIn, buf,
sizeof(buf), &readSize,
nullptr);
305 std::wstring line(buf, readSize);
308 std::wregex re(L
"\\S+");
309 std::wsregex_iterator iter(line.cbegin(), line.cend(), re);
310 std::wsregex_iterator end;
311 std::vector<std::wstring> result;
312 for (; iter != end; ++iter)
314 result.emplace_back(iter->str());
319 inline int stoi_s(
const std::wstring &
str,
int base = 10)
322 int ret = std::stoi(str, &idx, base);
323 if (idx != str.size()) {
324 throw std::invalid_argument(
"invalid stoi argument");
332 void LuaDebugger::hookDebug(lua_Debug *ar)
340 case LUA_HOOKTAILCALL:
346 if (m_debugState == DebugState::BREAK_LINE_ANY) {
350 if (m_debugState == DebugState::BREAK_LINE_DEPTH0 && m_callDepth == 0) {
356 lua_getinfo(L,
"Sl", ar);
358 m_fileNameStr = ar->source;
359 const auto &kv = m_debugInfo.find(m_fileNameStr);
360 if (kv != m_debugInfo.end()) {
361 const auto &bps = kv->second.breakPoints;
362 int ind = ar->currentline - 1;
363 if (ind >= 0 && static_cast<size_t>(ind) < bps.size() && bps[ind]) {
371 luaL_error(L,
"Instruction count exceeded");
388 void LuaDebugger::cmdLoop()
393 std::vector<std::wstring> argv = readLine();
397 const CmdEntry *entry = searchCommandList(argv[0]);
398 if (entry ==
nullptr) {
399 debug::writef(L
"[LuaDbg] Command not found: %s\n", argv[0].c_str());
402 argv.erase(argv.begin());
403 if ((this->*(entry->exec))(entry->usage, argv)) {
409 catch (error::Win32Error &) {
420 void LuaDebugger::summaryOnBreak(lua_Debug *ar)
423 lua_getinfo(L,
"nSltu", ar);
424 const char *name = (ar->name ==
nullptr) ?
"?" : ar->name;
426 name, ar->what, ar->source, ar->currentline);
427 printSrcLines(ar->source, ar->currentline, DefSrcLines, ar->currentline);
430 void LuaDebugger::printSrcLines(
const std::string &name,
431 int line,
int range,
int execLine)
434 const auto &kv = m_debugInfo.find(name);
435 if (kv != m_debugInfo.cend()) {
436 const ChunkDebugInfo &info = kv->second;
439 int uh = (range - 1) / 2;
440 int dh = range - uh - 1;
442 beg = (beg < 0) ? 0 : beg;
444 int ssize =
static_cast<int>(info.srcLines.size());
445 end = (end >= ssize) ? ssize - 1 : end;
446 for (
int i = beg; i <= end; i++) {
447 char execMark = (i == execLine) ?
'>' :
' ';
448 char bpMark = (info.validLines[i]) ?
'o' :
' ';
449 bpMark = (info.breakPoints[i]) ?
'*' : bpMark;
451 info.srcLines.at(i).substr(0, 69).c_str());
459 void LuaDebugger::printLocalAndUpvalue(lua_Debug *ar,
int maxDepth,
bool skipNoName)
464 const char *name =
nullptr;
466 while ((name = lua_getlocal(L, ar, n)) !=
nullptr) {
467 if (!skipNoName || name[0] !=
'(') {
480 lua_getinfo(L,
"f", ar);
482 const char *name =
nullptr;
484 while ((name = lua_getupvalue(L, -1, n)) !=
nullptr) {
500 int evalIndex(lua_State *L)
504 ASSERT(lua_istable(L, lua_upvalueindex(1)));
505 ASSERT(lua_islightuserdata(L, lua_upvalueindex(2)));
506 auto *ar =
static_cast<lua_Debug *
>(
507 lua_touserdata(L, lua_upvalueindex(2)));
510 if (lua_isstring(L, 2)) {
511 const char *key = lua_tostring(L, 2);
515 const char *name =
nullptr;
516 while ((name = lua_getlocal(L, ar, n)) !=
nullptr) {
517 if (name[0] !=
'(' && std::strcmp(key, name) == 0) {
528 lua_getinfo(L,
"f", ar);
530 const char *name =
nullptr;
531 while ((name = lua_getupvalue(L, -1, n)) !=
nullptr) {
532 if (std::strcmp(key, name) == 0) {
545 lua_gettable(L, lua_upvalueindex(1));
549 int evalNewIndex(lua_State *L)
553 ASSERT(lua_istable(L, lua_upvalueindex(1)));
554 ASSERT(lua_islightuserdata(L, lua_upvalueindex(2)));
555 auto *ar =
static_cast<lua_Debug *
>(
556 lua_touserdata(L, lua_upvalueindex(2)));
559 if (lua_isstring(L, 2)) {
560 const char *key = lua_tostring(L, 2);
564 const char *name =
nullptr;
565 while ((name = lua_getlocal(L, ar, n)) !=
nullptr) {
566 if (name[0] !=
'(' && std::strcmp(key, name) == 0) {
568 lua_setlocal(L, ar, n);
578 lua_getinfo(L,
"f", ar);
580 const char *name =
nullptr;
581 while ((name = lua_getupvalue(L, -1, n)) !=
nullptr) {
582 if (std::strcmp(key, name) == 0) {
584 lua_setlocal(L, ar, n);
597 lua_settable(L, lua_upvalueindex(1));
601 int evalPairs(lua_State *L)
606 ASSERT(lua_istable(L, lua_upvalueindex(1)));
607 ASSERT(lua_islightuserdata(L, lua_upvalueindex(2)));
608 auto *ar =
static_cast<lua_Debug *
>(
609 lua_touserdata(L, lua_upvalueindex(2)));
612 lua_getglobal(L,
"pairs");
613 lua_pushvalue(L, lua_upvalueindex(1));
619 void LuaDebugger::pushLocalEnv(lua_Debug *ar,
int frameNo)
623 int ret = lua_getstack(L, frameNo, ar);
625 ret = lua_getinfo(L,
"nSltu", ar);
630 int tabind = lua_gettop(L);
635 lua_pushliteral(L,
"__metatable");
636 lua_pushliteral(L,
"read only table");
639 lua_pushliteral(L,
"__index");
641 lua_getglobal(L,
"_G");
642 ASSERT(lua_istable(L, -1));
644 lua_pushlightuserdata(L, ar);
645 lua_pushcclosure(L, evalIndex, 2);
648 lua_pushliteral(L,
"__newindex");
650 lua_getglobal(L,
"_G");
652 lua_pushlightuserdata(L, ar);
653 lua_pushcclosure(L, evalNewIndex, 2);
656 lua_pushliteral(L,
"__pairs");
658 lua_getglobal(L,
"_G");
660 lua_pushlightuserdata(L, ar);
661 lua_pushcclosure(L, evalPairs, 2);
664 lua_setmetatable(L, -2);
667 void LuaDebugger::printEval(
const std::string &expr)
673 std::string addret(
"return ");
676 int ret = luaL_loadstring(L, addret.c_str());
679 ret = luaL_loadstring(L, expr.c_str());
681 throw LuaError(
"load failed", L);
685 lua_Debug ar = { 0 };
687 pushLocalEnv(&ar, m_currentFrame);
688 const char * uvName = lua_setupvalue(L, -2, 1);
689 ASSERT(std::strcmp(uvName,
"_ENV") == 0);
692 int retBase = lua_gettop(L);
693 ret = lua_pcall(L, 0, LUA_MULTRET, 0);
695 throw LuaError(
"pcall failed", L);
698 int retLast = lua_gettop(L);
699 if (retBase > retLast) {
703 for (
int i = retBase; i <= retLast; i++) {
709 lua_settop(L, retBase);
711 catch (
const LuaError &ex) {
716 void LuaDebugger::printWatchList()
718 if (m_watchList.size() != 0) {
722 for (
const auto &expr : m_watchList) {
735 for (
const auto &entry : CmdList) {
740 for (
const auto &cmdstr : args) {
741 const CmdEntry *entry = searchCommandList(cmdstr);
742 if (entry ==
nullptr) {
743 debug::writef(L
"[LuaDbg] Command not found: %s", cmdstr.c_str());
747 entry->brief, entry->usage, entry->description);
760 static_cast<long long>(LUA_MININTEGER),
761 static_cast<long long>(LUA_MAXINTEGER));
763 static_cast<long long>(LUA_MININTEGER),
764 static_cast<long long>(LUA_MAXINTEGER));
767 static_cast<long double>(std::numeric_limits<lua_Number>::min()),
768 static_cast<long double>(std::numeric_limits<lua_Number>::max()));
770 std::numeric_limits<lua_Number>::digits10);
772 std::numeric_limits<lua_Number>::min_exponent10,
773 std::numeric_limits<lua_Number>::max_exponent10);
782 for (
const auto &
str : args) {
793 lua_gc(L, LUA_GCCOLLECT, 0);
796 int kb = lua_gc(L, LUA_GCCOUNT, 0);
797 int b = kb * 1024 + lua_gc(L, LUA_GCCOUNTB, 0);
798 debug::writef(L
"%.3f / %.1f KiB", b / 1024.0, m_heapSize / 1024.0);
807 lua_Debug ar = { 0 };
808 while (lua_getstack(L, lv, &ar)) {
809 lua_getinfo(L,
"nSltu", &ar);
810 const char *name = (ar.name ==
nullptr) ?
"?" : ar.name;
812 name, ar.what, ar.source, ar.currentline);
820 int lv = m_currentFrame;
822 if (args.size() == 1) {
823 lv = stoi_s(args[0], 10);
825 else if (args.size() >= 2) {
826 throw std::invalid_argument(
"invalid argument");
835 lua_Debug ar = { 0 };
836 if (lua_getstack(L, lv, &ar)) {
837 lua_getinfo(L,
"nSltu", &ar);
838 const char *name = (ar.name ==
nullptr) ?
"?" : ar.name;
840 name, ar.what, ar.source, ar.currentline);
841 printSrcLines(ar.source, ar.currentline, DefSrcLines, ar.currentline);
842 printLocalAndUpvalue(&ar, 0,
true);
856 lua_Debug ar = { 0 };
857 if (!lua_getstack(L, m_currentFrame, &ar)) {
861 lua_getinfo(L,
"Sl", &ar);
864 std::string fileName = ar.source;
865 int line = ar.currentline;
866 int num = DefSrcLines;
869 for (
size_t i = 0; i < args.size(); i++) {
870 if (args[i] == L
"-f") {
874 else if (args[i] == L
"-n") {
876 num = stoi_s(args.at(i));
878 else if (args[i] == L
"-all") {
882 line = stoi_s(args[i]);
891 num = all ? 0x00ffffff : num;
892 printSrcLines(fileName, line, num);
907 for (
const auto &
str : args) {
913 printEval(
src.get());
921 std::vector<int> delInd;
922 std::string addScript;
927 else if (args[0] == L
"-d") {
928 for (
size_t i = 0; i < args.size(); i++) {
929 delInd.push_back(stoi_s(args[i]));
933 for (
size_t i = 0; i < args.size(); i++) {
944 std::sort(delInd.begin(), delInd.end(), std::greater<int>());
945 for (
int ind : delInd) {
946 if (ind < 0 || static_cast<size_t>(ind) >= m_watchList.size()) {
950 m_watchList.erase(m_watchList.begin() + ind);
953 if (!addScript.empty()) {
954 m_watchList.emplace_back(addScript);
964 m_debugState = DebugState::CONT;
971 m_debugState = DebugState::BREAK_LINE_ANY;
979 m_debugState = DebugState::BREAK_LINE_DEPTH0;
987 m_debugState = DebugState::BREAK_LINE_DEPTH0;
994 std::string fileName;
995 std::vector<int> lines;
998 lua_Debug ar = { 0 };
999 if (lua_getstack(L, m_currentFrame, &ar)) {
1000 lua_getinfo(L,
"S", &ar);
1001 fileName = ar.source;
1006 for (
size_t i = 0; i < args.size(); i++) {
1007 if (args[i] == L
"-d") {
1010 else if (args[i] == L
"-f") {
1015 lines.push_back(stoi_s(args[i], 10));
1024 for (
int line : lines) {
1025 auto kv = m_debugInfo.find(fileName);
1026 if (kv == m_debugInfo.cend()) {
1027 debug::writef(
"Error: Debug info not found: \"%s\"", fileName.c_str());
1030 ChunkDebugInfo &info = kv->second;
1031 if (line < 1 || static_cast<size_t>(line) > info.breakPoints.size()) {
1032 debug::writef(
"Error: %s:%d is out of range", fileName.c_str(), line);
1035 info.breakPoints[line - 1] = del ? 0 : 1;
1039 for (
const auto &kv : m_debugInfo) {
1040 const ChunkDebugInfo &info = kv.second;
1043 for (
size_t i = 0; i < info.breakPoints.size(); i++) {
1044 if (info.breakPoints[i]) {
bool c(const wchar_t *usage, const std::vector< std::wstring > &args)
std::unique_ptr< char[]> wc2utf8(const wchar_t *in)
Wide char to UTF-8.
void write(const wchar_t *str, bool newline) noexcept
Write debug string.
lua_State * getLuaState() const
Get Lua state.
bool fr(const wchar_t *usage, const std::vector< std::wstring > &args)
const wchar_t * description
void writef(const wchar_t *fmt,...) noexcept
Write debug message using format string like printf.
bool out(const wchar_t *usage, const std::vector< std::wstring > &args)
bool help(const wchar_t *usage, const std::vector< std::wstring > &args)
bool src(const wchar_t *usage, const std::vector< std::wstring > &args)
#define ASSERT(x)
Assertion which uses debug framework.
void writeLine(const wchar_t *str=L"") noexcept
Write debug string and new line.
void loadDebugInfo(const char *name, const char *src, size_t size)
Load debug info from chunk name and source string.
bool watch(const wchar_t *usage, const std::vector< std::wstring > &args)
bool conf(const wchar_t *usage, const std::vector< std::wstring > &args)
char msg[LINE_DATA_SIZE-sizeof(LARGE_INTEGER)-sizeof(uint32_t)]
bool bp(const wchar_t *usage, const std::vector< std::wstring > &args)
bool s(const wchar_t *usage, const std::vector< std::wstring > &args)
LuaDebugger(lua_State *L, bool debugEnable, int instLimit, size_t heapSize)
Constructor.
bool mem(const wchar_t *usage, const std::vector< std::wstring > &args)
void checkWin32Result(bool cond, const std::string &msg)
void writef_nonl(const wchar_t *fmt,...) noexcept
Write debug message using format string like printf. (No new line)
bool(LuaDebugger::* exec)(const wchar_t *usage, const std::vector< std::wstring > &argv)
bool eval(const wchar_t *usage, const std::vector< std::wstring > &args)
void pcall(int narg, int nret, bool autoBreak)
Prepare and call lua_pcall().
bool bt(const wchar_t *usage, const std::vector< std::wstring > &args)
bool n(const wchar_t *usage, const std::vector< std::wstring > &args)
std::vector< std::string > luaValueToStrList(lua_State *L, int ind, int maxDepth)