LUADebuggerComponent.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "LUADebuggerComponent.h"
  9. #include "LUAEditorContextMessages.h"
  10. #include <AzCore/Interface/Interface.h>
  11. #include <AzCore/Serialization/SerializeContext.h>
  12. #include <AzCore/Serialization/EditContext.h>
  13. #include <AzCore/PlatformIncl.h>
  14. #include <AzCore/Math/Crc.h>
  15. #include <AzCore/std/parallel/mutex.h>
  16. #include <AzCore/std/parallel/lock.h>
  17. #include <AzCore/Component/ComponentApplicationBus.h>
  18. #include <AzCore/Script/ScriptContext.h>
  19. #include <AzFramework/Script/ScriptDebugMsgReflection.h>
  20. #include <AzFramework/Script/ScriptRemoteDebuggingConstants.h>
  21. #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
  22. namespace LUADebugger
  23. {
  24. // Utility functions
  25. // Returns true if a valid target was found, in which case the info is returned in targetInfo.
  26. bool GetDesiredTarget(AzFramework::RemoteToolsEndpointInfo& targetInfo)
  27. {
  28. // discover what target the user is currently connected to, if any?
  29. AzFramework::IRemoteTools* remoteTools = AzFramework::RemoteToolsInterface::Get();
  30. if (!remoteTools)
  31. {
  32. return false;
  33. }
  34. AzFramework::RemoteToolsEndpointInfo info = remoteTools->GetDesiredEndpoint(AzFramework::LuaToolsKey);
  35. if (!info.GetPersistentId())
  36. {
  37. AZ_TracePrintf("Debug", "The user has not chosen a target to connect to.\n");
  38. return false;
  39. }
  40. targetInfo = info;
  41. if (!targetInfo.IsValid() || !targetInfo.IsOnline())
  42. {
  43. AZ_TracePrintf("Debug", "The target is currently not in a state that would allow debugging code (offline or not debuggable)\n");
  44. return false;
  45. }
  46. return true;
  47. }
  48. Component::Component()
  49. {
  50. }
  51. Component::~Component()
  52. {
  53. }
  54. //////////////////////////////////////////////////////////////////////////
  55. // AZ::Component
  56. void Component::Init()
  57. {
  58. }
  59. void Component::Activate()
  60. {
  61. m_remoteTools = AzFramework::RemoteToolsInterface::Get();
  62. LUAEditorDebuggerMessages::Bus::Handler::BusConnect();
  63. AZ::SystemTickBus::Handler::BusConnect();
  64. }
  65. void Component::Deactivate()
  66. {
  67. AZ::SystemTickBus::Handler::BusDisconnect();
  68. LUAEditorDebuggerMessages::Bus::Handler::BusDisconnect();
  69. m_remoteTools = nullptr;
  70. }
  71. void Component::OnSystemTick()
  72. {
  73. if (m_remoteTools)
  74. {
  75. const AzFramework::ReceivedRemoteToolsMessages* messages = m_remoteTools->GetReceivedMessages(AzFramework::LuaToolsKey);
  76. if (messages)
  77. {
  78. for (const AzFramework::RemoteToolsMessagePointer& msg : *messages)
  79. {
  80. if (AzFramework::ScriptDebugAck* ack = azdynamic_cast<AzFramework::ScriptDebugAck*>(msg.get()))
  81. {
  82. if (ack->m_ackCode == AZ_CRC_CE("Ack"))
  83. {
  84. if (ack->m_request == AZ_CRC_CE("Continue") || ack->m_request == AZ_CRC_CE("StepIn") ||
  85. ack->m_request == AZ_CRC_CE("StepOut") || ack->m_request == AZ_CRC_CE("StepOver"))
  86. {
  87. LUAEditor::Context_DebuggerManagement::Bus::Broadcast(
  88. &LUAEditor::Context_DebuggerManagement::OnExecutionResumed);
  89. }
  90. else if (ack->m_request == AZ_CRC_CE("AttachDebugger"))
  91. {
  92. LUAEditor::Context_DebuggerManagement::Bus::Broadcast(
  93. &LUAEditor::Context_DebuggerManagement::OnDebuggerAttached);
  94. }
  95. else if (ack->m_request == AZ_CRC_CE("DetachDebugger"))
  96. {
  97. LUAEditor::Context_DebuggerManagement::Bus::Broadcast(
  98. &LUAEditor::Context_DebuggerManagement::OnDebuggerDetached);
  99. }
  100. }
  101. else if (ack->m_ackCode == AZ_CRC_CE("IllegalOperation"))
  102. {
  103. if (ack->m_request == AZ_CRC_CE("ExecuteScript"))
  104. {
  105. LUAEditor::Context_DebuggerManagement::Bus::Broadcast(
  106. &LUAEditor::Context_DebuggerManagement::OnExecuteScriptResult, false);
  107. }
  108. else if (ack->m_request == AZ_CRC_CE("AttachDebugger"))
  109. {
  110. LUAEditor::Context_DebuggerManagement::Bus::Broadcast(
  111. &LUAEditor::Context_DebuggerManagement::OnDebuggerRefused);
  112. }
  113. else
  114. {
  115. AZ_TracePrintf(
  116. "LUA Debug",
  117. "Debug Agent: Illegal operation 0x%x. Script context is in the wrong state.\n",
  118. ack->m_request);
  119. }
  120. }
  121. else if (ack->m_ackCode == AZ_CRC_CE("AccessDenied"))
  122. {
  123. AZ_TracePrintf("LUA Debug", "Debug Agent: Access denied 0x%x. Attach debugger first!\n", ack->m_request);
  124. LUAEditor::Context_DebuggerManagement::Bus::Broadcast(
  125. &LUAEditor::Context_DebuggerManagement::OnDebuggerDetached);
  126. }
  127. else if (ack->m_ackCode == AZ_CRC_CE("InvalidCmd"))
  128. {
  129. AZ_TracePrintf(
  130. "LUA Debug",
  131. "The remote script debug agent claims that we sent it an invalid request(0x%x)!\n",
  132. ack->m_request);
  133. }
  134. }
  135. else if (azrtti_istypeof<AzFramework::ScriptDebugAckBreakpoint*>(msg.get()))
  136. {
  137. AzFramework::ScriptDebugAckBreakpoint* ackBreakpoint =
  138. azdynamic_cast<AzFramework::ScriptDebugAckBreakpoint*>(msg.get());
  139. if (ackBreakpoint->m_id == AZ_CRC_CE("BreakpointHit"))
  140. {
  141. LUAEditor::Context_DebuggerManagement::Bus::Broadcast(
  142. &LUAEditor::Context_DebuggerManagement::OnBreakpointHit,
  143. ackBreakpoint->m_moduleName,
  144. ackBreakpoint->m_line);
  145. }
  146. else if (ackBreakpoint->m_id == AZ_CRC_CE("AddBreakpoint"))
  147. {
  148. LUAEditor::Context_DebuggerManagement::Bus::Broadcast(
  149. &LUAEditor::Context_DebuggerManagement::OnBreakpointAdded,
  150. ackBreakpoint->m_moduleName,
  151. ackBreakpoint->m_line);
  152. }
  153. else if (ackBreakpoint->m_id == AZ_CRC_CE("RemoveBreakpoint"))
  154. {
  155. LUAEditor::Context_DebuggerManagement::Bus::Broadcast(
  156. &LUAEditor::Context_DebuggerManagement::OnBreakpointRemoved,
  157. ackBreakpoint->m_moduleName,
  158. ackBreakpoint->m_line);
  159. }
  160. }
  161. else if (
  162. AzFramework::ScriptDebugAckExecute* ackExecute = azdynamic_cast<AzFramework::ScriptDebugAckExecute*>(msg.get()))
  163. {
  164. LUAEditor::Context_DebuggerManagement::Bus::Broadcast(
  165. &LUAEditor::Context_DebuggerManagement::OnExecuteScriptResult, ackExecute->m_result);
  166. }
  167. else if (azrtti_istypeof<AzFramework::ScriptDebugEnumLocalsResult*>(msg.get()))
  168. {
  169. AzFramework::ScriptDebugEnumLocalsResult* enumLocals =
  170. azdynamic_cast<AzFramework::ScriptDebugEnumLocalsResult*>(msg.get());
  171. LUAEditor::Context_DebuggerManagement::Bus::Broadcast(
  172. &LUAEditor::Context_DebuggerManagement::OnReceivedLocalVariables, enumLocals->m_names);
  173. }
  174. else if (azrtti_istypeof<AzFramework::ScriptDebugEnumContextsResult*>(msg.get()))
  175. {
  176. AzFramework::ScriptDebugEnumContextsResult* enumContexts =
  177. azdynamic_cast<AzFramework::ScriptDebugEnumContextsResult*>(msg.get());
  178. LUAEditor::Context_DebuggerManagement::Bus::Broadcast(
  179. &LUAEditor::Context_DebuggerManagement::OnReceivedAvailableContexts, enumContexts->m_names);
  180. }
  181. else if (azrtti_istypeof<AzFramework::ScriptDebugGetValueResult*>(msg.get()))
  182. {
  183. AzFramework::ScriptDebugGetValueResult* getValues =
  184. azdynamic_cast<AzFramework::ScriptDebugGetValueResult*>(msg.get());
  185. LUAEditor::Context_DebuggerManagement::Bus::Broadcast(
  186. &LUAEditor::Context_DebuggerManagement::OnReceivedValueState, getValues->m_value);
  187. }
  188. else if (azrtti_istypeof<AzFramework::ScriptDebugSetValueResult*>(msg.get()))
  189. {
  190. AzFramework::ScriptDebugSetValueResult* setValue =
  191. azdynamic_cast<AzFramework::ScriptDebugSetValueResult*>(msg.get());
  192. LUAEditor::Context_DebuggerManagement::Bus::Broadcast(
  193. &LUAEditor::Context_DebuggerManagement::OnSetValueResult, setValue->m_name, setValue->m_result);
  194. }
  195. else if (azrtti_istypeof<AzFramework::ScriptDebugCallStackResult*>(msg.get()))
  196. {
  197. AzFramework::ScriptDebugCallStackResult* callStackResult =
  198. azdynamic_cast<AzFramework::ScriptDebugCallStackResult*>(msg.get());
  199. AZStd::vector<AZStd::string> callstack;
  200. const char* c1 = callStackResult->m_callstack.c_str();
  201. for (const char* c2 = c1; *c2; ++c2)
  202. {
  203. if (*c2 == '\n')
  204. {
  205. callstack.emplace_back().assign(c1, c2);
  206. c1 = c2 + 1;
  207. }
  208. }
  209. callstack.emplace_back() = c1;
  210. LUAEditor::Context_DebuggerManagement::Bus::Broadcast(
  211. &LUAEditor::Context_DebuggerManagement::OnReceivedCallstack, callstack);
  212. }
  213. else if (azrtti_istypeof<AzFramework::ScriptDebugRegisteredGlobalsResult*>(msg.get()))
  214. {
  215. AzFramework::ScriptDebugRegisteredGlobalsResult* registeredGlobals =
  216. azdynamic_cast<AzFramework::ScriptDebugRegisteredGlobalsResult*>(msg.get());
  217. LUAEditor::Context_DebuggerManagement::Bus::Broadcast(
  218. &LUAEditor::Context_DebuggerManagement::OnReceivedRegisteredGlobals,
  219. registeredGlobals->m_methods,
  220. registeredGlobals->m_properties);
  221. }
  222. else if (azrtti_istypeof<AzFramework::ScriptDebugRegisteredClassesResult*>(msg.get()))
  223. {
  224. AzFramework::ScriptDebugRegisteredClassesResult* registeredClasses =
  225. azdynamic_cast<AzFramework::ScriptDebugRegisteredClassesResult*>(msg.get());
  226. LUAEditor::Context_DebuggerManagement::Bus::Broadcast(
  227. &LUAEditor::Context_DebuggerManagement::OnReceivedRegisteredClasses, registeredClasses->m_classes);
  228. }
  229. else if (azrtti_istypeof<AzFramework::ScriptDebugRegisteredEBusesResult*>(msg.get()))
  230. {
  231. AzFramework::ScriptDebugRegisteredEBusesResult* registeredEBuses =
  232. azdynamic_cast<AzFramework::ScriptDebugRegisteredEBusesResult*>(msg.get());
  233. LUAEditor::Context_DebuggerManagement::Bus::Broadcast(
  234. &LUAEditor::Context_DebuggerManagement::OnReceivedRegisteredEBuses, registeredEBuses->m_ebusList);
  235. }
  236. else
  237. {
  238. AZ_Assert(false, "We received a message of an unrecognized class type!");
  239. }
  240. }
  241. m_remoteTools->ClearReceivedMessages(AzFramework::LuaToolsKey);
  242. }
  243. }
  244. }
  245. void Component::EnumerateContexts()
  246. {
  247. AZ_TracePrintf("LUA Debug", "Component::EnumerateContexts()\n");
  248. AzFramework::RemoteToolsEndpointInfo targetInfo;
  249. if (m_remoteTools && GetDesiredTarget(targetInfo))
  250. {
  251. m_remoteTools->SendRemoteToolsMessage(targetInfo, AzFramework::ScriptDebugRequest(AZ_CRC_CE("EnumContexts")));
  252. }
  253. }
  254. void Component::AttachDebugger(const char* scriptContextName)
  255. {
  256. AZ_TracePrintf("LUA Debug", "Component::AttachDebugger( %s )\n", scriptContextName);
  257. AZ_Assert(scriptContextName, "You need to supply a valid script context name to attach to!");
  258. AzFramework::RemoteToolsEndpointInfo targetInfo;
  259. if (m_remoteTools && GetDesiredTarget(targetInfo))
  260. {
  261. m_remoteTools->SendRemoteToolsMessage(targetInfo, AzFramework::ScriptDebugRequest(AZ_CRC_CE("AttachDebugger"), scriptContextName));
  262. }
  263. }
  264. void Component::DetachDebugger()
  265. {
  266. AZ_TracePrintf("LUA Debug", "Component::DetachDebugger()\n");
  267. AzFramework::RemoteToolsEndpointInfo targetInfo;
  268. if (m_remoteTools && GetDesiredTarget(targetInfo))
  269. {
  270. m_remoteTools->SendRemoteToolsMessage(targetInfo, AzFramework::ScriptDebugRequest(AZ_CRC_CE("DetachDebugger")));
  271. }
  272. }
  273. void Component::EnumRegisteredClasses(const char* scriptContextName)
  274. {
  275. AzFramework::RemoteToolsEndpointInfo targetInfo;
  276. if (m_remoteTools && GetDesiredTarget(targetInfo))
  277. {
  278. m_remoteTools->SendRemoteToolsMessage(targetInfo, AzFramework::ScriptDebugRequest(AZ_CRC_CE("EnumRegisteredClasses"), scriptContextName));
  279. }
  280. }
  281. void Component::EnumRegisteredEBuses(const char* scriptContextName)
  282. {
  283. AzFramework::RemoteToolsEndpointInfo targetInfo;
  284. if (m_remoteTools && GetDesiredTarget(targetInfo))
  285. {
  286. m_remoteTools->SendRemoteToolsMessage(targetInfo, AzFramework::ScriptDebugRequest(AZ_CRC_CE("EnumRegisteredEBuses"), scriptContextName));
  287. }
  288. }
  289. void Component::EnumRegisteredGlobals(const char* scriptContextName)
  290. {
  291. AzFramework::RemoteToolsEndpointInfo targetInfo;
  292. if (m_remoteTools && GetDesiredTarget(targetInfo))
  293. {
  294. m_remoteTools->SendRemoteToolsMessage(targetInfo, AzFramework::ScriptDebugRequest(AZ_CRC_CE("EnumRegisteredGlobals"), scriptContextName));
  295. }
  296. }
  297. void Component::CreateBreakpoint(const AZStd::string& debugName, int lineNumber)
  298. {
  299. // register a breakpoint.
  300. // Debug name will be the full, absolute path, so convert it to the relative path
  301. AZStd::string relativePath = debugName;
  302. AzToolsFramework::AssetSystemRequestBus::Broadcast(
  303. &AzToolsFramework::AssetSystemRequestBus::Events::GetRelativeProductPathFromFullSourceOrProductPath, debugName, relativePath);
  304. relativePath = "@" + relativePath;
  305. AzFramework::RemoteToolsEndpointInfo targetInfo;
  306. if (m_remoteTools && GetDesiredTarget(targetInfo))
  307. {
  308. // local editors are never debuggable (they'd never have the debuggable flag) so if you get here you know its over the network
  309. // and its network id is targetID.
  310. m_remoteTools->SendRemoteToolsMessage(targetInfo, AzFramework::ScriptDebugBreakpointRequest(AZ_CRC_CE("AddBreakpoint"), relativePath.c_str(), static_cast<AZ::u32>(lineNumber)));
  311. }
  312. }
  313. void Component::RemoveBreakpoint(const AZStd::string& debugName, int lineNumber)
  314. {
  315. // remove a breakpoint.
  316. // Debug name will be the full, absolute path, so convert it to the relative path
  317. AZStd::string relativePath = debugName;
  318. AzToolsFramework::AssetSystemRequestBus::Broadcast(
  319. &AzToolsFramework::AssetSystemRequestBus::Events::GetRelativeProductPathFromFullSourceOrProductPath, debugName, relativePath);
  320. relativePath = "@" + relativePath;
  321. AzFramework::RemoteToolsEndpointInfo targetInfo;
  322. if (m_remoteTools && GetDesiredTarget(targetInfo))
  323. {
  324. // local editors are never debuggable (they'd never have the debuggable flag) so if you get here you know its over the network
  325. // and its network id is targetID.
  326. m_remoteTools->SendRemoteToolsMessage(targetInfo, AzFramework::ScriptDebugBreakpointRequest(AZ_CRC_CE("RemoveBreakpoint"), relativePath.c_str(), static_cast<AZ::u32>(lineNumber)));
  327. }
  328. }
  329. void Component::DebugRunStepOver()
  330. {
  331. AzFramework::RemoteToolsEndpointInfo targetInfo;
  332. if (m_remoteTools && GetDesiredTarget(targetInfo))
  333. {
  334. m_remoteTools->SendRemoteToolsMessage(targetInfo, AzFramework::ScriptDebugRequest(AZ_CRC_CE("StepOver")));
  335. }
  336. }
  337. void Component::DebugRunStepIn()
  338. {
  339. AzFramework::RemoteToolsEndpointInfo targetInfo;
  340. if (m_remoteTools && GetDesiredTarget(targetInfo))
  341. {
  342. m_remoteTools->SendRemoteToolsMessage(targetInfo, AzFramework::ScriptDebugRequest(AZ_CRC_CE("StepIn")));
  343. }
  344. }
  345. void Component::DebugRunStepOut()
  346. {
  347. AzFramework::RemoteToolsEndpointInfo targetInfo;
  348. if (m_remoteTools && GetDesiredTarget(targetInfo))
  349. {
  350. m_remoteTools->SendRemoteToolsMessage(targetInfo, AzFramework::ScriptDebugRequest(AZ_CRC_CE("StepOut")));
  351. }
  352. }
  353. void Component::DebugRunStop()
  354. {
  355. // Script context can't be stopped. What should we do here?
  356. }
  357. void Component::DebugRunContinue()
  358. {
  359. AzFramework::RemoteToolsEndpointInfo targetInfo;
  360. if (m_remoteTools && GetDesiredTarget(targetInfo))
  361. {
  362. m_remoteTools->SendRemoteToolsMessage(targetInfo, AzFramework::ScriptDebugRequest(AZ_CRC_CE("Continue")));
  363. }
  364. }
  365. void Component::EnumLocals()
  366. {
  367. AzFramework::RemoteToolsEndpointInfo targetInfo;
  368. if (m_remoteTools && GetDesiredTarget(targetInfo))
  369. {
  370. m_remoteTools->SendRemoteToolsMessage(targetInfo, AzFramework::ScriptDebugRequest(AZ_CRC_CE("EnumLocals")));
  371. }
  372. }
  373. void Component::GetValue(const AZStd::string& varName)
  374. {
  375. AzFramework::RemoteToolsEndpointInfo targetInfo;
  376. if (m_remoteTools && GetDesiredTarget(targetInfo))
  377. {
  378. m_remoteTools->SendRemoteToolsMessage(targetInfo, AzFramework::ScriptDebugRequest(AZ_CRC_CE("GetValue"), varName.c_str()));
  379. }
  380. }
  381. void Component::SetValue(const AZ::ScriptContextDebug::DebugValue& value)
  382. {
  383. (void)value;
  384. AzFramework::RemoteToolsEndpointInfo targetInfo;
  385. if (m_remoteTools && GetDesiredTarget(targetInfo))
  386. {
  387. AzFramework::ScriptDebugSetValue request;
  388. request.m_value = value;
  389. m_remoteTools->SendRemoteToolsMessage(targetInfo, request);
  390. }
  391. }
  392. void Component::GetCallstack()
  393. {
  394. AzFramework::RemoteToolsEndpointInfo targetInfo;
  395. if (m_remoteTools && GetDesiredTarget(targetInfo))
  396. {
  397. m_remoteTools->SendRemoteToolsMessage(targetInfo, AzFramework::ScriptDebugRequest(AZ_CRC_CE("GetCallstack")));
  398. }
  399. }
  400. void Component::DesiredTargetChanged(AZ::u32 newTargetID, AZ::u32 oldTargetID)
  401. {
  402. (void)newTargetID;
  403. (void)oldTargetID;
  404. }
  405. void Component::Reflect(AZ::ReflectContext* reflection)
  406. {
  407. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
  408. if (serializeContext)
  409. {
  410. serializeContext->Class<Component, AZ::Component>()
  411. ->Version(1)
  412. ;
  413. }
  414. }
  415. }