{"id":48,"date":"2013-07-24T17:08:32","date_gmt":"2013-07-24T17:08:32","guid":{"rendered":"https:\/\/blogs.scummvm.org\/t0by\/?p=48"},"modified":"2022-05-24T17:09:56","modified_gmt":"2022-05-24T17:09:56","slug":"introducing-the-debugger","status":"publish","type":"post","link":"https:\/\/blogs.scummvm.org\/t0by\/2013\/07\/24\/introducing-the-debugger\/","title":{"rendered":"Introducing the debugger"},"content":{"rendered":"<p><a href=\"https:\/\/blogs.scummvm.org\/t0by\/wp-content\/uploads\/sites\/43\/2013\/07\/H96566k.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-50\" src=\"https:\/\/blogs.scummvm.org\/t0by\/wp-content\/uploads\/sites\/43\/2013\/07\/H96566k.jpg\" alt=\"\" width=\"740\" height=\"583\" srcset=\"https:\/\/blogs.scummvm.org\/t0by\/wp-content\/uploads\/sites\/43\/2013\/07\/H96566k.jpg 740w, https:\/\/blogs.scummvm.org\/t0by\/wp-content\/uploads\/sites\/43\/2013\/07\/H96566k-300x236.jpg 300w\" sizes=\"auto, (max-width: 740px) 100vw, 740px\" \/><\/a>I have been cooking up a little debugger for a while.<br \/>\nNow, writing a debugger for the Wintermute Engine is a bit weird, because things do work in a slightly peculiar fashion.<br \/>\nFor example, you can have no debugging symbols, because that would mean hacking the compiler, which is not part of the ScummVM package.<br \/>\nBut, even more interesting: you don\u2019t need any.<\/p>\n<p>Here\u2019s the deal: each script runs inside its own \u201cVM\u201d \u2013 let\u2019s call it that \u2013 with its iP, stack and execution unit.<br \/>\nThere is a scheduler that handles the various VMs, creates new ones (e.g. on event) and kills off those that have finished the execution of their scripts.<br \/>\nThe \u201cexecution unit\u201d is a method that gets called once per instruction, evaluates the instruction and advances the IP.<\/p>\n<p>Like this:<\/p>\n<pre>bool ScScript::executeInstruction() {\r\n    \/\/ ... \r\n    ScValue *op1; \r\n    ScValue *op2;\r\n    uint32 inst = getDWORD();\r\n    switch (inst) {\r\n \r\n    case II_DEF_VAR:\r\n        \/\/ ... \r\n    case II_DEF_GLOB_VAR:\r\n        \/\/ ... \r\n    case II_DEF_CONST_VAR: {\r\n        \/\/ ...\r\n    case II_RET:\r\n        \/\/ ...\r\n        \/\/ ...\r\n    case II_ADD:\r\n        op2 = _stack-&gt;pop();\r\n        op1 = _stack-&gt;pop();\r\n \r\n        if (op1-&gt;isNULL() || op2-&gt;isNULL()) {\r\n            _operand-&gt;setNULL();\r\n        } else if (op1-&gt;getType() == VAL_STRING || op2-&gt;getType() == VAL_STRING) {\r\n            char *tempStr = new char [strlen(op1-&gt;getString()) + strlen(op2-&gt;getString()) + 1];\r\n            strcpy(tempStr, op1-&gt;getString());\r\n            strcat(tempStr, op2-&gt;getString());\r\n            _operand-&gt;setString(tempStr);\r\n            delete[] tempStr;\r\n        } else if (op1-&gt;getType() == VAL_INT &amp;&amp; op2-&gt;getType() == VAL_INT) {\r\n            _operand-&gt;setInt(op1-&gt;getInt() + op2-&gt;getInt());\r\n        } else {\r\n            _operand-&gt;setFloat(op1-&gt;getFloat() + op2-&gt;getFloat());\r\n        }\r\n \r\n        _stack-&gt;push(_operand);\r\n \r\n        break;\r\n         \r\n    case II_SUB: {\r\n        \/\/ ...\r\n    }\r\n    case II_DBG_LINE: {\r\n        \/\/ ...\r\n    }\r\n    default:\r\n        _gameRef-&gt;LOG(0, \"Fatal: Invalid instruction %d ('%s', line %d, IP:0x%x)\\n\", inst, _filename, _currentLine, _iP - sizeof(uint32));\r\n        _state = SCRIPT_FINISHED;\r\n        ret = STATUS_FAILED;\r\n    } \r\n    return ret;\r\n}\r\n<\/pre>\n<p>So do we do?<br \/>\nWe put hooks in there, ideally.<\/p>\n<p>This way, each time an instruction is executed we can look around and see what\u2019s happening.<br \/>\nWe can implement watch, breakpoints and stuff.<br \/>\nOh, but then there\u2019s the problem of knowing where the hell we are first.<br \/>\nOkay \u2013 watching can easily implemented all the same, by keeping a watch list and keeping track of what has changed since last time, but breaking?<br \/>\nWe have no debug symbols, so how do we know WHERE we are?<br \/>\nInterestingly, the WME team has been so kind as to add a special instruction to the instruction set: II_DBG_LINE.<br \/>\nGuess what it does?<br \/>\nIt updates a counter, _line, in a way that it always contains the number of the current line.<\/p>\n<p>What we do then is to place our hooks in a way to monitor that number and act accordingly \u2013 e.g. we have a list of breakpoints, if the current line is in there, we break \u2013 that is, we open the debugger window like this:<\/p>\n<pre>case II_DBG_LINE: {\r\n    int newLine = getDWORD();\r\n    if (newLine != _currentLine) {\r\n        _currentLine = newLine;\r\n    }\r\n    for (int j = 0; j &lt; _engine-&gt;_breakpoints.size(); j++) {\r\n        if (_engine-&gt;_breakpoints[j]._line == _currentLine &amp;&amp;\r\n            !strcmp(_engine-&gt;_breakpoints[j]._filename.c_str(), _filename)) { \r\n                _engine-&gt;_breakpoints[j]._hits++;\r\n                _adapter-&gt;triggerBreakpoint(this); \r\n                \/\/ This one breaks execution and does housekeeping.\r\n                \/\/ We resume from here once user stops execution\r\n            }\r\n        }\r\n    }\r\n \r\n    if (1) { \r\n        if (_callStack-&gt;_sP &lt;= _step) { \/\/ This is set elsewhere, suffice saying that I may or may not want to enter a function past a certain call depth _adapter-&gt;triggerStep(this);\r\n        }\r\n    }\r\n \r\n    if (1) { \r\n             for (int i = 0; i &lt; _watchlist.size(); i++) {\r\n                \/\/ Etc\r\n            } \r\n        }\r\n    }\r\n    break;\r\n<\/pre>\n<p>Then, we wait for user input.<br \/>\nHow this is handled we\u2019ll see in the next post.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I have been cooking up a little debugger for a while. Now, writing a debugger for the Wintermute Engine is a bit weird, because things do work in a slightly peculiar fashion. For example, you can have no debugging symbols, because that would mean hacking the compiler, which is not part of the ScummVM package. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-48","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/blogs.scummvm.org\/t0by\/wp-json\/wp\/v2\/posts\/48","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.scummvm.org\/t0by\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.scummvm.org\/t0by\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.scummvm.org\/t0by\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.scummvm.org\/t0by\/wp-json\/wp\/v2\/comments?post=48"}],"version-history":[{"count":2,"href":"https:\/\/blogs.scummvm.org\/t0by\/wp-json\/wp\/v2\/posts\/48\/revisions"}],"predecessor-version":[{"id":51,"href":"https:\/\/blogs.scummvm.org\/t0by\/wp-json\/wp\/v2\/posts\/48\/revisions\/51"}],"wp:attachment":[{"href":"https:\/\/blogs.scummvm.org\/t0by\/wp-json\/wp\/v2\/media?parent=48"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.scummvm.org\/t0by\/wp-json\/wp\/v2\/categories?post=48"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.scummvm.org\/t0by\/wp-json\/wp\/v2\/tags?post=48"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}