Debugger.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. /*
  2. Debugger.h
  3. Original code by Steve Gray.
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, without restriction.
  10. */
  11. #pragma once
  12. #ifndef CONFIG_DEBUGGER
  13. #define DEBUGGER_STACK_PUSH(...)
  14. #define DEBUGGER_STACK_POP()
  15. #else
  16. #ifndef Debugger_h
  17. #define Debugger_h
  18. #include <winsock2.h>
  19. #include "script_object.h"
  20. #define DEBUGGER_INITIAL_BUFFER_SIZE 2048
  21. #define DEBUGGER_XML_TAG "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
  22. #define DEBUGGER_XML_TAG_SIZE (_countof(DEBUGGER_XML_TAG)-1)
  23. #define DEBUGGER_LANG_NAME AHK_NAME
  24. // DBGp Error Codes
  25. #define DEBUGGER_E_OK 0
  26. #define DEBUGGER_E_PARSE_ERROR 1
  27. #define DEBUGGER_E_INVALID_OPTIONS 3
  28. #define DEBUGGER_E_UNIMPL_COMMAND 4
  29. #define DEBUGGER_E_COMMAND_UNAVAIL 5
  30. #define DEBUGGER_E_CAN_NOT_OPEN_FILE 100
  31. #define DEBUGGER_E_BREAKPOINT_TYPE 201 // Breakpoint type not supported.
  32. #define DEBUGGER_E_BREAKPOINT_INVALID 202 // Invalid line number or filename.
  33. #define DEBUGGER_E_BREAKPOINT_NO_CODE 203 // No code on breakpoint line.
  34. #define DEBUGGER_E_BREAKPOINT_STATE 204 // Invalid breakpoint state.
  35. #define DEBUGGER_E_BREAKPOINT_NOT_FOUND 205 // No such breakpoint.
  36. #define DEBUGGER_E_EVAL_FAIL 206
  37. #define DEBUGGER_E_UNKNOWN_PROPERTY 300
  38. #define DEBUGGER_E_INVALID_STACK_DEPTH 301
  39. #define DEBUGGER_E_INVALID_CONTEXT 302
  40. #define DEBUGGER_E_INTERNAL_ERROR 998 // Unrecoverable internal error, usually the result of a Winsock error.
  41. #define DEBUGGER_E_CONTINUE -1 // Internal code used by continuation commands.
  42. // Error messages: these are shown directly to the user, so are in the native string format.
  43. #define DEBUGGER_ERR_INTERNAL _T("An internal error has occurred in the debugger engine.")
  44. #define DEBUGGER_ERR_DISCONNECT_PROMPT _T("\nContinue running the script without the debugger?")
  45. #define DEBUGGER_ERR_FAILEDTOCONNECT _T("Failed to connect to an active debugger client.")
  46. // Buffer size required for a given XML message size, plus protocol overhead.
  47. // Format: data_length NULL xml_tag data NULL
  48. //#define DEBUGGER_XML_SIZE_REQUIRED(xml_size) (MAX_NUMBER_LENGTH + DEBUGGER_XML_TAG_SIZE + xml_size + 2)
  49. #define DEBUGGER_RESPONSE_OVERHEAD (MAX_INTEGER_LENGTH + DEBUGGER_XML_TAG_SIZE + 2)
  50. class Debugger;
  51. extern Debugger g_Debugger;
  52. // jackieku: modified to hold the buffer.
  53. extern CStringA g_DebuggerHost;
  54. extern CStringA g_DebuggerPort;
  55. extern LPCTSTR g_AutoExecuteThreadDesc;
  56. enum BreakpointTypeType {BT_Line, BT_Call, BT_Return, BT_Exception, BT_Conditional, BT_Watch};
  57. enum BreakpointStateType {BS_Disabled=0, BS_Enabled};
  58. class Breakpoint
  59. {
  60. public:
  61. int id;
  62. char type;
  63. char state;
  64. bool temporary;
  65. // Not yet supported: function, hit_count, hit_value, hit_condition, exception
  66. Breakpoint() : id(AllocateID()), type(BT_Line), state(BS_Enabled), temporary(false)
  67. {
  68. }
  69. static int AllocateID() { return ++sMaxId; }
  70. private:
  71. static int sMaxId; // Highest used breakpoint ID.
  72. };
  73. // Forward-declarations (this file is included in script.h before these are declared).
  74. class Line;
  75. class NativeFunc;
  76. struct UDFCallInfo;
  77. template<typename T, int S> struct ScriptItemList;
  78. typedef ScriptItemList<Var, VARLIST_INITIAL_SIZE> VarList;
  79. struct DbgStack
  80. {
  81. enum StackEntryType {SE_Thread, SE_BIF, SE_UDF};
  82. struct Entry
  83. {
  84. Line *line;
  85. union
  86. {
  87. LPCTSTR desc; // SE_Thread -- "auto-exec", hotkey/hotstring name, "timer", etc.
  88. NativeFunc *func; // SE_BIF
  89. UDFCallInfo *udf; // SE_UDF
  90. };
  91. StackEntryType type;
  92. LPCTSTR Name();
  93. };
  94. Entry *mBottom, *mTop, *mTopBound;
  95. size_t mSize; // i.e. capacity.
  96. DbgStack()
  97. {
  98. // We don't want to set the following too low since the stack would need to be reallocated,
  99. // but also don't want to set it too high since the average script mightn't recurse deeply;
  100. // if the stack size never approaches its maximum, there'll be wasted memory:
  101. mSize = 128;
  102. mBottom = (Entry *)malloc(mSize * sizeof(Entry));
  103. mTop = mBottom - 1; // ++mTop will be the first entry.
  104. mTopBound = mTop + mSize; // Topmost valid position.
  105. }
  106. int Depth()
  107. {
  108. return (int)(mTop + 1 - mBottom);
  109. }
  110. // noinline currently seems to have a slight effect on benchmarks.
  111. // Since it should be called very rarely, we don't want it inlined.
  112. void __declspec(noinline) Expand()
  113. {
  114. mSize *= 2;
  115. // To keep the design as simple as possible, assume the allocation will never fail.
  116. // These reallocations should be very rare: if size starts at 128 and doubles each
  117. // time, three expansions would bring it to 1024 entries, which is probably larger
  118. // than any script could need. (Generally the program's stack will run out before
  119. // script recursion gets anywhere near that deep.)
  120. Entry *new_bottom = (Entry *)realloc(mBottom, mSize * sizeof(Entry));
  121. // Recalculate top of stack.
  122. mTop = mTop - mBottom + new_bottom;
  123. mBottom = new_bottom; // After using its old value.
  124. // Pre-calculate upper bound to keep Push() simple.
  125. mTopBound = mBottom - 1 + mSize;
  126. }
  127. Entry *Push();
  128. void Pop();
  129. void Push(LPCTSTR aDesc);
  130. void Push(NativeFunc *aFunc);
  131. void Push(UDFCallInfo *aRecurse);
  132. void GetLocalVars(int aDepth, VarList *&aVars, VarList *&aStaticVars, VarBkp *&aBkp, VarBkp *&aBkpEnd);
  133. };
  134. #define DEBUGGER_STACK_PUSH(aWhat) g_Debugger.mStack.Push(aWhat);
  135. #define DEBUGGER_STACK_POP() g_Debugger.mStack.Pop();
  136. enum PropertyContextType {PC_Local=0, PC_Global};
  137. class Debugger
  138. {
  139. public:
  140. int Connect(const char *aAddress, const char *aPort);
  141. int Disconnect();
  142. void Exit(ExitReasons aExitReason, char *aCommandName=NULL); // Called when exiting AutoHotkey.
  143. inline bool IsConnected() { return mSocket != INVALID_SOCKET; }
  144. inline bool IsStepping() { return mInternalState >= DIS_StepInto; }
  145. inline bool IsAtBreak() { return mProcessingCommands; }
  146. inline bool HasStdErrHook() { return mStdErrMode != SR_Disabled; }
  147. inline bool HasStdOutHook() { return mStdOutMode != SR_Disabled; }
  148. inline bool BreakOnExceptionIsEnabled() { return mBreakOnException; }
  149. LPCTSTR WhatThrew();
  150. __declspec(noinline) // Avoiding inlining should reduce the code size of ExpandExpression(), which might help performance since this is only called when the debugger is connected.
  151. void PostExecFunctionCall(Line *aExpressionLine)
  152. {
  153. // If the debugger is stepping into/over/out from a function call, we want to
  154. // break at the line which called that function, since the next line to execute
  155. // might be a line in some other function (i.e. because the line which called
  156. // the function is "return func()" or calls another function after this one).
  157. if ((mInternalState == DIS_StepInto
  158. || ((mInternalState == DIS_StepOut || mInternalState == DIS_StepOver)
  159. // Always '<' since '<=' (for StepOver) shouldn't be possible,
  160. // since we just returned from a function call:
  161. && mStack.Depth() < mContinuationDepth))
  162. // The final check ensures we don't repeatedly break at a line containing
  163. // multiple built-in function calls; i.e. don't break unless some script
  164. // has been executed since we began evaluating aExpressionLine. Something
  165. // like "return recursivefunc()" should work if this is StepInto or StepOver
  166. // since mCurrLine would probably be the '}' of that function:
  167. && mCurrLine != aExpressionLine)
  168. PreExecLine(aExpressionLine);
  169. }
  170. // Code flow notification functions:
  171. int PreExecLine(Line *aLine); // Called before executing each line.
  172. bool PreThrow(ExprTokenType *aException);
  173. // Receive and process commands. Returns when a continuation command is received.
  174. int ProcessCommands(LPCSTR aBreakReason = nullptr);
  175. int Break(LPCSTR aReason = "ok");
  176. bool HasPendingCommand();
  177. // Streams
  178. int WriteStreamPacket(LPCTSTR aText, LPCSTR aType);
  179. bool OutputStdErr(LPCTSTR aText);
  180. bool OutputStdOut(LPCTSTR aText);
  181. #define DEBUGGER_COMMAND(cmd) int cmd(char **aArgV, int aArgCount, char *aTransactionId)
  182. //
  183. // Debugger commands.
  184. //
  185. DEBUGGER_COMMAND(status);
  186. DEBUGGER_COMMAND(feature_get);
  187. DEBUGGER_COMMAND(feature_set);
  188. DEBUGGER_COMMAND(run);
  189. DEBUGGER_COMMAND(step_into);
  190. DEBUGGER_COMMAND(step_over);
  191. DEBUGGER_COMMAND(step_out);
  192. DEBUGGER_COMMAND(_break);
  193. DEBUGGER_COMMAND(stop);
  194. DEBUGGER_COMMAND(detach);
  195. DEBUGGER_COMMAND(breakpoint_set);
  196. DEBUGGER_COMMAND(breakpoint_get);
  197. DEBUGGER_COMMAND(breakpoint_update);
  198. DEBUGGER_COMMAND(breakpoint_remove);
  199. DEBUGGER_COMMAND(breakpoint_list);
  200. DEBUGGER_COMMAND(stack_depth);
  201. DEBUGGER_COMMAND(stack_get);
  202. DEBUGGER_COMMAND(context_names);
  203. DEBUGGER_COMMAND(context_get);
  204. DEBUGGER_COMMAND(typemap_get);
  205. DEBUGGER_COMMAND(property_get);
  206. DEBUGGER_COMMAND(property_set);
  207. DEBUGGER_COMMAND(property_value);
  208. DEBUGGER_COMMAND(source);
  209. DEBUGGER_COMMAND(redirect_stdout);
  210. DEBUGGER_COMMAND(redirect_stderr);
  211. Debugger() {}
  212. // Stack - keeps track of threads and function calls.
  213. DbgStack mStack;
  214. friend struct DbgStack;
  215. private:
  216. SOCKET mSocket = INVALID_SOCKET;
  217. Line *mCurrLine = nullptr; // Similar to g_script.mCurrLine, but may be different when breaking post-function-call, before continuing expression evaluation.
  218. ExprTokenType *mThrownToken = nullptr; // The exception that triggered the current exception breakpoint.
  219. bool mBreakOnExceptionWasSet = false, mBreakOnExceptionIsTemporary = false, mBreakOnException = false; // Supports a single coverall breakpoint exception.
  220. int mBreakOnExceptionID = 0;
  221. class Buffer
  222. {
  223. public:
  224. int Write(char *aData, size_t aDataSize=-1);
  225. int WriteF(const char *aFormat, ...);
  226. int WriteEncodeBase64(const char *aData, size_t aDataSize, bool aSkipBufferSizeCheck = false);
  227. int Expand();
  228. int ExpandIfNecessary(size_t aRequiredSize);
  229. void Delete(size_t aDataSize);
  230. void Clear();
  231. Buffer() : mData(NULL), mDataSize(0), mDataUsed(0), mFailed(FALSE) {}
  232. char *mData;
  233. size_t mDataSize;
  234. size_t mDataUsed;
  235. BOOL mFailed;
  236. ~Buffer() {
  237. if (mData)
  238. free(mData);
  239. }
  240. private:
  241. int EstimateFileURILength(LPCTSTR aPath);
  242. void WriteFileURI(LPCWSTR aPath);
  243. } mCommandBuf, mResponseBuf;
  244. enum DebuggerInternalStateType {
  245. DIS_None = 0,
  246. DIS_Starting = DIS_None,
  247. DIS_Run,
  248. DIS_Break,
  249. DIS_StepInto,
  250. DIS_StepOver,
  251. DIS_StepOut
  252. } mInternalState = DIS_Starting;
  253. enum StreamRedirectType {
  254. SR_Disabled = 0,
  255. SR_Copy = 1,
  256. SR_Redirect = 2
  257. } mStdErrMode = SR_Disabled, mStdOutMode = SR_Disabled;
  258. int mContinuationDepth = 0; // Stack depth at last continuation command, for step_into/step_over.
  259. CStringA mContinuationTransactionId {""}; // transaction_id of last continuation command.
  260. int mMaxPropertyData = 1024, mMaxChildren = 1000, mMaxDepth = 1;
  261. HookType mDisabledHooks = 0;
  262. bool mProcessingCommands;
  263. enum PropertyType
  264. {
  265. PropNone = 0,
  266. PropVar,
  267. PropVarBkp,
  268. PropValue, // Read-only (ExprTokenType)
  269. PropEnum
  270. };
  271. struct PropertySource
  272. {
  273. PropertyType kind = PropNone;
  274. Var *var;
  275. VarBkp *bkp;
  276. ResultToken value;
  277. IObject *invokee = nullptr;
  278. PropertySource(LPTSTR aResultBuf)
  279. {
  280. value.InitResult(aResultBuf);
  281. }
  282. ~PropertySource()
  283. {
  284. value.Free();
  285. }
  286. };
  287. struct PropertyInfo : PropertySource
  288. {
  289. LPCSTR name;
  290. CStringA &fullname;
  291. LPSTR facet; // Initialised during writing.
  292. bool is_alias = false, is_builtin = false, is_static = false; // Facets.
  293. int page = 0, pagesize; // Attributes which are also parameters.
  294. int max_data; // Parameters.
  295. int max_depth;
  296. PropertyInfo(CStringA &aNameBuf, LPTSTR aResultBuf)
  297. : PropertySource(aResultBuf), fullname(aNameBuf) {}
  298. };
  299. struct PropertyWriter : public IDebugProperties
  300. {
  301. Debugger &mDbg;
  302. PropertyInfo &mProp;
  303. size_t mNameLength;
  304. int mDepth;
  305. int mError;
  306. PropertyWriter(Debugger &aDbg, PropertyInfo &aProp)
  307. : mDbg(aDbg)
  308. , mProp(aProp)
  309. , mNameLength(aProp.fullname.GetLength())
  310. , mDepth(0)
  311. , mError(0)
  312. {
  313. }
  314. void WriteProperty(LPCSTR aName, ExprTokenType &aValue);
  315. void WriteProperty(LPCWSTR aName, ExprTokenType &aValue);
  316. void WriteProperty(ExprTokenType &aKey, ExprTokenType &aValue);
  317. void WriteBaseProperty(IObject *aBase);
  318. void WriteDynamicProperty(LPTSTR aName);
  319. void WriteEnumItems(IObject *aEnumerable, int aStart, int aEnd);
  320. void _WriteProperty(ExprTokenType &aValue, IObject *aInvokee = nullptr);
  321. void BeginProperty(LPCSTR aName, LPCSTR aType, int aNumChildren, DebugCookie &aCookie);
  322. void EndProperty(DebugCookie aCookie);
  323. ExprTokenType &ThisToken() { return mProp.value; }
  324. };
  325. // Receive next command from debugger UI:
  326. int ReceiveCommand(int *aCommandSize=NULL);
  327. // Send XML response to debugger UI:
  328. int SendResponse(size_t aStartOffset = 0);
  329. int SendErrorResponse(char *aCommandName, char *aTransactionId, int aError=999, char *aExtraAttributes=NULL);
  330. int SendStandardResponse(char *aCommandName, char *aTransactionId);
  331. int SendContinuationResponse(LPCSTR aCommand = nullptr, LPCSTR aStatus = "break", LPCSTR aReason = "ok");
  332. int EnterBreakState(LPCSTR aReason = "ok");
  333. void ExitBreakState();
  334. int WriteBreakpointXml(Breakpoint *aBreakpoint, Line *aLine);
  335. int WriteExceptionBreakpointXml();
  336. Line *FindFirstLineForBreakpoint(int file_index, UINT line_no);
  337. void AppendPropertyName(CStringA &aNameBuf, size_t aParentNameLength, const char *aName);
  338. void AppendStringKey(CStringA &aNameBuf, size_t aParentNameLength, const char *aKey);
  339. int GetPropertyInfo(Var &aVar, PropertyInfo &aProp);
  340. int GetPropertyInfo(VarBkp &aBkp, PropertyInfo &aProp);
  341. int GetPropertyValue(Var &aVar, ResultToken &aValue);
  342. int GetPropertyValue(VarBkp &aBkp, ResultToken &aValue);
  343. int WritePropertyXml(PropertyInfo &aProp);
  344. int WritePropertyXml(PropertyInfo &aProp, LPTSTR aName);
  345. int WritePropertyObjectXml(PropertyInfo &aProp);
  346. int WritePropertyData(LPCTSTR aData, size_t aDataSize, int aMaxEncodedSize);
  347. int WritePropertyData(ExprTokenType &aValue, int aMaxEncodedSize);
  348. int WriteEnumItems(PropertyInfo &aProp);
  349. LPWSTR ParsePropertyKeyLiteral(LPWSTR aPtr, ExprTokenType &aKey);
  350. int ParsePropertyName(LPCSTR aFullName, int aDepth, int aVarScope, ExprTokenType *aSetValue
  351. , PropertySource &aResult);
  352. int ParsePropertyName(LPWSTR aNamePtr, int aDepth, int aVarScope, ExprTokenType *aSetValue
  353. , PropertySource &aResult);
  354. int property_get_or_value(char **aArgV, int aArgCount, char *aTransactionId, bool aIsPropertyGet);
  355. int redirect_std(char **aArgV, int aArgCount, char *aTransactionId, char *aCommandName);
  356. int run_step(char **aArgV, int aArgCount, char *aTransactionId, char *aCommandName, DebuggerInternalStateType aNewState);
  357. // Decode a file URI in-place.
  358. void DecodeURI(char *aUri);
  359. static const char *sBase64Chars;
  360. static size_t Base64Encode(char *aBuf, const char *aInput, size_t aInputSize = -1);
  361. static size_t Base64Decode(char *aBuf, const char *aInput, size_t aInputSize = -1);
  362. //typedef int (Debugger::*CommandFunc)(char **aArgV, int aArgCount, char *aTransactionId);
  363. typedef DEBUGGER_COMMAND((Debugger::*CommandFunc));
  364. struct CommandDef
  365. {
  366. const char *mName;
  367. CommandFunc mFunc;
  368. };
  369. static CommandDef sCommands[];
  370. // Debugger::ParseArgs
  371. //
  372. // Returns DEBUGGER_E_OK on success, or a DBGp error code otherwise.
  373. //
  374. // The Xdebug/DBGp documentation is very vague about command line rules,
  375. // so this function has no special treatment of quotes, backslash, etc.
  376. // There is currently no way to include a literal " -" in an arg as it
  377. // would be recognized as the beginning of the next arg.
  378. //
  379. int ParseArgs(char *aArgs, char **aArgV, int &aArgCount, char *&aTransactionId);
  380. // Caller must verify that aArg is within bounds:
  381. inline char *ArgValue(char **aArgV, int aArg) { return aArgV[aArg] + 1; }
  382. inline char ArgChar(char **aArgV, int aArg) { return *aArgV[aArg]; }
  383. // Fatal debugger error. Prompt user to terminate script or only disconnect debugger.
  384. static int FatalError(LPCTSTR aMessage = DEBUGGER_ERR_INTERNAL DEBUGGER_ERR_DISCONNECT_PROMPT);
  385. };
  386. constexpr auto SCRIPT_STACK_BUF_SIZE = 2048; // Character limit for Error().Stack and stack traces generated by the error dialog.
  387. void GetScriptStack(LPTSTR aBuf, int aBufSize, DbgStack::Entry *aTop = nullptr);
  388. #endif
  389. #endif